aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authormaximius <none@none>2009-10-17 15:51:44 -0700
committermaximius <none@none>2009-10-17 15:51:44 -0700
commite585187b248f48b3c6e9247b49fa07c6565d65e5 (patch)
tree637c5b7ddacf41040bef4ea4f75a97da64c6a9bc /src
parent26b5e033ffde3d161382fc9addbfa99738379641 (diff)
*Backed out changeset 3be01fb200a5
--HG-- branch : trunk
Diffstat (limited to 'src')
-rw-r--r--src/bindings/scripts/ScriptMgr.cpp113
-rw-r--r--src/bindings/scripts/ScriptMgr.h13
-rw-r--r--src/bindings/scripts/base/escort_ai.cpp88
-rw-r--r--src/bindings/scripts/base/escort_ai.h29
-rw-r--r--src/bindings/scripts/base/follower_ai.cpp69
-rw-r--r--src/bindings/scripts/base/follower_ai.h21
-rw-r--r--src/bindings/scripts/base/guard_ai.cpp31
-rw-r--r--src/bindings/scripts/base/guard_ai.h13
-rw-r--r--src/bindings/scripts/base/simple_ai.cpp42
-rw-r--r--src/bindings/scripts/base/simple_ai.h17
-rw-r--r--src/bindings/scripts/include/precompiled.cpp1
-rw-r--r--src/bindings/scripts/include/precompiled.h4
-rw-r--r--src/bindings/scripts/include/sc_creature.cpp147
-rw-r--r--src/bindings/scripts/include/sc_creature.h63
-rw-r--r--src/bindings/scripts/include/sc_gossip.h24
-rw-r--r--src/bindings/scripts/include/sc_instance.h5
-rw-r--r--src/bindings/scripts/scripts/custom/npc_acherus_taxi.cpp11
-rw-r--r--src/bindings/scripts/scripts/custom/npc_wyrmresttempel_taxi.cpp26
-rw-r--r--src/bindings/scripts/scripts/custom/on_events.cpp22
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/alterac_mountains.cpp4
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/arathi_highlands.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp156
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp12
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp12
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp34
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/def_blackrock_depths.h7
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp48
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp24
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp12
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp42
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp30
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp21
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp36
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp47
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp2
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/blasted_lands.cpp25
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/boss_kruul.cpp25
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/burning_steppes.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/deadmines/deadmines.cpp29
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/deadmines/def_deadmines.h3
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/dun_morogh.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/duskwood.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/eastern_plaguelands.cpp33
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/elwynn_forest.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/eversong_woods.cpp135
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/ghostlands.cpp40
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/hinterlands.cpp57
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/ironforge.cpp11
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/isle_of_queldanas.cpp25
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_curator.cpp32
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp26
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp41
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp164
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp54
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp66
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp120
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp97
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp82
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp277
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/def_karazhan.h7
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp38
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/karazhan/karazhan.cpp100
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/loch_modan.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp119
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp247
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp67
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp41
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/def_magisters_terrace.h7
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp29
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp32
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_garr.cpp25
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp30
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp23
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp49
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp37
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/def_molten_core.h2
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp43
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/molten_core/molten_core.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp149
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp86
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp233
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp20
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp91
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp28
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp20
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp12
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp19
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp69
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp14
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/def_scarlet_monastery.h4
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp22
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp22
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp8
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp20
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp34
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/def_scholomance.h2
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/searing_gorge.cpp30
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/def_shadowfang_keep.h2
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp33
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp21
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/silvermoon_city.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/silverpine_forest.cpp34
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stormwind_city.cpp48
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stranglethorn_vale.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp32
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp32
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp20
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp15
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp19
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp18
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp12
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/def_stratholme.h5
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp56
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/stratholme/stratholme.cpp49
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/def_sunken_temple.h3
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp9
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp41
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp91
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp60
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp101
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp201
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp108
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/def_sunwell_plateau.h5
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp36
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp9
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/tirisfal_glades.cpp36
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp88
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp19
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp57
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/uldaman/uldaman.cpp27
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/undercity.cpp42
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/western_plaguelands.cpp29
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/westfall.cpp41
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/wetlands.cpp33
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp67
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp55
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp165
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp122
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp64
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp89
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/def_zulaman.h3
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp39
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulaman/zulaman.cpp28
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp51
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp16
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp34
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp17
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp49
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp45
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp50
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp43
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp28
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp90
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp29
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp13
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/def_zulgurub.h5
-rw-r--r--src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp21
-rw-r--r--src/bindings/scripts/scripts/examples/example_creature.cpp35
-rw-r--r--src/bindings/scripts/scripts/examples/example_escort.cpp28
-rw-r--r--src/bindings/scripts/scripts/examples/example_gossip_codebox.cpp15
-rw-r--r--src/bindings/scripts/scripts/examples/example_misc.cpp10
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ashenvale.cpp42
-rw-r--r--src/bindings/scripts/scripts/kalimdor/azshara.cpp70
-rw-r--r--src/bindings/scripts/scripts/kalimdor/azuremyst_isle.cpp117
-rw-r--r--src/bindings/scripts/scripts/kalimdor/blackfathom_depths/def_blackfathom_deeps.h4
-rw-r--r--src/bindings/scripts/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp22
-rw-r--r--src/bindings/scripts/scripts/kalimdor/bloodmyst_isle.cpp20
-rw-r--r--src/bindings/scripts/scripts/kalimdor/boss_azuregos.cpp20
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp14
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp16
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp27
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp20
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/def_culling_of_stratholme.h1
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp3
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp21
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp22
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp23
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp81
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/def_dark_portal.h7
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp62
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp42
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp99
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp43
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp24
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp21
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/def_hyjal.h4
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp37
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp157
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h37
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp205
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h10
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp47
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp22
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp23
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp29
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h2
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp31
-rw-r--r--src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp83
-rw-r--r--src/bindings/scripts/scripts/kalimdor/darkshore.cpp67
-rw-r--r--src/bindings/scripts/scripts/kalimdor/desolace.cpp30
-rw-r--r--src/bindings/scripts/scripts/kalimdor/dustwallow_marsh.cpp65
-rw-r--r--src/bindings/scripts/scripts/kalimdor/felwood.cpp13
-rw-r--r--src/bindings/scripts/scripts/kalimdor/feralas.cpp31
-rw-r--r--src/bindings/scripts/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp14
-rw-r--r--src/bindings/scripts/scripts/kalimdor/maraudon/boss_landslide.cpp13
-rw-r--r--src/bindings/scripts/scripts/kalimdor/maraudon/boss_noxxion.cpp16
-rw-r--r--src/bindings/scripts/scripts/kalimdor/maraudon/boss_princess_theradras.cpp15
-rw-r--r--src/bindings/scripts/scripts/kalimdor/moonglade.cpp59
-rw-r--r--src/bindings/scripts/scripts/kalimdor/mulgore.cpp43
-rw-r--r--src/bindings/scripts/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp55
-rw-r--r--src/bindings/scripts/scripts/kalimdor/orgrimmar.cpp42
-rw-r--r--src/bindings/scripts/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp19
-rw-r--r--src/bindings/scripts/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp14
-rw-r--r--src/bindings/scripts/scripts/kalimdor/razorfen_kraul/def_razorfen_kraul.h4
-rw-r--r--src/bindings/scripts/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp18
-rw-r--r--src/bindings/scripts/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp27
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp15
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp3
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp13
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp17
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp6
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp7
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp2
-rw-r--r--src/bindings/scripts/scripts/kalimdor/silithus.cpp118
-rw-r--r--src/bindings/scripts/scripts/kalimdor/stonetalon_mountains.cpp29
-rw-r--r--src/bindings/scripts/scripts/kalimdor/tanaris.cpp101
-rw-r--r--src/bindings/scripts/scripts/kalimdor/teldrassil.cpp19
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp57
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp271
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp21
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp21
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp24
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp45
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp41
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp85
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp4
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h3
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp26
-rw-r--r--src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp43
-rw-r--r--src/bindings/scripts/scripts/kalimdor/the_barrens.cpp104
-rw-r--r--src/bindings/scripts/scripts/kalimdor/thousand_needles.cpp68
-rw-r--r--src/bindings/scripts/scripts/kalimdor/thunder_bluff.cpp22
-rw-r--r--src/bindings/scripts/scripts/kalimdor/ungoro_crater.cpp53
-rw-r--r--src/bindings/scripts/scripts/kalimdor/wailing_caverns/def_wailing_caverns.h6
-rw-r--r--src/bindings/scripts/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp27
-rw-r--r--src/bindings/scripts/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp29
-rw-r--r--src/bindings/scripts/scripts/kalimdor/winterspring.cpp29
-rw-r--r--src/bindings/scripts/scripts/kalimdor/zulfarrak/zulfarrak.cpp48
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp44
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp51
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp28
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp14
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp59
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/def_ahnkahet.h5
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp85
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp15
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp20
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp106
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/def_azjol_nerub.h3
-rw-r--r--src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp39
-rw-r--r--src/bindings/scripts/scripts/northrend/borean_tundra.cpp86
-rw-r--r--src/bindings/scripts/scripts/northrend/dragonblight.cpp12
-rw-r--r--src/bindings/scripts/scripts/northrend/draktharon_keep/boss_dred.cpp12
-rw-r--r--src/bindings/scripts/scripts/northrend/draktharon_keep/boss_novos.cpp31
-rw-r--r--src/bindings/scripts/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp14
-rw-r--r--src/bindings/scripts/scripts/northrend/draktharon_keep/boss_trollgore.cpp12
-rw-r--r--src/bindings/scripts/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp34
-rw-r--r--src/bindings/scripts/scripts/northrend/grizzly_hills.cpp17
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/boss_drakkari_colossus.cpp63
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/boss_eck.cpp19
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/boss_gal_darah.cpp28
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/boss_moorabi.cpp31
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/boss_slad_ran.cpp49
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/def_gundrak.h3
-rw-r--r--src/bindings/scripts/scripts/northrend/gundrak/instance_gundrak.cpp47
-rw-r--r--src/bindings/scripts/scripts/northrend/howling_fjord.cpp29
-rw-r--r--src/bindings/scripts/scripts/northrend/icecrown.cpp14
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_anubrekhan.cpp21
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_faerlina.cpp17
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_four_horsemen.cpp22
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_gluth.cpp23
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_gothik.cpp57
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_grobbulus.cpp20
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_heigan.cpp21
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_kelthuzad.cpp35
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_loatheb.cpp12
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_maexxna.cpp19
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_noth.cpp26
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_patchwerk.cpp27
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_razuvious.cpp16
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_sapphiron.cpp60
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/boss_thaddius.cpp21
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/def_naxxramas.h5
-rw-r--r--src/bindings/scripts/scripts/northrend/naxxramas/instance_naxxramas.cpp36
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp16
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/def_eye_of_eternity.h1
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp3
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/boss_anomalus.cpp46
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/boss_keristrasza.cpp48
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp43
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/boss_ormorok.cpp39
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/commander_kolurg.cpp9
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp9
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/def_nexus.h4
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/nexus/instance_nexus.cpp30
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/oculus/boss_drakos.cpp9
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/oculus/boss_eregos.cpp18
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/oculus/boss_urom.cpp9
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/oculus/boss_varos.cpp9
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/oculus/def_oculus.h1
-rw-r--r--src/bindings/scripts/scripts/northrend/nexus/oculus/instance_oculus.cpp3
-rw-r--r--src/bindings/scripts/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp199
-rw-r--r--src/bindings/scripts/scripts/northrend/obsidian_sanctum/def_obsidian_sanctum.h4
-rw-r--r--src/bindings/scripts/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp13
-rw-r--r--src/bindings/scripts/scripts/northrend/sholazar_basin.cpp46
-rw-r--r--src/bindings/scripts/scripts/northrend/storm_peaks.cpp29
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp73
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp80
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp45
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp99
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/def_halls_of_lightning.h7
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp18
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp24
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp31
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp52
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp55
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp38
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_algalon.cpp1
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp81
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp16
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp59
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_freya.cpp17
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp1
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_hodir.cpp17
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_ignis.cpp17
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp18
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp1
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp55
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_thorim.cpp17
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_xt002.cpp18
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp1
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/def_ulduar.h6
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp41
-rw-r--r--src/bindings/scripts/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp10
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp69
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp61
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp56
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/def_utgarde_keep.h5
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp52
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp20
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp102
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp38
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp51
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp54
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/def_pinnacle.h4
-rw-r--r--src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp40
-rw-r--r--src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_archavon.cpp33
-rw-r--r--src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_emalon.cpp53
-rw-r--r--src/bindings/scripts/scripts/northrend/vault_of_archavon/def_vault_of_archavon.h1
-rw-r--r--src/bindings/scripts/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp26
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_cyanigosa.cpp23
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_erekem.cpp37
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_ichoron.cpp19
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_lavanthor.cpp18
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_moragg.cpp16
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_xevozz.cpp16
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/boss_zuramat.cpp18
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/def_violet_hold.h3
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/instance_violet_hold.cpp41
-rw-r--r--src/bindings/scripts/scripts/northrend/violet_hold/violet_hold.cpp15
-rw-r--r--src/bindings/scripts/scripts/northrend/wintergrasp.cpp12
-rw-r--r--src/bindings/scripts/scripts/northrend/zuldrak.cpp43
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp65
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp34
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp69
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp22
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp94
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp38
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/def_sethekk_halls.h2
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp11
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp30
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp27
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp43
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp29
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/def_shadow_labyrinth.h2
-rw-r--r--src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp42
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/black_temple.cpp12
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_bloodboil.cpp52
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_illidan.cpp275
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_mother_shahraz.cpp46
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_reliquary_of_souls.cpp99
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_shade_of_akama.cpp118
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_supremus.cpp42
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_teron_gorefiend.cpp80
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/boss_warlord_najentus.cpp31
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/def_black_temple.h3
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/illidari_council.cpp153
-rw-r--r--src/bindings/scripts/scripts/outland/black_temple/instance_black_temple.cpp41
-rw-r--r--src/bindings/scripts/scripts/outland/blades_edge_mountains.cpp69
-rw-r--r--src/bindings/scripts/scripts/outland/boss_doomlord_kazzak.cpp25
-rw-r--r--src/bindings/scripts/scripts/outland/boss_doomwalker.cpp31
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp119
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp52
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp155
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp120
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp78
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp55
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h1
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp44
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp33
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp46
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp36
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/def_steam_vault.h3
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp38
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp27
-rw-r--r--src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp22
-rw-r--r--src/bindings/scripts/scripts/outland/gruuls_lair/boss_gruul.cpp40
-rw-r--r--src/bindings/scripts/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp139
-rw-r--r--src/bindings/scripts/scripts/outland/gruuls_lair/def_gruuls_lair.h3
-rw-r--r--src/bindings/scripts/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp30
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp20
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp65
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp28
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/def_blood_furnace.h2
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp36
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp30
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp53
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp25
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/def_hellfire_ramparts.h4
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp11
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp93
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h2
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp35
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp68
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp74
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp44
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/def_shattered_halls.h3
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp15
-rw-r--r--src/bindings/scripts/scripts/outland/hellfire_peninsula.cpp79
-rw-r--r--src/bindings/scripts/scripts/outland/nagrand.cpp138
-rw-r--r--src/bindings/scripts/scripts/outland/netherstorm.cpp122
-rw-r--r--src/bindings/scripts/scripts/outland/shadowmoon_valley.cpp293
-rw-r--r--src/bindings/scripts/scripts/outland/shattrath_city.cpp96
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp65
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp53
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/def_arcatraz.h2
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp33
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp36
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_laj.cpp23
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp40
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_alar.cpp60
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp75
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp266
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp35
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/def_the_eye.h2
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp25
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_eye/the_eye.cpp18
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp4
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp20
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp41
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp42
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/def_mechanar.h1
-rw-r--r--src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp16
-rw-r--r--src/bindings/scripts/scripts/outland/terokkar_forest.cpp105
-rw-r--r--src/bindings/scripts/scripts/outland/zangarmarsh.cpp62
-rw-r--r--src/bindings/scripts/scripts/world/areatrigger_scripts.cpp15
-rw-r--r--src/bindings/scripts/scripts/world/boss_emeriss.cpp19
-rw-r--r--src/bindings/scripts/scripts/world/boss_lethon.cpp2
-rw-r--r--src/bindings/scripts/scripts/world/boss_taerar.cpp44
-rw-r--r--src/bindings/scripts/scripts/world/boss_ysondre.cpp37
-rw-r--r--src/bindings/scripts/scripts/world/go_scripts.cpp116
-rw-r--r--src/bindings/scripts/scripts/world/guards.cpp215
-rw-r--r--src/bindings/scripts/scripts/world/item_scripts.cpp38
-rw-r--r--src/bindings/scripts/scripts/world/mob_generic_creature.cpp41
-rw-r--r--src/bindings/scripts/scripts/world/npc_innkeeper.cpp12
-rw-r--r--src/bindings/scripts/scripts/world/npc_professions.cpp119
-rw-r--r--src/bindings/scripts/scripts/world/npc_taxi.cpp15
-rw-r--r--src/bindings/scripts/scripts/world/npcs_special.cpp297
-rw-r--r--src/bindings/scripts/system/ScriptLoader.cpp23
-rw-r--r--src/bindings/scripts/system/ScriptLoader.h3
-rw-r--r--src/bindings/scripts/system/system.cpp47
-rw-r--r--src/bindings/scripts/system/system.h24
-rw-r--r--src/bindings/scripts/trinityscripts.rc9
-rw-r--r--src/framework/Dynamic/FactoryHolder.h7
-rw-r--r--src/framework/Dynamic/ObjectRegistry.h13
-rw-r--r--src/framework/GameSystem/Grid.h22
-rw-r--r--src/framework/GameSystem/GridLoader.h6
-rw-r--r--src/framework/GameSystem/GridRefManager.h6
-rw-r--r--src/framework/GameSystem/GridReference.h4
-rw-r--r--src/framework/GameSystem/NGrid.h26
-rw-r--r--src/framework/GameSystem/TypeContainer.h18
-rw-r--r--src/framework/GameSystem/TypeContainerFunctions.h26
-rw-r--r--src/framework/GameSystem/TypeContainerVisitor.h19
-rw-r--r--src/framework/Network/SocketDefines.h7
-rw-r--r--src/framework/Platform/CompilerDefs.h6
-rw-r--r--src/framework/Platform/Define.h20
-rw-r--r--src/framework/Policies/CreationPolicy.h9
-rw-r--r--src/framework/Policies/ObjectLifeTime.cpp3
-rw-r--r--src/framework/Policies/ObjectLifeTime.h8
-rw-r--r--src/framework/Policies/Singleton.h9
-rw-r--r--src/framework/Policies/SingletonImp.h10
-rw-r--r--src/framework/Policies/ThreadingModel.h23
-rw-r--r--src/framework/Utilities/ByteConverter.h10
-rw-r--r--src/framework/Utilities/Callback.h35
-rw-r--r--src/framework/Utilities/CountedReference/Reference.h17
-rw-r--r--src/framework/Utilities/CountedReference/ReferenceHolder.h3
-rw-r--r--src/framework/Utilities/CountedReference/ReferenceImpl.h13
-rw-r--r--src/framework/Utilities/EventProcessor.cpp13
-rw-r--r--src/framework/Utilities/EventProcessor.h13
-rw-r--r--src/framework/Utilities/LinkedList.h42
-rw-r--r--src/framework/Utilities/LinkedReference/RefManager.h7
-rw-r--r--src/framework/Utilities/LinkedReference/Reference.h15
-rw-r--r--src/framework/Utilities/TypeList.h5
-rw-r--r--src/framework/Utilities/UnorderedMap.h7
-rw-r--r--src/game/AccountMgr.cpp51
-rw-r--r--src/game/AccountMgr.h10
-rw-r--r--src/game/AchievementMgr.cpp264
-rw-r--r--src/game/AchievementMgr.h38
-rw-r--r--src/game/AnimalRandomMovementGenerator.h2
-rw-r--r--src/game/ArenaTeam.cpp90
-rw-r--r--src/game/ArenaTeam.h37
-rw-r--r--src/game/ArenaTeamHandler.cpp85
-rw-r--r--src/game/AuctionHouseBot.cpp172
-rw-r--r--src/game/AuctionHouseBot.h34
-rw-r--r--src/game/AuctionHouseHandler.cpp99
-rw-r--r--src/game/AuctionHouseMgr.cpp114
-rw-r--r--src/game/AuctionHouseMgr.h33
-rw-r--r--src/game/Bag.cpp43
-rw-r--r--src/game/Bag.h15
-rw-r--r--src/game/BattleGround.cpp258
-rw-r--r--src/game/BattleGround.h88
-rw-r--r--src/game/BattleGroundAA.cpp14
-rw-r--r--src/game/BattleGroundAA.h6
-rw-r--r--src/game/BattleGroundAB.cpp82
-rw-r--r--src/game/BattleGroundAB.h37
-rw-r--r--src/game/BattleGroundAV.cpp93
-rw-r--r--src/game/BattleGroundAV.h111
-rw-r--r--src/game/BattleGroundBE.cpp36
-rw-r--r--src/game/BattleGroundBE.h9
-rw-r--r--src/game/BattleGroundDS.cpp14
-rw-r--r--src/game/BattleGroundDS.h6
-rw-r--r--src/game/BattleGroundEY.cpp120
-rw-r--r--src/game/BattleGroundEY.h43
-rw-r--r--src/game/BattleGroundHandler.cpp106
-rw-r--r--src/game/BattleGroundMgr.cpp201
-rw-r--r--src/game/BattleGroundMgr.h44
-rw-r--r--src/game/BattleGroundNA.cpp30
-rw-r--r--src/game/BattleGroundNA.h8
-rw-r--r--src/game/BattleGroundRL.cpp30
-rw-r--r--src/game/BattleGroundRL.h8
-rw-r--r--src/game/BattleGroundRV.cpp14
-rw-r--r--src/game/BattleGroundRV.h6
-rw-r--r--src/game/BattleGroundSA.cpp15
-rw-r--r--src/game/BattleGroundSA.h9
-rw-r--r--src/game/BattleGroundWS.cpp80
-rw-r--r--src/game/BattleGroundWS.h23
-rw-r--r--src/game/Calendar.h3
-rw-r--r--src/game/CalendarHandler.cpp40
-rw-r--r--src/game/Cell.h25
-rw-r--r--src/game/CellImpl.h36
-rw-r--r--src/game/Channel.cpp155
-rw-r--r--src/game/Channel.h22
-rw-r--r--src/game/ChannelHandler.cpp59
-rw-r--r--src/game/ChannelMgr.cpp17
-rw-r--r--src/game/ChannelMgr.h9
-rw-r--r--src/game/CharacterHandler.cpp246
-rw-r--r--src/game/Chat.cpp312
-rw-r--r--src/game/Chat.h83
-rw-r--r--src/game/ChatHandler.cpp120
-rw-r--r--src/game/CombatAI.cpp40
-rw-r--r--src/game/CombatAI.h14
-rw-r--r--src/game/CombatHandler.cpp15
-rw-r--r--src/game/ConfusedMovementGenerator.cpp27
-rw-r--r--src/game/ConfusedMovementGenerator.h7
-rw-r--r--src/game/Corpse.cpp48
-rw-r--r--src/game/Corpse.h18
-rw-r--r--src/game/Creature.cpp419
-rw-r--r--src/game/Creature.h128
-rw-r--r--src/game/CreatureAI.cpp23
-rw-r--r--src/game/CreatureAI.h51
-rw-r--r--src/game/CreatureAIFactory.h7
-rw-r--r--src/game/CreatureAIImpl.h60
-rw-r--r--src/game/CreatureAIRegistry.cpp3
-rw-r--r--src/game/CreatureAIRegistry.h2
-rw-r--r--src/game/CreatureAISelector.cpp15
-rw-r--r--src/game/CreatureAISelector.h3
-rw-r--r--src/game/CreatureEventAI.cpp171
-rw-r--r--src/game/CreatureEventAI.h41
-rw-r--r--src/game/CreatureEventAIMgr.cpp73
-rw-r--r--src/game/CreatureEventAIMgr.h7
-rw-r--r--src/game/CreatureGroups.cpp48
-rw-r--r--src/game/CreatureGroups.h21
-rw-r--r--src/game/DBCEnums.h22
-rw-r--r--src/game/DBCStores.cpp109
-rw-r--r--src/game/DBCStores.h16
-rw-r--r--src/game/DBCStructure.h183
-rw-r--r--src/game/DBCfmt.h3
-rw-r--r--src/game/Debugcmds.cpp178
-rw-r--r--src/game/DestinationHolder.cpp1
-rw-r--r--src/game/DestinationHolder.h9
-rw-r--r--src/game/DestinationHolderImp.h24
-rw-r--r--src/game/DuelHandler.cpp15
-rw-r--r--src/game/DynamicObject.cpp24
-rw-r--r--src/game/DynamicObject.h8
-rw-r--r--src/game/FleeingMovementGenerator.cpp51
-rw-r--r--src/game/FleeingMovementGenerator.h13
-rw-r--r--src/game/FollowerRefManager.h5
-rw-r--r--src/game/FollowerReference.cpp4
-rw-r--r--src/game/FollowerReference.h4
-rw-r--r--src/game/Formulas.h13
-rw-r--r--src/game/GameEventMgr.cpp235
-rw-r--r--src/game/GameEventMgr.h15
-rw-r--r--src/game/GameObject.cpp241
-rw-r--r--src/game/GameObject.h54
-rw-r--r--src/game/GlobalEvents.cpp11
-rw-r--r--src/game/GlobalEvents.h3
-rw-r--r--src/game/GossipDef.cpp130
-rw-r--r--src/game/GossipDef.h35
-rw-r--r--src/game/GridDefines.h35
-rw-r--r--src/game/GridNotifiers.cpp35
-rw-r--r--src/game/GridNotifiers.h192
-rw-r--r--src/game/GridNotifiersImpl.h112
-rw-r--r--src/game/GridStates.cpp5
-rw-r--r--src/game/GridStates.h11
-rw-r--r--src/game/Group.cpp238
-rw-r--r--src/game/Group.h55
-rw-r--r--src/game/GroupHandler.cpp152
-rw-r--r--src/game/GroupRefManager.h4
-rw-r--r--src/game/GroupReference.cpp4
-rw-r--r--src/game/GroupReference.h4
-rw-r--r--src/game/GuardAI.cpp19
-rw-r--r--src/game/GuardAI.h9
-rw-r--r--src/game/Guild.cpp401
-rw-r--r--src/game/Guild.h59
-rw-r--r--src/game/GuildHandler.cpp278
-rw-r--r--src/game/HomeMovementGenerator.cpp14
-rw-r--r--src/game/HomeMovementGenerator.h10
-rw-r--r--src/game/HostilRefManager.cpp20
-rw-r--r--src/game/HostilRefManager.h14
-rw-r--r--src/game/IdleMovementGenerator.cpp19
-rw-r--r--src/game/IdleMovementGenerator.h14
-rw-r--r--src/game/InstanceData.cpp42
-rw-r--r--src/game/InstanceData.h35
-rw-r--r--src/game/InstanceSaveMgr.cpp76
-rw-r--r--src/game/InstanceSaveMgr.h25
-rw-r--r--src/game/Item.cpp159
-rw-r--r--src/game/Item.h39
-rw-r--r--src/game/ItemEnchantmentMgr.cpp30
-rw-r--r--src/game/ItemEnchantmentMgr.h3
-rw-r--r--src/game/ItemHandler.cpp214
-rw-r--r--src/game/ItemPrototype.h68
-rw-r--r--src/game/LFGHandler.cpp88
-rw-r--r--src/game/Language.h84
-rw-r--r--src/game/Level0.cpp45
-rw-r--r--src/game/Level1.cpp495
-rw-r--r--src/game/Level2.cpp796
-rw-r--r--src/game/Level3.cpp1130
-rw-r--r--src/game/LootHandler.cpp85
-rw-r--r--src/game/LootMgr.cpp191
-rw-r--r--src/game/LootMgr.h64
-rw-r--r--src/game/Mail.cpp150
-rw-r--r--src/game/Mail.h23
-rw-r--r--src/game/Map.cpp518
-rw-r--r--src/game/Map.h123
-rw-r--r--src/game/MapInstanced.cpp35
-rw-r--r--src/game/MapInstanced.h14
-rw-r--r--src/game/MapManager.cpp50
-rw-r--r--src/game/MapManager.h29
-rw-r--r--src/game/MapRefManager.h6
-rw-r--r--src/game/MapReference.h3
-rw-r--r--src/game/MiscHandler.cpp302
-rw-r--r--src/game/MotionMaster.cpp67
-rw-r--r--src/game/MotionMaster.h24
-rw-r--r--src/game/MovementGenerator.cpp2
-rw-r--r--src/game/MovementGenerator.h15
-rw-r--r--src/game/MovementHandler.cpp108
-rw-r--r--src/game/NPCHandler.cpp169
-rw-r--r--src/game/NPCHandler.h11
-rw-r--r--src/game/Object.cpp338
-rw-r--r--src/game/Object.h116
-rw-r--r--src/game/ObjectAccessor.cpp74
-rw-r--r--src/game/ObjectAccessor.h51
-rw-r--r--src/game/ObjectDefines.h13
-rw-r--r--src/game/ObjectGridLoader.cpp47
-rw-r--r--src/game/ObjectGridLoader.h20
-rw-r--r--src/game/ObjectMgr.cpp1463
-rw-r--r--src/game/ObjectMgr.h169
-rw-r--r--src/game/Opcodes.cpp3
-rw-r--r--src/game/Opcodes.h10
-rw-r--r--src/game/OutdoorPvP.cpp84
-rw-r--r--src/game/OutdoorPvP.h53
-rw-r--r--src/game/OutdoorPvPEP.cpp73
-rw-r--r--src/game/OutdoorPvPEP.h37
-rw-r--r--src/game/OutdoorPvPHP.cpp28
-rw-r--r--src/game/OutdoorPvPHP.h22
-rw-r--r--src/game/OutdoorPvPImpl.h3
-rw-r--r--src/game/OutdoorPvPMgr.cpp26
-rw-r--r--src/game/OutdoorPvPMgr.h15
-rw-r--r--src/game/OutdoorPvPNA.cpp55
-rw-r--r--src/game/OutdoorPvPNA.h41
-rw-r--r--src/game/OutdoorPvPSI.cpp13
-rw-r--r--src/game/OutdoorPvPSI.h14
-rw-r--r--src/game/OutdoorPvPTF.cpp34
-rw-r--r--src/game/OutdoorPvPTF.h19
-rw-r--r--src/game/OutdoorPvPZM.cpp35
-rw-r--r--src/game/OutdoorPvPZM.h27
-rw-r--r--src/game/PassiveAI.h16
-rw-r--r--src/game/Path.h10
-rw-r--r--src/game/Pet.cpp302
-rw-r--r--src/game/Pet.h45
-rw-r--r--src/game/PetAI.cpp75
-rw-r--r--src/game/PetAI.h12
-rw-r--r--src/game/PetHandler.cpp122
-rw-r--r--src/game/PetitionsHandler.cpp132
-rw-r--r--src/game/Player.cpp3380
-rw-r--r--src/game/Player.h402
-rw-r--r--src/game/PlayerDump.cpp101
-rw-r--r--src/game/PlayerDump.h19
-rw-r--r--src/game/PointMovementGenerator.cpp14
-rw-r--r--src/game/PointMovementGenerator.h10
-rw-r--r--src/game/PoolHandler.cpp84
-rw-r--r--src/game/PoolHandler.h11
-rw-r--r--src/game/QueryHandler.cpp54
-rw-r--r--src/game/QuestDef.cpp32
-rw-r--r--src/game/QuestDef.h29
-rw-r--r--src/game/QuestHandler.cpp100
-rw-r--r--src/game/RandomMovementGenerator.cpp31
-rw-r--r--src/game/RandomMovementGenerator.h5
-rw-r--r--src/game/ReactorAI.cpp7
-rw-r--r--src/game/ReactorAI.h7
-rw-r--r--src/game/ReputationMgr.cpp102
-rw-r--r--src/game/ReputationMgr.h22
-rw-r--r--src/game/ScriptCalls.cpp14
-rw-r--r--src/game/ScriptCalls.h11
-rw-r--r--src/game/SharedDefines.h131
-rw-r--r--src/game/SkillDiscovery.cpp44
-rw-r--r--src/game/SkillDiscovery.h4
-rw-r--r--src/game/SkillExtraItems.cpp28
-rw-r--r--src/game/SkillExtraItems.h3
-rw-r--r--src/game/SkillHandler.cpp15
-rw-r--r--src/game/SocialMgr.cpp48
-rw-r--r--src/game/SocialMgr.h15
-rw-r--r--src/game/Spell.cpp875
-rw-r--r--src/game/Spell.h100
-rw-r--r--src/game/SpellAuraDefines.h5
-rw-r--r--src/game/SpellAuras.cpp753
-rw-r--r--src/game/SpellAuras.h48
-rw-r--r--src/game/SpellEffects.cpp862
-rw-r--r--src/game/SpellHandler.cpp111
-rw-r--r--src/game/SpellMgr.cpp500
-rw-r--r--src/game/SpellMgr.h187
-rw-r--r--src/game/StatSystem.cpp175
-rw-r--r--src/game/TargetedMovementGenerator.cpp33
-rw-r--r--src/game/TargetedMovementGenerator.h11
-rw-r--r--src/game/TaxiHandler.cpp56
-rw-r--r--src/game/TemporarySummon.cpp52
-rw-r--r--src/game/TemporarySummon.h10
-rw-r--r--src/game/ThreatManager.cpp84
-rw-r--r--src/game/ThreatManager.h64
-rw-r--r--src/game/TicketHandler.cpp27
-rw-r--r--src/game/Totem.cpp23
-rw-r--r--src/game/Totem.h8
-rw-r--r--src/game/TotemAI.cpp17
-rw-r--r--src/game/TotemAI.h8
-rw-r--r--src/game/TradeHandler.cpp83
-rw-r--r--src/game/Transports.cpp96
-rw-r--r--src/game/Transports.h17
-rw-r--r--src/game/Traveller.h19
-rw-r--r--src/game/Unit.cpp1801
-rw-r--r--src/game/Unit.h274
-rw-r--r--src/game/UnitAI.cpp40
-rw-r--r--src/game/UnitAI.h19
-rw-r--r--src/game/UnitEvents.h36
-rw-r--r--src/game/UpdateData.cpp26
-rw-r--r--src/game/UpdateData.h9
-rw-r--r--src/game/UpdateFields.h10
-rw-r--r--src/game/UpdateMask.h23
-rw-r--r--src/game/Vehicle.cpp47
-rw-r--r--src/game/Vehicle.h13
-rw-r--r--src/game/VoiceChatHandler.cpp4
-rw-r--r--src/game/WaypointManager.cpp32
-rw-r--r--src/game/WaypointManager.h10
-rw-r--r--src/game/WaypointMovementGenerator.cpp93
-rw-r--r--src/game/WaypointMovementGenerator.h18
-rw-r--r--src/game/Weather.cpp37
-rw-r--r--src/game/Weather.h7
-rw-r--r--src/game/Wintergrasp.cpp153
-rw-r--r--src/game/Wintergrasp.h50
-rw-r--r--src/game/World.cpp423
-rw-r--r--src/game/World.h76
-rw-r--r--src/game/WorldLog.cpp18
-rw-r--r--src/game/WorldLog.h10
-rw-r--r--src/game/WorldSession.cpp135
-rw-r--r--src/game/WorldSession.h119
-rw-r--r--src/game/WorldSocket.cpp204
-rw-r--r--src/game/WorldSocket.h48
-rw-r--r--src/game/WorldSocketMgr.cpp80
-rw-r--r--src/game/WorldSocketMgr.h17
-rw-r--r--src/game/ZoneScript.h12
-rw-r--r--src/game/pchdef.h1
-rw-r--r--src/shared/Auth/AuthCrypt.cpp17
-rw-r--r--src/shared/Auth/AuthCrypt.h7
-rw-r--r--src/shared/Auth/BigNumber.cpp39
-rw-r--r--src/shared/Auth/BigNumber.h15
-rw-r--r--src/shared/Auth/Hmac.cpp8
-rw-r--r--src/shared/Auth/Hmac.h5
-rw-r--r--src/shared/Auth/SARC4.cpp6
-rw-r--r--src/shared/Auth/SARC4.h3
-rw-r--r--src/shared/Auth/Sha1.cpp9
-rw-r--r--src/shared/Auth/Sha1.h9
-rw-r--r--src/shared/ByteBuffer.h88
-rw-r--r--src/shared/Common.cpp4
-rw-r--r--src/shared/Common.h32
-rw-r--r--src/shared/Config/Config.cpp14
-rw-r--r--src/shared/Config/Config.h9
-rw-r--r--src/shared/Config/ConfigEnv.h4
-rw-r--r--src/shared/Config/dotconfpp/dotconfpp.cpp76
-rw-r--r--src/shared/Config/dotconfpp/dotconfpp.h18
-rw-r--r--src/shared/Config/dotconfpp/mempool.cpp15
-rw-r--r--src/shared/Config/dotconfpp/mempool.h10
-rw-r--r--src/shared/Database/DBCFileLoader.cpp42
-rw-r--r--src/shared/Database/DBCFileLoader.h12
-rw-r--r--src/shared/Database/DBCStore.h24
-rw-r--r--src/shared/Database/Database.cpp42
-rw-r--r--src/shared/Database/Database.h22
-rw-r--r--src/shared/Database/DatabaseEnv.h6
-rw-r--r--src/shared/Database/DatabaseImpl.h27
-rw-r--r--src/shared/Database/DatabaseMysql.cpp70
-rw-r--r--src/shared/Database/DatabaseMysql.h14
-rw-r--r--src/shared/Database/Field.cpp10
-rw-r--r--src/shared/Database/Field.h10
-rw-r--r--src/shared/Database/MySQLDelayThread.h3
-rw-r--r--src/shared/Database/QueryResult.h15
-rw-r--r--src/shared/Database/QueryResultMysql.cpp15
-rw-r--r--src/shared/Database/QueryResultMysql.h8
-rw-r--r--src/shared/Database/SQLStorage.cpp8
-rw-r--r--src/shared/Database/SQLStorage.h19
-rw-r--r--src/shared/Database/SQLStorageImpl.h20
-rw-r--r--src/shared/Database/SqlDelayThread.cpp6
-rw-r--r--src/shared/Database/SqlDelayThread.h8
-rw-r--r--src/shared/Database/SqlOperations.cpp26
-rw-r--r--src/shared/Database/SqlOperations.h15
-rw-r--r--src/shared/Errors.h7
-rw-r--r--src/shared/LockedQueue.h18
-rw-r--r--src/shared/Log.cpp166
-rw-r--r--src/shared/Log.h28
-rw-r--r--src/shared/ProgressBar.cpp8
-rw-r--r--src/shared/ProgressBar.h5
-rw-r--r--src/shared/ServiceWin32.cpp41
-rw-r--r--src/shared/ServiceWin32.h3
-rw-r--r--src/shared/SystemConfig.h7
-rw-r--r--src/shared/Threading.cpp53
-rw-r--r--src/shared/Threading.h19
-rw-r--r--src/shared/Timer.h12
-rw-r--r--src/shared/Util.cpp74
-rw-r--r--src/shared/Util.h72
-rw-r--r--src/shared/WheatyExceptionReport.cpp131
-rw-r--r--src/shared/WheatyExceptionReport.h18
-rw-r--r--src/shared/WorldPacket.h6
-rw-r--r--src/shared/vmap/AABSPTree.h288
-rw-r--r--src/shared/vmap/BaseModel.cpp17
-rw-r--r--src/shared/vmap/BaseModel.h24
-rw-r--r--src/shared/vmap/CoordModelMapping.cpp25
-rw-r--r--src/shared/vmap/CoordModelMapping.h27
-rw-r--r--src/shared/vmap/DebugCmdLogger.cpp10
-rw-r--r--src/shared/vmap/DebugCmdLogger.h14
-rw-r--r--src/shared/vmap/IVMapManager.h18
-rw-r--r--src/shared/vmap/ManagedModelContainer.cpp6
-rw-r--r--src/shared/vmap/ManagedModelContainer.h7
-rw-r--r--src/shared/vmap/ModelContainer.cpp66
-rw-r--r--src/shared/vmap/ModelContainer.h29
-rw-r--r--src/shared/vmap/NodeValueAccess.h7
-rw-r--r--src/shared/vmap/ShortBox.h21
-rw-r--r--src/shared/vmap/ShortVector.h15
-rw-r--r--src/shared/vmap/SubModel.cpp41
-rw-r--r--src/shared/vmap/SubModel.h19
-rw-r--r--src/shared/vmap/TileAssembler.cpp87
-rw-r--r--src/shared/vmap/TileAssembler.h13
-rw-r--r--src/shared/vmap/TreeNode.cpp5
-rw-r--r--src/shared/vmap/TreeNode.h34
-rw-r--r--src/shared/vmap/VMapDefinitions.h4
-rw-r--r--src/shared/vmap/VMapFactory.cpp12
-rw-r--r--src/shared/vmap/VMapFactory.h7
-rw-r--r--src/shared/vmap/VMapManager.cpp60
-rw-r--r--src/shared/vmap/VMapManager.h29
-rw-r--r--src/shared/vmap/VMapTools.h20
-rw-r--r--src/tools/genrevision/genrevision.cpp58
-rw-r--r--src/trinitycore/CliRunnable.cpp66
-rw-r--r--src/trinitycore/CliRunnable.h3
-rw-r--r--src/trinitycore/Main.cpp18
-rw-r--r--src/trinitycore/Master.cpp87
-rw-r--r--src/trinitycore/Master.h8
-rw-r--r--src/trinitycore/RASocket.cpp42
-rw-r--r--src/trinitycore/RASocket.h12
-rw-r--r--src/trinitycore/TrinityCore.rc9
-rw-r--r--src/trinitycore/WorldRunnable.cpp18
-rw-r--r--src/trinitycore/WorldRunnable.h3
-rw-r--r--src/trinitycore/resource.h1
-rw-r--r--src/trinityrealm/AuthCodes.h7
-rw-r--r--src/trinityrealm/AuthSocket.cpp163
-rw-r--r--src/trinityrealm/AuthSocket.h15
-rw-r--r--src/trinityrealm/Main.cpp48
-rw-r--r--src/trinityrealm/RealmList.cpp19
-rw-r--r--src/trinityrealm/RealmList.h10
-rw-r--r--src/trinityrealm/TrinityRealm.rc9
949 files changed, 53534 insertions, 731 deletions
diff --git a/src/bindings/scripts/ScriptMgr.cpp b/src/bindings/scripts/ScriptMgr.cpp
index 7d38c82a278..0b48f5f7cef 100644
--- a/src/bindings/scripts/ScriptMgr.cpp
+++ b/src/bindings/scripts/ScriptMgr.cpp
@@ -1,6 +1,7 @@
/* Copyright (C) 2006 - 2008 TrinityScript <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#include "precompiled.h"
#include "Config/Config.h"
#include "Database/DatabaseEnv.h"
@@ -9,30 +10,39 @@
#include "ProgressBar.h"
#include "../system/ScriptLoader.h"
#include "../system/system.h"
+
#define _FULLVERSION "TrinityScript"
+
#ifndef _TRINITY_SCRIPT_CONFIG
# define _TRINITY_SCRIPT_CONFIG "trinitycore.conf"
#endif _TRINITY_SCRIPT_CONFIG
+
int num_sc_scripts;
Script *m_scripts[MAX_SCRIPTS];
+
Config TScriptConfig;
+
void FillSpellSummary();
void LoadOverridenSQLData();
void LoadOverridenDBCData();
+
void LoadDatabase()
{
//Get db string from file
std::string dbstring = TScriptConfig.GetStringDefault("WorldDatabaseInfo", "");
+
if (dbstring.empty())
{
error_log("TSCR: Missing world database info from configuration file. Load database aborted.");
return;
}
+
//Initialize connection to DB
if (!dbstring.empty() && TScriptDB.Initialize(dbstring.c_str()))
{
outstring_log("TSCR: TrinityScript database initialized successfully.");
outstring_log("");
+
pSystemMgr.LoadVersion();
pSystemMgr.LoadScriptTexts();
pSystemMgr.LoadScriptTextsCustom();
@@ -43,22 +53,29 @@ void LoadDatabase()
error_log("TSCR: Unable to connect to database at %s. Load database aborted.", dbstring.c_str());
return;
}
+
TScriptDB.HaltDelayThread();
+
}
+
struct TSpellSummary {
uint8 Targets; // set of enum SelectTarget
uint8 Effects; // set of enum SelectEffect
}extern *SpellSummary;
+
TRINITY_DLL_EXPORT
void ScriptsFree()
{
// Free Spell Summary
delete []SpellSummary;
+
// Free resources before library unload
- for (uint16 i =0; i<MAX_SCRIPTS; ++i)
+ for(uint16 i =0;i<MAX_SCRIPTS;++i)
delete m_scripts[i];
+
num_sc_scripts = 0;
}
+
TRINITY_DLL_EXPORT
void ScriptsInit(char const* cfg_file = "trinitycore.conf")
{
@@ -71,30 +88,41 @@ void ScriptsInit(char const* cfg_file = "trinitycore.conf")
outstring_log(" |___/ |_| ");
outstring_log("Trinity Script initializing %s", _FULLVERSION);
outstring_log("");
+
//Get configuration file
if (!TScriptConfig.SetSource(cfg_file))
error_log("TSCR: Unable to open configuration file. Database will be unaccessible. Configuration values will use default.");
else
outstring_log("TSCR: Using configuration file %s",cfg_file);
+
outstring_log("");
+
//Load database (must be called after SD2Config.SetSource).
LoadDatabase();
+
outstring_log("TSCR: Loading C++ scripts");
barGoLink bar(1);
bar.step();
outstring_log("");
- for (uint16 i =0; i<MAX_SCRIPTS; ++i)
+
+ for(uint16 i =0;i<MAX_SCRIPTS;++i)
m_scripts[i]=NULL;
+
FillSpellSummary();
+
AddScripts();
+
outstring_log(">> Loaded %i C++ Scripts.", num_sc_scripts);
+
outstring_log(">> Load Overriden SQL Data.");
LoadOverridenSQLData();
outstring_log(">> Load Overriden DBC Data.");
LoadOverridenDBCData();
}
+
//*********************************
//*** Functions used globally ***
+
void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget)
{
if (!pSource)
@@ -102,18 +130,23 @@ void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget)
error_log("TSCR: DoScriptText entry %i, invalid Source pointer.", iTextEntry);
return;
}
+
if (iTextEntry >= 0)
{
error_log("TSCR: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.", pSource->GetEntry(), pSource->GetTypeId(), pSource->GetGUIDLow(), iTextEntry);
return;
}
+
const StringTextData* pData = pSystemMgr.GetTextData(iTextEntry);
+
if (!pData)
{
error_log("TSCR: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.", pSource->GetEntry(), pSource->GetTypeId(), pSource->GetGUIDLow(), iTextEntry);
return;
}
+
debug_log("TSCR: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u", iTextEntry, pData->uiSoundId, pData->uiType, pData->uiLanguage, pData->uiEmote);
+
if(pData->uiSoundId)
{
if(GetSoundEntriesStore()->LookupEntry(pData->uiSoundId))
@@ -123,6 +156,7 @@ void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget)
else
error_log("TSCR: DoScriptText entry %i tried to process invalid sound id %u.", iTextEntry, pData->uiSoundId);
}
+
if(pData->uiEmote)
{
if (pSource->GetTypeId() == TYPEID_UNIT || pSource->GetTypeId() == TYPEID_PLAYER)
@@ -130,6 +164,7 @@ void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget)
else
error_log("TSCR: DoScriptText entry %i tried to process emote for invalid TypeId (%u).", iTextEntry, pSource->GetTypeId());
}
+
switch(pData->uiType)
{
case CHAT_TYPE_SAY:
@@ -163,8 +198,10 @@ void DoScriptText(int32 iTextEntry, WorldObject* pSource, Unit* pTarget)
break;
}
}
+
//*********************************
//*** Functions used internally ***
+
void Script::RegisterSelf()
{
int id = GetScriptId(Name.c_str());
@@ -179,8 +216,10 @@ void Script::RegisterSelf()
delete this;
}
}
+
//********************************
//*** Functions to be Exported ***
+
TRINITY_DLL_EXPORT
void OnLogin(Player *pPlayer)
{
@@ -188,6 +227,7 @@ void OnLogin(Player *pPlayer)
if (!tmpscript || !tmpscript->pOnLogin) return;
tmpscript->pOnLogin(pPlayer);
}
+
TRINITY_DLL_EXPORT
void OnLogout(Player *pPlayer)
{
@@ -195,6 +235,7 @@ void OnLogout(Player *pPlayer)
if (!tmpscript || !tmpscript->pOnLogout) return;
tmpscript->pOnLogout(pPlayer);
}
+
TRINITY_DLL_EXPORT
void OnPVPKill(Player *killer, Player *killed)
{
@@ -202,6 +243,7 @@ void OnPVPKill(Player *killer, Player *killed)
if (!tmpscript || !tmpscript->pOnPVPKill) return;
tmpscript->pOnPVPKill(killer, killed);
}
+
TRINITY_DLL_EXPORT
bool OnSpellCast (Unit *pUnitTarget, Item *pItemTarget, GameObject *pGoTarget, uint32 i, SpellEntry const *spell)
{
@@ -209,6 +251,7 @@ bool OnSpellCast (Unit *pUnitTarget, Item *pItemTarget, GameObject *pGoTarget, u
if (!tmpscript || !tmpscript->pOnSpellCast) return true;
return tmpscript->pOnSpellCast(pUnitTarget,pItemTarget,pGoTarget,i,spell);
}
+
TRINITY_DLL_EXPORT
uint32 OnGetXP(Player *pPlayer, uint32 amount)
{
@@ -216,6 +259,7 @@ uint32 OnGetXP(Player *pPlayer, uint32 amount)
if (!tmpscript || !tmpscript->pOnGetXP) return amount;
return tmpscript->pOnGetXP(pPlayer,amount);
}
+
TRINITY_DLL_EXPORT
uint32 OnGetMoney(Player *pPlayer, int32 amount)
{
@@ -223,6 +267,7 @@ uint32 OnGetMoney(Player *pPlayer, int32 amount)
if (!tmpscript || !tmpscript->pOnGetMoney) return amount;
return tmpscript->pOnGetMoney(pPlayer,amount);
}
+
TRINITY_DLL_EXPORT
bool OnPlayerChat(Player *pPlayer, const char *text)
{
@@ -230,6 +275,7 @@ bool OnPlayerChat(Player *pPlayer, const char *text)
if (!tmpscript || !tmpscript->pOnPlayerChat) return true;
return tmpscript->pOnPlayerChat(pPlayer,text);
}
+
TRINITY_DLL_EXPORT
void OnServerStartup()
{
@@ -237,6 +283,7 @@ void OnServerStartup()
if (!tmpscript || !tmpscript->pOnServerStartup) return;
tmpscript->pOnServerStartup();
}
+
TRINITY_DLL_EXPORT
void OnServerShutdown()
{
@@ -244,6 +291,7 @@ void OnServerShutdown()
if (!tmpscript || !tmpscript->pOnServerShutdown) return;
tmpscript->pOnServerShutdown();
}
+
TRINITY_DLL_EXPORT
void OnAreaChange(Player *pPlayer, AreaTableEntry const *pArea)
{
@@ -251,6 +299,7 @@ void OnAreaChange(Player *pPlayer, AreaTableEntry const *pArea)
if (!tmpscript || !tmpscript->pOnAreaChange) return;
tmpscript->pOnAreaChange(pPlayer, pArea);
}
+
TRINITY_DLL_EXPORT
bool OnItemClick (Player *pPlayer, Item *pItem)
{
@@ -258,6 +307,7 @@ bool OnItemClick (Player *pPlayer, Item *pItem)
if (!tmpscript || !tmpscript->pOnItemClick) return true;
return tmpscript->pOnItemClick(pPlayer,pItem);
}
+
TRINITY_DLL_EXPORT
bool OnItemOpen (Player *pPlayer, Item *pItem)
{
@@ -265,6 +315,7 @@ bool OnItemOpen (Player *pPlayer, Item *pItem)
if (!tmpscript || !tmpscript->pOnItemOpen) return true;
return tmpscript->pOnItemOpen(pPlayer,pItem);
}
+
TRINITY_DLL_EXPORT
bool OnGoClick (Player *pPlayer, GameObject *pGameObject)
{
@@ -272,6 +323,7 @@ bool OnGoClick (Player *pPlayer, GameObject *pGameObject)
if (!tmpscript || !tmpscript->pOnGoClick) return true;
return tmpscript->pOnGoClick(pPlayer,pGameObject);
}
+
TRINITY_DLL_EXPORT
void OnCreatureKill (Player *pPlayer, Creature *pCreature)
{
@@ -279,202 +331,259 @@ void OnCreatureKill (Player *pPlayer, Creature *pCreature)
if (!tmpscript || !tmpscript->pOnCreatureKill) return;
tmpscript->pOnCreatureKill(pPlayer,pCreature);
}
+
TRINITY_DLL_EXPORT
char const* ScriptsVersion()
{
return "Default Trinity scripting library";
}
+
TRINITY_DLL_EXPORT
bool GossipHello (Player * pPlayer, Creature* pCreature)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pGossipHello) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGossipHello(pPlayer, pCreature);
}
+
TRINITY_DLL_EXPORT
bool GossipSelect(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
debug_log("TSCR: Gossip selection, sender: %d, action: %d", uiSender, uiAction);
+
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pGossipSelect) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGossipSelect(pPlayer, pCreature, uiSender, uiAction);
}
+
TRINITY_DLL_EXPORT
bool GossipSelectWithCode(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction, const char* sCode)
{
debug_log("TSCR: Gossip selection with code, sender: %d, action: %d", uiSender, uiAction);
+
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pGossipSelectWithCode) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGossipSelectWithCode(pPlayer, pCreature, uiSender, uiAction, sCode);
}
+
TRINITY_DLL_EXPORT
bool GOSelect(Player* pPlayer, GameObject* pGO, uint32 uiSender, uint32 uiAction)
{
if(!pGO)
return false;
debug_log("TSCR: Gossip selection, sender: %d, action: %d", uiSender, uiAction);
+
Script *tmpscript = m_scripts[pGO->GetGOInfo()->ScriptId];
if(!tmpscript || !tmpscript->pGOSelect) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGOSelect(pPlayer, pGO, uiSender, uiAction);
}
+
TRINITY_DLL_EXPORT
bool GOSelectWithCode(Player* pPlayer, GameObject* pGO, uint32 uiSender, uint32 uiAction, const char* sCode)
{
if(!pGO)
return false;
debug_log("TSCR: Gossip selection, sender: %d, action: %d",uiSender, uiAction);
+
Script *tmpscript = m_scripts[pGO->GetGOInfo()->ScriptId];
if(!tmpscript || !tmpscript->pGOSelectWithCode) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGOSelectWithCode(pPlayer, pGO, uiSender ,uiAction, sCode);
}
+
TRINITY_DLL_EXPORT
bool QuestAccept(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pQuestAccept) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pQuestAccept(pPlayer, pCreature, pQuest);
}
+
TRINITY_DLL_EXPORT
bool QuestSelect(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pQuestSelect) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pQuestSelect(pPlayer, pCreature, pQuest);
}
+
TRINITY_DLL_EXPORT
bool QuestComplete(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pQuestComplete) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pQuestComplete(pPlayer, pCreature, pQuest);
}
+
TRINITY_DLL_EXPORT
bool ChooseReward(Player* pPlayer, Creature* pCreature, Quest const* pQuest, uint32 opt)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pChooseReward) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pChooseReward(pPlayer, pCreature, pQuest, opt);
}
+
TRINITY_DLL_EXPORT
uint32 NPCDialogStatus(Player* pPlayer, Creature* pCreature)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->pNPCDialogStatus) return 100;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pNPCDialogStatus(pPlayer, pCreature);
}
+
TRINITY_DLL_EXPORT
uint32 GODialogStatus(Player* pPlayer, GameObject* pGO)
{
Script *tmpscript = m_scripts[pGO->GetGOInfo()->ScriptId];
if (!tmpscript || !tmpscript->pGODialogStatus) return 100;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGODialogStatus(pPlayer, pGO);
}
+
TRINITY_DLL_EXPORT
bool ItemHello(Player* pPlayer, Item* pItem, Quest const* pQuest)
{
Script *tmpscript = m_scripts[pItem->GetProto()->ScriptId];
if (!tmpscript || !tmpscript->pItemHello) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pItemHello(pPlayer, pItem, pQuest);
}
+
TRINITY_DLL_EXPORT
bool ItemQuestAccept(Player* pPlayer, Item* pItem, Quest const* pQuest)
{
Script *tmpscript = m_scripts[pItem->GetProto()->ScriptId];
if (!tmpscript || !tmpscript->pItemQuestAccept) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pItemQuestAccept(pPlayer, pItem, pQuest);
}
+
TRINITY_DLL_EXPORT
bool GOHello(Player* pPlayer, GameObject* pGO)
{
Script *tmpscript = m_scripts[pGO->GetGOInfo()->ScriptId];
if (!tmpscript || !tmpscript->pGOHello) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGOHello(pPlayer, pGO);
}
+
TRINITY_DLL_EXPORT
bool GOQuestAccept(Player* pPlayer, GameObject* pGO, Quest const* pQuest)
{
Script *tmpscript = m_scripts[pGO->GetGOInfo()->ScriptId];
if (!tmpscript || !tmpscript->pGOQuestAccept) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGOQuestAccept(pPlayer, pGO, pQuest);
}
+
TRINITY_DLL_EXPORT
bool GOChooseReward(Player* pPlayer, GameObject* pGO, Quest const* pQuest, uint32 opt)
{
Script *tmpscript = m_scripts[pGO->GetGOInfo()->ScriptId];
if (!tmpscript || !tmpscript->pGOChooseReward) return false;
+
pPlayer->PlayerTalkClass->ClearMenus();
return tmpscript->pGOChooseReward(pPlayer, pGO, pQuest, opt);
}
+
TRINITY_DLL_EXPORT
bool AreaTrigger(Player* pPlayer, AreaTriggerEntry * atEntry)
{
Script *tmpscript = m_scripts[GetAreaTriggerScriptId(atEntry->id)];
if (!tmpscript || !tmpscript->pAreaTrigger) return false;
+
return tmpscript->pAreaTrigger(pPlayer, atEntry);
}
+
TRINITY_DLL_EXPORT
CreatureAI* GetAI(Creature* pCreature)
{
Script *tmpscript = m_scripts[pCreature->GetScriptId()];
if (!tmpscript || !tmpscript->GetAI) return NULL;
+
return tmpscript->GetAI(pCreature);
}
+
TRINITY_DLL_EXPORT
bool ItemUse(Player* pPlayer, Item* pItem, SpellCastTargets const& targets)
{
Script *tmpscript = m_scripts[pItem->GetProto()->ScriptId];
if (!tmpscript || !tmpscript->pItemUse) return false;
+
return tmpscript->pItemUse(pPlayer, pItem, targets);
}
+
TRINITY_DLL_EXPORT
bool ItemExpire(Player* pPlayer, ItemPrototype const * pItemProto)
{
Script *tmpscript = m_scripts[pItemProto->ScriptId];
if (!tmpscript || !tmpscript->pItemExpire) return true;
+
return tmpscript->pItemExpire(pPlayer, pItemProto);
}
+
TRINITY_DLL_EXPORT
bool EffectDummyCreature(Unit *caster, uint32 spellId, uint32 effIndex, Creature *crTarget)
{
Script *tmpscript = m_scripts[crTarget->GetScriptId()];
+
if (!tmpscript || !tmpscript->pEffectDummyCreature) return false;
+
return tmpscript->pEffectDummyCreature(caster, spellId, effIndex, crTarget);
}
+
TRINITY_DLL_EXPORT
bool EffectDummyGameObj(Unit *caster, uint32 spellId, uint32 effIndex, GameObject *gameObjTarget)
{
Script *tmpscript = m_scripts[gameObjTarget->GetGOInfo()->ScriptId];
+
if (!tmpscript || !tmpscript->pEffectDummyGameObj) return false;
+
return tmpscript->pEffectDummyGameObj(caster, spellId, effIndex, gameObjTarget);
}
+
TRINITY_DLL_EXPORT
bool EffectDummyItem(Unit *caster, uint32 spellId, uint32 effIndex, Item *itemTarget)
{
Script *tmpscript = m_scripts[itemTarget->GetProto()->ScriptId];
+
if (!tmpscript || !tmpscript->pEffectDummyItem) return false;
+
return tmpscript->pEffectDummyItem(caster, spellId, effIndex, itemTarget);
}
+
TRINITY_DLL_EXPORT
InstanceData* CreateInstanceData(Map *map)
{
if (!map->IsDungeon()) return NULL;
+
Script *tmpscript = m_scripts[((InstanceMap*)map)->GetScriptId()];
if (!tmpscript || !tmpscript->GetInstanceData) return NULL;
+
return tmpscript->GetInstanceData(map);
}
diff --git a/src/bindings/scripts/ScriptMgr.h b/src/bindings/scripts/ScriptMgr.h
index 7e9ac9b89f2..9456a0da661 100644
--- a/src/bindings/scripts/ScriptMgr.h
+++ b/src/bindings/scripts/ScriptMgr.h
@@ -4,11 +4,14 @@
*
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_SCRIPTMGR_H
#define SC_SCRIPTMGR_H
+
#include "Common.h"
#include "Platform/CompilerDefs.h"
#include "DBCStructure.h"
+
class Player;
class Creature;
class CreatureAI;
@@ -21,9 +24,11 @@ class Map;
class Unit;
class WorldObject;
struct ItemPrototype;
+
#define MAX_SCRIPTS 5000 //72 bytes each (approx 351kb)
#define VISIBLE_RANGE (166.0f) //MAX visible range (size of grid)
#define DEFAULT_TEXT "<Trinity Script Text Entry Missing!>"
+
struct Script
{
Script() :
@@ -34,7 +39,9 @@ struct Script
pEffectDummyCreature(NULL), pEffectDummyGameObj(NULL), pEffectDummyItem(NULL),
GetAI(NULL), GetInstanceData(NULL)
{}
+
std::string Name;
+
//Methods to be scripted
void (*pOnLogin )(Player*);
void (*pOnLogout )(Player*);
@@ -72,17 +79,22 @@ struct Script
bool (*pEffectDummyCreature )(Unit*, uint32, uint32, Creature* );
bool (*pEffectDummyGameObj )(Unit*, uint32, uint32, GameObject* );
bool (*pEffectDummyItem )(Unit*, uint32, uint32, Item* );
+
CreatureAI* (*GetAI)(Creature*);
InstanceData* (*GetInstanceData)(Map*);
+
void RegisterSelf();
};
+
//Generic scripting text function
void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target = NULL);
+
#if COMPILER == COMPILER_GNU
#define FUNC_PTR(name,callconvention,returntype,parameters) typedef returntype(*name)parameters __attribute__ ((callconvention));
#else
#define FUNC_PTR(name, callconvention, returntype, parameters) typedef returntype(callconvention *name)parameters;
#endif
+
#ifdef WIN32
#define TRINITY_DLL_EXPORT extern "C" __declspec(dllexport)
#elif defined( __GNUC__ )
@@ -90,5 +102,6 @@ void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target = NULL);
#else
#define TRINITY_DLL_EXPORT extern "C" export
#endif
+
#endif
diff --git a/src/bindings/scripts/base/escort_ai.cpp b/src/bindings/scripts/base/escort_ai.cpp
index 5c256aceb65..4d6083ab470 100644
--- a/src/bindings/scripts/base/escort_ai.cpp
+++ b/src/bindings/scripts/base/escort_ai.cpp
@@ -1,19 +1,23 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
/* ScriptData
SDName: Npc_EscortAI
SD%Complete: 100
SDComment:
SDCategory: Npc
EndScriptData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
enum ePoints
{
POINT_LAST_POINT = 0xFFFFFF,
POINT_HOME = 0xFFFFFE
};
+
npc_escortAI::npc_escortAI(Creature* pCreature) : ScriptedAI(pCreature),
m_uiPlayerGUID(0),
MaxPlayerDistance(DEFAULT_MAX_PLAYER_DISTANCE),
@@ -30,32 +34,40 @@ npc_escortAI::npc_escortAI(Creature* pCreature) : ScriptedAI(pCreature),
m_bCanReturnToStart(false),
ScriptWP(false)
{}
+
void npc_escortAI::AttackStart(Unit* pWho)
{
if (!pWho)
return;
+
if (m_creature->Attack(pWho, true))
{
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == POINT_MOTION_TYPE)
m_creature->GetMotionMaster()->MovementExpired();
+
if (IsCombatMovement())
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
+
//see followerAI
bool npc_escortAI::AssistPlayerInCombat(Unit* pWho)
{
if (!pWho || !pWho->getVictim())
return false;
+
//experimental (unknown) flag not present
if (!(m_creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13))
return false;
+
//not a player
if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
+
//never attack friendly
if (m_creature->IsFriendlyTo(pWho))
return false;
+
//too far away and no free sight?
if (m_creature->IsWithinDistInMap(pWho, GetMaxPlayerDistance()) && m_creature->IsWithinLOSInMap(pWho))
{
@@ -72,16 +84,20 @@ bool npc_escortAI::AssistPlayerInCombat(Unit* pWho)
return true;
}
}
+
return false;
}
+
void npc_escortAI::MoveInLineOfSight(Unit* pWho)
{
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(m_creature))
{
if (HasEscortState(STATE_ESCORT_ESCORTING) && AssistPlayerInCombat(pWho))
return;
+
if (!m_creature->canFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE)
return;
+
if (m_creature->IsHostileTo(pWho))
{
float fAttackRadius = m_creature->GetAttackDistance(pWho);
@@ -101,15 +117,17 @@ void npc_escortAI::MoveInLineOfSight(Unit* pWho)
}
}
}
+
void npc_escortAI::JustDied(Unit* pKiller)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING) || !m_uiPlayerGUID || !m_pQuestForEscort)
return;
+
if (Player* pPlayer = GetPlayerForEscort())
{
if (Group* pGroup = pPlayer->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
if (Player* pMember = pRef->getSource())
{
@@ -125,29 +143,37 @@ void npc_escortAI::JustDied(Unit* pKiller)
}
}
}
+
void npc_escortAI::JustRespawned()
{
m_uiEscortState = STATE_ESCORT_NONE;
+
if (!IsCombatMovement())
SetCombatMovement(true);
+
//add a small delay before going to first waypoint, normal in near all cases
m_uiWPWaitTimer = 2500;
+
if (m_creature->getFaction() != m_creature->GetCreatureInfo()->faction_A)
me->RestoreFaction();
+
Reset();
}
+
void npc_escortAI::ReturnToLastPoint()
{
float x, y, z, o;
m_creature->GetHomePosition(x, y, z, o);
m_creature->GetMotionMaster()->MovePoint(POINT_LAST_POINT, x, y, z);
}
+
void npc_escortAI::EnterEvadeMode()
{
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
m_creature->SetLootRecipient(NULL);
+
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
AddEscortState(STATE_ESCORT_RETURNING);
@@ -156,17 +182,20 @@ void npc_escortAI::EnterEvadeMode()
}
else
m_creature->GetMotionMaster()->MoveTargetedHome();
+
Reset();
}
+
bool npc_escortAI::IsPlayerOrGroupInRange()
{
if (Player* pPlayer = GetPlayerForEscort())
{
if (Group* pGroup = pPlayer->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
Player* pMember = pRef->getSource();
+
if (pMember && m_creature->IsWithinDistInMap(pMember, GetMaxPlayerDistance()))
{
return true;
@@ -182,6 +211,7 @@ bool npc_escortAI::IsPlayerOrGroupInRange()
}
return false;
}
+
void npc_escortAI::UpdateAI(const uint32 uiDiff)
{
//Waypoint Updating
@@ -195,15 +225,20 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
if (DespawnAtEnd)
{
debug_log("TSCR: EscortAI reached end of waypoints");
+
if (m_bCanReturnToStart)
{
float fRetX, fRetY, fRetZ;
m_creature->GetRespawnCoord(fRetX, fRetY, fRetZ);
+
m_creature->GetMotionMaster()->MovePoint(POINT_HOME, fRetX, fRetY, fRetZ);
+
m_uiWPWaitTimer = 0;
+
debug_log("TSCR: EscortAI are returning home to spawn location: %u, %f, %f, %f", POINT_HOME, fRetX, fRetY, fRetZ);
return;
}
+
if (m_bCanInstantRespawn)
{
m_creature->setDeathState(JUST_DIED);
@@ -211,25 +246,31 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
}
else
m_creature->ForcedDespawn();
+
return;
}
else
{
debug_log("TSCR: EscortAI reached end of waypoints with Despawn off");
+
return;
}
}
+
if (!HasEscortState(STATE_ESCORT_PAUSED))
{
m_creature->GetMotionMaster()->MovePoint(CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
debug_log("TSCR: EscortAI start waypoint %u (%f, %f, %f).", CurrentWP->id, CurrentWP->x, CurrentWP->y, CurrentWP->z);
+
WaypointStart(CurrentWP->id);
+
m_uiWPWaitTimer = 0;
}
}
else
m_uiWPWaitTimer -= uiDiff;
}
+
//Check if player or any member of his group is within range
if (HasEscortState(STATE_ESCORT_ESCORTING) && m_uiPlayerGUID && !m_creature->getVictim() && !HasEscortState(STATE_ESCORT_RETURNING))
{
@@ -238,6 +279,7 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
if (DespawnAtFar && !IsPlayerOrGroupInRange())
{
debug_log("TSCR: EscortAI failed because player/group was to far away or not found");
+
if (m_bCanInstantRespawn)
{
m_creature->setDeathState(JUST_DIED);
@@ -245,39 +287,49 @@ void npc_escortAI::UpdateAI(const uint32 uiDiff)
}
else
m_creature->ForcedDespawn();
+
return;
}
+
m_uiPlayerCheckTimer = 1000;
}
else
m_uiPlayerCheckTimer -= uiDiff;
}
+
UpdateEscortAI(uiDiff);
}
+
void npc_escortAI::UpdateEscortAI(const uint32 uiDiff)
{
if (CanMelee && UpdateVictim())
DoMeleeAttackIfReady();
}
+
void npc_escortAI::MovementInform(uint32 uiMoveType, uint32 uiPointId)
{
if (uiMoveType != POINT_MOTION_TYPE || !HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
//Combat start position reached, continue waypoint movement
if (uiPointId == POINT_LAST_POINT)
{
debug_log("TSCR: EscortAI has returned to original position before combat");
+
if (m_bIsRunning && m_creature->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
else if (!m_bIsRunning && !m_creature->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
RemoveEscortState(STATE_ESCORT_RETURNING);
+
if (!m_uiWPWaitTimer)
m_uiWPWaitTimer = 1;
}
else if (uiPointId == POINT_HOME)
{
debug_log("TSCR: EscortAI has returned to original home location and will continue from beginning of waypoint list.");
+
CurrentWP = WaypointList.begin();
m_uiWPWaitTimer = 1;
}
@@ -289,13 +341,18 @@ void npc_escortAI::MovementInform(uint32 uiMoveType, uint32 uiPointId)
error_log("TSCR ERROR: EscortAI reached waypoint out of order %u, expected %u", uiPointId, CurrentWP->id);
return;
}
+
debug_log("TSCR: EscortAI Waypoint %u reached", CurrentWP->id);
+
//Call WP function
WaypointReached(CurrentWP->id);
+
m_uiWPWaitTimer = CurrentWP->WaitTimeMs + 1;
+
++CurrentWP;
}
}
+
/*
void npc_escortAI::OnPossess(bool apply)
{
@@ -314,10 +371,13 @@ void npc_escortAI::OnPossess(bool apply)
}
}
*/
+
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);
+
// i think SD2 no longer uses this function
ScriptWP = true;
/*PointMovement wp;
@@ -329,18 +389,23 @@ void npc_escortAI::AddWaypoint(uint32 id, float x, float y, float z, uint32 Wait
wp.m_uiWaitTime = WaitTimeMs;
PointMovementMap[wp.m_uiCreatureEntry].push_back(wp);*/
}
+
void npc_escortAI::FillPointMovementListForCreature()
{
std::vector<ScriptPointMove> const &pPointsEntries = pSystemMgr.GetPointMoveList(m_creature->GetEntry());
+
if (pPointsEntries.empty())
return;
+
std::vector<ScriptPointMove>::const_iterator itr;
+
for (itr = pPointsEntries.begin(); itr != pPointsEntries.end(); ++itr)
{
Escort_Waypoint pPoint(itr->uiPointId, itr->fX, itr->fY, itr->fZ, itr->uiWaitTime);
WaypointList.push_back(pPoint);
}
}
+
void npc_escortAI::SetRun(bool bRun)
{
if (bRun)
@@ -359,6 +424,7 @@ void npc_escortAI::SetRun(bool bRun)
}
m_bIsRunning = bRun;
}
+
//TODO: get rid of this many variables passed in function.
void npc_escortAI::Start(bool bIsActiveAttacker, bool bRun, uint64 uiPlayerGUID, const Quest* pQuest, bool bInstantRespawn, bool bCanLoopPath)
{
@@ -367,52 +433,70 @@ void npc_escortAI::Start(bool bIsActiveAttacker, bool bRun, uint64 uiPlayerGUID,
error_log("TSCR ERROR: EscortAI attempt to Start while in combat.");
return;
}
+
if (HasEscortState(STATE_ESCORT_ESCORTING))
{
error_log("TSCR: EscortAI attempt to Start while already escorting.");
return;
}
+
if(!ScriptWP) // sd2 never adds wp in script, but tc does
{
+
if (!WaypointList.empty())
WaypointList.clear();
+
FillPointMovementListForCreature();
+
}
+
if (WaypointList.empty())
{
error_db_log("TSCR: EscortAI Start with 0 waypoints (possible missing entry in script_waypoint).");
return;
}
+
//set variables
m_bIsActiveAttacker = bIsActiveAttacker;
m_bIsRunning = bRun;
+
m_uiPlayerGUID = uiPlayerGUID;
m_pQuestForEscort = pQuest;
+
m_bCanInstantRespawn = bInstantRespawn;
m_bCanReturnToStart = bCanLoopPath;
+
if (m_bCanReturnToStart && m_bCanInstantRespawn)
debug_log("TSCR: EscortAI is set to return home after waypoint end and instant respawn at waypoint end. Creature will never despawn.");
+
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
m_creature->GetMotionMaster()->MovementExpired();
m_creature->GetMotionMaster()->MoveIdle();
debug_log("TSCR: EscortAI start with WAYPOINT_MOTION_TYPE, changed to MoveIdle.");
}
+
//disable npcflags
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
+
debug_log("TSCR: EscortAI started with %u waypoints. ActiveAttacker = %d, Run = %d, PlayerGUID = %u", WaypointList.size(), m_bIsActiveAttacker, m_bIsRunning, m_uiPlayerGUID);
+
CurrentWP = WaypointList.begin();
+
//Set initial speed
if (m_bIsRunning)
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
else
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
AddEscortState(STATE_ESCORT_ESCORTING);
}
+
void npc_escortAI::SetEscortPaused(bool bPaused)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (bPaused)
AddEscortState(STATE_ESCORT_PAUSED);
else
diff --git a/src/bindings/scripts/base/escort_ai.h b/src/bindings/scripts/base/escort_ai.h
index 89a0fc596e8..d0cb55b100a 100644
--- a/src/bindings/scripts/base/escort_ai.h
+++ b/src/bindings/scripts/base/escort_ai.h
@@ -1,10 +1,14 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_ESCORTAI_H
#define SC_ESCORTAI_H
+
#include "../system/system.h"
+
#define DEFAULT_MAX_PLAYER_DISTANCE 50
+
struct Escort_Waypoint
{
Escort_Waypoint(uint32 _id, float _x, float _y, float _z, uint32 _w)
@@ -15,12 +19,14 @@ struct Escort_Waypoint
z = _z;
WaitTimeMs = _w;
}
+
uint32 id;
float x;
float y;
float z;
uint32 WaitTimeMs;
};
+
enum eEscortState
{
STATE_ESCORT_NONE = 0x000, //nothing in progress
@@ -28,53 +34,76 @@ enum eEscortState
STATE_ESCORT_RETURNING = 0x002, //escort is returning after being in combat
STATE_ESCORT_PAUSED = 0x004 //will not proceed with waypoints before state is removed
};
+
struct TRINITY_DLL_DECL npc_escortAI : public ScriptedAI
{
public:
explicit npc_escortAI(Creature* pCreature);
~npc_escortAI() {}
+
// CreatureAI functions
void AttackStart(Unit* who);
+
void MoveInLineOfSight(Unit* who);
+
void JustDied(Unit*);
+
void JustRespawned();
+
void ReturnToLastPoint();
+
void EnterEvadeMode();
+
void UpdateAI(const uint32); //the "internal" update, calls UpdateEscortAI()
virtual void UpdateEscortAI(const uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
+
void MovementInform(uint32, uint32);
+
// EscortAI functions
void AddWaypoint(uint32 id, float x, float y, float z, uint32 WaitTimeMs = 0);
+
virtual void WaypointReached(uint32 uiPointId) = 0;
virtual void WaypointStart(uint32 uiPointId) {}
+
void Start(bool bIsActiveAttacker = true, bool bRun = false, uint64 uiPlayerGUID = 0, const Quest* pQuest = NULL, bool bInstantRespawn = false, bool bCanLoopPath = false);
+
void SetRun(bool bRun = true);
void SetEscortPaused(bool uPaused);
+
bool HasEscortState(uint32 uiEscortState) { return (m_uiEscortState & uiEscortState); }
+
void SetMaxPlayerDistance(float newMax) { MaxPlayerDistance = newMax; }
float GetMaxPlayerDistance() { return MaxPlayerDistance; }
+
void SetCanMelee(bool usemelee) { CanMelee = usemelee; }
void SetDespawnAtEnd(bool despawn) { DespawnAtEnd = despawn; }
void SetDespawnAtFar(bool despawn) { DespawnAtFar = despawn; }
bool GetAttack() { return m_bIsActiveAttacker; }//used in EnterEvadeMode override
void SetCanAttack(bool attack) { m_bIsActiveAttacker = attack; }
uint64 GetEventStarterGUID() { return m_uiPlayerGUID; }
+
protected:
Player* GetPlayerForEscort() { return (Player*)Unit::GetUnit(*m_creature, m_uiPlayerGUID); }
+
private:
bool AssistPlayerInCombat(Unit* pWho);
bool IsPlayerOrGroupInRange();
void FillPointMovementListForCreature();
+
void AddEscortState(uint32 uiEscortState) { m_uiEscortState |= uiEscortState; }
void RemoveEscortState(uint32 uiEscortState) { m_uiEscortState &= ~uiEscortState; }
+
uint64 m_uiPlayerGUID;
uint32 m_uiWPWaitTimer;
uint32 m_uiPlayerCheckTimer;
uint32 m_uiEscortState;
float MaxPlayerDistance;
+
const Quest* m_pQuestForEscort; //generally passed in Start() when regular escort script.
+
std::list<Escort_Waypoint> WaypointList;
std::list<Escort_Waypoint>::iterator CurrentWP;
+
bool m_bIsActiveAttacker; //obsolete, determined by faction.
bool m_bIsRunning; //all creatures are walking by default (has flag MOVEMENTFLAG_WALK)
bool m_bCanInstantRespawn; //if creature should respawn instantly after escort over (if not, database respawntime are used)
diff --git a/src/bindings/scripts/base/follower_ai.cpp b/src/bindings/scripts/base/follower_ai.cpp
index 67c03c396ce..8284af7aec0 100644
--- a/src/bindings/scripts/base/follower_ai.cpp
+++ b/src/bindings/scripts/base/follower_ai.cpp
@@ -1,40 +1,50 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
/* ScriptData
SDName: FollowerAI
SD%Complete: 50
SDComment: This AI is under development
SDCategory: Npc
EndScriptData */
+
#include "precompiled.h"
#include "follower_ai.h"
+
const float MAX_PLAYER_DISTANCE = 100.0f;
+
enum ePoints
{
POINT_COMBAT_START = 0xFFFFFF
};
+
FollowerAI::FollowerAI(Creature* pCreature) : ScriptedAI(pCreature),
m_uiLeaderGUID(0),
m_pQuestForFollow(NULL),
m_uiUpdateFollowTimer(2500),
m_uiFollowState(STATE_FOLLOW_NONE)
{}
+
void FollowerAI::AttackStart(Unit* pWho)
{
if (!pWho)
return;
+
if (m_creature->Attack(pWho, true))
{
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
m_creature->clearUnitState(UNIT_STAT_FOLLOW);
+
if (IsCombatMovement())
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
+
//This part provides assistance to a player that are attacked by pWho, even if out of normal aggro range
//It will cause m_creature to attack pWho that are attacking _any_ player (which has been confirmed may happen also on offi)
//The flag (type_flag) is unconfirmed, but used here for further research and is a good candidate.
@@ -42,15 +52,19 @@ bool FollowerAI::AssistPlayerInCombat(Unit* pWho)
{
if (!pWho || !pWho->getVictim())
return false;
+
//experimental (unknown) flag not present
if (!(m_creature->GetCreatureInfo()->type_flags & CREATURE_TYPEFLAGS_UNK13))
return false;
+
//not a player
if (!pWho->getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself())
return false;
+
//never attack friendly
if (m_creature->IsFriendlyTo(pWho))
return false;
+
//too far away and no free sight?
if (m_creature->IsWithinDistInMap(pWho, MAX_PLAYER_DISTANCE) && m_creature->IsWithinLOSInMap(pWho))
{
@@ -67,16 +81,20 @@ bool FollowerAI::AssistPlayerInCombat(Unit* pWho)
return true;
}
}
+
return false;
}
+
void FollowerAI::MoveInLineOfSight(Unit* pWho)
{
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && pWho->isTargetableForAttack() && pWho->isInAccessiblePlaceFor(m_creature))
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && AssistPlayerInCombat(pWho))
return;
+
if (!m_creature->canFly() && m_creature->GetDistanceZ(pWho) > CREATURE_Z_ATTACK_RANGE)
return;
+
if (m_creature->IsHostileTo(pWho))
{
float fAttackRadius = m_creature->GetAttackDistance(pWho);
@@ -96,16 +114,18 @@ void FollowerAI::MoveInLineOfSight(Unit* pWho)
}
}
}
+
void FollowerAI::JustDied(Unit* pKiller)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || !m_uiLeaderGUID || !m_pQuestForFollow)
return;
+
//TODO: need a better check for quests with time limit.
if (Player* pPlayer = GetLeaderForFollower())
{
if (Group* pGroup = pPlayer->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
if (Player* pMember = pRef->getSource())
{
@@ -121,24 +141,31 @@ void FollowerAI::JustDied(Unit* pKiller)
}
}
}
+
void FollowerAI::JustRespawned()
{
m_uiFollowState = STATE_FOLLOW_NONE;
+
if (!IsCombatMovement())
SetCombatMovement(true);
+
if (m_creature->getFaction() != m_creature->GetCreatureInfo()->faction_A)
m_creature->setFaction(m_creature->GetCreatureInfo()->faction_A);
+
Reset();
}
+
void FollowerAI::EnterEvadeMode()
{
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
m_creature->SetLootRecipient(NULL);
+
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{
debug_log("TSCR: FollowerAI left combat, returning to CombatStartPosition.");
+
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
{
float fPosX, fPosY, fPosZ;
@@ -151,8 +178,10 @@ void FollowerAI::EnterEvadeMode()
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
m_creature->GetMotionMaster()->MoveTargetedHome();
}
+
Reset();
}
+
void FollowerAI::UpdateAI(const uint32 uiDiff)
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS) && !m_creature->getVictim())
@@ -165,21 +194,26 @@ void FollowerAI::UpdateAI(const uint32 uiDiff)
m_creature->ForcedDespawn();
return;
}
+
bool bIsMaxRangeExceeded = true;
+
if (Player* pPlayer = GetLeaderForFollower())
{
if (HasFollowState(STATE_FOLLOW_RETURNING))
{
debug_log("TSCR: FollowerAI is returning to leader.");
+
RemoveFollowState(STATE_FOLLOW_RETURNING);
m_creature->GetMotionMaster()->MoveFollow(pPlayer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
return;
}
+
if (Group* pGroup = pPlayer->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
Player* pMember = pRef->getSource();
+
if (pMember && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
{
bIsMaxRangeExceeded = false;
@@ -193,29 +227,36 @@ void FollowerAI::UpdateAI(const uint32 uiDiff)
bIsMaxRangeExceeded = false;
}
}
+
if (bIsMaxRangeExceeded)
{
debug_log("TSCR: FollowerAI failed because player/group was to far away or not found");
m_creature->ForcedDespawn();
return;
}
+
m_uiUpdateFollowTimer = 1000;
}
else
m_uiUpdateFollowTimer -= uiDiff;
}
+
UpdateFollowerAI(uiDiff);
}
+
void FollowerAI::UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId)
{
if (uiMotionType != POINT_MOTION_TYPE || !HasFollowState(STATE_FOLLOW_INPROGRESS))
return;
+
if (uiPointId == POINT_COMBAT_START)
{
if (GetLeaderForFollower())
@@ -227,6 +268,7 @@ void FollowerAI::MovementInform(uint32 uiMotionType, uint32 uiPointId)
m_creature->ForcedDespawn();
}
}
+
void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const Quest* pQuest)
{
if (m_creature->getVictim())
@@ -234,27 +276,37 @@ void FollowerAI::StartFollow(Player* pLeader, uint32 uiFactionForFollower, const
debug_log("TSCR: FollowerAI attempt to StartFollow while in combat.");
return;
}
+
if (HasFollowState(STATE_FOLLOW_INPROGRESS))
{
error_log("TSCR: FollowerAI attempt to StartFollow while already following.");
return;
}
+
//set variables
m_uiLeaderGUID = pLeader->GetGUID();
+
if (uiFactionForFollower)
m_creature->setFaction(uiFactionForFollower);
+
m_pQuestForFollow = pQuest;
+
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
debug_log("TSCR: FollowerAI start with WAYPOINT_MOTION_TYPE, set to MoveIdle.");
}
+
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
+
AddFollowState(STATE_FOLLOW_INPROGRESS);
+
m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+
debug_log("TSCR: FollowerAI start follow %s (GUID %u)", pLeader->GetName(), m_uiLeaderGUID);
}
+
Player* FollowerAI::GetLeaderForFollower()
{
if (Player* pLeader = Unit::GetPlayer(m_uiLeaderGUID))
@@ -265,9 +317,10 @@ Player* FollowerAI::GetLeaderForFollower()
{
if (Group* pGroup = pLeader->GetGroup())
{
- for (GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
+ for(GroupReference* pRef = pGroup->GetFirstMember(); pRef != NULL; pRef = pRef->next())
{
Player* pMember = pRef->getSource();
+
if (pMember && pMember->isAlive() && m_creature->IsWithinDistInMap(pMember, MAX_PLAYER_DISTANCE))
{
debug_log("TSCR: FollowerAI GetLeader changed and returned new leader.");
@@ -279,18 +332,22 @@ Player* FollowerAI::GetLeaderForFollower()
}
}
}
+
debug_log("TSCR: FollowerAI GetLeader can not find suitable leader.");
return NULL;
}
+
void FollowerAI::SetFollowComplete(bool bWithEndEvent)
{
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
{
m_creature->clearUnitState(UNIT_STAT_FOLLOW);
+
m_creature->StopMoving();
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
}
+
if (bWithEndEvent)
AddFollowState(STATE_FOLLOW_POSTEVENT);
else
@@ -298,18 +355,23 @@ void FollowerAI::SetFollowComplete(bool bWithEndEvent)
if (HasFollowState(STATE_FOLLOW_POSTEVENT))
RemoveFollowState(STATE_FOLLOW_POSTEVENT);
}
+
AddFollowState(STATE_FOLLOW_COMPLETE);
}
+
void FollowerAI::SetFollowPaused(bool bPaused)
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS) || HasFollowState(STATE_FOLLOW_COMPLETE))
return;
+
if (bPaused)
{
AddFollowState(STATE_FOLLOW_PAUSED);
+
if (m_creature->hasUnitState(UNIT_STAT_FOLLOW))
{
m_creature->clearUnitState(UNIT_STAT_FOLLOW);
+
m_creature->StopMoving();
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
@@ -318,6 +380,7 @@ void FollowerAI::SetFollowPaused(bool bPaused)
else
{
RemoveFollowState(STATE_FOLLOW_PAUSED);
+
if (Player* pLeader = GetLeaderForFollower())
m_creature->GetMotionMaster()->MoveFollow(pLeader, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
}
diff --git a/src/bindings/scripts/base/follower_ai.h b/src/bindings/scripts/base/follower_ai.h
index 2a7ab778629..289efd4a6b0 100644
--- a/src/bindings/scripts/base/follower_ai.h
+++ b/src/bindings/scripts/base/follower_ai.h
@@ -1,9 +1,12 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_FOLLOWERAI_H
#define SC_FOLLOWERAI_H
+
#include "../system/system.h"
+
enum eFollowState
{
STATE_FOLLOW_NONE = 0x000,
@@ -14,33 +17,51 @@ enum eFollowState
STATE_FOLLOW_PREEVENT = 0x010, //not implemented (allow pre event to run, before follow is initiated)
STATE_FOLLOW_POSTEVENT = 0x020 //can be set at complete and allow post event to run
};
+
class TRINITY_DLL_DECL FollowerAI : public ScriptedAI
{
public:
explicit FollowerAI(Creature* pCreature);
~FollowerAI() {}
+
//virtual void WaypointReached(uint32 uiPointId) = 0;
+
void MovementInform(uint32 uiMotionType, uint32 uiPointId);
+
void AttackStart(Unit*);
+
void MoveInLineOfSight(Unit*);
+
void EnterEvadeMode();
+
void JustDied(Unit*);
+
void JustRespawned();
+
void UpdateAI(const uint32); //the "internal" update, calls UpdateFollowerAI()
virtual void UpdateFollowerAI(const uint32); //used when it's needed to add code in update (abilities, scripted events, etc)
+
void StartFollow(Player* pPlayer, uint32 uiFactionForFollower = 0, const Quest* pQuest = NULL);
+
void SetFollowPaused(bool bPaused); //if special event require follow mode to hold/resume during the follow
void SetFollowComplete(bool bWithEndEvent = false);
+
bool HasFollowState(uint32 uiFollowState) { return (m_uiFollowState & uiFollowState); }
+
protected:
Player* GetLeaderForFollower();
+
private:
void AddFollowState(uint32 uiFollowState) { m_uiFollowState |= uiFollowState; }
void RemoveFollowState(uint32 uiFollowState) { m_uiFollowState &= ~uiFollowState; }
+
bool AssistPlayerInCombat(Unit* pWho);
+
uint64 m_uiLeaderGUID;
uint32 m_uiUpdateFollowTimer;
uint32 m_uiFollowState;
+
const Quest* m_pQuestForFollow; //normally we have a quest
};
+
#endif
diff --git a/src/bindings/scripts/base/guard_ai.cpp b/src/bindings/scripts/base/guard_ai.cpp
index badd763a78b..1001d3a1170 100644
--- a/src/bindings/scripts/base/guard_ai.cpp
+++ b/src/bindings/scripts/base/guard_ai.cpp
@@ -13,67 +13,84 @@
* along 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
+
#define SAY_GUARD_SIL_AGGRO1 -1070001
#define SAY_GUARD_SIL_AGGRO2 -1070002
#define SAY_GUARD_SIL_AGGRO3 -1070003
+
guardAI::guardAI(Creature* pCreature) : ScriptedAI(pCreature),
GlobalCooldown(0),
BuffTimer(0)
{}
+
void guardAI::Reset()
{
GlobalCooldown = 0;
BuffTimer = 0; //Rebuff as soon as we can
}
+
void guardAI::EnterCombat(Unit *who)
{
if (m_creature->GetEntry() == 15184)
DoScriptText(RAND(SAY_GUARD_SIL_AGGRO1,SAY_GUARD_SIL_AGGRO2,SAY_GUARD_SIL_AGGRO3), m_creature, who);
+
if (SpellEntry const *spell = m_creature->reachWithSpellAttack(who))
DoCastSpell(who, spell);
}
+
void guardAI::JustDied(Unit *Killer)
{
//Send Zone Under Attack message to the LocalDefense and WorldDefense Channels
if (Player* pKiller = Killer->GetCharmerOrOwnerPlayerOrPlayerItself())
m_creature->SendZoneUnderAttackMessage(pKiller);
}
+
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() && !m_creature->isInCombat())
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 (!UpdateVictim())
return;
+
// Make sure our attack is ready and we arn't currently casting
if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
{
@@ -82,22 +99,27 @@ void guardAI::UpdateAI(const uint32 diff)
{
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();
}
}
@@ -108,12 +130,15 @@ void guardAI::UpdateAI(const uint32 diff)
{
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, NOMINAL_MELEE_RANGE, 0, SELECT_EFFECT_DONTCARE);
+
//Found a spell, check if we arn't on cooldown
if (info && !GlobalCooldown)
{
@@ -123,11 +148,14 @@ void guardAI::UpdateAI(const uint32 diff)
(*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)
{
@@ -139,6 +167,7 @@ void guardAI::UpdateAI(const uint32 diff)
}
}
}
+
void guardAI::DoReplyToTextEmote(uint32 em)
{
switch(em)
@@ -151,11 +180,13 @@ void guardAI::DoReplyToTextEmote(uint32 em)
case TEXTEMOTE_CHICKEN: m_creature->HandleEmoteCommand(EMOTE_ONESHOT_POINT); break;
}
}
+
void guardAI_orgrimmar::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (pPlayer->GetTeam()==HORDE)
DoReplyToTextEmote(text_emote);
}
+
void guardAI_stormwind::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (pPlayer->GetTeam() == ALLIANCE)
diff --git a/src/bindings/scripts/base/guard_ai.h b/src/bindings/scripts/base/guard_ai.h
index dd4e414d1ae..a7fff32e3ab 100644
--- a/src/bindings/scripts/base/guard_ai.h
+++ b/src/bindings/scripts/base/guard_ai.h
@@ -1,31 +1,44 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_GUARDAI_H
#define SC_GUARDAI_H
+
#define GENERIC_CREATURE_COOLDOWN 5000
+
struct TRINITY_DLL_DECL guardAI : public ScriptedAI
{
public:
explicit guardAI(Creature* pCreature);
~guardAI() {}
+
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 EnterCombat(Unit *who);
+
void JustDied(Unit *Killer);
+
void UpdateAI(const uint32 diff);
+
//common used for guards in main cities
void DoReplyToTextEmote(uint32 em);
};
+
struct TRINITY_DLL_DECL guardAI_orgrimmar : public guardAI
{
guardAI_orgrimmar(Creature *c) : guardAI(c) {}
+
void ReceiveEmote(Player *player, uint32 text_emote);
};
+
struct TRINITY_DLL_DECL guardAI_stormwind : public guardAI
{
guardAI_stormwind(Creature *c) : guardAI(c) {}
+
void ReceiveEmote(Player *player, uint32 text_emote);
};
#endif
diff --git a/src/bindings/scripts/base/simple_ai.cpp b/src/bindings/scripts/base/simple_ai.cpp
index c607885f4d3..60511d163b7 100644
--- a/src/bindings/scripts/base/simple_ai.cpp
+++ b/src/bindings/scripts/base/simple_ai.cpp
@@ -13,14 +13,17 @@
* along 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
@@ -30,6 +33,7 @@ SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
Aggro_Sound[0] = 0;
Aggro_Sound[1] = 0;
Aggro_Sound[2] = 0;
+
Death_TextId[0] = 0;
Death_TextId[1] = 0;
Death_TextId[2] = 0;
@@ -38,6 +42,7 @@ SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
Death_Sound[2] = 0;
Death_Spell = 0;
Death_Target_Type = 0;
+
Kill_TextId[0] = 0;
Kill_TextId[1] = 0;
Kill_TextId[2] = 0;
@@ -46,12 +51,16 @@ SimpleAI::SimpleAI(Creature *c) : ScriptedAI(c)
Kill_Sound[2] = 0;
Kill_Spell = 0;
Kill_Target_Type = 0;
+
memset(Spell,0,sizeof(Spell));
+
EnterEvadeMode();
}
+
void SimpleAI::Reset()
{
}
+
void SimpleAI::EnterCombat(Unit *who)
{
//Reset cast timers
@@ -85,26 +94,35 @@ void SimpleAI::EnterCombat(Unit *who)
if (Spell[9].First_Cast >= 0)
Spell_Timer[9] = Spell[9].First_Cast;
else Spell_Timer[9] = 1000;
+
uint8 random_text = urand(0,2);
+
//Random text
if (Aggro_TextId[random_text])
DoScriptText(Aggro_TextId[random_text], m_creature, who);
+
//Random sound
if (Aggro_Sound[random_text])
DoPlaySoundToSet(m_creature, Aggro_Sound[random_text]);
}
+
void SimpleAI::KilledUnit(Unit *victim)
{
uint8 random_text = urand(0,2);
+
//Random yell
if (Kill_TextId[random_text])
DoScriptText(Kill_TextId[random_text], m_creature, 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:
@@ -126,25 +144,33 @@ void SimpleAI::KilledUnit(Unit *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;
+
uint8 random_text = urand(0,2);
+
//Random yell
if (Death_TextId[random_text])
DoScriptText(Death_TextId[random_text], m_creature, 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:
@@ -166,30 +192,36 @@ void SimpleAI::DamageTaken(Unit *killer, uint32 &damage)
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 (!UpdateVictim())
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:
@@ -208,29 +240,39 @@ void SimpleAI::UpdateAI(const uint32 diff)
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
uint8 random_text = urand(0,2);
+
//Random yell
if (Spell[i].TextId[random_text])
DoScriptText(Spell[i].TextId[random_text], m_creature, 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/base/simple_ai.h b/src/bindings/scripts/base/simple_ai.h
index 308f5a475ec..3a2e8a9341a 100644
--- a/src/bindings/scripts/base/simple_ai.h
+++ b/src/bindings/scripts/base/simple_ai.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_SIMPLEAI_H
#define SC_SIMPLEAI_H
+
enum CastTarget
{
CAST_SELF = 0, //Self cast
@@ -11,29 +13,41 @@ enum CastTarget
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 TRINITY_DLL_DECL SimpleAI : public ScriptedAI
{
SimpleAI(Creature *c);// : ScriptedAI(c);
+
void Reset();
+
void EnterCombat(Unit *who);
+
void KilledUnit(Unit *victim);
+
void DamageTaken(Unit *killer, uint32 &damage);
+
void UpdateAI(const uint32 diff);
+
public:
+
int32 Aggro_TextId[3];
uint32 Aggro_Sound[3];
+
int32 Death_TextId[3];
uint32 Death_Sound[3];
uint32 Death_Spell;
uint32 Death_Target_Type;
+
int32 Kill_TextId[3];
uint32 Kill_Sound[3];
uint32 Kill_Spell;
uint32 Kill_Target_Type;
+
struct SimpleAI_Spell
{
uint32 Spell_Id; //Spell ID to cast
@@ -43,12 +57,15 @@ public:
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?
int32 TextId[3];
uint32 Text_Sound[3];
}Spell[10];
+
protected:
uint32 Spell_Timer[10];
};
+
#endif
diff --git a/src/bindings/scripts/include/precompiled.cpp b/src/bindings/scripts/include/precompiled.cpp
index 49f792c8fd8..75bfae9e1c4 100644
--- a/src/bindings/scripts/include/precompiled.cpp
+++ b/src/bindings/scripts/include/precompiled.cpp
@@ -1,5 +1,6 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#include "precompiled.h"
diff --git a/src/bindings/scripts/include/precompiled.h b/src/bindings/scripts/include/precompiled.h
index e5fc9a3eeb9..3f5bc7e7265 100644
--- a/src/bindings/scripts/include/precompiled.h
+++ b/src/bindings/scripts/include/precompiled.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_PRECOMPILED_H
#define SC_PRECOMPILED_H
+
#include "../ScriptMgr.h"
#include "Cell.h"
#include "CellImpl.h"
@@ -18,6 +20,7 @@
#include "Chat.h"
#include "DBCStructure.h"
#include "DBCStores.h"
+
#ifdef WIN32
#include <windows.h>
BOOL APIENTRY DllMain( HANDLE hModule,
@@ -28,5 +31,6 @@ LPVOID lpReserved
return true;
}
#endif
+
#endif
diff --git a/src/bindings/scripts/include/sc_creature.cpp b/src/bindings/scripts/include/sc_creature.cpp
index 7df3b66d8d7..21c8842fb44 100644
--- a/src/bindings/scripts/include/sc_creature.cpp
+++ b/src/bindings/scripts/include/sc_creature.cpp
@@ -4,20 +4,23 @@
*
* 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 "ObjectMgr.h"
#include "TemporarySummon.h"
+
// Spell summary for ScriptedAI::SelectSpell
struct TSpellSummary
{
uint8 Targets; // set of enum SelectTarget
uint8 Effects; // set of enum SelectEffect
} *SpellSummary;
+
void SummonList::DoZoneInCombat(uint32 entry)
{
- for (iterator i = begin(); i != end(); )
+ for(iterator i = begin(); i != end();)
{
Creature *summon = Unit::GetCreature(*m_creature, *i);
++i;
@@ -26,9 +29,10 @@ void SummonList::DoZoneInCombat(uint32 entry)
summon->AI()->DoZoneInCombat();
}
}
+
void SummonList::DoAction(uint32 entry, uint32 info)
{
- for (iterator i = begin(); i != end(); )
+ for(iterator i = begin(); i != end();)
{
Creature *summon = Unit::GetCreature(*m_creature, *i);
++i;
@@ -37,9 +41,10 @@ void SummonList::DoAction(uint32 entry, uint32 info)
summon->AI()->DoAction(info);
}
}
+
void SummonList::DespawnEntry(uint32 entry)
{
- for (iterator i = begin(); i != end(); )
+ for(iterator i = begin(); i != end();)
{
Creature *summon = Unit::GetCreature(*m_creature, *i);
if(!summon)
@@ -54,6 +59,7 @@ void SummonList::DespawnEntry(uint32 entry)
++i;
}
}
+
void SummonList::DespawnAll()
{
while(!empty())
@@ -74,6 +80,7 @@ void SummonList::DespawnAll()
}
}
}
+
ScriptedAI::ScriptedAI(Creature* pCreature) : CreatureAI(pCreature),
m_creature(pCreature),
IsFleeing(false),
@@ -82,18 +89,22 @@ ScriptedAI::ScriptedAI(Creature* pCreature) : CreatureAI(pCreature),
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
void ScriptedAI::AttackStartNoMove(Unit* pWho)
{
if (!pWho)
return;
+
if(m_creature->Attack(pWho, false))
DoStartNoMovement(pWho);
}
+
void ScriptedAI::UpdateAI(const uint32 uiDiff)
{
//Check if we have a current target
if (!UpdateVictim())
return;
+
if (m_creature->isAttackReady())
{
//If we are within range melee the target
@@ -104,180 +115,232 @@ void ScriptedAI::UpdateAI(const uint32 uiDiff)
}
}
}
+
void ScriptedAI::DoStartMovement(Unit* pVictim, float fDistance, float fAngle)
{
if (pVictim)
m_creature->GetMotionMaster()->MoveChase(pVictim, fDistance, fAngle);
}
+
void ScriptedAI::DoStartNoMovement(Unit* pVictim)
{
if (!pVictim)
return;
+
m_creature->GetMotionMaster()->MoveIdle();
}
+
void ScriptedAI::DoStopAttack()
{
if (m_creature->getVictim())
m_creature->AttackStop();
}
+
void ScriptedAI::DoCastSpell(Unit* pTarget, SpellEntry const* pSpellInfo, bool bTriggered)
{
if (!pTarget || m_creature->IsNonMeleeSpellCasted(false))
return;
+
m_creature->StopMoving();
m_creature->CastSpell(pTarget, pSpellInfo, bTriggered);
}
+
void ScriptedAI::DoPlaySoundToSet(WorldObject* pSource, uint32 uiSoundId)
{
if (!pSource)
return;
+
if (!GetSoundEntriesStore()->LookupEntry(uiSoundId))
{
error_log("TSCR: Invalid soundId %u used in DoPlaySoundToSet (Source: TypeId %u, GUID %u)", uiSoundId, pSource->GetTypeId(), pSource->GetGUIDLow());
return;
}
+
pSource->PlayDirectSound(uiSoundId);
}
+
Creature* ScriptedAI::DoSpawnCreature(uint32 uiId, float fX, float fY, float fZ, float fAngle, uint32 uiType, uint32 uiDespawntime)
{
return m_creature->SummonCreature(uiId, m_creature->GetPositionX()+fX, m_creature->GetPositionY()+fY, m_creature->GetPositionZ()+fZ, fAngle, (TempSummonType)uiType, uiDespawntime);
}
+
Unit* ScriptedAI::SelectUnit(SelectAggroTarget target, uint32 uiPosition)
{
//ThreatList m_threatlist;
std::list<HostilReference*>& threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator itr = threatlist.begin();
std::list<HostilReference*>::reverse_iterator ritr = threatlist.rbegin();
+
if (uiPosition >= threatlist.size() || !threatlist.size())
return NULL;
+
switch (target)
{
case SELECT_TARGET_RANDOM:
advance (itr , uiPosition + (rand() % (threatlist.size() - uiPosition ) ));
return Unit::GetUnit((*m_creature),(*itr)->getUnitGuid());
break;
+
case SELECT_TARGET_TOPAGGRO:
advance (itr , uiPosition);
return Unit::GetUnit((*m_creature),(*itr)->getUnitGuid());
break;
+
case SELECT_TARGET_BOTTOMAGGRO:
advance (ritr , uiPosition);
return Unit::GetUnit((*m_creature),(*ritr)->getUnitGuid());
break;
}
+
return NULL;
}
+
SpellEntry const* ScriptedAI::SelectSpell(Unit* pTarget, int32 uiSchool, int32 uiMechanic, SelectTargetType selectTargets, uint32 uiPowerCostMin, uint32 uiPowerCostMax, float fRangeMin, float fRangeMax, SelectEffect selectEffects)
{
//No target so we can't cast
if (!pTarget)
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* apSpell[CREATURE_MAX_SPELLS];
memset(apSpell, 0, sizeof(SpellEntry*)*CREATURE_MAX_SPELLS);
+
uint32 uiSpellCount = 0;
+
SpellEntry const* pTempSpell;
SpellRangeEntry const* pTempRange;
+
//Check if each spell is viable(set it to null if not)
for (uint32 i = 0; i < CREATURE_MAX_SPELLS; i++)
{
pTempSpell = GetSpellStore()->LookupEntry(m_creature->m_spells[i]);
+
//This spell doesn't exist
if (!pTempSpell)
continue;
+
// Targets and Effects checked first as most used restrictions
//Check the spell targets if specified
if (selectTargets && !(SpellSummary[m_creature->m_spells[i]].Targets & (1 << (selectTargets-1))))
continue;
+
//Check the type of spell if we are looking for a specific spell type
if (selectEffects && !(SpellSummary[m_creature->m_spells[i]].Effects & (1 << (selectEffects-1))))
continue;
+
//Check for school if specified
if (uiSchool >= 0 && pTempSpell->SchoolMask & uiSchool)
continue;
+
//Check for spell mechanic if specified
if (uiMechanic >= 0 && pTempSpell->Mechanic != uiMechanic)
continue;
+
//Make sure that the spell uses the requested amount of power
if (uiPowerCostMin && pTempSpell->manaCost < uiPowerCostMin)
continue;
+
if (uiPowerCostMax && pTempSpell->manaCost > uiPowerCostMax)
continue;
+
//Continue if we don't have the mana to actually cast this spell
if (pTempSpell->manaCost > m_creature->GetPower((Powers)pTempSpell->powerType))
continue;
+
//Get the Range
pTempRange = GetSpellRangeStore()->LookupEntry(pTempSpell->rangeIndex);
+
//Spell has invalid range store so we can't use it
if (!pTempRange)
continue;
+
//Check if the spell meets our range requirements
if (fRangeMin && m_creature->GetSpellMinRangeForTarget(pTarget, pTempRange) < fRangeMin)
continue;
if (fRangeMax && m_creature->GetSpellMaxRangeForTarget(pTarget, pTempRange) > fRangeMax)
continue;
+
//Check if our target is in range
if (m_creature->IsWithinDistInMap(pTarget, m_creature->GetSpellMinRangeForTarget(pTarget, pTempRange)) || !m_creature->IsWithinDistInMap(pTarget, m_creature->GetSpellMaxRangeForTarget(pTarget, pTempRange)))
continue;
+
//All good so lets add it to the spell list
apSpell[uiSpellCount] = pTempSpell;
++uiSpellCount;
}
+
//We got our usable spells so now lets randomly pick one
if (!uiSpellCount)
return NULL;
+
return apSpell[rand()%uiSpellCount];
}
+
bool ScriptedAI::CanCast(Unit* pTarget, SpellEntry const* pSpell, bool bTriggered)
{
//No target so we can't cast
if (!pTarget || !pSpell)
return false;
+
//Silenced so we can't cast
if (!bTriggered && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
return false;
+
//Check for power
if (!bTriggered && me->GetPower((Powers)pSpell->powerType) < pSpell->manaCost)
return false;
+
SpellRangeEntry const* pTempRange = GetSpellRangeStore()->LookupEntry(pSpell->rangeIndex);
+
//Spell has invalid range store so we can't use it
if (!pTempRange)
return false;
+
//Unit is out of range of this spell
if (me->IsInRange(pTarget, m_creature->GetSpellMinRangeForTarget(pTarget, pTempRange), m_creature->GetSpellMaxRangeForTarget(pTarget, pTempRange)))
return false;
+
return true;
}
+
void FillSpellSummary()
{
SpellSummary = new TSpellSummary[GetSpellStore()->GetNumRows()];
+
SpellEntry const* pTempSpell;
- for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+
+ for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
{
SpellSummary[i].Effects = 0;
SpellSummary[i].Targets = 0;
+
pTempSpell = GetSpellStore()->LookupEntry(i);
//This spell doesn't exist
if (!pTempSpell)
continue;
- for (uint32 j = 0; j < 3; ++j)
+
+ for(uint32 j = 0; j < 3; ++j)
{
//Spell targets self
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_CASTER )
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SELF-1);
+
//Spell targets a single enemy
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ENEMY ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_DST_TARGET_ENEMY )
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_ENEMY-1);
+
//Spell targets AoE at enemy
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_AREA_ENEMY_SRC ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_AREA_ENEMY_DST ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_DEST_DYNOBJ_ENEMY )
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_ENEMY-1);
+
//Spell targets an enemy
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ENEMY ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_DST_TARGET_ENEMY ||
@@ -286,16 +349,19 @@ void FillSpellSummary()
pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_DEST_DYNOBJ_ENEMY )
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_ENEMY-1);
+
//Spell targets a single friend(or self)
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_CASTER ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ALLY ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_PARTY )
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_SINGLE_FRIEND-1);
+
//Spell targets aoe friends
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_CASTER ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_TARGET ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER)
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_AOE_FRIEND-1);
+
//Spell targets any friend(or self)
if (pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_CASTER ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_TARGET_ALLY ||
@@ -304,24 +370,28 @@ void FillSpellSummary()
pTempSpell->EffectImplicitTargetA[j] == TARGET_UNIT_PARTY_TARGET ||
pTempSpell->EffectImplicitTargetA[j] == TARGET_SRC_CASTER)
SpellSummary[i].Targets |= 1 << (SELECT_TARGET_ANY_FRIEND-1);
+
//Make sure that this spell includes a damage effect
if (pTempSpell->Effect[j] == SPELL_EFFECT_SCHOOL_DAMAGE ||
pTempSpell->Effect[j] == SPELL_EFFECT_INSTAKILL ||
pTempSpell->Effect[j] == SPELL_EFFECT_ENVIRONMENTAL_DAMAGE ||
pTempSpell->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 (pTempSpell->Effect[j] == SPELL_EFFECT_HEAL ||
pTempSpell->Effect[j] == SPELL_EFFECT_HEAL_MAX_HEALTH ||
pTempSpell->Effect[j] == SPELL_EFFECT_HEAL_MECHANICAL ||
(pTempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA && pTempSpell->EffectApplyAuraName[j]== 8 ))
SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_HEALING-1);
+
//Make sure that this spell applies an aura
if (pTempSpell->Effect[j] == SPELL_EFFECT_APPLY_AURA )
SpellSummary[i].Effects |= 1 << (SELECT_EFFECT_AURA-1);
}
}
}
+
void ScriptedAI::DoResetThreat()
{
if (!m_creature->CanHaveThreatList() || m_creature->getThreatManager().isThreatListEmpty())
@@ -329,33 +399,41 @@ void ScriptedAI::DoResetThreat()
error_log("TSCR: DoResetThreat called for creature that either cannot have threat list or has empty threat list (m_creature entry = %d)", m_creature->GetEntry());
return;
}
+
std::list<HostilReference*>& threatlist = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
+
+ for(std::list<HostilReference*>::iterator itr = threatlist.begin(); itr != threatlist.end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
+
if(pUnit && DoGetThreat(pUnit))
DoModifyThreatPercent(pUnit, -100);
}
}
+
float ScriptedAI::DoGetThreat(Unit* pUnit)
{
if(!pUnit) return 0.0f;
return m_creature->getThreatManager().getThreat(pUnit);
}
+
void ScriptedAI::DoModifyThreatPercent(Unit* pUnit, int32 pct)
{
if(!pUnit) return;
m_creature->getThreatManager().modifyThreatPercent(pUnit, pct);
}
+
void ScriptedAI::DoTeleportTo(float fX, float fY, float fZ, uint32 uiTime)
{
m_creature->Relocate(fX, fY, fZ);
m_creature->SendMonsterMove(fX, fY, fZ, uiTime);
}
+
void ScriptedAI::DoTeleportTo(const float fPos[4])
{
me->NearTeleportTo(fPos[0], fPos[1], fPos[2], fPos[3]);
}
+
void ScriptedAI::DoTeleportPlayer(Unit* pUnit, float fX, float fY, float fZ, float fO)
{
if(!pUnit || pUnit->GetTypeId() != TYPEID_PLAYER)
@@ -364,27 +442,33 @@ void ScriptedAI::DoTeleportPlayer(Unit* pUnit, float fX, float fY, float fZ, flo
error_log("TSCR: 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(), fX, fY, fZ, fO);
return;
}
+
CAST_PLR(pUnit)->TeleportTo(pUnit->GetMapId(), fX, fY, fZ, fO, TELE_TO_NOT_LEAVE_COMBAT);
}
+
void ScriptedAI::DoTeleportAll(float fX, float fY, float fZ, float fO)
{
Map *map = m_creature->GetMap();
if (!map->IsDungeon())
return;
+
Map::PlayerList const &PlayerList = map->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (Player* i_pl = i->getSource())
if (i_pl->isAlive())
i_pl->TeleportTo(m_creature->GetMapId(), fX, fY, fZ, fO, TELE_TO_NOT_LEAVE_COMBAT);
}
+
Unit* ScriptedAI::DoSelectLowestHpFriendly(float fRange, uint32 uiMinHPDiff)
{
Unit* pUnit = NULL;
Trinity::MostHPMissingInRange u_check(m_creature, fRange, uiMinHPDiff);
Trinity::UnitLastSearcher<Trinity::MostHPMissingInRange> searcher(m_creature, pUnit, u_check);
m_creature->VisitNearbyObject(fRange, searcher);
+
return pUnit;
}
+
std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float fRange)
{
std::list<Creature*> pList;
@@ -393,6 +477,7 @@ std::list<Creature*> ScriptedAI::DoFindFriendlyCC(float fRange)
m_creature->VisitNearbyObject(fRange, searcher);
return pList;
}
+
std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float fRange, uint32 uiSpellid)
{
std::list<Creature*> pList;
@@ -401,39 +486,51 @@ std::list<Creature*> ScriptedAI::DoFindFriendlyMissingBuff(float fRange, uint32
m_creature->VisitNearbyObject(fRange, searcher);
return pList;
}
+
Player* ScriptedAI::GetPlayerAtMinimumRange(float fMinimumRange)
{
Player* pPlayer = NULL;
+
CellPair pair(Trinity::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::PlayerAtMinimumRangeAway check(m_creature, fMinimumRange);
Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway> searcher(m_creature, pPlayer, check);
TypeContainerVisitor<Trinity::PlayerSearcher<Trinity::PlayerAtMinimumRangeAway>, GridTypeMapContainer> visitor(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, visitor, *(m_creature->GetMap()));
+
return pPlayer;
}
+
void ScriptedAI::SetEquipmentSlots(bool bLoadDefault, int32 uiMainHand, int32 uiOffHand, int32 uiRanged)
{
if (bLoadDefault)
{
if (CreatureInfo const* pInfo = GetCreatureTemplateStore(m_creature->GetEntry()))
m_creature->LoadEquipment(pInfo->equipmentId,true);
+
return;
}
+
if (uiMainHand >= 0)
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 0, uint32(uiMainHand));
+
if (uiOffHand >= 0)
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, uint32(uiOffHand));
+
if (uiRanged >= 0)
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, uint32(uiRanged));
}
+
void ScriptedAI::SetCombatMovement(bool bCombatMove)
{
m_bCombatMovement = bCombatMove;
}
+
enum eNPCs
{
NPC_BROODLORD = 12017,
@@ -441,6 +538,7 @@ enum eNPCs
NPC_JAN_ALAI = 23578,
NPC_SARTHARION = 28860
};
+
// Hacklike storage used for misc creatures that are expected to evade of outside of a certain area.
// It is assumed the information is found elswehere and can be handled by mangos. So far no luck finding such information/way to extract it.
bool ScriptedAI::EnterEvadeIfOutOfCombatArea(const uint32 uiDiff)
@@ -452,11 +550,14 @@ bool ScriptedAI::EnterEvadeIfOutOfCombatArea(const uint32 uiDiff)
m_uiEvadeCheckCooldown -= uiDiff;
return false;
}
+
if (m_creature->IsInEvadeMode() || !m_creature->getVictim())
return false;
+
float fX = m_creature->GetPositionX();
float fY = m_creature->GetPositionY();
float fZ = m_creature->GetPositionZ();
+
switch(m_creature->GetEntry())
{
case NPC_BROODLORD: // broodlord (not move down stairs)
@@ -479,32 +580,39 @@ bool ScriptedAI::EnterEvadeIfOutOfCombatArea(const uint32 uiDiff)
error_log("TSCR: EnterEvadeIfOutOfCombatArea used for creature entry %u, but does not have any definition.", m_creature->GetEntry());
return false;
}
+
EnterEvadeMode();
return true;
}
+
void Scripted_NoMovementAI::AttackStart(Unit* pWho)
{
if (!pWho)
return;
+
if (m_creature->Attack(pWho, true))
{
DoStartNoMovement(pWho);
}
}
+
BossAI::BossAI(Creature *c, uint32 id) : ScriptedAI(c)
, bossId(id), summons(me), instance(c->GetInstanceData())
, boundary(instance ? instance->GetBossBoundary(id) : NULL)
{
}
+
void BossAI::_Reset()
{
if(!me->isAlive())
return;
+
events.Reset();
summons.DespawnAll();
if(instance)
instance->SetBossState(bossId, NOT_STARTED);
}
+
void BossAI::_JustDied()
{
events.Reset();
@@ -512,6 +620,7 @@ void BossAI::_JustDied()
if(instance)
instance->SetBossState(bossId, DONE);
}
+
void BossAI::_EnterCombat()
{
me->setActive(true);
@@ -519,20 +628,23 @@ void BossAI::_EnterCombat()
if(instance)
instance->SetBossState(bossId, IN_PROGRESS);
}
+
void BossAI::TeleportCheaters()
{
float x, y, z;
me->GetPosition(x, y, z);
std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr)
if((*itr)->getTarget()->GetTypeId() == TYPEID_PLAYER && !CheckBoundary((*itr)->getTarget()))
(*itr)->getTarget()->NearTeleportTo(x, y, z, 0);
}
+
bool BossAI::CheckBoundary(Unit *who)
{
if(!boundary || !who)
return true;
- for (BossBoundaryMap::const_iterator itr = boundary->begin(); itr != boundary->end(); ++itr)
+
+ for(BossBoundaryMap::const_iterator itr = boundary->begin(); itr != boundary->end(); ++itr)
{
switch(itr->first)
{
@@ -570,39 +682,48 @@ bool BossAI::CheckBoundary(Unit *who)
break;
}
}
+
return true;
}
+
void BossAI::JustSummoned(Creature *summon)
{
summons.Summon(summon);
if(me->isInCombat())
DoZoneInCombat(summon);
}
+
void BossAI::SummonedCreatureDespawn(Creature *summon)
{
summons.Despawn(summon);
}
+
#define GOBJECT(x) (const_cast<GameObjectInfo*>(GetGameObjectInfo(x)))
+
void LoadOverridenSQLData()
{
GameObjectInfo *goInfo;
+
// Sunwell Plateau : Kalecgos : Spectral Rift
if(goInfo = GOBJECT(187055))
if(goInfo->type == GAMEOBJECT_TYPE_GOOBER)
goInfo->goober.lockId = 57; // need LOCKTYPE_QUICK_OPEN
+
// Naxxramas : Sapphiron Birth
if(goInfo = GOBJECT(181356))
if(goInfo->type == GAMEOBJECT_TYPE_TRAP)
goInfo->trap.radius = 50;
}
+
void LoadOverridenDBCData()
{
SpellEntry *spellInfo;
- for (uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
+ for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
{
spellInfo = GET_SPELL(i);
if(!spellInfo)
continue;
+
switch(i)
{
// Black Temple : Illidan : Parasitic Shadowfiend Passive
@@ -628,17 +749,23 @@ void LoadOverridenDBCData()
}
}
+
Creature* GetClosestCreatureWithEntry(WorldObject* pSource, uint32 Entry, float MaxSearchRange)
{
Creature* pCreature = NULL;
+
CellPair pair(Trinity::ComputeCellPair(pSource->GetPositionX(), pSource->GetPositionY()));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck creature_check(*pSource, Entry, true, MaxSearchRange);
Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(pSource, pCreature, creature_check);
+
TypeContainerVisitor<Trinity::CreatureLastSearcher<Trinity::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer> creature_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, creature_searcher,*(pSource->GetMap()));
+
return pCreature;
}
diff --git a/src/bindings/scripts/include/sc_creature.h b/src/bindings/scripts/include/sc_creature.h
index a5b4953bb28..91073bf4032 100644
--- a/src/bindings/scripts/include/sc_creature.h
+++ b/src/bindings/scripts/include/sc_creature.h
@@ -4,21 +4,28 @@
*
* 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 "Creature.h"
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
#include "InstanceData.h"
+
#define SCRIPT_CAST_TYPE dynamic_cast
//#define SCRIPT_CAST_TYPE static_cast
+
#define CAST_PLR(a) (SCRIPT_CAST_TYPE<Player*>(a))
#define CAST_CRE(a) (SCRIPT_CAST_TYPE<Creature*>(a))
#define CAST_SUM(a) (SCRIPT_CAST_TYPE<TempSummon*>(a))
#define CAST_PET(a) (SCRIPT_CAST_TYPE<Pet*>(a))
#define CAST_AI(a,b) (SCRIPT_CAST_TYPE<a*>(b))
+
#define GET_SPELL(a) (const_cast<SpellEntry*>(GetSpellStore()->LookupEntry(a)))
+
class ScriptedInstance;
+
class SummonList : public std::list<uint64>
{
public:
@@ -32,125 +39,178 @@ class SummonList : public std::list<uint64>
private:
Creature *m_creature;
};
+
struct TRINITY_DLL_DECL ScriptedAI : public CreatureAI
{
explicit ScriptedAI(Creature* pCreature);
~ScriptedAI() {}
+
//*************
//CreatureAI Functions
//*************
+
void AttackStartNoMove(Unit *target);
+
// Called at any Damage from any attacker (before damage apply)
void DamageTaken(Unit* pDone_by, uint32& uiDamage) {}
+
//Called at World update tick
void UpdateAI(const uint32);
+
//Called at creature death
void JustDied(Unit* who){}
+
//Called at creature killing another unit
void KilledUnit(Unit* who){}
+
// Called when the creature summon successfully other creature
void JustSummoned(Creature* ) {}
+
// Called when a summoned creature is despawned
void SummonedCreatureDespawn(Creature*) {}
+
// Called when hit by a spell
void SpellHit(Unit* caster, const SpellEntry *spell) {}
+
// Called when spell hits a target
void SpellHitTarget(Unit* target, const SpellEntry *spell) {}
+
//Called at waypoint reached or PointMovement end
void MovementInform(uint32 type, uint32 id){}
+
// Called when AI is temporarily replaced or put back when possess is applied or removed
void OnPossess(bool apply) {}
+
//*************
// Variables
//*************
+
//Pointer to creature we are manipulating
Creature* m_creature;
+
//For fleeing
bool IsFleeing;
+
bool HeroicMode;
+
//*************
//Pure virtual functions
//*************
+
//Called at creature reset either by death or evade
void Reset() {}
+
//Called at creature aggro either by MoveInLOS or Attack Start
void EnterCombat(Unit* who) {}
+
//*************
//AI Helper Functions
//*************
+
//Start movement toward victim
void DoStartMovement(Unit* pVictim, float fDistance = 0, float fAngle = 0);
+
//Start no movement on victim
void DoStartNoMovement(Unit* pVictim);
+
//Stop attack of current victim
void DoStopAttack();
+
//Cast spell by spell info
void DoCastSpell(Unit* pTarget, SpellEntry const* pSpellInfo, bool bTriggered = false);
+
//Plays a sound to all nearby players
void DoPlaySoundToSet(WorldObject* pSource, uint32 sound);
+
//Drops all threat to 0%. Does not remove players from the threat list
void DoResetThreat();
+
float DoGetThreat(Unit* u);
void DoModifyThreatPercent(Unit* pUnit, int32 pct);
+
void DoTeleportTo(float fX, float fY, float fZ, uint32 uiTime = 0);
void DoTeleportTo(const float pos[4]);
+
void DoAction(const int32 param) {}
+
//Teleports a player without dropping threat (only teleports to same map)
void DoTeleportPlayer(Unit* pUnit, float fX, float fY, float fZ, float fO);
void DoTeleportAll(float fX, float fY, float fZ, float fO);
+
//Returns friendly unit with the most amount of hp missing from max hp
Unit* DoSelectLowestHpFriendly(float fRange, uint32 uiMinHPDiff = 1);
+
//Returns a list of friendly CC'd units within range
std::list<Creature*> DoFindFriendlyCC(float fRange);
+
//Returns a list of all friendly units missing a specific buff within range
std::list<Creature*> DoFindFriendlyMissingBuff(float fRange, uint32 uiSpellId);
+
//Return a player with at least minimumRange from m_creature
Player* GetPlayerAtMinimumRange(float fMinimumRange);
+
//Spawns a creature relative to m_creature
Creature* DoSpawnCreature(uint32 uiId, float fX, float fY, float fZ, float fAngle, uint32 uiType, uint32 uiDespawntime);
+
//Selects a unit from the creature's current aggro list
Unit* SelectUnit(SelectAggroTarget target, uint32 uiPosition);
+
bool HealthBelowPct(uint32 pct) const { return me->GetHealth() * 100 < m_creature->GetMaxHealth() * pct; }
+
//Returns spells that meet the specified criteria from the creatures spell list
SpellEntry const* SelectSpell(Unit* Target, int32 School, int32 Mechanic, SelectTargetType Targets, uint32 PowerCostMin, uint32 PowerCostMax, float RangeMin, float RangeMax, SelectEffect Effect);
+
//Checks if you can cast the specified spell
bool CanCast(Unit* pTarget, SpellEntry const* pSpell, bool bTriggered = false);
+
void SetEquipmentSlots(bool bLoadDefault, int32 uiMainHand = EQUIP_NO_CHANGE, int32 uiOffHand = EQUIP_NO_CHANGE, int32 uiRanged = EQUIP_NO_CHANGE);
+
//Generally used to control if MoveChase() is to be used or not in AttackStart(). Some creatures does not chase victims
void SetCombatMovement(bool CombatMove);
bool IsCombatMovement() { return m_bCombatMovement; }
+
bool EnterEvadeIfOutOfCombatArea(const uint32 uiDiff);
+
private:
bool m_bCombatMovement;
uint32 m_uiEvadeCheckCooldown;
};
+
struct TRINITY_DLL_DECL Scripted_NoMovementAI : public ScriptedAI
{
Scripted_NoMovementAI(Creature* creature) : ScriptedAI(creature) {}
+
//Called at each attack of m_creature by any victim
void AttackStart(Unit* who);
};
+
struct TRINITY_DLL_DECL BossAI : public ScriptedAI
{
BossAI(Creature *c, uint32 id);
+
const uint32 bossId;
EventMap events;
SummonList summons;
InstanceData * const instance;
const BossBoundaryMap * const boundary;
+
void JustSummoned(Creature *summon);
void SummonedCreatureDespawn(Creature *summon);
+
void UpdateAI(const uint32 diff) = 0;
+
void Reset() { _Reset(); }
void EnterCombat(Unit *who) { _EnterCombat(); }
void JustDied(Unit *killer) { _JustDied(); }
void JustReachedHome() { me->setActive(false); }
+
protected:
void _Reset();
void _EnterCombat();
void _JustDied();
void _JustReachedHome() { me->setActive(false); }
+
bool CheckInRoom()
{
if(CheckBoundary(me))
@@ -161,8 +221,11 @@ struct TRINITY_DLL_DECL BossAI : public ScriptedAI
bool CheckBoundary(Unit *who);
void TeleportCheaters();
};
+
// SD2's grid searchers
+
//return closest creature alive in grid, with range from pSource
Creature* GetClosestCreatureWithEntry(WorldObject* pSource, uint32 Entry, float MaxSearchRange);
+
#endif
diff --git a/src/bindings/scripts/include/sc_gossip.h b/src/bindings/scripts/include/sc_gossip.h
index 1ade3edd3f5..5c8ded7d579 100644
--- a/src/bindings/scripts/include/sc_gossip.h
+++ b/src/bindings/scripts/include/sc_gossip.h
@@ -4,14 +4,18 @@
*
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_GOSSIP_H
#define SC_GOSSIP_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_IRONFORGE_BANK "The bank of Ironforge"
#define GOSSIP_TEXT_STORMWIND_BANK "The bank of Stormwind"
@@ -35,12 +39,14 @@
#define GOSSIP_TEXT_CLASSTRAINER "A class trainer"
#define GOSSIP_TEXT_PROFTRAINER "A profession trainer"
#define GOSSIP_TEXT_LEXICON "Lexicon of Power"
+
#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_STRANDOFANCIENT "Strand of the Ancients"
+
#define GOSSIP_TEXT_DEATH_KNIGHT "Death Knight"
#define GOSSIP_TEXT_DRUID "Druid"
#define GOSSIP_TEXT_HUNTER "Hunter"
@@ -51,6 +57,7 @@
#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"
@@ -65,6 +72,7 @@
#define GOSSIP_TEXT_MINING "Mining"
#define GOSSIP_TEXT_FISHING "Fishing"
#define GOSSIP_TEXT_SKINNING "Skinning"
+
enum eTradeskill
{
// Skill defines
@@ -83,6 +91,7 @@ enum eTradeskill
TRADESKILL_SKINNING = 13,
TRADESKILL_JEWLCRAFTING = 14,
TRADESKILL_INSCRIPTION = 15,
+
TRADESKILL_LEVEL_NONE = 0,
TRADESKILL_LEVEL_APPRENTICE = 1,
TRADESKILL_LEVEL_JOURNEYMAN = 2,
@@ -90,6 +99,7 @@ enum eTradeskill
TRADESKILL_LEVEL_ARTISAN = 4,
TRADESKILL_LEVEL_MASTER = 5,
TRADESKILL_LEVEL_GRAND_MASTER = 6,
+
// Gossip defines
GOSSIP_ACTION_TRADE = 1,
GOSSIP_ACTION_TRAIN = 2,
@@ -104,6 +114,7 @@ enum eTradeskill
GOSSIP_ACTION_INN_INFO = 11,
GOSSIP_ACTION_UNLEARN = 12,
GOSSIP_ACTION_INFO_DEF = 1000,
+
GOSSIP_SENDER_MAIN = 1,
GOSSIP_SENDER_INN_INFO = 2,
GOSSIP_SENDER_INFO = 3,
@@ -115,8 +126,11 @@ enum eTradeskill
GOSSIP_SENDER_SEC_MAILBOX = 9,
GOSSIP_SENDER_SEC_STABLEMASTER = 10
};
+
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
@@ -126,8 +140,10 @@ extern uint32 GetSkillLevel(Player *player,uint32 skill);
// f - Money value in pop up box
#define ADD_GOSSIP_ITEM(a,b,c,d) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,"",0)
#define ADD_GOSSIP_ITEM_EXTENDED(a,b,c,d,e,f,g) PlayerTalkClass->GetGossipMenu().AddMenuItem(a,b,c,d,e,f,g)
+
// This fuction Sends the current menu to show to client, a - NPCTEXTID(uint32) , b - npc guid(uint64)
#define SEND_GOSSIP_MENU(a,b) PlayerTalkClass->SendGossipMenu(a,b)
+
// This fuction shows POI(point of interest) to client.
// a - position X
// b - position Y
@@ -136,19 +152,23 @@ extern uint32 GetSkillLevel(Player *player,uint32 skill);
// 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)
@@ -156,12 +176,16 @@ extern uint32 GetSkillLevel(Player *player,uint32 skill);
#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_instance.h b/src/bindings/scripts/include/sc_instance.h
index 7849d97ad61..25593e05300 100644
--- a/src/bindings/scripts/include/sc_instance.h
+++ b/src/bindings/scripts/include/sc_instance.h
@@ -1,15 +1,20 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_INSTANCE_H
#define SC_INSTANCE_H
+
#include "InstanceData.h"
#include "Map.h"
+
#define OUT_SAVE_INST_DATA debug_log("TSCR: 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("TSCR: 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("TSCR: 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("TSCR: 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("TSCR: Unable to load Instance Data for Instance %s (Map %d, Instance Id: %d).",instance->GetMapName(), instance->GetId(), instance->GetInstanceId())
+
#define ScriptedInstance InstanceData
+
#endif
diff --git a/src/bindings/scripts/scripts/custom/npc_acherus_taxi.cpp b/src/bindings/scripts/scripts/custom/npc_acherus_taxi.cpp
index d69006277f3..b09f129788e 100644
--- a/src/bindings/scripts/scripts/custom/npc_acherus_taxi.cpp
+++ b/src/bindings/scripts/scripts/custom/npc_acherus_taxi.cpp
@@ -1,13 +1,17 @@
#include "precompiled.h"
#include "WorldPacket.h"
+
#define GOSSIP_FLIGHT "I need a ride"
+
bool GossipHello_npc_acherus_taxi(Player* pPlayer, Creature* pCreature)
{
pPlayer->SetTaxiCheater(true);
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(9978, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_acherus_taxi(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -15,17 +19,18 @@ bool GossipSelect_npc_acherus_taxi(Player* pPlayer, Creature* pCreature, uint32
if (pPlayer->GetPositionZ() >=316)
{
pPlayer->GetSession()->SendDoFlight(24446, 1053);
- }
- else
- {
+ }else{
pPlayer->GetSession()->SendDoFlight(24446, 1054);
}
+
}
return true;
}
+
void AddSC_npc_acherus_taxi()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_acherus_taxi";
newscript->pGossipHello = &GossipHello_npc_acherus_taxi;
diff --git a/src/bindings/scripts/scripts/custom/npc_wyrmresttempel_taxi.cpp b/src/bindings/scripts/scripts/custom/npc_wyrmresttempel_taxi.cpp
index ea5ea5001f1..1f25d15205d 100644
--- a/src/bindings/scripts/scripts/custom/npc_wyrmresttempel_taxi.cpp
+++ b/src/bindings/scripts/scripts/custom/npc_wyrmresttempel_taxi.cpp
@@ -1,19 +1,24 @@
#include "precompiled.h"
#include "WorldPacket.h"
+
#define GOSSIP_UP "My Lord, I must go to the upper floor of the temple."
#define GOSSIP_DOWN "I would like to take a flight to the ground, Lord Afrasastrasz."
#define GOSSIP_MIDDLE "Can you spare a drake to travel to Lord Afrasastrasz, in the middle of the temple?"
#define GOSSIP_TOP "Please, Let me take one of these dragons to the top floor of the temple."
#define GOSSIP_BOTTOM "Yes, Please. I would like to return to the ground floor of the temple."
#define GOSSIP_ONEDOWN "I would like to see Lord Afrasastrasz, in the middle of the temple."
+
bool GossipHello_npc_wyrmresttempel_middle_taxi(Player* pPlayer, Creature* pCreature)
{
pPlayer->SetTaxiCheater(true);
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_UP, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DOWN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
pPlayer->SEND_GOSSIP_MENU(12887, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_wyrmresttempel_middle_taxi(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -21,21 +26,28 @@ bool GossipSelect_npc_wyrmresttempel_middle_taxi(Player* pPlayer, Creature* pCre
pPlayer->GetSession()->SendDoFlight(6376, 881);
}
+
if (uiAction == GOSSIP_ACTION_INFO_DEF + 2)
{
pPlayer->GetSession()->SendDoFlight(6376, 882);
+
}
return true;
}
+
bool GossipHello_npc_wyrmresttempel_bottom_taxi(Player* pPlayer, Creature* pCreature)
{
pPlayer->SetTaxiCheater(true);
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TOP, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MIDDLE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
pPlayer->SEND_GOSSIP_MENU(12713, pCreature->GetGUID());
+
return true;
}
+
+
bool GossipSelect_npc_wyrmresttempel_bottom_taxi(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 4)
@@ -43,48 +55,62 @@ bool GossipSelect_npc_wyrmresttempel_bottom_taxi(Player* pPlayer, Creature* pCre
pPlayer->GetSession()->SendDoFlight(6376, 878);
}
+
if (uiAction == GOSSIP_ACTION_INFO_DEF + 3)
{
pPlayer->GetSession()->SendDoFlight(6376, 883);
+
}
return true;
}
+
bool GossipHello_npc_wyrmresttempel_top_taxi(Player* pPlayer, Creature* pCreature)
{
pPlayer->SetTaxiCheater(true);
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BOTTOM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ONEDOWN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
pPlayer->SEND_GOSSIP_MENU(12714, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_wyrmresttempel_top_taxi(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 5)
{
pPlayer->GetSession()->SendDoFlight(6376, 879);
}
+
if (uiAction == GOSSIP_ACTION_INFO_DEF + 6)
+
{
pPlayer->GetSession()->SendDoFlight(6376, 880);
+
}
return true;
}
+
void AddSC_npc_wyrmresttempel_taxi()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_wyrmresttempelmiddle_taxi";
newscript->pGossipHello = &GossipHello_npc_wyrmresttempel_middle_taxi;
newscript->pGossipSelect = &GossipSelect_npc_wyrmresttempel_middle_taxi;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_wyrmresttempelbottom_taxi";
newscript->pGossipHello = &GossipHello_npc_wyrmresttempel_bottom_taxi;
newscript->pGossipSelect = &GossipSelect_npc_wyrmresttempel_bottom_taxi;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_wyrmresttempeltop_taxi";
newscript->pGossipHello = &GossipHello_npc_wyrmresttempel_top_taxi;
newscript->pGossipSelect = &GossipSelect_npc_wyrmresttempel_top_taxi;
newscript->RegisterSelf();
+
}
diff --git a/src/bindings/scripts/scripts/custom/on_events.cpp b/src/bindings/scripts/scripts/custom/on_events.cpp
index 2580db7836f..e8d9a68e793 100644
--- a/src/bindings/scripts/scripts/custom/on_events.cpp
+++ b/src/bindings/scripts/scripts/custom/on_events.cpp
@@ -1,68 +1,89 @@
#include "precompiled.h"
#include <cstring>
+
//This function is called when the player logs in (every login)
void OnLogin(Player *pPlayer)
{
+
}
+
//This function is called when the player logs out
void OnLogout(Player *pPlayer)
{
+
}
+
//This function is called when the player kills another player
void OnPVPKill(Player *killer, Player *killed)
{
+
}
+
//This function is called when a players AreaID changes
void OnAreaChange(Player *pPlayer, AreaTableEntry const *pArea)
{
+
}
+
//This is called when a player kills a creature (non pvp)
void OnCreatureKill(Player *pPlayer, Creature *pCreature)
{
+
}
+
//This function is called when a player has a money exchange
int32 OnGetMoney(Player *pPlayer, int32 amount)
{
return amount;
}
+
//This function is called whenever a player gets XP
uint32 OnGetXP(Player *pPlayer, uint32 amount)
{
return amount;
}
+
//This function is called when a player clicks a GO Object
bool OnGoClick(Player *pPlayer, GameObject *pGameObject)
{
return true;
}
+
//This function is called when a player clicks and item
bool OnItemClick(Player *pPlayer, Item *pItem)
{
return true;
}
+
//This function is called when a player opens an item (like a clam)
bool OnItemOpen(Player *pPlayer, Item *pItem)
{
return true;
}
+
//This function is called when a player sends a chat message
bool OnPlayerChat(Player *pPlayer, const char *text)
{
return true;
}
+
//this function is called when the server starts
void OnServerStartup()
{
+
}
//this function is called when the server shuts down
void OnServerShutdown()
{
+
}
+
//this function is called when a player casts a spell
bool OnSpellCast(Unit *pUnitTarget, Item *pItemTarget, GameObject *pGoTarget, uint32 i, SpellEntry const *spell)
{
return true;
}
+
void AddSC_onevents()
{
Script *newscript;
@@ -82,5 +103,6 @@ bool OnSpellCast(Unit *pUnitTarget, Item *pItemTarget, GameObject *pGoTarget, ui
newscript->pOnServerShutdown = &OnServerShutdown;
newscript->pOnServerStartup = &OnServerStartup;
newscript->pOnSpellCast = &OnSpellCast;
+
newscript->RegisterSelf();
}
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/alterac_mountains.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/alterac_mountains.cpp
index c3f93b426e1..8b4c360a886 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/alterac_mountains.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/alterac_mountains.cpp
@@ -13,15 +13,19 @@
* along 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: 0
SDComment: Placeholder
SDCategory: Alterac Mountains
EndScriptData */
+
/* ContentData
EndContentData */
+
#include "precompiled.h"
+
/*void AddSC_alterac_mountains()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/arathi_highlands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/arathi_highlands.cpp
index 7757d8da84d..60255570c59 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/arathi_highlands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/arathi_highlands.cpp
@@ -13,20 +13,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Arathi Highlands
SD%Complete: 100
SDComment: Quest support: 665
SDCategory: Arathi Highlands
EndScriptData */
+
/* ContentData
npc_professor_phizzlethorpe
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_professor_phizzlethorpe
######*/
+
enum eEnums
{
SAY_PROGRESS_1 = -1000235,
@@ -39,17 +44,22 @@ enum eEnums
SAY_PROGRESS_7 = -1000242,
EMOTE_PROGRESS_8 = -1000243,
SAY_PROGRESS_9 = -1000244,
+
QUEST_SUNKEN_TREASURE = 665,
MOB_VENGEFUL_SURGE = 2776
};
+
struct TRINITY_DLL_DECL npc_professor_phizzlethorpeAI : public npc_escortAI
{
npc_professor_phizzlethorpeAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 uiPointId)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(uiPointId)
{
case 4:DoScriptText(SAY_PROGRESS_2, m_creature, pPlayer);break;
@@ -75,19 +85,23 @@ struct TRINITY_DLL_DECL npc_professor_phizzlethorpeAI : public npc_escortAI
break;
}
}
+
void JustSummoned(Creature* pSummoned)
{
pSummoned->AI()->AttackStart(m_creature);
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
}
};
+
bool QuestAccept_npc_professor_phizzlethorpe(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
if (pQuest->GetQuestId() == QUEST_SUNKEN_TREASURE)
@@ -95,17 +109,21 @@ bool QuestAccept_npc_professor_phizzlethorpe(Player* pPlayer, Creature* pCreatur
DoScriptText(SAY_PROGRESS_1, pCreature, pPlayer);
if (npc_escortAI* pEscortAI = CAST_AI(npc_professor_phizzlethorpeAI, (pCreature->AI())))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
+
pCreature->setFaction(113);
}
return true;
}
+
CreatureAI* GetAI_npc_professor_phizzlethorpeAI(Creature* pCreature)
{
return new npc_professor_phizzlethorpeAI(pCreature);
}
+
void AddSC_arathi_highlands()
{
Script * newscript;
+
newscript = new Script;
newscript->Name = "npc_professor_phizzlethorpe";
newscript->GetAI = &GetAI_npc_professor_phizzlethorpeAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp
index 7d97bd80a22..a7bb66ac9cf 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/blackrock_depths.cpp
@@ -1,5 +1,6 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software; you can redistribute it and/or modify
+
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
@@ -13,12 +14,14 @@
* along 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, 4322. Vendor Lokhtos Darkbargainer. Need to rewrite the Jail Break support
SDCategory: Blackrock Depths
EndScriptData */
+
/* ContentData
go_shadowforge_brazier
at_ring_of_law
@@ -32,12 +35,15 @@ npc_marshal_reginald_windsor
npc_tobias_seecher
npc_rocknot
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "def_blackrock_depths.h"
+
/*######
+## go_shadowforge_brazier
+######*/
+
bool GOHello_go_shadowforge_brazier(Player* pPlayer, GameObject* pGo)
{
if (ScriptedInstance* pInstance = pGo->GetInstanceData())
@@ -49,16 +55,20 @@ bool GOHello_go_shadowforge_brazier(Player* pPlayer, GameObject* pGo)
}
return false;
}
+
/*######
## npc_grimstone
######*/
+
enum eGrimstone
{
NPC_GRIMSTONE = 10096,
NPC_THELDREN = 16059,
+
//4 or 6 in total? 1+2+1 / 2+2+2 / 3+3. Depending on this, code should be changed.
MAX_MOB_AMOUNT = 4
};
+
uint32 RingMob[]=
{
8925, // Dredge Worm
@@ -68,6 +78,7 @@ uint32 RingMob[]=
8933, // Cave Creeper
8932, // Borer Beetle
};
+
uint32 RingBoss[]=
{
9027, // Gorosh
@@ -77,27 +88,33 @@ uint32 RingBoss[]=
9031, // Anub'shiah
9032, // Hedrum
};
+
bool AreaTrigger_at_ring_of_law(Player* pPlayer, AreaTriggerEntry *at)
{
if (ScriptedInstance* pInstance = pPlayer->GetInstanceData())
{
if (pInstance->GetData(TYPE_RING_OF_LAW) == IN_PROGRESS || pInstance->GetData(TYPE_RING_OF_LAW) == DONE)
return false;
+
pInstance->SetData(TYPE_RING_OF_LAW,IN_PROGRESS);
pPlayer->SummonCreature(NPC_GRIMSTONE,625.559,-205.618,-52.735,2.609,TEMPSUMMON_DEAD_DESPAWN,0);
+
return false;
}
return false;
}
+
/*######
## npc_grimstone
######*/
+
#define SCRIPT_TEXT1 -1000000
#define SCRIPT_TEXT2 -1000000
#define SCRIPT_TEXT3 -1000000
#define SCRIPT_TEXT4 -1000000
#define SCRIPT_TEXT5 -1000000
#define SCRIPT_TEXT6 -1000000
+
//TODO: implement quest part of event (different end boss)
struct TRINITY_DLL_DECL npc_grimstoneAI : public npc_escortAI
{
@@ -106,43 +123,60 @@ struct TRINITY_DLL_DECL npc_grimstoneAI : public npc_escortAI
pInstance = c->GetInstanceData();
MobSpawnId = rand()%6;
}
+
ScriptedInstance* pInstance;
+
uint8 EventPhase;
uint32 Event_Timer;
+
uint8 MobSpawnId;
uint8 MobCount;
uint32 MobDeath_Timer;
+
uint64 RingMobGUID[4];
uint64 RingBossGUID;
+
bool CanWalk;
+
void Reset()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
EventPhase = 0;
Event_Timer = 1000;
+
MobCount = 0;
MobDeath_Timer = 0;
- for (uint8 i = 0; i < MAX_MOB_AMOUNT; ++i)
+
+ for(uint8 i = 0; i < MAX_MOB_AMOUNT; ++i)
RingMobGUID[i] = 0;
+
RingBossGUID = 0;
+
CanWalk = false;
}
+
//TODO: move them to center
void SummonRingMob()
{
if (Creature* tmp = m_creature->SummonCreature(RingMob[MobSpawnId],608.960,-235.322,-53.907,1.857,TEMPSUMMON_DEAD_DESPAWN,0))
RingMobGUID[MobCount] = tmp->GetGUID();
+
++MobCount;
+
if (MobCount == MAX_MOB_AMOUNT)
MobDeath_Timer = 2500;
}
+
//TODO: move them to center
void SummonRingBoss()
{
if (Creature* tmp = m_creature->SummonCreature(RingBoss[rand()%6],644.300,-175.989,-53.739,3.418,TEMPSUMMON_DEAD_DESPAWN,0))
RingBossGUID = tmp->GetGUID();
+
MobDeath_Timer = 2500;
}
+
void WaypointReached(uint32 i)
{
switch(i)
@@ -177,19 +211,23 @@ struct TRINITY_DLL_DECL npc_grimstoneAI : public npc_escortAI
break;
}
}
+
void HandleGameObject(uint32 id, bool open)
{
pInstance->HandleGameObject(pInstance->GetData64(id), open);
}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance)
return;
+
if (MobDeath_Timer)
{
if (MobDeath_Timer <= diff)
{
MobDeath_Timer = 2500;
+
if (RingBossGUID)
{
Creature *boss = Unit::GetCreature(*m_creature,RingBossGUID);
@@ -202,13 +240,15 @@ struct TRINITY_DLL_DECL npc_grimstoneAI : public npc_escortAI
}
return;
}
- for (uint8 i = 0; i < MAX_MOB_AMOUNT; ++i)
+
+ for(uint8 i = 0; i < MAX_MOB_AMOUNT; ++i)
{
Creature *mob = Unit::GetCreature(*m_creature,RingMobGUID[i]);
if (mob && !mob->isAlive() && mob->isDead())
{
RingMobGUID[i] = 0;
--MobCount;
+
//seems all are gone, so set timer to continue and discontinue this
if (!MobCount)
{
@@ -219,6 +259,7 @@ struct TRINITY_DLL_DECL npc_grimstoneAI : public npc_escortAI
}
}else MobDeath_Timer -= diff;
}
+
if (Event_Timer)
{
if (Event_Timer <= diff)
@@ -286,43 +327,53 @@ struct TRINITY_DLL_DECL npc_grimstoneAI : public npc_escortAI
++EventPhase;
}else Event_Timer -= diff;
}
+
if (CanWalk)
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_grimstone(Creature* pCreature)
{
return new npc_grimstoneAI(pCreature);
}
+
/*######
## mob_phalanx
######*/
+
#define SPELL_THUNDERCLAP 8732
#define SPELL_FIREBALLVOLLEY 22425
#define SPELL_MIGHTYBLOW 14099
+
struct TRINITY_DLL_DECL mob_phalanxAI : public ScriptedAI
{
mob_phalanxAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ThunderClap_Timer;
uint32 FireballVolley_Timer;
uint32 MightyBlow_Timer;
+
void Reset()
{
ThunderClap_Timer = 12000;
FireballVolley_Timer =0;
MightyBlow_Timer = 15000;
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -332,12 +383,14 @@ struct TRINITY_DLL_DECL mob_phalanxAI : public ScriptedAI
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();
}
};
@@ -345,13 +398,17 @@ CreatureAI* GetAI_mob_phalanx(Creature* pCreature)
{
return new mob_phalanxAI (pCreature);
}
+
/*######
## npc_kharan_mighthammer
######*/
+
#define QUEST_4001 4001
#define QUEST_4342 4342
+
#define GOSSIP_ITEM_KHARAN_1 "I need to know where the princess are, Kharan!"
#define GOSSIP_ITEM_KHARAN_2 "All is not lost, Kharan!"
+
#define GOSSIP_ITEM_KHARAN_3 "Gor'shak is my friend, you can trust me."
#define GOSSIP_ITEM_KHARAN_4 "Not enough, you need to tell me more."
#define GOSSIP_ITEM_KHARAN_5 "So what happened?"
@@ -360,20 +417,26 @@ CreatureAI* GetAI_mob_phalanx(Creature* pCreature)
#define GOSSIP_ITEM_KHARAN_8 "Continue with your story please."
#define GOSSIP_ITEM_KHARAN_9 "Indeed."
#define GOSSIP_ITEM_KHARAN_10 "The door is open, Kharan. You are a free man."
+
bool GossipHello_npc_kharan_mighthammer(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_4001) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
if (pPlayer->GetQuestStatus(4342) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
+
if (pPlayer->GetTeam() == HORDE)
pPlayer->SEND_GOSSIP_MENU(2473, pCreature->GetGUID());
else
pPlayer->SEND_GOSSIP_MENU(2474, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_kharan_mighthammer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -386,6 +449,7 @@ bool GossipSelect_npc_kharan_mighthammer(Player* pPlayer, Creature* pCreature, u
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
pPlayer->SEND_GOSSIP_MENU(2476, pCreature->GetGUID());
break;
+
case GOSSIP_ACTION_INFO_DEF+3:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KHARAN_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+4);
pPlayer->SEND_GOSSIP_MENU(2477, pCreature->GetGUID());
@@ -420,33 +484,42 @@ bool GossipSelect_npc_kharan_mighthammer(Player* pPlayer, Creature* pCreature, u
}
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
+
#define GOSSIP_ITEM_SHOW_ACCESS "Show me what I have access to, Lothos."
#define GOSSIP_ITEM_GET_CONTRACT "Get Thorium Brotherhood Contract"
+
bool GossipHello_npc_lokhtos_darkbargainer(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetReputationRank(59) >= REP_FRIENDLY)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_ITEM_SHOW_ACCESS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
if (pPlayer->GetQuestRewardStatus(QUEST_A_BINDING_CONTRACT) != 1 &&
!pPlayer->HasItemCount(ITEM_THRORIUM_BROTHERHOOD_CONTRACT, 1, true) &&
pPlayer->HasItemCount(ITEM_SULFURON_INGOT, 1))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_GET_CONTRACT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
}
+
if (pPlayer->GetReputationRank(59) < REP_FRIENDLY)
pPlayer->SEND_GOSSIP_MENU(3673, pCreature->GetGUID());
else
pPlayer->SEND_GOSSIP_MENU(3677, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lokhtos_darkbargainer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -456,18 +529,23 @@ bool GossipSelect_npc_lokhtos_darkbargainer(Player* pPlayer, Creature* pCreature
}
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_dughal_stormwing
######*/
+
#define QUEST_JAIL_BREAK 4322
#define SAY_DUGHAL_FREE "Thank you, $N! I'm free!!!"
#define GOSSIP_DUGHAL "You're free, Dughal! Get out of here!"
+
/*
struct TRINITY_DLL_DECL npc_dughal_stormwingAI : public npc_escortAI
{
npc_dughal_stormwingAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
switch(i)
@@ -482,8 +560,10 @@ struct TRINITY_DLL_DECL npc_dughal_stormwingAI : public npc_escortAI
break;
}
}
+
void EnterCombat(Unit* who) { }
void Reset() {}
+
void JustDied(Unit* killer)
{
if (IsBeingEscorted && killer == m_creature)
@@ -494,6 +574,7 @@ struct TRINITY_DLL_DECL npc_dughal_stormwingAI : public npc_escortAI
pInstance->SetData(DATA_DUGHAL,ENCOUNTER_STATE_ENDED);
}
}
+
void UpdateAI(const uint32 diff)
{
if (pInstance->GetData(DATA_QUEST_JAIL_BREAK) == ENCOUNTER_STATE_NOT_STARTED) return;
@@ -515,9 +596,11 @@ struct TRINITY_DLL_DECL npc_dughal_stormwingAI : public npc_escortAI
CreatureAI* GetAI_npc_dughal_stormwing(Creature* pCreature)
{
npc_dughal_stormwingAI* dughal_stormwingAI = new npc_dughal_stormwingAI(pCreature);
+
dughal_stormwingAI->AddWaypoint(0, 280.42,-82.86, -77.12,0);
dughal_stormwingAI->AddWaypoint(1, 287.64,-87.01, -76.79,0);
dughal_stormwingAI->AddWaypoint(2, 354.63,-64.95, -67.53,0);
+
return dughal_stormwingAI;
}
bool GossipHello_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature)
@@ -529,6 +612,7 @@ bool GossipHello_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
bool GossipSelect_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -544,6 +628,7 @@ bool GossipSelect_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature, uin
/*######
## npc_marshal_windsor
######*/
+
#define SAY_WINDSOR_AGGRO1 "You locked up the wrong Marshal. Prepare to be destroyed!"
#define SAY_WINDSOR_AGGRO2 "I bet you're sorry now, aren't you !?!!"
#define SAY_WINDSOR_AGGRO3 "You better hold me back $N or they are going to feel some prison house beatings."
@@ -554,6 +639,7 @@ bool GossipSelect_npc_dughal_stormwing(Player* pPlayer, Creature* pCreature, uin
#define SAY_WINDSOR_6 "This is it, $N. My stuff should be in that room. Cover me, I'm going in!"
#define SAY_WINDSOR_9 "Ah, there it is!"
#define MOB_ENTRY_REGINALD_WINDSOR 9682
+
Player* pPlayerStart;
/*
struct TRINITY_DLL_DECL npc_marshal_windsorAI : public npc_escortAI
@@ -562,6 +648,7 @@ struct TRINITY_DLL_DECL npc_marshal_windsorAI : public npc_escortAI
{
pInstance = c->GetInstanceData();
}
+
void WaypointReached(uint32 i)
{
switch(i)
@@ -606,6 +693,7 @@ struct TRINITY_DLL_DECL npc_marshal_windsorAI : public npc_escortAI
break;
}
}
+
void EnterCombat(Unit* who)
{
switch(rand()%3)
@@ -615,11 +703,14 @@ struct TRINITY_DLL_DECL npc_marshal_windsorAI : public npc_escortAI
case 2:m_creature->Say(SAY_WINDSOR_AGGRO3, LANG_UNIVERSAL, PlayerGUID);break;
}
}
+
void Reset() {}
+
void JustDied(Unit *slayer)
{
pInstance->SetData(DATA_QUEST_JAIL_BREAK,ENCOUNTER_STATE_FAILED);
}
+
void UpdateAI(const uint32 diff)
{
if (pInstance->GetData(DATA_QUEST_JAIL_BREAK) == ENCOUNTER_STATE_NOT_STARTED) return;
@@ -653,6 +744,7 @@ struct TRINITY_DLL_DECL npc_marshal_windsorAI : public npc_escortAI
CreatureAI* GetAI_npc_marshal_windsor(Creature* pCreature)
{
npc_marshal_windsorAI* marshal_windsorAI = new npc_marshal_windsorAI(pCreature);
+
marshal_windsorAI->AddWaypoint(0, 316.336,-225.528, -77.7258,7000);
marshal_windsorAI->AddWaypoint(1, 316.336,-225.528, -77.7258,2000);
marshal_windsorAI->AddWaypoint(2, 322.96,-207.13, -77.87,0);
@@ -673,8 +765,10 @@ CreatureAI* GetAI_npc_marshal_windsor(Creature* pCreature)
marshal_windsorAI->AddWaypoint(17, 403.61,-51.71, -63.92,2000);
marshal_windsorAI->AddWaypoint(18, 403.61,-51.71, -63.92,1000);
marshal_windsorAI->AddWaypoint(19, 403.61,-51.71, -63.92,0);
+
return marshal_windsorAI;
}
+
bool QuestAccept_npc_marshal_windsor(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if (quest->GetQuestId() == 4322)
@@ -685,6 +779,7 @@ bool QuestAccept_npc_marshal_windsor(Player* pPlayer, Creature* pCreature, Quest
pInstance->SetData(DATA_QUEST_JAIL_BREAK,ENCOUNTER_STATE_IN_PROGRESS);
pCreature->setFaction(11);
}
+
}
return false;
}
@@ -692,6 +787,7 @@ bool QuestAccept_npc_marshal_windsor(Player* pPlayer, Creature* pCreature, Quest
/*######
## npc_marshal_reginald_windsor
######*/
+
#define SAY_REGINALD_WINDSOR_0_1 "Can you feel the power, $N??? It's time to ROCK!"
#define SAY_REGINALD_WINDSOR_0_2 "Now we just have to free Tobias and we can get out of here. This way!"
#define SAY_REGINALD_WINDSOR_5_1 "Open it."
@@ -708,6 +804,7 @@ bool QuestAccept_npc_marshal_windsor(Player* pPlayer, Creature* pCreature, Quest
#define SAY_REGINALD_WINDSOR_20_2 "Meet me at Maxwell's encampment. We'll go over the next stages of the plan there and figure out a way to decode my tablets without the decryption ring."
#define MOB_ENTRY_SHILL_DINGER 9678
#define MOB_ENTRY_CREST_KILLER 9680
+
int wp = 0;
/*
struct TRINITY_DLL_DECL npc_marshal_reginald_windsorAI : public npc_escortAI
@@ -715,6 +812,7 @@ struct TRINITY_DLL_DECL npc_marshal_reginald_windsorAI : public npc_escortAI
npc_marshal_reginald_windsorAI(Creature *c) : npc_escortAI(c)
{
}
+
void WaypointReached(uint32 i)
{
wp=i;
@@ -772,10 +870,12 @@ struct TRINITY_DLL_DECL npc_marshal_reginald_windsorAI : public npc_escortAI
break;
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (who->GetTypeId() == TYPEID_PLAYER)
{
if (CAST_PLR(who)->GetQuestStatus(4322) == QUEST_STATUS_INCOMPLETE)
@@ -789,6 +889,7 @@ struct TRINITY_DLL_DECL npc_marshal_reginald_windsorAI : public npc_escortAI
}
}
}
+
void EnterCombat(Unit* who)
{
switch(rand()%3)
@@ -799,10 +900,12 @@ struct TRINITY_DLL_DECL npc_marshal_reginald_windsorAI : public npc_escortAI
}
}
void Reset() {}
+
void JustDied(Unit *slayer)
{
pInstance->SetData(DATA_QUEST_JAIL_BREAK,ENCOUNTER_STATE_FAILED);
}
+
void UpdateAI(const uint32 diff)
{
if (pInstance->GetData(DATA_QUEST_JAIL_BREAK) == ENCOUNTER_STATE_NOT_STARTED) return;
@@ -853,6 +956,7 @@ struct TRINITY_DLL_DECL npc_marshal_reginald_windsorAI : public npc_escortAI
CreatureAI* GetAI_npc_marshal_reginald_windsor(Creature* pCreature)
{
npc_marshal_reginald_windsorAI* marshal_reginald_windsorAI = new npc_marshal_reginald_windsorAI(pCreature);
+
marshal_reginald_windsorAI->AddWaypoint(0, 403.61,-52.71, -63.92,4000);
marshal_reginald_windsorAI->AddWaypoint(1, 403.61,-52.71, -63.92,4000);
marshal_reginald_windsorAI->AddWaypoint(2, 406.33,-54.87, -63.95,0);
@@ -888,19 +992,23 @@ CreatureAI* GetAI_npc_marshal_reginald_windsor(Creature* pCreature)
marshal_reginald_windsorAI->AddWaypoint(32, 452.45,29.85, -70.37,7000);
marshal_reginald_windsorAI->AddWaypoint(33, 452.45,29.85, -70.37,10000);
marshal_reginald_windsorAI->AddWaypoint(34, 451.27,31.85, -70.07,0);
+
return marshal_reginald_windsorAI;
}
*/
/*######
## npc_tobias_seecher
######*/
+
#define SAY_TOBIAS_FREE "Thank you! I will run for safety immediately!"
/*
struct TRINITY_DLL_DECL npc_tobias_seecherAI : public npc_escortAI
{
npc_tobias_seecherAI(Creature *c) :npc_escortAI(c) {}
+
void EnterCombat(Unit* who) { }
void Reset() {}
+
void JustDied(Unit* killer)
{
if (IsBeingEscorted && killer == m_creature)
@@ -911,6 +1019,7 @@ struct TRINITY_DLL_DECL npc_tobias_seecherAI : public npc_escortAI
pInstance->SetData(DATA_TOBIAS,ENCOUNTER_STATE_ENDED);
}
}
+
void WaypointReached(uint32 i)
{
switch(i)
@@ -926,6 +1035,7 @@ struct TRINITY_DLL_DECL npc_tobias_seecherAI : public npc_escortAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (pInstance->GetData(DATA_QUEST_JAIL_BREAK) == ENCOUNTER_STATE_NOT_STARTED) return;
@@ -944,16 +1054,20 @@ struct TRINITY_DLL_DECL npc_tobias_seecherAI : public npc_escortAI
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_tobias_seecher(Creature* pCreature)
{
npc_tobias_seecherAI* tobias_seecherAI = new npc_tobias_seecherAI(pCreature);
+
tobias_seecherAI->AddWaypoint(0, 549.21, -281.07, -75.27);
tobias_seecherAI->AddWaypoint(1, 554.39, -267.39, -73.68);
tobias_seecherAI->AddWaypoint(2, 533.59, -249.38, -67.04);
tobias_seecherAI->AddWaypoint(3, 519.44, -217.02, -59.34);
tobias_seecherAI->AddWaypoint(4, 506.55, -153.49, -62.34);
+
return tobias_seecherAI;
}
+
bool GossipHello_npc_tobias_seecher(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_JAIL_BREAK) == QUEST_STATUS_INCOMPLETE && pInstance->GetData(DATA_QUEST_JAIL_BREAK) == ENCOUNTER_STATE_IN_PROGRESS)
@@ -963,6 +1077,7 @@ bool GossipHello_npc_tobias_seecher(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
bool GossipSelect_npc_tobias_seecher(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -975,37 +1090,47 @@ bool GossipSelect_npc_tobias_seecher(Player* pPlayer, Creature* pCreature, uint3
return true;
}
*/
+
/*######
## npc_rocknot
######*/
+
#define SAY_GOT_BEER -1230000
#define SPELL_DRUNKEN_RAGE 14872
#define QUEST_ALE 4295
+
struct TRINITY_DLL_DECL npc_rocknotAI : public npc_escortAI
{
npc_rocknotAI(Creature *c) : npc_escortAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 BreakKeg_Timer;
uint32 BreakDoor_Timer;
+
void Reset()
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
BreakKeg_Timer = 0;
BreakDoor_Timer = 0;
}
+
void DoGo(uint32 id, uint32 state)
{
if (GameObject* pGo = pInstance->instance->GetGameObject(pInstance->GetData64(id)))
pGo->SetGoState((GOState)state);
}
+
void WaypointReached(uint32 i)
{
if (!pInstance)
return;
+
switch(i)
{
case 1:
@@ -1026,10 +1151,12 @@ struct TRINITY_DLL_DECL npc_rocknotAI : public npc_escortAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance)
return;
+
if (BreakKeg_Timer)
{
if (BreakKeg_Timer <= diff)
@@ -1039,6 +1166,7 @@ struct TRINITY_DLL_DECL npc_rocknotAI : public npc_escortAI
BreakDoor_Timer = 1000;
}else BreakKeg_Timer -= diff;
}
+
if (BreakDoor_Timer)
{
if (BreakDoor_Timer <= diff)
@@ -1046,33 +1174,44 @@ struct TRINITY_DLL_DECL npc_rocknotAI : public npc_escortAI
DoGo(DATA_GO_BAR_DOOR,2);
DoGo(DATA_GO_BAR_KEG_TRAP,0); //doesn't work very well, leaving code here for future
//spell by trap has effect61, this indicate the bar go hostile
+
if (Unit *tmp = Unit::GetUnit(*m_creature,pInstance->GetData64(DATA_PHALANX)))
tmp->setFaction(14);
+
//for later, this event(s) has alot more to it.
//optionally, DONE can trigger bar to go hostile.
pInstance->SetData(TYPE_BAR,DONE);
+
BreakDoor_Timer = 0;
}else BreakDoor_Timer -= diff;
}
+
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_rocknot(Creature* pCreature)
{
return new npc_rocknotAI(pCreature);
}
+
bool ChooseReward_npc_rocknot(Player* pPlayer, Creature* pCreature, const Quest *_Quest, uint32 item)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
+
if (!pInstance)
return true;
+
if (pInstance->GetData(TYPE_BAR) == DONE || pInstance->GetData(TYPE_BAR) == SPECIAL)
return true;
+
if (_Quest->GetQuestId() == QUEST_ALE)
{
if (pInstance->GetData(TYPE_BAR) != IN_PROGRESS)
pInstance->SetData(TYPE_BAR,IN_PROGRESS);
+
pInstance->SetData(TYPE_BAR,SPECIAL);
+
//keep track of amount in instance script, returns SPECIAL if amount ok and event in progress
if (pInstance->GetData(TYPE_BAR) == SPECIAL)
{
@@ -1082,35 +1221,44 @@ bool ChooseReward_npc_rocknot(Player* pPlayer, Creature* pCreature, const Quest
pEscortAI->Start(false, false);
}
}
+
return true;
}
+
/*######
##
######*/
+
void AddSC_blackrock_depths()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_shadowforge_brazier";
newscript->pGOHello = &GOHello_go_shadowforge_brazier;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "at_ring_of_law";
newscript->pAreaTrigger = &AreaTrigger_at_ring_of_law;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_grimstone";
newscript->GetAI = &GetAI_npc_grimstone;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_phalanx";
newscript->GetAI = &GetAI_mob_phalanx;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_kharan_mighthammer";
newscript->pGossipHello = &GossipHello_npc_kharan_mighthammer;
newscript->pGossipSelect = &GossipSelect_npc_kharan_mighthammer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lokhtos_darkbargainer";
newscript->pGossipHello = &GossipHello_npc_lokhtos_darkbargainer;
@@ -1123,22 +1271,26 @@ void AddSC_blackrock_depths()
newscript->pGossipSelect = &GossipSelect_npc_dughal_stormwing;
newscript->GetAI = &GetAI_npc_dughal_stormwing;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tobias_seecher";
newscript->pGossipHello = &GossipHello_npc_tobias_seecher;
newscript->pGossipSelect = &GossipSelect_npc_tobias_seecher;
newscript->GetAI = &GetAI_npc_tobias_seecher;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_marshal_windsor";
newscript->pQuestAccept = &QuestAccept_npc_marshal_windsor;
newscript->GetAI = &GetAI_npc_marshal_windsor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_marshal_reginald_windsor";
newscript->GetAI = &GetAI_npc_marshal_reginald_windsor;
newscript->RegisterSelf();
*/
+
newscript = new Script;
newscript->Name = "npc_rocknot";
newscript->GetAI = &GetAI_npc_rocknot;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp
index 39f2be7e644..ba10c873b23 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_ambassador_flamelash.cpp
@@ -13,29 +13,37 @@
* along 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 TRINITY_DLL_DECL boss_ambassador_flamelashAI : public ScriptedAI
{
boss_ambassador_flamelashAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FireBlast_Timer;
uint32 Spirit_Timer;
int Rand;
int RandX;
int RandY;
Creature* Summoned;
+
void Reset()
{
FireBlast_Timer = 2000;
Spirit_Timer = 24000;
}
+
void EnterCombat(Unit *who) {}
+
void SummonSpirits(Unit* victim)
{
Rand = rand()%10;
@@ -55,17 +63,20 @@ struct TRINITY_DLL_DECL boss_ambassador_flamelashAI : public ScriptedAI
if (Summoned)
(Summoned->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -73,8 +84,10 @@ struct TRINITY_DLL_DECL boss_ambassador_flamelashAI : public ScriptedAI
SummonSpirits(m_creature->getVictim());
SummonSpirits(m_creature->getVictim());
SummonSpirits(m_creature->getVictim());
+
Spirit_Timer = 30000;
}else Spirit_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -82,6 +95,7 @@ CreatureAI* GetAI_boss_ambassador_flamelash(Creature* pCreature)
{
return new boss_ambassador_flamelashAI (pCreature);
}
+
void AddSC_boss_ambassador_flamelash()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp
index a6589ed0fd7..d5589967cac 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_anubshiah.cpp
@@ -13,26 +13,32 @@
* along 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 TRINITY_DLL_DECL boss_anubshiahAI : public ScriptedAI
{
boss_anubshiahAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowBolt_Timer;
uint32 CurseOfTongues_Timer;
uint32 CurseOfWeakness_Timer;
uint32 DemonArmor_Timer;
uint32 EnvelopingWeb_Timer;
+
void Reset()
{
ShadowBolt_Timer = 7000;
@@ -41,20 +47,24 @@ struct TRINITY_DLL_DECL boss_anubshiahAI : public ScriptedAI
DemonArmor_Timer = 3000;
EnvelopingWeb_Timer = 16000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -63,18 +73,21 @@ struct TRINITY_DLL_DECL boss_anubshiahAI : public ScriptedAI
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)
{
@@ -83,6 +96,7 @@ struct TRINITY_DLL_DECL boss_anubshiahAI : public ScriptedAI
if (target) DoCast(target,SPELL_ENVELOPINGWEB);
EnvelopingWeb_Timer = 12000;
}else EnvelopingWeb_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -90,6 +104,7 @@ CreatureAI* GetAI_boss_anubshiah(Creature* pCreature)
{
return new boss_anubshiahAI (pCreature);
}
+
void AddSC_boss_anubshiah()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp
index 0b9c430917d..83a3839b44f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_emperor_dagran_thaurissan.cpp
@@ -13,47 +13,59 @@
* along 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 SAY_AGGRO -1230001
#define SAY_SLAY -1230002
+
#define SPELL_HANDOFTHAURISSAN 17492
#define SPELL_AVATAROFFLAME 15636
+
struct TRINITY_DLL_DECL boss_draganthaurissanAI : public ScriptedAI
{
boss_draganthaurissanAI(Creature *c) : ScriptedAI(c) {}
+
uint32 HandOfThaurissan_Timer;
uint32 AvatarOfFlame_Timer;
//uint32 Counter;
+
void Reset()
{
HandOfThaurissan_Timer = 4000;
AvatarOfFlame_Timer = 25000;
//Counter= 0;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
m_creature->CallForHelp(VISIBLE_RANGE);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (HandOfThaurissan_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_HANDOFTHAURISSAN);
+
//3 Hands of Thaurissan will be casted
//if (Counter < 3)
//{
@@ -66,19 +78,23 @@ struct TRINITY_DLL_DECL boss_draganthaurissanAI : public ScriptedAI
//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* pCreature)
{
return new boss_draganthaurissanAI (pCreature);
}
+
void AddSC_boss_draganthaurissan()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp
index 0ba98b48998..4c02c438347 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_general_angerforge.cpp
@@ -13,19 +13,24 @@
* along 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 TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
{
boss_general_angerforgeAI(Creature *c) : ScriptedAI(c) {}
+
uint32 MightyBlow_Timer;
uint32 HamString_Timer;
uint32 Cleave_Timer;
@@ -39,6 +44,7 @@ struct TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
int Rand2Y;
Creature* SummonedAdds;
Creature* SummonedMedics;
+
void Reset()
{
MightyBlow_Timer = 8000;
@@ -47,9 +53,11 @@ struct TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
Adds_Timer = 0;
Medics = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void SummonAdds(Unit* victim)
{
Rand1 = rand()%15;
@@ -70,6 +78,7 @@ struct TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
if (SummonedAdds)
(SummonedAdds->AI())->AttackStart(victim);
}
+
void SummonMedics(Unit* victim)
{
Rand2 = rand()%10;
@@ -90,29 +99,34 @@ struct TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
if (SummonedMedics)
(SummonedMedics->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -122,9 +136,11 @@ struct TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
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)
{
@@ -132,6 +148,7 @@ struct TRINITY_DLL_DECL boss_general_angerforgeAI : public ScriptedAI
SummonMedics(m_creature->getVictim());
Medics = true;
}
+
DoMeleeAttackIfReady();
}
};
@@ -139,6 +156,7 @@ CreatureAI* GetAI_boss_general_angerforge(Creature* pCreature)
{
return new boss_general_angerforgeAI (pCreature);
}
+
void AddSC_boss_general_angerforge()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp
index 6aa1c60252d..5fcb18295f0 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_gorosh_the_dervish.cpp
@@ -13,45 +13,56 @@
* along 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 TRINITY_DLL_DECL boss_gorosh_the_dervishAI : public ScriptedAI
{
boss_gorosh_the_dervishAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WhirlWind_Timer;
uint32 MortalStrike_Timer;
+
void Reset()
{
WhirlWind_Timer = 12000;
MortalStrike_Timer = 22000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -59,6 +70,7 @@ CreatureAI* GetAI_boss_gorosh_the_dervish(Creature* pCreature)
{
return new boss_gorosh_the_dervishAI (pCreature);
}
+
void AddSC_boss_gorosh_the_dervish()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp
index 560e5874c0d..5950e92612f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_grizzle.cpp
@@ -13,40 +13,51 @@
* along 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 EMOTE_GENERIC_FRENZY_KILL -1000001
+
#define SPELL_GROUNDTREMOR 6524
#define SPELL_FRENZY 28371
+
struct TRINITY_DLL_DECL boss_grizzleAI : public ScriptedAI
{
boss_grizzleAI(Creature *c) : ScriptedAI(c) {}
+
uint32 GroundTremor_Timer;
uint32 Frenzy_Timer;
+
void Reset()
{
GroundTremor_Timer = 12000;
Frenzy_Timer =0;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -54,9 +65,11 @@ struct TRINITY_DLL_DECL boss_grizzleAI : public ScriptedAI
{
DoCast(m_creature,SPELL_FRENZY);
DoScriptText(EMOTE_GENERIC_FRENZY_KILL, m_creature);
+
Frenzy_Timer = 15000;
}else Frenzy_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
@@ -64,6 +77,7 @@ CreatureAI* GetAI_boss_grizzle(Creature* pCreature)
{
return new boss_grizzleAI (pCreature);
}
+
void AddSC_boss_grizzle()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp
index a61e47ccfb2..94e8b8281e1 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_high_interrogator_gerstahn.cpp
@@ -13,24 +13,30 @@
* along 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 TRINITY_DLL_DECL boss_high_interrogator_gerstahnAI : public ScriptedAI
{
boss_high_interrogator_gerstahnAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowWordPain_Timer;
uint32 ManaBurn_Timer;
uint32 PsychicScream_Timer;
uint32 ShadowShield_Timer;
+
void Reset()
{
ShadowWordPain_Timer = 4000;
@@ -38,14 +44,17 @@ struct TRINITY_DLL_DECL boss_high_interrogator_gerstahnAI : public ScriptedAI
PsychicScream_Timer = 32000;
ShadowShield_Timer = 8000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//ShadowWordPain_Timer
if (ShadowWordPain_Timer < diff)
{
@@ -54,6 +63,7 @@ struct TRINITY_DLL_DECL boss_high_interrogator_gerstahnAI : public ScriptedAI
if (target)DoCast(target,SPELL_SHADOWWORDPAIN);
ShadowWordPain_Timer = 7000;
}else ShadowWordPain_Timer -= diff;
+
//ManaBurn_Timer
if (ManaBurn_Timer < diff)
{
@@ -62,18 +72,21 @@ struct TRINITY_DLL_DECL boss_high_interrogator_gerstahnAI : public ScriptedAI
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();
}
};
@@ -81,6 +94,7 @@ CreatureAI* GetAI_boss_high_interrogator_gerstahn(Creature* pCreature)
{
return new boss_high_interrogator_gerstahnAI (pCreature);
}
+
void AddSC_boss_high_interrogator_gerstahn()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp
index dc8b9a269e9..773271d7e5e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_magmus.cpp
@@ -13,39 +13,49 @@
* along 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 TRINITY_DLL_DECL boss_magmusAI : public ScriptedAI
{
boss_magmusAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FieryBurst_Timer;
uint32 WarStomp_Timer;
+
void Reset()
{
FieryBurst_Timer = 5000;
WarStomp_Timer =0;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -55,6 +65,7 @@ struct TRINITY_DLL_DECL boss_magmusAI : public ScriptedAI
WarStomp_Timer = 8000;
}else WarStomp_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
@@ -62,6 +73,7 @@ CreatureAI* GetAI_boss_magmus(Creature* pCreature)
{
return new boss_magmusAI (pCreature);
}
+
void AddSC_boss_magmus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp
index 8687a1fbeeb..63395dd5016 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_moira_bronzebeard.cpp
@@ -13,22 +13,27 @@
* along 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 TRINITY_DLL_DECL boss_moira_bronzebeardAI : public ScriptedAI
{
boss_moira_bronzebeardAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Heal_Timer;
uint32 MindBlast_Timer;
uint32 ShadowWordPain_Timer;
@@ -36,6 +41,7 @@ struct TRINITY_DLL_DECL boss_moira_bronzebeardAI : public ScriptedAI
Unit* PlayerHolder;
Unit* Target;
bool Heal;
+
void Reset()
{
Target = NULL;
@@ -44,38 +50,45 @@ struct TRINITY_DLL_DECL boss_moira_bronzebeardAI : public ScriptedAI
ShadowWordPain_Timer = 2000;
Smite_Timer = 8000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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* pCreature)
{
return new boss_moira_bronzebeardAI (pCreature);
}
+
void AddSC_boss_moira_bronzebeard()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp
index 57ffc0bb52c..80b2ea8ac08 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/boss_tomb_of_seven.cpp
@@ -13,14 +13,17 @@
* along 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_Tomb_Of_Seven
SD%Complete: 90
SDComment: Learning Smelt Dark Iron if tribute quest rewarded. Missing event.
SDCategory: Blackrock Depths
EndScriptData */
+
#include "precompiled.h"
#include "def_blackrock_depths.h"
+
enum eEnums
{
SPELL_SMELT_DARK_IRON = 14891,
@@ -28,19 +31,24 @@ enum eEnums
QUEST_SPECTRAL_CHALICE = 4083,
SKILLPOINT_MIN = 230
};
+
#define GOSSIP_ITEM_TEACH_1 "Teach me the art of smelting dark iron"
#define GOSSIP_ITEM_TEACH_2 "Continue..."
#define GOSSIP_ITEM_TEACH_3 "[PH] Continue..."
#define GOSSIP_ITEM_TRIBUTE "I want to pay tribute"
+
bool GossipHello_boss_gloomrel(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestRewardStatus(QUEST_SPECTRAL_CHALICE) == 1 && pPlayer->GetSkillValue(SKILL_MINING) >= SKILLPOINT_MIN && !pPlayer->HasSpell(SPELL_SMELT_DARK_IRON))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TEACH_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
if (pPlayer->GetQuestRewardStatus(QUEST_SPECTRAL_CHALICE) == 0 && pPlayer->GetSkillValue(SKILL_MINING) >= SKILLPOINT_MIN)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TRIBUTE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_boss_gloomrel(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -68,6 +76,7 @@ bool GossipSelect_boss_gloomrel(Player* pPlayer, Creature* pCreature, uint32 uiS
}
return true;
}
+
enum eSpells
{
SPELL_SHADOWBOLTVOLLEY = 15245,
@@ -76,12 +85,14 @@ enum eSpells
SPELL_DEMONARMOR = 13787,
SPELL_SUMMON_VOIDWALKERS = 15092
};
+
struct TRINITY_DLL_DECL boss_doomrelAI : public ScriptedAI
{
boss_doomrelAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
uint32 ShadowVolley_Timer;
uint32 Immolate_Timer;
@@ -92,6 +103,7 @@ struct TRINITY_DLL_DECL boss_doomrelAI : public ScriptedAI
int RandX;
int RandY;
Creature* Summoned;
+
void Reset()
{
ShadowVolley_Timer = 10000;
@@ -99,18 +111,23 @@ struct TRINITY_DLL_DECL boss_doomrelAI : public ScriptedAI
CurseOfWeakness_Timer = 5000;
DemonArmor_Timer = 16000;
Voidwalkers = false;
+
m_creature->setFaction(FACTION_FRIEND);
+
// was set before event start, so set again
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
+
if (pInstance)
if (pInstance->GetData(DATA_GHOSTKILL) >= 7)
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
else
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
+
void EnterCombat(Unit *who)
{
}
+
void EnterEvadeMode()
{
m_creature->RemoveAllAuras();
@@ -123,61 +140,75 @@ struct TRINITY_DLL_DECL boss_doomrelAI : public ScriptedAI
if (pInstance)
pInstance->SetData64(DATA_EVENSTARTER, 0);
}
+
void JustDied(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_GHOSTKILL, 1);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
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)
{
m_creature->CastSpell(m_creature->getVictim(), SPELL_SUMMON_VOIDWALKERS, true);
Voidwalkers = true;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_doomrel(Creature* pCreature)
{
return new boss_doomrelAI (pCreature);
}
+
#define GOSSIP_ITEM_CHALLENGE "Your bondage is at an end, Doom'rel. I challenge you!"
#define GOSSIP_SELECT_DOOMREL "[PH] Continue..."
+
bool GossipHello_boss_doomrel(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_CHALLENGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(2601, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_boss_doomrel(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -199,14 +230,17 @@ bool GossipSelect_boss_doomrel(Player* pPlayer, Creature* pCreature, uint32 uiSe
}
return true;
}
+
void AddSC_boss_tomb_of_seven()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_gloomrel";
newscript->pGossipHello = &GossipHello_boss_gloomrel;
newscript->pGossipSelect = &GossipSelect_boss_gloomrel;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_doomrel";
newscript->GetAI = &GetAI_boss_doomrel;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/def_blackrock_depths.h b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/def_blackrock_depths.h
index 4f31378a495..6ced6bafe15 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/def_blackrock_depths.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/def_blackrock_depths.h
@@ -1,11 +1,14 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_BRD_H
#define DEF_BRD_H
+
#define FACTION_NEUTRAL 734
#define FACTION_HOSTILE 754
#define FACTION_FRIEND 35
+
enum eTypes
{
TYPE_RING_OF_LAW = 1,
@@ -14,16 +17,20 @@ enum eTypes
TYPE_TOMB_OF_SEVEN = 4,
TYPE_LYCEUM = 5,
TYPE_IRON_HALL = 6,
+
DATA_EMPEROR = 10,
DATA_PHALANX = 11,
+
DATA_ARENA1 = 12,
DATA_ARENA2 = 13,
DATA_ARENA3 = 14,
DATA_ARENA4 = 15,
+
DATA_GO_BAR_KEG = 16,
DATA_GO_BAR_KEG_TRAP = 17,
DATA_GO_BAR_DOOR = 18,
DATA_GO_CHALICE = 19,
+
DATA_GHOSTKILL = 20,
DATA_EVENSTARTER = 21
};
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp
index c17c27209e2..f14b7603fa7 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_depths/instance_blackrock_depths.cpp
@@ -13,12 +13,14 @@
* along 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_Blackrock_Depths
SD%Complete: 100
SDComment:
SDCategory: Blackrock Depths
EndScriptData */
+
/*
update `creature_template` set `npcflag`='1',`ScriptName`='npc_dughal_stormwing' where `entry`='9022';
update `creature_template` set `ScriptName`='npc_marshal_windsor' where `entry`='9023';
@@ -26,12 +28,16 @@ update `creature_template` set `ScriptName`='npc_marshal_reginald_windsor' where
update `creature_template` set `npcflag`='1',`ScriptName`='npc_tobias_seecher' where `entry`='9679';
update `instance_template` set `script`='instance_blackrock_depths' where `map`='230';
*/
+
#include "precompiled.h"
#include "def_blackrock_depths.h"
+
#define TIMER_TOMBOFTHESEVEN 15000
+
enum eEnums
{
MAX_ENCOUNTER = 6,
+
NPC_EMPEROR = 9019,
NPC_PHALANX = 9502,
NPC_ANGERREL = 9035,
@@ -41,6 +47,7 @@ enum eEnums
NPC_SEETHREL = 9038,
NPC_GLOOMREL = 9037,
NPC_DOOMREL = 9039,
+
GO_ARENA1 = 161525,
GO_ARENA2 = 161522,
GO_ARENA3 = 161524,
@@ -58,16 +65,21 @@ enum eEnums
GO_GOLEM_ROOM_N = 170573,
GO_GOLEM_ROOM_S = 170574,
GO_THONE_ROOM = 170575,
+
GO_SPECTRAL_CHALICE = 164869,
GO_CHEST_SEVEN = 169243
};
+
struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
{
instance_blackrock_depths(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
+
uint64 EmperorGUID;
uint64 PhalanxGUID;
+
uint64 GoArena1GUID;
uint64 GoArena2GUID;
uint64 GoArena3GUID;
@@ -86,17 +98,21 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
uint64 GoGolemSGUID;
uint64 GoThoneGUID;
uint64 GoChestGUID;
+
uint32 BarAleCount;
uint32 GhostKillCount;
uint64 TombBossGUIDs[7];
uint64 TombEventStarterGUID;
uint32 TombTimer;
uint32 TombEventCounter;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
EmperorGUID = 0;
PhalanxGUID = 0;
+
GoArena1GUID = 0;
GoArena2GUID = 0;
GoArena3GUID = 0;
@@ -115,14 +131,17 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
GoGolemSGUID = 0;
GoThoneGUID = 0;
GoChestGUID = 0;
+
BarAleCount = 0;
GhostKillCount = 0;
TombEventStarterGUID = 0;
TombTimer = TIMER_TOMBOFTHESEVEN;
TombEventCounter = 0;
- for (uint8 i = 0; i < 7; ++i)
+
+ for(uint8 i = 0; i < 7; ++i)
TombBossGUIDs[i] = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -138,6 +157,7 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
case NPC_ANGERREL: TombBossGUIDs[6] = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -168,9 +188,11 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
case GO_CHEST_SEVEN: GoChestGUID = pGo->GetGUID(); break;
}
}
+
void SetData64(uint32 type, uint64 data)
{
debug_log("TSCR: Instance Blackrock Depths: SetData64 update (Type: %u Data %u)", type, data);
+
switch(type)
{
case DATA_EVENSTARTER:
@@ -182,9 +204,11 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
debug_log("TSCR: Instance Blackrock Depths: SetData update (Type: %u Data %u)", type, data);
+
switch(type)
{
case TYPE_RING_OF_LAW:
@@ -212,17 +236,22 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
GhostKillCount += data;
break;
}
+
if (data == DONE || GhostKillCount >= 7)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " "
<< m_auiEncounter[3] << " " << m_auiEncounter[4] << " " << m_auiEncounter[5] << " " << GhostKillCount;
+
str_data = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -247,6 +276,7 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -274,10 +304,12 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -285,19 +317,24 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]
>> m_auiEncounter[4] >> m_auiEncounter[5] >> GhostKillCount;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
if (GhostKillCount > 0 && GhostKillCount < 7)
GhostKillCount = 0;//reset tomb of seven event
if (GhostKillCount > 7)
GhostKillCount = 7;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
+
void TombOfSevenEvent()
{
if (GhostKillCount < 7 && TombBossGUIDs[TombEventCounter])
@@ -311,11 +348,12 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
}
}
}
+
void TombOfSevenReset()
{
HandleGameObject(GoTombExitGUID,false);//event reseted, close exit door
HandleGameObject(GoTombEnterGUID,true);//event reseted, open entrance door
- for (uint8 i = 0; i < 7; ++i)
+ for(uint8 i = 0; i < 7; ++i)
{
if (Creature* boss = instance->GetCreature(TombBossGUIDs[i]))
{
@@ -338,12 +376,14 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
TombTimer = TIMER_TOMBOFTHESEVEN;
SetData(TYPE_TOMB_OF_SEVEN, NOT_STARTED);
}
+
void TombOfSevenStart()
{
HandleGameObject(GoTombExitGUID,false);//event started, close exit door
HandleGameObject(GoTombEnterGUID,false);//event started, close entrance door
SetData(TYPE_TOMB_OF_SEVEN, IN_PROGRESS);
}
+
void TombOfSevenEnd()
{
DoRespawnGameObject(GoChestGUID,DAY);
@@ -367,10 +407,12 @@ struct TRINITY_DLL_DECL instance_blackrock_depths : public ScriptedInstance
TombOfSevenEnd();
}
};
+
InstanceData* GetInstanceData_instance_blackrock_depths(Map* pMap)
{
return new instance_blackrock_depths(pMap);
}
+
void AddSC_instance_blackrock_depths()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp
index 28a9d09aa0d..5aa5ea42520 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_drakkisath.cpp
@@ -13,24 +13,30 @@
* along 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 TRINITY_DLL_DECL boss_drakkisathAI : public ScriptedAI
{
boss_drakkisathAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FireNova_Timer;
uint32 Cleave_Timer;
uint32 Confliguration_Timer;
uint32 Thunderclap_Timer;
+
void Reset()
{
FireNova_Timer = 6000;
@@ -38,38 +44,45 @@ struct TRINITY_DLL_DECL boss_drakkisathAI : public ScriptedAI
Confliguration_Timer = 15000;
Thunderclap_Timer = 17000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -77,6 +90,7 @@ CreatureAI* GetAI_boss_drakkisath(Creature* pCreature)
{
return new boss_drakkisathAI (pCreature);
}
+
void AddSC_boss_drakkisath()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp
index d37d03d07b0..423a5cc1268 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_gyth.cpp
@@ -13,19 +13,24 @@
* along 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 TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
{
boss_gythAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Aggro_Timer;
uint32 Dragons_Timer;
uint32 Orc_Timer;
@@ -34,6 +39,7 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
uint32 Flamebreath_Timer;
uint32 Line1Count;
uint32 Line2Count;
+
bool Event;
bool SummonedDragons;
bool SummonedOrcs;
@@ -41,6 +47,7 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
bool bAggro;
bool RootSelf;
Creature *SummonedCreature;
+
void Reset()
{
Dragons_Timer = 3000;
@@ -55,6 +62,7 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
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;
@@ -62,13 +70,16 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
Line2Count = rand() % (5 - Line1Count) + 2;
else
Line2Count = 2;
+
//Invisible for event start
m_creature->SetDisplayId(11686);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(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);
@@ -79,18 +90,22 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
Summoned->AddThreat(target, 1.0f);
}
}
+
void UpdateAI(const uint32 diff)
{
//char buf[200];
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (!RootSelf)
{
//m_creature->m_canMove = true;
DoCast(m_creature, 33356);
RootSelf = true;
}
+
if (!bAggro && Line1Count == 0 && Line2Count == 0)
{
if (Aggro_Timer < diff)
@@ -102,6 +117,7 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
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)
{
@@ -116,6 +132,7 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
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)
{
@@ -130,6 +147,7 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
Orc_Timer = 60000;
} else Orc_Timer -= diff;
}
+
// we take part in the fight
if (bAggro)
{
@@ -139,18 +157,21 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
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)
@@ -163,14 +184,17 @@ struct TRINITY_DLL_DECL boss_gythAI : public ScriptedAI
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* pCreature)
{
return new boss_gythAI (pCreature);
}
+
void AddSC_boss_gyth()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp
index bcde2df4baf..6937aae0dc5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_halycon.cpp
@@ -13,57 +13,70 @@
* along 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 TRINITY_DLL_DECL boss_halyconAI : public ScriptedAI
{
boss_halyconAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CrowdPummel_Timer;
uint32 MightyBlow_Timer;
bool Summoned;
+
void Reset()
{
CrowdPummel_Timer = 8000;
MightyBlow_Timer = 14000;
Summoned = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -71,6 +84,7 @@ CreatureAI* GetAI_boss_halycon(Creature* pCreature)
{
return new boss_halyconAI (pCreature);
}
+
void AddSC_boss_halycon()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp
index edd5d039d4d..5fccc3333a5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_highlord_omokk.cpp
@@ -13,13 +13,16 @@
* along 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
@@ -27,9 +30,11 @@ EndScriptData */
#define SPELL_SUNDERARMOR 24317
#define SPELL_KNOCKAWAY 20686
#define SPELL_SLOW 22356
+
struct TRINITY_DLL_DECL boss_highlordomokkAI : public ScriptedAI
{
boss_highlordomokkAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WarStomp_Timer;
uint32 Cleave_Timer;
uint32 Strike_Timer;
@@ -37,6 +42,7 @@ struct TRINITY_DLL_DECL boss_highlordomokkAI : public ScriptedAI
uint32 SunderArmor_Timer;
uint32 KnockAway_Timer;
uint32 Slow_Timer;
+
void Reset()
{
WarStomp_Timer = 15000;
@@ -47,56 +53,66 @@ struct TRINITY_DLL_DECL boss_highlordomokkAI : public ScriptedAI
KnockAway_Timer = 18000;
Slow_Timer = 24000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -104,6 +120,7 @@ CreatureAI* GetAI_boss_highlordomokk(Creature* pCreature)
{
return new boss_highlordomokkAI (pCreature);
}
+
void AddSC_boss_highlordomokk()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp
index cc858a33628..61a92ebfe40 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_mother_smolderweb.cpp
@@ -13,49 +13,61 @@
* along 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 TRINITY_DLL_DECL boss_mothersmolderwebAI : public ScriptedAI
{
boss_mothersmolderwebAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Crystalize_Timer;
uint32 MothersMilk_Timer;
+
void Reset()
{
Crystalize_Timer = 20000;
MothersMilk_Timer = 10000;
}
+
void EnterCombat(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 (!UpdateVictim())
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();
}
};
@@ -63,6 +75,7 @@ CreatureAI* GetAI_boss_mothersmolderweb(Creature* pCreature)
{
return new boss_mothersmolderwebAI (pCreature);
}
+
void AddSC_boss_mothersmolderweb()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp
index 285c455d437..3e5611da95a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_overlord_wyrmthalak.cpp
@@ -13,34 +13,42 @@
* along 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 TRINITY_DLL_DECL boss_overlordwyrmthalakAI : public ScriptedAI
{
boss_overlordwyrmthalakAI(Creature *c) : ScriptedAI(c) {}
+
uint32 BlastWave_Timer;
uint32 Shout_Timer;
uint32 Cleave_Timer;
uint32 Knockaway_Timer;
bool Summoned;
Creature *SummonedCreature;
+
void Reset()
{
BlastWave_Timer = 20000;
@@ -49,43 +57,51 @@ struct TRINITY_DLL_DECL boss_overlordwyrmthalakAI : public ScriptedAI
Knockaway_Timer = 12000;
Summoned = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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);
if (SummonedCreature)
(SummonedCreature->AI())->AttackStart(target);
@@ -94,6 +110,7 @@ struct TRINITY_DLL_DECL boss_overlordwyrmthalakAI : public ScriptedAI
(SummonedCreature->AI())->AttackStart(target);
Summoned = true;
}
+
DoMeleeAttackIfReady();
}
};
@@ -101,6 +118,7 @@ CreatureAI* GetAI_boss_overlordwyrmthalak(Creature* pCreature)
{
return new boss_overlordwyrmthalakAI (pCreature);
}
+
void AddSC_boss_overlordwyrmthalak()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp
index 36d0b38b304..9b9460181c2 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_pyroguard_emberseer.cpp
@@ -13,48 +13,59 @@
* along 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 TRINITY_DLL_DECL boss_pyroguard_emberseerAI : public ScriptedAI
{
boss_pyroguard_emberseerAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FireNova_Timer;
uint32 FlameBuffet_Timer;
uint32 PyroBlast_Timer;
+
void Reset()
{
FireNova_Timer = 6000;
FlameBuffet_Timer = 3000;
PyroBlast_Timer = 14000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -63,6 +74,7 @@ struct TRINITY_DLL_DECL boss_pyroguard_emberseerAI : public ScriptedAI
if (target) DoCast(target,SPELL_PYROBLAST);
PyroBlast_Timer = 15000;
}else PyroBlast_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -70,6 +82,7 @@ CreatureAI* GetAI_boss_pyroguard_emberseer(Creature* pCreature)
{
return new boss_pyroguard_emberseerAI (pCreature);
}
+
void AddSC_boss_pyroguard_emberseer()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp
index 5c295d97f12..a8c560c71c7 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_quartermaster_zigris.cpp
@@ -13,49 +13,60 @@
* along 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 TRINITY_DLL_DECL boss_quatermasterzigrisAI : public ScriptedAI
{
boss_quatermasterzigrisAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Shoot_Timer;
uint32 StunBomb_Timer;
//uint32 HelingPotion_Timer;
+
void Reset()
{
Shoot_Timer = 1000;
StunBomb_Timer = 16000;
//HelingPotion_Timer = 25000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -63,6 +74,7 @@ CreatureAI* GetAI_boss_quatermasterzigris(Creature* pCreature)
{
return new boss_quatermasterzigrisAI (pCreature);
}
+
void AddSC_boss_quatermasterzigris()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp
index 28c1ffcf0d8..cd2aa32039e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_rend_blackhand.cpp
@@ -13,54 +13,66 @@
* along 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 TRINITY_DLL_DECL boss_rend_blackhandAI : public ScriptedAI
{
boss_rend_blackhandAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WhirlWind_Timer;
uint32 Cleave_Timer;
uint32 Thunderclap_Timer;
+
void Reset()
{
WhirlWind_Timer = 20000;
Cleave_Timer = 5000;
Thunderclap_Timer = 9000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -68,6 +80,7 @@ CreatureAI* GetAI_boss_rend_blackhand(Creature* pCreature)
{
return new boss_rend_blackhandAI (pCreature);
}
+
void AddSC_boss_rend_blackhand()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp
index 4525722f3e7..3e4be74b777 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_shadow_hunter_voshgajin.cpp
@@ -13,43 +13,54 @@
* along 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 TRINITY_DLL_DECL boss_shadowvoshAI : public ScriptedAI
{
boss_shadowvoshAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -58,12 +69,14 @@ struct TRINITY_DLL_DECL boss_shadowvoshAI : public ScriptedAI
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();
}
};
@@ -71,6 +84,7 @@ CreatureAI* GetAI_boss_shadowvosh(Creature* pCreature)
{
return new boss_shadowvoshAI (pCreature);
}
+
void AddSC_boss_shadowvosh()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp
index 971d4551806..0776c155562 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_the_beast.cpp
@@ -13,42 +13,52 @@
* along 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 TRINITY_DLL_DECL boss_thebeastAI : public ScriptedAI
{
boss_thebeastAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Flamebreak_Timer;
uint32 Immolate_Timer;
uint32 TerrifyingRoar_Timer;
+
void Reset()
{
Flamebreak_Timer = 12000;
Immolate_Timer = 3000;
TerrifyingRoar_Timer = 23000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -57,12 +67,14 @@ struct TRINITY_DLL_DECL boss_thebeastAI : public ScriptedAI
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();
}
};
@@ -70,6 +82,7 @@ CreatureAI* GetAI_boss_thebeast(Creature* pCreature)
{
return new boss_thebeastAI (pCreature);
}
+
void AddSC_boss_thebeast()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp
index 428c570d086..26fe20f8b05 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackrock_spire/boss_warmaster_voone.cpp
@@ -13,28 +13,34 @@
* along 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 TRINITY_DLL_DECL boss_warmastervooneAI : public ScriptedAI
{
boss_warmastervooneAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Snapkick_Timer;
uint32 Cleave_Timer;
uint32 Uppercut_Timer;
uint32 MortalStrike_Timer;
uint32 Pummel_Timer;
uint32 ThrowAxe_Timer;
+
void Reset()
{
Snapkick_Timer = 8000;
@@ -44,50 +50,59 @@ struct TRINITY_DLL_DECL boss_warmastervooneAI : public ScriptedAI
Pummel_Timer = 32000;
ThrowAxe_Timer = 1000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -95,6 +110,7 @@ CreatureAI* GetAI_boss_warmastervoone(Creature* pCreature)
{
return new boss_warmastervooneAI (pCreature);
}
+
void AddSC_boss_warmastervoone()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp
index a6842af41c0..71b9850454f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_broodlord_lashlayer.cpp
@@ -13,26 +13,33 @@
* along 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 SAY_AGGRO -1469000
#define SAY_LEASH -1469001
+
#define SPELL_CLEAVE 26350
#define SPELL_BLASTWAVE 23331
#define SPELL_MORTALSTRIKE 24573
#define SPELL_KNOCKBACK 25778
+
struct TRINITY_DLL_DECL boss_broodlordAI : public ScriptedAI
{
boss_broodlordAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Cleave_Timer;
uint32 BlastWave_Timer;
uint32 MortalStrike_Timer;
uint32 KnockBack_Timer;
+
void Reset()
{
Cleave_Timer = 8000; //These times are probably wrong
@@ -40,43 +47,52 @@ struct TRINITY_DLL_DECL boss_broodlordAI : public ScriptedAI
MortalStrike_Timer = 20000;
KnockBack_Timer = 30000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(m_creature->getVictim(),-50);
+
KnockBack_Timer = 15000 + rand()%15000;
}else KnockBack_Timer -= diff;
+
if (EnterEvadeIfOutOfCombatArea(diff))
DoScriptText(SAY_LEASH, m_creature);
+
DoMeleeAttackIfReady();
}
};
@@ -84,6 +100,7 @@ CreatureAI* GetAI_boss_broodlord(Creature* pCreature)
{
return new boss_broodlordAI (pCreature);
}
+
void AddSC_boss_broodlord()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp
index 9d492ce8460..b36b4afe7b1 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_chromaggus.cpp
@@ -13,15 +13,19 @@
* along 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"
+
#define EMOTE_FRENZY -1469002
#define EMOTE_SHIMMER -1469003
+
//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%
@@ -30,11 +34,13 @@ EndScriptData */
#define SPELL_SHADOW_VURNALBILTY 22279
#define SPELL_NATURE_VURNALBILTY 22280
#define SPELL_ARCANE_VURNALBILTY 22281
+
#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
@@ -42,9 +48,12 @@ EndScriptData */
#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
+
struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
{
boss_chromaggusAI(Creature *c) : ScriptedAI(c)
@@ -73,6 +82,7 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
Breath1_Spell = SPELL_INCINERATE;
Breath2_Spell = SPELL_FROSTBURN;
break;
+
//B1 - TL
case 4:
Breath1_Spell = SPELL_TIMELAPSE;
@@ -90,6 +100,7 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
Breath1_Spell = SPELL_TIMELAPSE;
Breath2_Spell = SPELL_FROSTBURN;
break;
+
//B1 - Acid
case 8:
Breath1_Spell = SPELL_CORROSIVEACID;
@@ -107,6 +118,7 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
Breath1_Spell = SPELL_CORROSIVEACID;
Breath2_Spell = SPELL_FROSTBURN;
break;
+
//B1 - Ignite
case 12:
Breath1_Spell = SPELL_IGNITEFLESH;
@@ -124,6 +136,7 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
Breath1_Spell = SPELL_IGNITEFLESH;
Breath2_Spell = SPELL_FROSTBURN;
break;
+
//B1 - Frost
case 16:
Breath1_Spell = SPELL_FROSTBURN;
@@ -142,40 +155,50 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
@@ -186,27 +209,33 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
case 3: spell = SPELL_NATURE_VURNALBILTY; break;
case 4: spell = SPELL_ARCANE_VURNALBILTY; break;
}
+
DoCast(m_creature,spell);
CurrentVurln_Spell = spell;
+
DoScriptText(EMOTE_SHIMMER, m_creature);
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;
@@ -215,16 +244,20 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
case 3: SpellAfflict = SPELL_BROODAF_BRONZE; break;
case 4: SpellAfflict = SPELL_BROODAF_GREEN; break;
}
+
std::list<HostilReference*>::iterator i;
- for (i = m_creature->getThreatManager().getThreatList().begin(); i != m_creature->getThreatManager().getThreatList().end(); )
+
+ for (i = m_creature->getThreatManager().getThreatList().begin();i != m_creature->getThreatManager().getThreatList().end();)
{
Unit* pUnit = NULL;
pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid());
++i;
+
if (pUnit)
{
//Cast affliction
DoCast(pUnit, SpellAfflict, true);
+
//Chromatic mutation if target is effected by all afflictions
if (pUnit->HasAura(SPELL_BROODAF_BLUE)
&& pUnit->HasAura(SPELL_BROODAF_BLACK)
@@ -234,17 +267,21 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
{
//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)
pUnit->CastSpell(pUnit, 5, false);
}
}
}
+
Affliction_Timer = 10000;
}else Affliction_Timer -= diff;
+
//Frenzy_Timer
if (Frenzy_Timer < diff)
{
@@ -252,12 +289,14 @@ struct TRINITY_DLL_DECL boss_chromaggusAI : public ScriptedAI
DoScriptText(EMOTE_FRENZY, m_creature);
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();
}
};
@@ -265,6 +304,7 @@ CreatureAI* GetAI_boss_chromaggus(Creature* pCreature)
{
return new boss_chromaggusAI (pCreature);
}
+
void AddSC_boss_chromaggus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp
index aee7d02f33e..a09da22e398 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_ebonroc.cpp
@@ -13,24 +13,30 @@
* along 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 TRINITY_DLL_DECL boss_ebonrocAI : public ScriptedAI
{
boss_ebonrocAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowFlame_Timer;
uint32 WingBuffet_Timer;
uint32 ShadowOfEbonroc_Timer;
uint32 Heal_Timer;
+
void Reset()
{
ShadowFlame_Timer = 15000; //These times are probably wrong
@@ -38,32 +44,38 @@ struct TRINITY_DLL_DECL boss_ebonrocAI : public ScriptedAI
ShadowOfEbonroc_Timer = 45000;
Heal_Timer = 1000;
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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))
{
if (Heal_Timer < diff)
@@ -72,6 +84,7 @@ struct TRINITY_DLL_DECL boss_ebonrocAI : public ScriptedAI
Heal_Timer = 1000 + rand()%2000;
}else Heal_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
@@ -79,6 +92,7 @@ CreatureAI* GetAI_boss_ebonroc(Creature* pCreature)
{
return new boss_ebonrocAI (pCreature);
}
+
void AddSC_boss_ebonroc()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp
index 8692563f4e7..73d546e0a2c 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_firemaw.cpp
@@ -13,56 +13,69 @@
* along 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 TRINITY_DLL_DECL boss_firemawAI : public ScriptedAI
{
boss_firemawAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(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();
}
};
@@ -70,6 +83,7 @@ CreatureAI* GetAI_boss_firemaw(Creature* pCreature)
{
return new boss_firemawAI (pCreature);
}
+
void AddSC_boss_firemaw()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp
index 6cf1f43b485..622ba445306 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_flamegor.cpp
@@ -13,51 +13,64 @@
* along 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 EMOTE_FRENZY -1469031
+
#define SPELL_SHADOWFLAME 22539
#define SPELL_WINGBUFFET 23339
#define SPELL_FRENZY 23342 //This spell periodically triggers fire nova
+
struct TRINITY_DLL_DECL boss_flamegorAI : public ScriptedAI
{
boss_flamegorAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(m_creature->getVictim(),-75);
+
WingBuffet_Timer = 25000;
}else WingBuffet_Timer -= diff;
+
//Frenzy_Timer
if (Frenzy_Timer < diff)
{
@@ -65,6 +78,7 @@ struct TRINITY_DLL_DECL boss_flamegorAI : public ScriptedAI
DoCast(m_creature,SPELL_FRENZY);
Frenzy_Timer = 8000 + (rand()%2000);
}else Frenzy_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -72,6 +86,7 @@ CreatureAI* GetAI_boss_flamegor(Creature* pCreature)
{
return new boss_flamegorAI (pCreature);
}
+
void AddSC_boss_flamegor()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp
index 0f0d4d7d613..b0d2d01494e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_nefarian.cpp
@@ -13,19 +13,23 @@
* along 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: 80
SDComment: Some issues with class calls effecting more than one class
SDCategory: Blackwing Lair
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO -1469007
#define SAY_XHEALTH -1469008
#define SAY_SHADOWFLAME -1469009
#define SAY_RAISE_SKELETONS -1469010
#define SAY_SLAY -1469011
#define SAY_DEATH -1469012
+
#define SAY_MAGE -1469013
#define SAY_WARRIOR -1469014
#define SAY_DRUID -1469015
@@ -35,6 +39,7 @@ EndScriptData */
#define SAY_WARLOCK -1469019
#define SAY_HUNTER -1469020
#define SAY_ROGUE -1469021
+
#define SPELL_SHADOWFLAME_INITIAL 22972
#define SPELL_SHADOWFLAME 22539
#define SPELL_BELLOWINGROAR 22686
@@ -42,6 +47,7 @@ EndScriptData */
#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
@@ -51,9 +57,11 @@ EndScriptData */
#define SPELL_WARLOCK 23427 //infernals
#define SPELL_HUNTER 23436 //bow broke
#define SPELL_ROGUE 23414 //Paralise
+
struct TRINITY_DLL_DECL boss_nefarianAI : public ScriptedAI
{
boss_nefarianAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowFlame_Timer;
uint32 BellowingRoar_Timer;
uint32 VeilOfShadow_Timer;
@@ -61,7 +69,9 @@ struct TRINITY_DLL_DECL boss_nefarianAI : public ScriptedAI
uint32 TailLash_Timer;
uint32 ClassCall_Timer;
bool Phase3;
+
uint32 DespawnTimer;
+
void Reset()
{
ShadowFlame_Timer = 12000; //These times are probably wrong
@@ -71,24 +81,31 @@ struct TRINITY_DLL_DECL boss_nefarianAI : public ScriptedAI
TailLash_Timer = 10000;
ClassCall_Timer = 35000; //35-40 seconds
Phase3 = false;
+
DespawnTimer = 5000;
}
+
void KilledUnit(Unit* Victim)
{
if (rand()%5)
return;
+
DoScriptText(SAY_SLAY, m_creature, Victim);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_XHEALTH,SAY_AGGRO,SAY_SHADOWFLAME), m_creature);
+
DoCast(who,SPELL_SHADOWFLAME_INITIAL);
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
if(DespawnTimer < diff)
@@ -97,45 +114,54 @@ struct TRINITY_DLL_DECL boss_nefarianAI : public ScriptedAI
m_creature->ForcedDespawn();
DespawnTimer = 5000;
}else DespawnTimer -= diff;
+
if (!UpdateVictim())
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:
@@ -175,14 +201,17 @@ struct TRINITY_DLL_DECL boss_nefarianAI : public ScriptedAI
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;
DoScriptText(SAY_RAISE_SKELETONS, m_creature);
}
+
DoMeleeAttackIfReady();
}
};
@@ -190,6 +219,7 @@ CreatureAI* GetAI_boss_nefarian(Creature* pCreature)
{
return new boss_nefarianAI (pCreature);
}
+
void AddSC_boss_nefarian()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp
index e88b479d43c..5496448b6ed 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_razorgore.cpp
@@ -13,29 +13,37 @@
* along 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 (Grethok the Controller)
SDCategory: Blackwing Lair
EndScriptData */
+
#include "precompiled.h"
+
//Razorgore Phase 2 Script
+
#define SAY_EGGS_BROKEN1 -1469022
#define SAY_EGGS_BROKEN2 -1469023
#define SAY_EGGS_BROKEN3 -1469024
#define SAY_DEATH -1469025
+
#define SPELL_CLEAVE 22540
#define SPELL_WARSTOMP 24375
#define SPELL_FIREBALLVOLLEY 22425
#define SPELL_CONFLAGRATION 23023
+
struct TRINITY_DLL_DECL boss_razorgoreAI : public ScriptedAI
{
boss_razorgoreAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Cleave_Timer;
uint32 WarStomp_Timer;
uint32 FireballVolley_Timer;
uint32 Conflagration_Timer;
+
void Reset()
{
Cleave_Timer = 15000; //These times are probably wrong
@@ -43,45 +51,55 @@ struct TRINITY_DLL_DECL boss_razorgoreAI : public ScriptedAI
FireballVolley_Timer = 7000;
Conflagration_Timer = 12000;
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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 (DoGetThreat(m_creature->getVictim()))
//DoModifyThreatPercent(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() && m_creature->getVictim()->HasAura(SPELL_CONFLAGRATION))
{
@@ -90,13 +108,16 @@ struct TRINITY_DLL_DECL boss_razorgoreAI : public ScriptedAI
if (target)
m_creature->TauntApply(target);
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_razorgore(Creature* pCreature)
{
return new boss_razorgoreAI (pCreature);
}
+
void AddSC_boss_razorgore()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp
index 9fb1f05d24e..ac9a0f6aaaa 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_vaelastrasz.cpp
@@ -13,25 +13,31 @@
* along 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 SAY_LINE1 -1469026
#define SAY_LINE2 -1469027
#define SAY_LINE3 -1469028
#define SAY_HALFLIFE -1469029
#define SAY_KILLTARGET -1469030
+
#define GOSSIP_ITEM "Start Event <Needs Gossip Text>"
+
#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
+
struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
{
boss_vaelAI(Creature *c) : ScriptedAI(c)
@@ -40,6 +46,7 @@ struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
c->setFaction(35);
c->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
uint64 PlayerGUID;
uint32 SpeachTimer;
uint32 SpeachNum;
@@ -51,6 +58,7 @@ struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
uint32 TailSwipe_Timer;
bool HasYelled;
bool DoingSpeach;
+
void Reset()
{
PlayerGUID = 0;
@@ -65,29 +73,37 @@ struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
HasYelled = false;
DoingSpeach = false;
}
+
void BeginSpeach(Unit* target)
{
//Stand up and begin speach
PlayerGUID = target->GetGUID();
+
//10 seconds
DoScriptText(SAY_LINE1, m_creature);
+
SpeachTimer = 10000;
SpeachNum = 0;
DoingSpeach = true;
+
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
+
void KilledUnit(Unit *victim)
{
if (rand()%5)
return;
+
DoScriptText(SAY_KILLTARGET, m_creature, victim);
}
+
void EnterCombat(Unit *who)
{
DoCast(m_creature,SPELL_ESSENCEOFTHERED);
DoZoneInCombat();
m_creature->SetHealth(int(m_creature->GetMaxHealth()*.3));
}
+
void UpdateAI(const uint32 diff)
{
//Speach
@@ -122,31 +138,37 @@ struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
}
}else SpeachTimer -= diff;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
// Yell if hp lower than 15%
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 15 && !HasYelled)
{
DoScriptText(SAY_HALFLIFE, m_creature);
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
{
@@ -158,22 +180,27 @@ struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
}
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)
{
@@ -182,11 +209,14 @@ struct TRINITY_DLL_DECL boss_vaelAI : public ScriptedAI
{
DoCast(m_creature->getVictim(),SPELL_TAILSWIPE);
}*/
+
TailSwipe_Timer = 20000;
}else TailSwipe_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
void SendDefaultMenu_boss_vael(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) //Fight time
@@ -195,22 +225,28 @@ void SendDefaultMenu_boss_vael(Player* pPlayer, Creature* pCreature, uint32 uiAc
CAST_AI(boss_vaelAI, pCreature->AI())->BeginSpeach(pPlayer);
}
}
+
bool GossipSelect_boss_vael(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiSender == GOSSIP_SENDER_MAIN)
SendDefaultMenu_boss_vael(pPlayer, pCreature, uiAction);
+
return true;
}
+
bool GossipHello_boss_vael(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(907, pCreature->GetGUID());
+
return true;
}
+
CreatureAI* GetAI_boss_vael(Creature* pCreature)
{
return new boss_vaelAI (pCreature);
}
+
void AddSC_boss_vael()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp
index d0c8b510124..4b7fcecf683 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/boss_victor_nefarius.cpp
@@ -13,40 +13,51 @@
* along 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 -1469004
#define SAY_GAMESBEGIN_2 -1469005
#define SAY_VAEL_INTRO -1469006 //when he corrupts Vaelastrasz
+
#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
@@ -55,6 +66,7 @@ EndScriptData */
//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 TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
{
boss_victor_nefariusAI(Creature *c) : ScriptedAI(c)
@@ -145,6 +157,7 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
break;
}
}
+
uint32 SpawnedAdds;
uint32 AddSpawnTimer;
uint32 ShadowBoltTimer;
@@ -155,6 +168,7 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
uint32 DrakType2;
uint64 NefarianGUID;
uint32 NefCheckTime;
+
void Reset()
{
SpawnedAdds = 0;
@@ -164,16 +178,20 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
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)
{
DoScriptText(SAY_GAMESBEGIN_2, m_creature);
+
//Trinity::Singleton<MapManager>::Instance().GetMap(m_creature->GetMapId(), m_creature)->GetPlayers().begin();
/*
list <Player*>::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));
@@ -184,22 +202,27 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
AttackStart(target);
}
+
void EnterCombat(Unit *who)
{
}
+
void MoveInLineOfSight(Unit *who)
{
//We simply use this function to find players until we can use pMap->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 (!UpdateVictim())
return;
+
//Only do this if we haven't spawned nef yet
if (SpawnedAdds < 42)
{
@@ -210,8 +233,10 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (target)
DoCast(target,SPELL_SHADOWBOLT);
+
ShadowBoltTimer = 3000 + (rand()%7000);
}else ShadowBoltTimer -= diff;
+
//FearTimer
if (FearTimer < diff)
{
@@ -219,8 +244,10 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
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)
{
@@ -228,11 +255,14 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
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);
@@ -241,11 +271,14 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
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);
@@ -255,20 +288,26 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
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 Trinity log but no real issues)
DoTeleportTo(HIDE_X,HIDE_Y,HIDE_Z);
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);
@@ -282,6 +321,7 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
}
else error_log("TSCR: Blackwing Lair: Unable to spawn nefarian properly.");
}
+
AddSpawnTimer = 4000;
}else AddSpawnTimer -= diff;
}
@@ -290,6 +330,7 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
if (NefCheckTime < diff)
{
Unit* Nefarian = Unit::GetCreature((*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())
@@ -297,21 +338,25 @@ struct TRINITY_DLL_DECL boss_victor_nefariusAI : public ScriptedAI
NefarianGUID = 0;
m_creature->ForcedDespawn();
}
+
NefCheckTime = 2000;
}else NefCheckTime -= diff;
}
}
};
+
CreatureAI* GetAI_boss_victor_nefarius(Creature* pCreature)
{
return new boss_victor_nefariusAI (pCreature);
}
+
bool GossipHello_boss_victor_nefarius(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_1 , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(7134, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_boss_victor_nefarius(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -332,9 +377,11 @@ bool GossipSelect_boss_victor_nefarius(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
void AddSC_boss_victor_nefarius()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_victor_nefarius";
newscript->GetAI = &GetAI_boss_victor_nefarius;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp
index 781ae028c9f..97bf717589a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blackwing_lair/instance_blackwing_lair.cpp
@@ -13,11 +13,13 @@
* along 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_Blackwing_Lair
SD%Complete: 0
SDComment:
SDCategory: Blackwing Lair
EndScriptData */
+
#include "precompiled.h"
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/blasted_lands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/blasted_lands.cpp
index 42fdbb98c60..97e10f59e59 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/blasted_lands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/blasted_lands.cpp
@@ -13,31 +13,41 @@
* along 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* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(3628) == QUEST_STATUS_INCOMPLETE && pPlayer->HasItemCount(10757, 1))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_USHER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_deathly_usher(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -45,32 +55,43 @@ bool GossipSelect_npc_deathly_usher(Player* pPlayer, Creature* pCreature, uint32
pPlayer->CLOSE_GOSSIP_MENU();
pCreature->CastSpell(pPlayer, SPELL_TELEPORT_SINGLE, true);
}
+
return true;
}
+
/*######
## npc_fallen_hero_of_horde
######*/
#define GOSSIP_H_F1 "Why are you here?"
#define GOSSIP_H_F2 "Continue story..."
+
#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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(2784) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_H_F1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
if (pPlayer->GetQuestStatus(2801) == QUEST_STATUS_INCOMPLETE && pPlayer->GetTeam() == HORDE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_H_F2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
+
if (pPlayer->GetQuestStatus(2801) == QUEST_STATUS_INCOMPLETE && pPlayer->GetTeam() == ALLIANCE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_H_F1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_fallen_hero_of_horde(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -89,6 +110,7 @@ bool GossipSelect_npc_fallen_hero_of_horde(Player* pPlayer, Creature* pCreature,
pPlayer->SEND_GOSSIP_MENU(1411, pCreature->GetGUID());
}
break;
+
case GOSSIP_ACTION_INFO_DEF+2:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FALLEN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21);
pPlayer->SEND_GOSSIP_MENU(1451, pCreature->GetGUID());
@@ -120,14 +142,17 @@ bool GossipSelect_npc_fallen_hero_of_horde(Player* pPlayer, Creature* pCreature,
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_fallen_hero_of_horde";
newscript->pGossipHello = &GossipHello_npc_fallen_hero_of_horde;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/boss_kruul.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/boss_kruul.cpp
index e85d7ed2ce2..267bbdefe95 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/boss_kruul.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/boss_kruul.cpp
@@ -13,13 +13,16 @@
* along 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
@@ -27,9 +30,11 @@ EndScriptData */
#define SPELL_VOIDBOLT 21066
#define SPELL_RAGE 21340
#define SPELL_CAPTURESOUL 21054
+
struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
{
boss_kruulAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowVolley_Timer;
uint32 Cleave_Timer;
uint32 ThunderClap_Timer;
@@ -41,6 +46,7 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
int RandX;
int RandY;
Creature* Summoned;
+
void Reset()
{
ShadowVolley_Timer = 10000;
@@ -51,14 +57,18 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
Rage_Timer = 60000; //Cast rage after 1 minute
Hound_Timer = 8000;
}
+
void EnterCombat(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;
@@ -79,11 +89,13 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
if (Summoned)
(Summoned->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//ShadowVolley_Timer
if (ShadowVolley_Timer < diff)
{
@@ -91,8 +103,10 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
{
DoCast(m_creature->getVictim(),SPELL_SHADOWVOLLEY);
}
+
ShadowVolley_Timer = 5000;
}else ShadowVolley_Timer -= diff;
+
//Cleave_Timer
if (Cleave_Timer < diff)
{
@@ -100,8 +114,10 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
{
DoCast(m_creature->getVictim(),SPELL_CLEAVE);
}
+
Cleave_Timer = 10000;
}else Cleave_Timer -= diff;
+
//ThunderClap_Timer
if (ThunderClap_Timer < diff)
{
@@ -109,14 +125,17 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
{
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)
{
@@ -124,22 +143,27 @@ struct TRINITY_DLL_DECL boss_kruulAI : public ScriptedAI
{
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();
}
};
@@ -147,6 +171,7 @@ CreatureAI* GetAI_boss_kruul(Creature* pCreature)
{
return new boss_kruulAI (pCreature);
}
+
void AddSC_boss_kruul()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/burning_steppes.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/burning_steppes.cpp
index 38a0a8052c6..d1b4c778a1f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/burning_steppes.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/burning_steppes.cpp
@@ -13,19 +13,24 @@
* along 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
######*/
+
#define GOSSIP_HELLO "Official buisness, John. I need some information about Marsha Windsor. Tell me about the last time you saw him."
#define GOSSIP_SELECT1 "So what did you do?"
#define GOSSIP_SELECT2 "Start making sense, dwarf. I don't want to have anything to do with your cracker, your pappy, or any sort of 'discreditin'."
@@ -38,10 +43,13 @@ EndContentData */
#define GOSSIP_SELECT9 "300? So the Dark Irons killed him and dragged him into the Depths?"
#define GOSSIP_SELECT10 "Ahh... Ironfoe"
#define GOSSIP_SELECT11 "Thanks, Ragged John. Your story was very uplifting and informative"
+
struct TRINITY_DLL_DECL npc_ragged_johnAI : public ScriptedAI
{
npc_ragged_johnAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
+
void MoveInLineOfSight(Unit *who)
{
if (who->HasAura(16468))
@@ -52,23 +60,30 @@ struct TRINITY_DLL_DECL npc_ragged_johnAI : public ScriptedAI
CAST_PLR(who)->AreaExploredOrEventHappens(4866);
}
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void EnterCombat(Unit *who) {}
};
+
CreatureAI* GetAI_npc_ragged_john(Creature* pCreature)
{
return new npc_ragged_johnAI (pCreature);
}
+
bool GossipHello_npc_ragged_john(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(4224) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(2713, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_ragged_john(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -124,9 +139,11 @@ bool GossipSelect_npc_ragged_john(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
void AddSC_burning_steppes()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_ragged_john";
newscript->GetAI = &GetAI_npc_ragged_john;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/deadmines.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/deadmines.cpp
index c2a052917f6..d2544cc881f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/deadmines.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/deadmines.cpp
@@ -13,29 +13,36 @@
* along 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"
#include "def_deadmines.h"
#include "Spell.h"
+
#define SOUND_CANNONFIRE 1400
#define SOUND_DESTROYDOOR 3079
#define SAY_MR_SMITE_ALARM1 "You there, check out that noise!"
#define SOUND_MR_SMITE_ALARM1 5775
#define SAY_MR_SMITE_ALARM2 "We're under attack! A vast, ye swabs! Repel the invaders!"
#define SOUND_MR_SMITE_ALARM2 5777
+
#define GO_IRONCLAD_DOOR 16397
#define GO_DEFIAS_CANNON 16398
#define GO_DOOR_LEVER 101833
+
#define CANNON_BLAST_TIMER 3000
#define PIRATES_DELAY_TIMER 1000
+
struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
{
instance_deadmines(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
GameObject* IronCladDoor;
GameObject* DefiasCannon;
GameObject* DoorLever;
@@ -45,6 +52,7 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
uint32 State;
uint32 CannonBlast_Timer;
uint32 PiratesDelay_Timer;
+
void Initialize()
{
IronCladDoor = NULL;
@@ -52,10 +60,12 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
DoorLever = NULL;
State = CANNON_NOT_USED;
}
+
virtual void Update(uint32 diff)
{
if (!IronCladDoor || !DefiasCannon || !DoorLever)
return;
+
switch(State)
{
case CANNON_GUNPOWDER_USED:
@@ -90,39 +100,47 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
break;
}
}
+
void SummonCreatures()
{
DefiasPirate1 = IronCladDoor->SummonCreature(657,IronCladDoor->GetPositionX() - 2,IronCladDoor->GetPositionY()-7,IronCladDoor->GetPositionZ(), 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000);
DefiasPirate2 = IronCladDoor->SummonCreature(657,IronCladDoor->GetPositionX() + 3,IronCladDoor->GetPositionY()-6,IronCladDoor->GetPositionZ(), 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000);
DefiasCompanion = IronCladDoor->SummonCreature(3450,IronCladDoor->GetPositionX() + 2,IronCladDoor->GetPositionY()-6,IronCladDoor->GetPositionZ(), 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 3000);
}
+
void MoveCreaturesInside()
{
if (!DefiasPirate1 || !DefiasPirate2 || !DefiasCompanion)
return;
+
MoveCreatureInside(DefiasPirate1);
MoveCreatureInside(DefiasPirate2);
MoveCreatureInside(DefiasCompanion);
}
+
void MoveCreatureInside(Creature* pCreature)
{
pCreature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pCreature->GetMotionMaster()->MovePoint(0, -102.7,-655.9, pCreature->GetPositionZ());
}
+
void ShootCannon()
{
DefiasCannon->SetGoState(GO_STATE_ACTIVE);
DoPlaySound(DefiasCannon, SOUND_CANNONFIRE);
}
+
void BlastOutDoor()
{
IronCladDoor->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE);
DoPlaySound(IronCladDoor, SOUND_DESTROYDOOR);
}
+
void LeverStucked()
{
DoorLever->SetUInt32Value(GAMEOBJECT_FLAGS, 4);
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -138,6 +156,7 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
if (type == EVENT_STATE)
@@ -146,12 +165,14 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
State=data;
}
}
+
uint32 GetData(uint32 type)
{
if (type == EVENT_STATE)
return State;
return 0;
}
+
void DoPlaySound(GameObject* unit, uint32 sound)
{
WorldPacket data(4);
@@ -159,6 +180,7 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
data << uint32(sound);
unit->SendMessageToSet(&data,false);
}
+
void DoPlaySoundCreature(Unit* unit, uint32 sound)
{
WorldPacket data(4);
@@ -167,12 +189,15 @@ struct TRINITY_DLL_DECL instance_deadmines : public ScriptedInstance
unit->SendMessageToSet(&data,false);
}
};
+
/*#####
# item_Defias_Gunpowder
#####*/
+
bool ItemUse_item_defias_gunpowder(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
ScriptedInstance *pInstance = pPlayer->GetInstanceData();
+
if (!pInstance)
{
pPlayer->GetSession()->SendNotification("Instance script not initialized");
@@ -185,13 +210,16 @@ bool ItemUse_item_defias_gunpowder(Player* pPlayer, Item* _Item, SpellCastTarget
{
pInstance->SetData(EVENT_STATE, CANNON_GUNPOWDER_USED);
}
+
pPlayer->DestroyItemCount(_Item->GetEntry(), 1, true);
return true;
}
+
InstanceData* GetInstanceData_instance_deadmines(Map* pMap)
{
return new instance_deadmines(pMap);
}
+
void AddSC_instance_deadmines()
{
Script *newscript;
@@ -199,6 +227,7 @@ void AddSC_instance_deadmines()
newscript->Name = "instance_deadmines";
newscript->GetInstanceData = &GetInstanceData_instance_deadmines;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_defias_gunpowder";
newscript->pItemUse = &ItemUse_item_defias_gunpowder;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/def_deadmines.h b/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/def_deadmines.h
index faa55500209..9b535a9fd80 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/def_deadmines.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/deadmines/def_deadmines.h
@@ -1,11 +1,14 @@
#ifndef DEF_DEADMINES_H
#define DEF_DEADMINES_H
+
#include "precompiled.h"
+
#define CANNON_NOT_USED 1
#define CANNON_GUNPOWDER_USED 2
#define CANNON_BLAST_INITIATED 3
#define PIRATES_ATTACK 4
#define EVENT_DONE 5
+
#define EVENT_STATE 1
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/dun_morogh.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/dun_morogh.cpp
index 488807a0109..f2d589d5302 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/dun_morogh.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/dun_morogh.cpp
@@ -13,26 +13,34 @@
* along 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
######*/
+
// signed for 6172
#define SAY_HEAL -1000280
+
struct TRINITY_DLL_DECL npc_narm_faulkAI : public ScriptedAI
{
uint32 lifeTimer;
bool spellHit;
+
npc_narm_faulkAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
lifeTimer = 120000;
@@ -40,13 +48,16 @@ struct TRINITY_DLL_DECL npc_narm_faulkAI : public ScriptedAI
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
spellHit = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void MoveInLineOfSight(Unit *who)
{
return;
}
+
void UpdateAI(const uint32 diff)
{
if (m_creature->IsStandState())
@@ -60,6 +71,7 @@ struct TRINITY_DLL_DECL npc_narm_faulkAI : public ScriptedAI
lifeTimer -= diff;
}
}
+
void SpellHit(Unit *Hitter, const SpellEntry *Spellkind)
{
if (Spellkind->Id == 8593 && !spellHit)
@@ -72,14 +84,17 @@ struct TRINITY_DLL_DECL npc_narm_faulkAI : public ScriptedAI
spellHit = true;
}
}
+
};
CreatureAI* GetAI_npc_narm_faulk(Creature* pCreature)
{
return new npc_narm_faulkAI (pCreature);
}
+
void AddSC_dun_morogh()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_narm_faulk";
newscript->GetAI = &GetAI_npc_narm_faulk;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/duskwood.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/duskwood.cpp
index e97094ecc23..394f652d8ff 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/duskwood.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/duskwood.cpp
@@ -13,16 +13,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Duskwood
SD%Complete: 100
SDComment: Quest Support:8735
SDCategory: Duskwood
EndScriptData */
+
#include "precompiled.h"
+
/*######
# at_twilight_grove
######*/
+
bool AreaTrigger_at_twilight_grove(Player* pPlayer, AreaTriggerEntry *at)
{
if (pPlayer->HasQuestForItem(21149))
@@ -41,18 +45,23 @@ bool AreaTrigger_at_twilight_grove(Player* pPlayer, AreaTriggerEntry *at)
}
return false;
};
+
/*######
# boss_twilight_corrupter
######*/
+
#define SPELL_SOUL_CORRUPTION 25805
#define SPELL_CREATURE_OF_NIGHTMARE 25806
#define SPELL_LEVEL_UP 24312
+
struct TRINITY_DLL_DECL boss_twilight_corrupterAI : public ScriptedAI
{
boss_twilight_corrupterAI(Creature *c) : ScriptedAI(c) {}
+
uint32 SoulCorruption_Timer;
uint32 CreatureOfNightmare_Timer;
uint8 KillCount;
+
void Reset()
{
SoulCorruption_Timer = 15000;
@@ -63,12 +72,14 @@ struct TRINITY_DLL_DECL boss_twilight_corrupterAI : public ScriptedAI
{
m_creature->MonsterYell("The Nightmare cannot be stopped!",0,m_creature->GetGUID());
}
+
void KilledUnit(Unit* victim)
{
if (victim->GetTypeId() == TYPEID_PLAYER)
{
++KillCount;
m_creature->MonsterTextEmote("Twilight Corrupter squeezes the last bit of life out of $N and swallows their soul.", victim->GetGUID(),true);
+
if (KillCount == 3)
{
DoCast(m_creature, SPELL_LEVEL_UP, true);
@@ -76,6 +87,7 @@ struct TRINITY_DLL_DECL boss_twilight_corrupterAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
@@ -97,13 +109,16 @@ CreatureAI* GetAI_boss_twilight_corrupter(Creature* pCreature)
{
return new boss_twilight_corrupterAI (pCreature);
}
+
void AddSC_duskwood()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_twilight_corrupter";
newscript->GetAI = &GetAI_boss_twilight_corrupter;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "at_twilight_grove";
newscript->pAreaTrigger = &AreaTrigger_at_twilight_grove;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/eastern_plaguelands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/eastern_plaguelands.cpp
index b7ae3722615..c801398b830 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/eastern_plaguelands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/eastern_plaguelands.cpp
@@ -13,73 +13,94 @@
* along 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 TRINITY_DLL_DECL mobs_ghoul_flayerAI : public ScriptedAI
{
mobs_ghoul_flayerAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
+
void EnterCombat(Unit* who) { }
+
void JustDied(Unit* Killer)
{
if (Killer->GetTypeId() == TYPEID_PLAYER)
m_creature->SummonCreature(11064, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 60000);
}
};
+
CreatureAI* GetAI_mobs_ghoul_flayer(Creature* pCreature)
{
return new mobs_ghoul_flayerAI (pCreature);
}
+
/*######
## npc_augustus_the_touched
######*/
+
bool GossipHello_npc_augustus_the_touched(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetQuestRewardStatus(6164))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_augustus_the_touched(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
return true;
}
+
/*######
## npc_darrowshire_spirit
######*/
+
#define SPELL_SPIRIT_SPAWNIN 17321
+
struct TRINITY_DLL_DECL npc_darrowshire_spiritAI : public ScriptedAI
{
npc_darrowshire_spiritAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
DoCast(m_creature,SPELL_SPIRIT_SPAWNIN);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(Unit *who) { }
+
};
CreatureAI* GetAI_npc_darrowshire_spirit(Creature* pCreature)
{
return new npc_darrowshire_spiritAI (pCreature);
}
+
bool GossipHello_npc_darrowshire_spirit(Player* pPlayer, Creature* pCreature)
{
pPlayer->SEND_GOSSIP_MENU(3873, pCreature->GetGUID());
@@ -87,22 +108,29 @@ bool GossipHello_npc_darrowshire_spirit(Player* pPlayer, Creature* pCreature)
pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
return true;
}
+
/*######
## npc_tirion_fordring
######*/
+
#define GOSSIP_HELLO "I am ready to hear your tale, Tirion."
#define GOSSIP_SELECT1 "Thank you, Tirion. What of your identity?"
#define GOSSIP_SELECT2 "That is terrible."
#define GOSSIP_SELECT3 "I will, Tirion."
+
bool GossipHello_npc_tirion_fordring(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(5742) == QUEST_STATUS_INCOMPLETE && pPlayer->getStandState() == UNIT_STAND_STATE_SIT)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_tirion_fordring(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -126,23 +154,28 @@ bool GossipSelect_npc_tirion_fordring(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
void AddSC_eastern_plaguelands()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mobs_ghoul_flayer";
newscript->GetAI = &GetAI_mobs_ghoul_flayer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_augustus_the_touched";
newscript->pGossipHello = &GossipHello_npc_augustus_the_touched;
newscript->pGossipSelect = &GossipSelect_npc_augustus_the_touched;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_darrowshire_spirit";
newscript->GetAI = &GetAI_npc_darrowshire_spirit;
newscript->pGossipHello = &GossipHello_npc_darrowshire_spirit;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tirion_fordring";
newscript->pGossipHello = &GossipHello_npc_tirion_fordring;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/elwynn_forest.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/elwynn_forest.cpp
index ea69f408e94..40f0ceecd09 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/elwynn_forest.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/elwynn_forest.cpp
@@ -13,25 +13,33 @@
* along 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 -1000280
+
struct TRINITY_DLL_DECL npc_henze_faulkAI : public ScriptedAI
{
uint32 lifeTimer;
bool spellHit;
+
npc_henze_faulkAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
lifeTimer = 120000;
@@ -39,13 +47,16 @@ struct TRINITY_DLL_DECL npc_henze_faulkAI : public ScriptedAI
m_creature->SetStandState(UNIT_STAND_STATE_DEAD); // lay down
spellHit = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void MoveInLineOfSight(Unit *who)
{
return;
}
+
void UpdateAI(const uint32 diff)
{
if (m_creature->IsStandState())
@@ -59,6 +70,7 @@ struct TRINITY_DLL_DECL npc_henze_faulkAI : public ScriptedAI
lifeTimer -= diff;
}
}
+
void SpellHit(Unit *Hitter, const SpellEntry *Spellkind)
{
if (Spellkind->Id == 8593 && !spellHit)
@@ -71,14 +83,17 @@ struct TRINITY_DLL_DECL npc_henze_faulkAI : public ScriptedAI
spellHit = true;
}
}
+
};
CreatureAI* GetAI_npc_henze_faulk(Creature* pCreature)
{
return new npc_henze_faulkAI (pCreature);
}
+
void AddSC_elwynn_forest()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_henze_faulk";
newscript->GetAI = &GetAI_npc_henze_faulk;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/eversong_woods.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/eversong_woods.cpp
index 3474d46f1df..b3d598c7669 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/eversong_woods.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/eversong_woods.cpp
@@ -13,12 +13,14 @@
* along 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: 8483, 8488, 8490, 9686
SDCategory: Eversong Woods
EndScriptData */
+
/* ContentData
npc_prospector_anvilward
npc_apprentice_mirveda
@@ -26,26 +28,35 @@ npc_infused_crystal
npc_kelerun_bloodmourn
go_harbinger_second_trial
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_prospector_anvilward
######*/
+
#define GOSSIP_HELLO "I need a moment of your time, sir."
#define GOSSIP_SELECT "Why... yes, of course. I've something to show you right inside this building, Mr. Anvilward."
+
#define SAY_PR_1 -1000281
#define SAY_PR_2 -1000282
+
#define QUEST_THE_DWARVEN_SPY 8483
+
struct TRINITY_DLL_DECL npc_prospector_anvilwardAI : public npc_escortAI
{
// CreatureAI functions
npc_prospector_anvilwardAI(Creature *c) : npc_escortAI(c) {}
+
// Pure Virtual Functions
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch (i)
{
case 0: DoScriptText(SAY_PR_1, m_creature, pPlayer); break;
@@ -53,26 +64,32 @@ struct TRINITY_DLL_DECL npc_prospector_anvilwardAI : public npc_escortAI
case 6: m_creature->setFaction(24); break;
}
}
+
void Reset()
{
me->RestoreFaction();
}
+
void JustDied(Unit* killer)
{
me->RestoreFaction();
}
};
+
CreatureAI* GetAI_npc_prospector_anvilward(Creature* pCreature)
{
return new npc_prospector_anvilwardAI(pCreature);
}
+
bool GossipHello_npc_prospector_anvilward(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_THE_DWARVEN_SPY) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(8239, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_prospector_anvilward(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -89,36 +106,50 @@ bool GossipSelect_npc_prospector_anvilward(Player* pPlayer, Creature* pCreature,
}
return true;
}
+
/*######
## Quest 9686 Second Trial
######*/
+
#define QUEST_SECOND_TRIAL 9686
+
#define MASTER_KELERUN_BLOODMOURN 17807
+
#define CHAMPION_BLOODWRATH 17809
#define CHAMPION_LIGHTREND 17810
#define CHAMPION_SWIFTBLADE 17811
#define CHAMPION_SUNSTRIKER 17812
+
#define HARBINGER_OF_THE_SECOND_TRIAL 182052
+
#define SPELL_FLASH_OF_LIGHT 19939
#define TIMER_FLASH_OF_LIGHT 3225
+
#define SPELL_SEAL_OF_JUSTICE 20164
#define TIMER_SEAL_OF_JUSTICE 10000
+
#define SPELL_JUDGEMENT_OF_LIGHT 20271
#define TIMER_JUDGEMENT_OF_LIGHT 10000
+
#define SPELL_SEAL_OF_COMMAND 20375
#define TIMER_SEAL_OF_COMMAND 20000
+
#define OFFSET_NEXT_ATTACK 750
+
#define FACTION_HOSTILE 45
#define FACTION_FRIENDLY 7
+
// missing from db
#define TEXT_SECOND_TRIAL_1 -1645006
#define TEXT_SECOND_TRIAL_2 -1645007
#define TEXT_SECOND_TRIAL_3 -1645008
#define TEXT_SECOND_TRIAL_4 -1645009
+
struct Locations
{
float x, y, z, o;
};
+
static Locations SpawnPosition[]=
{
{5.3, -11.8, 0.361, 4.2},
@@ -126,35 +157,46 @@ static Locations SpawnPosition[]=
{-5.7, -34.85, 0.361, 1.09},
{-11.9, -18, 0.361, 5.87}
};
+
static uint32 PaladinEntry[]= {CHAMPION_BLOODWRATH, CHAMPION_LIGHTREND, CHAMPION_SWIFTBLADE, CHAMPION_SUNSTRIKER};
+
/*######
## npc_second_trial_paladin
######*/
+
struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
{
npc_secondTrialAI(Creature *c) : ScriptedAI(c) {}
+
uint32 timer;
uint8 questPhase;
uint64 summonerGuid;
+
bool spellFlashLight;
bool spellJustice;
bool spellJudLight;
bool spellCommand;
+
uint32 timerFlashLight;
uint32 timerJustice;
uint32 timerJudLight;
uint32 timerCommand;
+
void Reset() {
+
timer = 2000;
questPhase = 0;
summonerGuid = 0;
+
m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_KNEEL);
m_creature->setFaction(FACTION_FRIENDLY);
+
spellFlashLight = false;
spellJustice = false;
spellJudLight = false;
spellCommand = false;
+
switch(m_creature->GetEntry()) {
case CHAMPION_BLOODWRATH :
spellFlashLight = true;
@@ -178,14 +220,18 @@ struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
break;
}
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (questPhase == 1) {
+
if (timer < diff) {
m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, UNIT_STAND_STATE_STAND);
m_creature->setFaction(FACTION_HOSTILE);
questPhase = 0;
+
Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (target && target->GetTypeId() == TYPEID_PLAYER) // only on players.
{
@@ -195,8 +241,10 @@ struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
}
else timer -= diff;
}
+
if (!UpdateVictim())
return;
+
// healer
if (spellFlashLight) {
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 70){
@@ -208,6 +256,7 @@ struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
timerFlashLight -= diff;
}
}
+
if (spellJustice) {
if (timerJustice < diff)
{
@@ -217,6 +266,7 @@ struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
else
timerJustice -= diff;
}
+
if (spellJudLight) {
if (timerJudLight < diff) {
DoCast(m_creature, SPELL_JUDGEMENT_OF_LIGHT);
@@ -225,6 +275,7 @@ struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
else
timerJudLight -= diff;
}
+
if (spellCommand) {
if (timerCommand < diff) {
DoCast(m_creature, TIMER_SEAL_OF_COMMAND);
@@ -233,29 +284,41 @@ struct TRINITY_DLL_DECL npc_secondTrialAI : public ScriptedAI
else
timerCommand -= diff;
}
+
DoMeleeAttackIfReady();
}
+
void Activate(uint64 summonerguid);
void KilledUnit(Unit* Killed);
void JustDied(Unit* Killer);
+
};
+
/*######
## npc_second_trial_controller
######*/
+
struct TRINITY_DLL_DECL master_kelerun_bloodmournAI : public ScriptedAI
{
master_kelerun_bloodmournAI(Creature *c) : ScriptedAI(c) {}
+
uint8 questPhase;
uint8 paladinPhase;
uint32 timer;
+
uint64 paladinGuid[4];
+
void Reset() {
+
questPhase = 0;
timer = 60000;
paladinPhase = 0;
uint64 paladinGuid[] = {0,0,0,0};
+
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
// Quest accepted but object not activated, object despawned (if in sync 1 minute!)
@@ -265,11 +328,14 @@ struct TRINITY_DLL_DECL master_kelerun_bloodmournAI : public ScriptedAI
}
// fight the 4 paladin mobs phase
else if (questPhase == 2) {
+
if (timer < diff) {
+
Creature* paladinSpawn;
paladinSpawn = (Unit::GetCreature((*m_creature), paladinGuid[paladinPhase]));
if (paladinSpawn) {
CAST_AI(npc_secondTrialAI, paladinSpawn->AI())->Activate(m_creature->GetGUID());
+
switch(paladinPhase) {
case 0:
DoScriptText(TEXT_SECOND_TRIAL_1,m_creature);
@@ -287,75 +353,99 @@ struct TRINITY_DLL_DECL master_kelerun_bloodmournAI : public ScriptedAI
}
else
Reset();
+
questPhase=4;
timer = OFFSET_NEXT_ATTACK;
}
else timer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void StartEvent()
{
+
if (questPhase == 1) { // no player check, quest can be finished as group, so no complex PlayerGUID/group search code
+
for (int i = 0; i<4; ++i) {
Creature* Summoned;
Summoned = DoSpawnCreature(PaladinEntry[i], SpawnPosition[i].x, SpawnPosition[i].y, SpawnPosition[i].z, SpawnPosition[i].o, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 180000);
+
if (Summoned)
paladinGuid[i] = Summoned->GetGUID();
}
+
timer = OFFSET_NEXT_ATTACK;
questPhase = 2;
}
}
+
void SecondTrialKill();
void SummonedCreatureDespawn(Creature* c) {}
};
+
bool GossipHello_master_kelerun_bloodmourn(Player* pPlayer, Creature* pCreature)
{
// quest only available if not already started
// Quest_template flag is set to : QUEST_FLAGS_EVENT
// Escort quests or any other event-driven quests. If player in party, all players that can accept this quest will receive confirmation box to accept quest.
// !not sure if this really works!
+
if (CAST_AI(master_kelerun_bloodmournAI, pCreature->AI())->questPhase == 0) {
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
pPlayer->SendPreparedQuest(pCreature->GetGUID());
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetEntry(), pCreature->GetGUID());
return true;
}
+
bool QuestAccept_master_kelerun_bloodmourn(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
// One Player exclusive quest, wait for user go activation
if (quest->GetQuestId() == QUEST_SECOND_TRIAL)
CAST_AI(master_kelerun_bloodmournAI, pCreature->AI())->questPhase = 1;
+
return true;
}
+
void master_kelerun_bloodmournAI::SecondTrialKill() {
+
if (questPhase > 0) {
+
++paladinPhase;
+
if (paladinPhase < 4)
questPhase=2;
else
Reset(); // Quest Complete, QuestComplete handler is in npc_secondTrialAI::JustDied
}
}
+
void npc_secondTrialAI::JustDied(Unit* Killer) {
+
if (Killer->GetTypeId() == TYPEID_PLAYER)
{
Creature* Summoner;
Summoner = (Unit::GetCreature((*m_creature), summonerGuid));
+
if (Summoner)
CAST_AI(master_kelerun_bloodmournAI, Summoner->AI())->SecondTrialKill();
+
// last kill quest complete for group
if (m_creature->GetEntry() == CHAMPION_SUNSTRIKER) {
+
if (Group *pGroup = CAST_PLR(Killer)->GetGroup())
{
- for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ 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(m_creature) && !pGroupGuy->GetCorpse() && pGroupGuy->GetQuestStatus(QUEST_SECOND_TRIAL) == QUEST_STATUS_INCOMPLETE)
pGroupGuy->CompleteQuest(QUEST_SECOND_TRIAL);
@@ -368,47 +458,62 @@ void npc_secondTrialAI::JustDied(Unit* Killer) {
}
}
}
+
void npc_secondTrialAI::KilledUnit(Unit* Killed) {
+
if (Killed->GetTypeId() == TYPEID_PLAYER) {
+
if (CAST_PLR(Killed)->GetQuestStatus(QUEST_SECOND_TRIAL) == QUEST_STATUS_INCOMPLETE)
CAST_PLR(Killed)->FailQuest(QUEST_SECOND_TRIAL);
}
}
+
void npc_secondTrialAI::Activate(uint64 summonerguid) {
+
questPhase=1;
summonerGuid = summonerguid;
}
+
CreatureAI* GetAI_master_kelerun_bloodmourn(Creature* pCreature)
{
return new master_kelerun_bloodmournAI (pCreature);
}
+
CreatureAI* GetAI_npc_secondTrial(Creature* pCreature)
{
return new npc_secondTrialAI (pCreature);
}
+
/*######
## go_second_trial
######*/
+
bool GOHello_go_second_trial(Player* pPlayer, GameObject* pGO)
{
// find spawn :: master_kelerun_bloodmourn
if (Creature* pCreature = pGO->FindNearestCreature(MASTER_KELERUN_BLOODMOURN, 30.0f))
CAST_AI(master_kelerun_bloodmournAI, pCreature->AI())->StartEvent();
+
return true;
}
+
/*######
## npc_apprentice_mirveda
######*/
+
#define QUEST_UNEXPECTED_RESULT 8488
#define MOB_GHARZUL 15958
#define MOB_ANGERSHADE 15656
+
struct TRINITY_DLL_DECL npc_apprentice_mirvedaAI : public ScriptedAI
{
npc_apprentice_mirvedaAI(Creature* c) : ScriptedAI(c), Summons(m_creature) {}
+
uint32 KillCount;
uint64 PlayerGUID;
bool Summon;
SummonList Summons;
+
void Reset()
{
KillCount = 0;
@@ -416,17 +521,21 @@ struct TRINITY_DLL_DECL npc_apprentice_mirvedaAI : public ScriptedAI
Summons.DespawnAll();
Summon = false;
}
+
void EnterCombat(Unit* who){}
+
void JustSummoned(Creature *summoned)
{
summoned->AI()->AttackStart(m_creature);
Summons.Summon(summoned);
}
+
void SummonedCreatureDespawn(Creature* summoned)
{
Summons.Despawn(summoned);
++KillCount;
}
+
void JustDied(Unit* killer)
{
if (PlayerGUID)
@@ -436,6 +545,7 @@ struct TRINITY_DLL_DECL npc_apprentice_mirvedaAI : public ScriptedAI
CAST_PLR(pPlayer)->FailQuest(QUEST_UNEXPECTED_RESULT);
}
}
+
void UpdateAI(const uint32 diff)
{
if (KillCount >= 3)
@@ -447,6 +557,7 @@ struct TRINITY_DLL_DECL npc_apprentice_mirvedaAI : public ScriptedAI
CAST_PLR(pPlayer)->CompleteQuest(QUEST_UNEXPECTED_RESULT);
}
}
+
if (Summon)
{
m_creature->SummonCreature(MOB_GHARZUL, 8745, -7134.32, 35.22, 0, TEMPSUMMON_CORPSE_DESPAWN, 4000);
@@ -456,6 +567,7 @@ struct TRINITY_DLL_DECL npc_apprentice_mirvedaAI : public ScriptedAI
}
}
};
+
bool QuestAccept_npc_apprentice_mirveda(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_UNEXPECTED_RESULT)
@@ -465,20 +577,25 @@ bool QuestAccept_npc_apprentice_mirveda(Player* pPlayer, Creature* pCreature, Qu
}
return true;
}
+
CreatureAI* GetAI_npc_apprentice_mirvedaAI(Creature* pCreature)
{
return new npc_apprentice_mirvedaAI (pCreature);
}
+
/*######
## npc_infused_crystal
######*/
+
#define MOB_ENRAGED_WRAITH 17086
#define EMOTE -1000283
#define QUEST_POWERING_OUR_DEFENSES 8490
+
struct Location
{
float x, y, z;
};
+
static Location SpawnLocations[]=
{
{8270.68, -7188.53, 139.619},
@@ -490,14 +607,17 @@ static Location SpawnLocations[]=
{8278.51, -7242.13, 139.162},
{8267.97, -7239.17, 139.517}
};
+
struct TRINITY_DLL_DECL npc_infused_crystalAI : public Scripted_NoMovementAI
{
npc_infused_crystalAI(Creature* c) : Scripted_NoMovementAI(c) {}
+
uint32 EndTimer;
uint32 WaveTimer;
bool Completed;
bool Progress;
uint64 PlayerGUID;
+
void Reset()
{
EndTimer = 0;
@@ -506,6 +626,7 @@ struct TRINITY_DLL_DECL npc_infused_crystalAI : public Scripted_NoMovementAI
PlayerGUID = 0;
WaveTimer = 0;
}
+
void MoveInLineOfSight(Unit* who)
{
if (!Progress && who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 10.0f))
@@ -519,10 +640,12 @@ struct TRINITY_DLL_DECL npc_infused_crystalAI : public Scripted_NoMovementAI
}
}
}
+
void JustSummoned(Creature *summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void JustDied(Unit* killer)
{
if (PlayerGUID && !Completed)
@@ -532,6 +655,7 @@ struct TRINITY_DLL_DECL npc_infused_crystalAI : public Scripted_NoMovementAI
CAST_PLR(pPlayer)->FailQuest(QUEST_POWERING_OUR_DEFENSES);
}
}
+
void UpdateAI(const uint32 diff)
{
if (EndTimer < diff && Progress)
@@ -547,6 +671,7 @@ struct TRINITY_DLL_DECL npc_infused_crystalAI : public Scripted_NoMovementAI
m_creature->DealDamage(m_creature,m_creature->GetHealth(),NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
m_creature->RemoveCorpse();
}else EndTimer -= diff;
+
if (WaveTimer < diff && !Completed && Progress)
{
uint32 ran1 = rand()%8;
@@ -559,38 +684,46 @@ struct TRINITY_DLL_DECL npc_infused_crystalAI : public Scripted_NoMovementAI
}else WaveTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_infused_crystalAI(Creature* pCreature)
{
return new npc_infused_crystalAI (pCreature);
}
+
void AddSC_eversong_woods()
{
Script *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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_second_trial_controller";
newscript->GetAI = &GetAI_master_kelerun_bloodmourn;
newscript->pGossipHello = &GossipHello_master_kelerun_bloodmourn;
newscript->pQuestAccept = &QuestAccept_master_kelerun_bloodmourn;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_second_trial_paladin";
newscript->GetAI = &GetAI_npc_secondTrial;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_second_trial";
newscript->pGOHello = &GOHello_go_second_trial;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_apprentice_mirveda";
newscript->GetAI = &GetAI_npc_apprentice_mirvedaAI;
newscript->pQuestAccept = &QuestAccept_npc_apprentice_mirveda;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_infused_crystal";
newscript->GetAI = &GetAI_npc_infused_crystalAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/ghostlands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/ghostlands.cpp
index cc31deb29a3..8dd1a67a0fc 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/ghostlands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/ghostlands.cpp
@@ -13,31 +13,40 @@
* along 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, 9212. Obtain Budd's Guise of Zul'aman. Vendor Rathis Tomber
SDCategory: Ghostlands
EndScriptData */
+
/* ContentData
npc_blood_knight_dawnstar
npc_budd_nedreck
npc_rathis_tomber
npc_ranger_lilatha
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_blood_knight_dawnstar
######*/
+
#define GOSSIP_H_BKD "Take Blood Knight Insignia"
+
bool GossipHello_npc_blood_knight_dawnstar(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(9692) == QUEST_STATUS_INCOMPLETE && !pPlayer->HasItemCount(24226,1,true))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_H_BKD, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_blood_knight_dawnstar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -52,19 +61,25 @@ bool GossipSelect_npc_blood_knight_dawnstar(Player* pPlayer, Creature* pCreature
}
return true;
}
+
/*######
## npc_budd_nedreck
######*/
+
#define GOSSIP_HBN "You gave the crew disguises?"
+
bool GossipHello_npc_budd_nedreck(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(11166) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HBN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_budd_nedreck(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -74,30 +89,37 @@ bool GossipSelect_npc_budd_nedreck(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*######
## npc_rathis_tomber
######*/
+
bool GossipHello_npc_rathis_tomber(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetQuestRewardStatus(9152))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
pPlayer->SEND_GOSSIP_MENU(8432, pCreature->GetGUID());
}else
pPlayer->SEND_GOSSIP_MENU(8431, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_rathis_tomber(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
return true;
}
+
/*#####
## go_gilded_brazier (Paladin First Trail quest (9678))
#####*/
+
bool GOHello_gilded_brazier(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestStatus(9678) == QUEST_STATUS_INCOMPLETE)
@@ -108,9 +130,11 @@ bool GOHello_gilded_brazier(Player* pPlayer, GameObject* pGo)
}
return true;
};
+
/*######
## npc_ranger_lilatha
######*/
+
enum eEnums
{
SAY_START = -1000140,
@@ -120,20 +144,26 @@ enum eEnums
SAY_END1 = -1000144,
SAY_END2 = -1000145,
SAY_CAPTAIN_ANSWER = -1000146,
+
QUEST_ESCAPE_FROM_THE_CATACOMBS = 9212,
GO_CAGE = 181152,
NPC_CAPTAIN_HELIOS = 16220,
FACTION_SMOON_E = 1603,
};
+
struct TRINITY_DLL_DECL npc_ranger_lilathaAI : public npc_escortAI
{
npc_ranger_lilathaAI(Creature *c) : npc_escortAI(c) {}
+
std::list<GameObject*> CageList;
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 0:
@@ -182,48 +212,58 @@ struct TRINITY_DLL_DECL npc_ranger_lilathaAI : public npc_escortAI
break;
}
}
+
void Reset()
{
if (GameObject* Cage = me->FindNearestGameObject(GO_CAGE, 20))
Cage->SetGoState(GO_STATE_READY);
}
};
+
bool QuestAccept_npc_ranger_lilatha(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_ESCAPE_FROM_THE_CATACOMBS)
{
pCreature->setFaction(113);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_ranger_lilathaAI, pCreature->AI()))
pEscortAI->Start(true, false, pPlayer->GetGUID());
}
return true;
}
+
CreatureAI* GetAI_npc_ranger_lilathaAI(Creature* pCreature)
{
return new npc_ranger_lilathaAI(pCreature);
}
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_budd_nedreck";
newscript->pGossipHello = &GossipHello_npc_budd_nedreck;
newscript->pGossipSelect = &GossipSelect_npc_budd_nedreck;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_rathis_tomber";
newscript->pGossipHello = &GossipHello_npc_rathis_tomber;
newscript->pGossipSelect = &GossipSelect_npc_rathis_tomber;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_gilded_brazier";
newscript->pGOHello = &GOHello_gilded_brazier;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_ranger_lilatha";
newscript->GetAI = &GetAI_npc_ranger_lilathaAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/hinterlands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/hinterlands.cpp
index 103d3340595..2a4bf67868b 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/hinterlands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/hinterlands.cpp
@@ -13,21 +13,26 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Hinterlands
SD%Complete: 100
SDComment: Quest support: 863, 2742
SDCategory: The Hinterlands
EndScriptData */
+
/* ContentData
npc_00x09hl
npc_rinji
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_00x09hl
######*/
+
enum eOOX
{
SAY_OOX_START = -1000287,
@@ -35,16 +40,22 @@ enum eOOX
SAY_OOX_AGGRO2 = -1000289,
SAY_OOX_AMBUSH = -1000290,
SAY_OOX_END = -1000292,
+
QUEST_RESQUE_OOX_09 = 836,
+
NPC_MARAUDING_OWL = 7808,
NPC_VILE_AMBUSHER = 7809,
+
FACTION_ESCORTEE_A = 774,
FACTION_ESCORTEE_H = 775
};
+
struct MANGOS_DLL_DECL npc_00x09hlAI : public npc_escortAI
{
npc_00x09hlAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void Reset() { }
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -62,12 +73,13 @@ struct MANGOS_DLL_DECL npc_00x09hlAI : public npc_escortAI
break;
}
}
+
void WaypointStart(uint32 uiPointId)
{
switch(uiPointId)
{
case 27:
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
const Position src = {147.927444f, -3851.513428f, 130.893f, 0};
Position pos;
@@ -76,7 +88,7 @@ struct MANGOS_DLL_DECL npc_00x09hlAI : public npc_escortAI
}
break;
case 44:
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
const Position src = {-141.151581f, -4291.213867f, 120.130f, 0};
Position dst;
@@ -86,42 +98,52 @@ struct MANGOS_DLL_DECL npc_00x09hlAI : public npc_escortAI
break;
}
}
+
void Aggro(Unit* pWho)
{
if (pWho->GetEntry() == NPC_MARAUDING_OWL || pWho->GetEntry() == NPC_VILE_AMBUSHER)
return;
+
if (rand()%1)
DoScriptText(SAY_OOX_AGGRO1, m_creature);
else
DoScriptText(SAY_OOX_AGGRO2, m_creature);
}
+
void JustSummoned(Creature* pSummoned)
{
pSummoned->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ());
}
};
+
bool QuestAccept_npc_00x09hl(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_RESQUE_OOX_09)
{
pCreature->SetStandState(UNIT_STAND_STATE_STAND);
+
if (pPlayer->GetTeam() == ALLIANCE)
pCreature->setFaction(FACTION_ESCORTEE_A);
else if (pPlayer->GetTeam() == HORDE)
pCreature->setFaction(FACTION_ESCORTEE_H);
+
DoScriptText(SAY_OOX_START, pCreature, pPlayer);
+
if (npc_00x09hlAI* pEscortAI = CAST_AI(npc_00x09hlAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return true;
}
+
CreatureAI* GetAI_npc_00x09hl(Creature* pCreature)
{
return new npc_00x09hlAI(pCreature);
}
+
/*######
## npc_rinji
######*/
+
enum eRinji
{
SAY_RIN_FREE = -1000403, //from here
@@ -131,25 +153,30 @@ enum eRinji
SAY_RIN_COMPLETE = -1000407,
SAY_RIN_PROGRESS_1 = -1000408,
SAY_RIN_PROGRESS_2 = -1000409,
+
QUEST_RINJI_TRAPPED = 2742,
NPC_RANGER = 2694,
NPC_OUTRUNNER = 2691,
GO_RINJI_CAGE = 142036
};
+
struct Location
{
float m_fX, m_fY, m_fZ;
};
+
Location m_afAmbushSpawn[] =
{
{191.296204, -2839.329346, 107.388},
{70.972466, -2848.674805, 109.459}
};
+
Location m_afAmbushMoveTo[] =
{
{166.630386, -2824.780273, 108.153},
{70.886589, -2874.335449, 116.675}
};
+
struct TRINITY_DLL_DECL npc_rinjiAI : public npc_escortAI
{
npc_rinjiAI(Creature* pCreature) : npc_escortAI(pCreature)
@@ -157,21 +184,26 @@ struct TRINITY_DLL_DECL npc_rinjiAI : public npc_escortAI
m_bIsByOutrunner = false;
m_iSpawnId = 0;
}
+
bool m_bIsByOutrunner;
uint32 m_uiPostEventCount;
uint32 m_uiPostEventTimer;
int m_iSpawnId;
+
void Reset()
{
m_uiPostEventCount = 0;
m_uiPostEventTimer = 3000;
}
+
void JustRespawned()
{
m_bIsByOutrunner = false;
m_iSpawnId = 0;
+
npc_escortAI::JustRespawned();
}
+
void EnterCombat(Unit* pWho)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
@@ -181,36 +213,45 @@ struct TRINITY_DLL_DECL npc_rinjiAI : public npc_escortAI
DoScriptText(SAY_RIN_BY_OUTRUNNER, pWho);
m_bIsByOutrunner = true;
}
+
if (rand()%4)
return;
+
//only if attacked and escorter is not in combat?
DoScriptText(RAND(SAY_RIN_HELP_1,SAY_RIN_HELP_2), m_creature);
}
}
+
void DoSpawnAmbush(bool bFirst)
{
if (!bFirst)
m_iSpawnId = 1;
+
m_creature->SummonCreature(NPC_RANGER,
m_afAmbushSpawn[m_iSpawnId].m_fX, m_afAmbushSpawn[m_iSpawnId].m_fY, m_afAmbushSpawn[m_iSpawnId].m_fZ, 0.0f,
TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000);
- for (int i = 0; i < 2; ++i)
+
+ for(int i = 0; i < 2; ++i)
{
m_creature->SummonCreature(NPC_OUTRUNNER,
m_afAmbushSpawn[m_iSpawnId].m_fX, m_afAmbushSpawn[m_iSpawnId].m_fY, m_afAmbushSpawn[m_iSpawnId].m_fZ, 0.0f,
TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000);
}
}
+
void JustSummoned(Creature* pSummoned)
{
pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pSummoned->GetMotionMaster()->MovePoint(0, m_afAmbushMoveTo[m_iSpawnId].m_fX, m_afAmbushMoveTo[m_iSpawnId].m_fY, m_afAmbushMoveTo[m_iSpawnId].m_fZ);
}
+
void WaypointReached(uint32 uiPointId)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(uiPointId)
{
case 1:
@@ -230,6 +271,7 @@ struct TRINITY_DLL_DECL npc_rinjiAI : public npc_escortAI
break;
}
}
+
void UpdateEscortAI(const uint32 uiDiff)
{
//Check if we have a current target
@@ -240,6 +282,7 @@ struct TRINITY_DLL_DECL npc_rinjiAI : public npc_escortAI
if (m_uiPostEventTimer < uiDiff)
{
m_uiPostEventTimer = 3000;
+
if (Unit* pPlayer = GetPlayerForEscort())
{
switch(m_uiPostEventCount)
@@ -263,34 +306,42 @@ struct TRINITY_DLL_DECL npc_rinjiAI : public npc_escortAI
else
m_uiPostEventTimer -= uiDiff;
}
+
return;
}
+
DoMeleeAttackIfReady();
}
};
+
bool QuestAccept_npc_rinji(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_RINJI_TRAPPED)
{
if (GameObject* pGo = pCreature->FindNearestGameObject(GO_RINJI_CAGE, INTERACTION_DISTANCE))
pGo->UseDoorOrButton();
+
if (npc_rinjiAI* pEscortAI = CAST_AI(npc_rinjiAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return true;
}
+
CreatureAI* GetAI_npc_rinji(Creature* pCreature)
{
return new npc_rinjiAI(pCreature);
}
+
void AddSC_hinterlands()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "npc_00x09hl";
newscript->GetAI = &GetAI_npc_00x09hl;
newscript->pQuestAccept = &QuestAccept_npc_00x09hl;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_rinji";
newscript->GetAI = &GetAI_npc_rinji;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/ironforge.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/ironforge.cpp
index 27f4286bd58..c087434f28c 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/ironforge.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/ironforge.cpp
@@ -13,28 +13,35 @@
* along 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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(3702) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ROYAL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
@@ -42,8 +49,10 @@ bool GossipHello_npc_royal_historian_archesonus(Player* pPlayer, Creature* pCrea
}
else
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_royal_historian_archesonus(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -71,9 +80,11 @@ bool GossipSelect_npc_royal_historian_archesonus(Player* pPlayer, Creature* pCre
}
return true;
}
+
void AddSC_ironforge()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_royal_historian_archesonus";
newscript->pGossipHello = &GossipHello_npc_royal_historian_archesonus;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/isle_of_queldanas.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/isle_of_queldanas.cpp
index 7c28190609a..9a9db09c514 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/isle_of_queldanas.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/isle_of_queldanas.cpp
@@ -13,37 +13,48 @@
* along 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, 11541
SDCategory: Isle Of Quel'Danas
EndScriptData */
+
/* ContentData
npc_converted_sentry
npc_greengill_slave
EndContentData */
+
#include "precompiled.h"
+
/*######
## npc_converted_sentry
######*/
+
#define SAY_CONVERTED_1 -1000284
#define SAY_CONVERTED_2 -1000284
+
#define SPELL_CONVERT_CREDIT 45009
+
struct TRINITY_DLL_DECL npc_converted_sentryAI : public ScriptedAI
{
npc_converted_sentryAI(Creature *c) : ScriptedAI(c) {}
+
bool Credit;
uint32 Timer;
+
void Reset()
{
Credit = false;
Timer = 2500;
}
+
void MoveInLineOfSight(Unit *who)
{ return; }
void EnterCombat(Unit* who)
{ }
+
void UpdateAI(const uint32 diff)
{
if (!Credit)
@@ -55,6 +66,7 @@ struct TRINITY_DLL_DECL npc_converted_sentryAI : public ScriptedAI
DoScriptText(SAY_CONVERTED_1, m_creature);
else
DoScriptText(SAY_CONVERTED_2, m_creature);
+
DoCast(m_creature, SPELL_CONVERT_CREDIT);
if (m_creature->isPet())
CAST_PET(m_creature)->SetDuration(7500);
@@ -67,26 +79,34 @@ CreatureAI* GetAI_npc_converted_sentry(Creature* pCreature)
{
return new npc_converted_sentryAI (pCreature);
}
+
/*######
## npc_greengill_slave
######*/
+
#define ENRAGE 45111
#define ORB 45109
#define QUESTG 11541
#define DM 25060
+
struct TRINITY_DLL_DECL npc_greengill_slaveAI : public ScriptedAI
{
npc_greengill_slaveAI(Creature* c) : ScriptedAI(c) {}
+
uint64 PlayerGUID;
+
void EnterCombat(Unit* who){}
+
void Reset()
{
PlayerGUID = 0;
}
+
void SpellHit(Unit* caster, const SpellEntry* spell)
{
if (!caster)
return;
+
if (caster->GetTypeId() == TYPEID_PLAYER && spell->Id == ORB && !m_creature->HasAura(ENRAGE))
{
PlayerGUID = caster->GetGUID();
@@ -105,22 +125,27 @@ struct TRINITY_DLL_DECL npc_greengill_slaveAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_greengill_slaveAI(Creature* pCreature)
{
return new npc_greengill_slaveAI(pCreature);
}
+
void AddSC_isle_of_queldanas()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_converted_sentry";
newscript->GetAI = &GetAI_npc_converted_sentry;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_greengill_slave";
newscript->GetAI = &GetAI_npc_greengill_slaveAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_curator.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_curator.cpp
index 9416ab3b2c3..2a2d64e412e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_curator.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_curator.cpp
@@ -13,13 +13,16 @@
* along 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:
SDCategory: Karazhan
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO -1532057
#define SAY_SUMMON1 -1532058
#define SAY_SUMMON2 -1532059
@@ -28,21 +31,27 @@ EndScriptData */
#define SAY_KILL1 -1532062
#define SAY_KILL2 -1532063
#define SAY_DEATH -1532064
+
//Flare spell info
#define SPELL_ASTRAL_FLARE_PASSIVE 30234 //Visual effect + Flare damage
+
//Curator spell info
#define SPELL_HATEFUL_BOLT 30383
#define SPELL_EVOCATION 30254
#define SPELL_ENRAGE 30403 //Arcane Infusion: Transforms Curator and adds damage.
#define SPELL_BERSERK 26662
+
struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
{
boss_curatorAI(Creature *c) : ScriptedAI(c) {}
+
uint32 AddTimer;
uint32 HatefulBoltTimer;
uint32 BerserkTimer;
+
bool Enraged;
bool Evocating;
+
void Reset()
{
AddTimer = 10000;
@@ -50,24 +59,30 @@ struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
BerserkTimer = 720000; //12 minutes
Enraged = false;
Evocating = false;
+
m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_ARCANE, true);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL1,SAY_KILL2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//always decrease BerserkTimer
if (BerserkTimer < diff)
{
@@ -76,15 +91,20 @@ struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
{
if (m_creature->HasAura(SPELL_EVOCATION))
m_creature->RemoveAurasDueToSpell(SPELL_EVOCATION);
+
Evocating = false;
}
+
//may not be correct SAY (generic hard enrage)
DoScriptText(SAY_ENRAGE, m_creature);
+
m_creature->InterruptNonMeleeSpells(true);
DoCast(m_creature, SPELL_BERSERK);
+
//don't know if he's supposed to do summon/evocate after hard enrage (probably not)
Enraged = true;
}else BerserkTimer -= diff;
+
if (Evocating)
{
//not supposed to do anything while evocate
@@ -93,6 +113,7 @@ struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
else
Evocating = false;
}
+
if (!Enraged)
{
if (AddTimer < diff)
@@ -101,16 +122,19 @@ struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
Creature* AstralFlare = DoSpawnCreature(17096, rand()%37, rand()%37, 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% of max health
if (int32 mana = m_creature->GetMaxPower(POWER_MANA))
{
mana /= 10;
m_creature->ModifyPower(POWER_MANA, -mana);
+
//if this get's us below 10%, then we evocate (the 10th should be summoned now)
if (m_creature->GetPower(POWER_MANA)*100 / m_creature->GetMaxPower(POWER_MANA) < 10)
{
@@ -129,8 +153,10 @@ struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
}
}
}
+
AddTimer = 10000;
}else AddTimer -= diff;
+
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 15)
{
Enraged = true;
@@ -138,22 +164,28 @@ struct TRINITY_DLL_DECL boss_curatorAI : public ScriptedAI
DoScriptText(SAY_ENRAGE, m_creature);
}
}
+
if (HatefulBoltTimer < diff)
{
if (Enraged)
HatefulBoltTimer = 7000;
else
HatefulBoltTimer = 15000;
+
if (Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 1))
DoCast(target, SPELL_HATEFUL_BOLT);
+
}else HatefulBoltTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_curator(Creature* pCreature)
{
return new boss_curatorAI (pCreature);
}
+
void AddSC_boss_curator()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp
index 6ecf759b11f..df30deb1ff3 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_maiden_of_virtue.cpp
@@ -13,13 +13,16 @@
* along 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 SAY_AGGRO -1532018
#define SAY_SLAY1 -1532019
#define SAY_SLAY2 -1532020
@@ -27,20 +30,25 @@ EndScriptData */
#define SAY_REPENTANCE1 -1532022
#define SAY_REPENTANCE2 -1532023
#define SAY_DEATH -1532024
+
#define SPELL_REPENTANCE 29511
#define SPELL_HOLYFIRE 29522
#define SPELL_HOLYWRATH 32445
#define SPELL_HOLYGROUND 29512
#define SPELL_BERSERK 26662
+
struct TRINITY_DLL_DECL boss_maiden_of_virtueAI : public ScriptedAI
{
boss_maiden_of_virtueAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Repentance_Timer;
uint32 Holyfire_Timer;
uint32 Holywrath_Timer;
uint32 Holyground_Timer;
uint32 Enrage_Timer;
+
bool Enraged;
+
void Reset()
{
Repentance_Timer = 25000+(rand()%15000);
@@ -48,60 +56,78 @@ struct TRINITY_DLL_DECL boss_maiden_of_virtueAI : public ScriptedAI
Holywrath_Timer = 15000+(rand()%10000);
Holyground_Timer = 3000;
Enrage_Timer = 600000;
+
Enraged = false;
}
+
void KilledUnit(Unit* Victim)
{
if (urand(0,1) == 0)
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Enrage_Timer < diff && !Enraged)
{
DoCast(m_creature, SPELL_BERSERK,true);
Enraged = true;
} else Enrage_Timer -=diff;
+
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);
DoScriptText(RAND(SAY_REPENTANCE1,SAY_REPENTANCE2), m_creature);
+
Repentance_Timer = 25000 + rand()%10000; //A little randomness on that spell
} else Repentance_Timer -= diff;
+
if (Holyfire_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_HOLYFIRE);
+
Holyfire_Timer = 8000 + rand()%15000; //Anywhere from 8 to 23 seconds, good luck having several of those in a row!
} else Holyfire_Timer -= diff;
+
if (Holywrath_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_HOLYWRATH);
+
Holywrath_Timer = 20000+(rand()%5000); //20-30 secs sounds nice
+
} else Holywrath_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
};
+
CreatureAI* GetAI_boss_maiden_of_virtue(Creature* pCreature)
{
return new boss_maiden_of_virtueAI (pCreature);
}
+
void AddSC_boss_maiden_of_virtue()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp
index d81d50b52f9..57a0a1c1c5b 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_midnight.cpp
@@ -13,13 +13,16 @@
* along 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 -1532000
#define SAY_APPEAR1 -1532001
#define SAY_APPEAR2 -1532002
@@ -31,27 +34,36 @@ EndScriptData */
#define SAY_DEATH -1532008
#define SAY_RANDOM1 -1532009
#define SAY_RANDOM2 -1532010
+
#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 TRINITY_DLL_DECL boss_midnightAI : public ScriptedAI
{
boss_midnightAI(Creature *c) : ScriptedAI(c) {}
+
uint64 Attumen;
uint8 Phase;
uint32 Mount_Timer;
+
void Reset()
{
Phase = 1;
Attumen = 0;
Mount_Timer = 0;
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetVisibility(VISIBILITY_ON);
}
+
void EnterCombat(Unit* who) {}
+
void KilledUnit(Unit *victim)
{
if (Phase == 2)
@@ -60,10 +72,12 @@ struct TRINITY_DLL_DECL boss_midnightAI : public ScriptedAI
DoScriptText(SAY_MIDNIGHT_KILL, pUnit);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Phase == 1 && (m_creature->GetHealth()*100)/m_creature->GetMaxHealth() < 95)
{
Phase = 2;
@@ -103,9 +117,11 @@ struct TRINITY_DLL_DECL boss_midnightAI : public ScriptedAI
} else Mount_Timer -= diff;
}
}
+
if (Phase != 3)
DoMeleeAttackIfReady();
}
+
void Mount(Unit *pAttumen)
{
DoScriptText(SAY_MOUNT, pAttumen);
@@ -130,23 +146,28 @@ struct TRINITY_DLL_DECL boss_midnightAI : public ScriptedAI
//pAttumen->SendMonsterMove(newX, newY, newZ, 0, true, 1000);
Mount_Timer = 1000;
}
+
void SetMidnight(Creature *, uint64); //Below ..
};
+
CreatureAI* GetAI_boss_midnight(Creature* pCreature)
{
return new boss_midnightAI(pCreature);
}
+
struct TRINITY_DLL_DECL boss_attumenAI : public ScriptedAI
{
boss_attumenAI(Creature *c) : ScriptedAI(c)
{
Phase = 1;
+
CleaveTimer = urand(10000,15000);
CurseTimer = 30000;
RandomYellTimer = urand(30000,60000); //Occasionally yell
ChargeTimer = 20000;
ResetTimer = 0;
}
+
uint64 Midnight;
uint8 Phase;
uint32 CleaveTimer;
@@ -154,21 +175,26 @@ struct TRINITY_DLL_DECL boss_attumenAI : public ScriptedAI
uint32 RandomYellTimer;
uint32 ChargeTimer; //only when mounted
uint32 ResetTimer;
+
void Reset()
{
ResetTimer = 2000;
}
+
void EnterCombat(Unit* who) {}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL1,SAY_KILL2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
if (Unit *pMidnight = Unit::GetUnit(*m_creature, Midnight))
pMidnight->Kill(pMidnight);
}
+
void UpdateAI(const uint32 diff)
{
if (ResetTimer)
@@ -187,26 +213,32 @@ struct TRINITY_DLL_DECL boss_attumenAI : public ScriptedAI
m_creature->Kill(m_creature);
}
} else ResetTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE))
return;
+
if (CleaveTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHADOWCLEAVE);
CleaveTimer = urand(10000,15000);
} else CleaveTimer -= diff;
+
if (CurseTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_INTANGIBLE_PRESENCE);
CurseTimer = 30000;
} else CurseTimer -= diff;
+
if (RandomYellTimer < diff)
{
DoScriptText(RAND(SAY_RANDOM1,SAY_RANDOM2), m_creature);
RandomYellTimer = urand(30000,60000);
} else RandomYellTimer -= diff;
+
if (m_creature->GetUInt32Value(UNIT_FIELD_DISPLAYID) == MOUNTED_DISPLAYID)
{
if (ChargeTimer < diff)
@@ -214,7 +246,7 @@ struct TRINITY_DLL_DECL boss_attumenAI : public ScriptedAI
Unit *target;
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
std::vector<Unit *> target_list;
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
if (target && !target->IsWithinDist(m_creature, ATTACK_DISTANCE, false))
@@ -223,6 +255,7 @@ struct TRINITY_DLL_DECL boss_attumenAI : public ScriptedAI
}
if (target_list.size())
target = *(target_list.begin()+rand()%target_list.size());
+
DoCast(target, SPELL_BERSERKER_CHARGE);
ChargeTimer = 20000;
} else ChargeTimer -= diff;
@@ -240,22 +273,27 @@ struct TRINITY_DLL_DECL boss_attumenAI : public ScriptedAI
}
}
}
+
DoMeleeAttackIfReady();
}
+
void SpellHit(Unit *source, const SpellEntry *spell)
{
if (spell->Mechanic == MECHANIC_DISARM)
DoScriptText(SAY_DISARMED, m_creature);
}
};
+
void boss_midnightAI::SetMidnight(Creature *pAttumen, uint64 value)
{
CAST_AI(boss_attumenAI, pAttumen->AI())->Midnight = value;
}
+
CreatureAI* GetAI_boss_attumen(Creature* pCreature)
{
return new boss_attumenAI (pCreature);
}
+
void AddSC_boss_attumen()
{
Script *newscript;
@@ -263,6 +301,7 @@ void AddSC_boss_attumen()
newscript->Name = "boss_attumen";
newscript->GetAI = &GetAI_boss_attumen;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_midnight";
newscript->GetAI = &GetAI_boss_midnight;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp
index 9a6970905fe..2714b95f3c4 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_moroes.cpp
@@ -13,14 +13,17 @@
* along 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: 95
SDComment:
SDCategory: Karazhan
EndScriptData */
+
#include "precompiled.h"
#include "def_karazhan.h"
+
#define SAY_AGGRO -1532011
#define SAY_SPECIAL_1 -1532012
#define SAY_SPECIAL_2 -1532013
@@ -28,12 +31,15 @@ EndScriptData */
#define SAY_KILL_2 -1532015
#define SAY_KILL_3 -1532016
#define SAY_DEATH -1532017
+
#define SPELL_VANISH 29448
#define SPELL_GARROTE 37066
#define SPELL_BLIND 34694
#define SPELL_GOUGE 29425
#define SPELL_FRENZY 37023
+
#define POS_Z 81.73
+
float Locations[4][3]=
{
{-10991.0, -1884.33, 0.614315},
@@ -41,6 +47,7 @@ float Locations[4][3]=
{-10978.1, -1887.07, 2.035550},
{-10975.9, -1885.81, 2.253890},
};
+
const uint32 Adds[6]=
{
17007,
@@ -50,26 +57,32 @@ const uint32 Adds[6]=
19875,
19876,
};
+
struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
{
boss_moroesAI(Creature *c) : ScriptedAI(c)
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
AddId[i] = 0;
}
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint64 AddGUID[4];
+
uint32 Vanish_Timer;
uint32 Blind_Timer;
uint32 Gouge_Timer;
uint32 Wait_Timer;
uint32 CheckAdds_Timer;
uint32 AddId[4];
+
bool InVanish;
bool Enrage;
+
void Reset()
{
Vanish_Timer = 30000;
@@ -77,45 +90,58 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
Gouge_Timer = 23000;
Wait_Timer = 0;
CheckAdds_Timer = 5000;
+
Enrage = false;
InVanish = false;
if (m_creature->GetHealth() > 0)
{
SpawnAdds();
}
+
if (pInstance)
pInstance->SetData(TYPE_MOROES, NOT_STARTED);
}
+
void StartEvent()
{
if (pInstance)
pInstance->SetData(TYPE_MOROES, IN_PROGRESS);
+
DoZoneInCombat();
}
+
void EnterCombat(Unit* who)
{
StartEvent();
+
DoScriptText(SAY_AGGRO, m_creature);
AddsAttack();
DoZoneInCombat();
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2,SAY_KILL_3), m_creature);
}
+
void JustDied(Unit* victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_MOROES, DONE);
+
DeSpawnAdds();
+
//remove aura from spell Garrote when Moroes dies
Map* pMap = m_creature->GetMap();
if (pMap->IsDungeon())
{
Map::PlayerList const &PlayerList = pMap->GetPlayers();
+
if (PlayerList.isEmpty())
return;
+
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (i->getSource()->isAlive() && i->getSource()->HasAura(SPELL_GARROTE,0))
@@ -123,6 +149,7 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
}
}
}
+
void SpawnAdds()
{
DeSpawnAdds();
@@ -131,14 +158,18 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
Creature *pCreature = NULL;
std::vector<uint32> AddList;
- for (uint8 i = 0; i < 6; ++i)
+
+ for(uint8 i = 0; i < 6; ++i)
AddList.push_back(Adds[i]);
+
while(AddList.size() > 4)
AddList.erase((AddList.begin())+(rand()%AddList.size()));
+
uint8 i = 0;
- for (std::vector<uint32>::iterator itr = AddList.begin(); itr != AddList.end(); ++itr)
+ for(std::vector<uint32>::iterator itr = AddList.begin(); itr != AddList.end(); ++itr)
{
uint32 entry = *itr;
+
pCreature = m_creature->SummonCreature(entry, Locations[i][0], Locations[i][1], POS_Z, Locations[i][2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
if (pCreature)
{
@@ -149,7 +180,7 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
}
}else
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Creature *pCreature = m_creature->SummonCreature(AddId[i], Locations[i][0], Locations[i][1], POS_Z, Locations[i][2], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 10000);
if (pCreature)
@@ -159,18 +190,20 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
}
}
}
+
bool isAddlistEmpty()
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (AddId[i] == 0)
return true;
}
return false;
}
+
void DeSpawnAdds()
{
- for (uint8 i = 0; i < 4 ; ++i)
+ for(uint8 i = 0; i < 4 ; ++i)
{
Creature* Temp = NULL;
if (AddGUID[i])
@@ -181,9 +214,10 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
}
}
}
+
void AddsAttack()
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Creature* Temp = NULL;
if (AddGUID[i])
@@ -198,20 +232,24 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (pInstance && !pInstance->GetData(TYPE_MOROES))
{
EnterEvadeMode();
return;
}
+
if (!Enrage && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30)
{
DoCast(m_creature, SPELL_FRENZY);
Enrage = true;
}
+
if (CheckAdds_Timer < diff)
{
for (uint8 i = 0; i < 4; ++i)
@@ -227,6 +265,7 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
}
CheckAdds_Timer = 5000;
} else CheckAdds_Timer -= diff;
+
if (!Enrage)
{
//Cast Vanish, then Garrote random victim
@@ -237,61 +276,75 @@ struct TRINITY_DLL_DECL boss_moroesAI : public ScriptedAI
Vanish_Timer = 30000;
Wait_Timer = 5000;
} else Vanish_Timer -= diff;
+
if (Gouge_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_GOUGE);
Gouge_Timer = 40000;
} else Gouge_Timer -= diff;
+
if (Blind_Timer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (target && target->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinMeleeRange(target))
{
DoCast(target, SPELL_BLIND);
+
Blind_Timer = 40000;
}
else
Blind_Timer = 1000;
} else Blind_Timer -= diff;
}
+
if (InVanish)
{
if (Wait_Timer < diff)
{
DoScriptText(RAND(SAY_SPECIAL_1,SAY_SPECIAL_2), m_creature);
+
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
target->CastSpell(target, SPELL_GARROTE,true);
+
InVanish = false;
} else Wait_Timer -= diff;
}
+
if (!InVanish)
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_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)
+ for(uint8 i = 0; i < 4; ++i)
GuestGUID[i] = 0;
+
pInstance = c->GetInstanceData();
}
+
void Reset()
{
if (pInstance)
pInstance->SetData(TYPE_MOROES, NOT_STARTED);
}
+
void AcquireGUID()
{
if (!pInstance)
return;
+
GuestGUID[0] = pInstance->GetData64(DATA_MOROES);
Creature* Moroes = (Unit::GetCreature((*m_creature), GuestGUID[0]));
if (Moroes)
{
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
uint64 GUID = CAST_AI(boss_moroesAI, Moroes->AI())->AddGUID[i];
if (GUID && GUID != m_creature->GetGUID())
@@ -299,6 +352,7 @@ struct TRINITY_DLL_DECL boss_moroes_guestAI : public ScriptedAI
}
}
}
+
Unit* SelectTarget()
{
uint64 TempGUID = GuestGUID[rand()%5];
@@ -308,44 +362,57 @@ struct TRINITY_DLL_DECL boss_moroes_guestAI : public ScriptedAI
if (pUnit && pUnit->isAlive())
return pUnit;
}
+
return m_creature;
}
+
void UpdateAI(const uint32 diff)
{
if (pInstance && !pInstance->GetData(TYPE_MOROES))
EnterEvadeMode();
+
DoMeleeAttackIfReady();
}
};
+
#define SPELL_MANABURN 29405
#define SPELL_MINDFLY 29570
#define SPELL_SWPAIN 34441
#define SPELL_SHADOWFORM 29406
+
struct TRINITY_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 (!UpdateVictim())
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);
@@ -353,6 +420,7 @@ struct TRINITY_DLL_DECL boss_baroness_dorothea_millstipeAI : public boss_moroes_
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);
@@ -364,39 +432,49 @@ struct TRINITY_DLL_DECL boss_baroness_dorothea_millstipeAI : public boss_moroes_
}else ShadowWordPain_Timer -= diff;
}
};
+
#define SPELL_HAMMEROFJUSTICE 13005
#define SPELL_JUDGEMENTOFCOMMAND 29386
#define SPELL_SEALOFCOMMAND 29385
+
struct TRINITY_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 (!UpdateVictim())
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);
@@ -404,144 +482,186 @@ struct TRINITY_DLL_DECL boss_baron_rafe_dreugerAI : public boss_moroes_guestAI
}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 TRINITY_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 (!UpdateVictim())
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 TRINITY_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 (!UpdateVictim())
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 TRINITY_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 (!UpdateVictim())
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);
@@ -549,46 +669,57 @@ struct TRINITY_DLL_DECL boss_lord_robin_darisAI : public boss_moroes_guestAI
}else WhirlWind_Timer -= diff;
}
};
+
#define SPELL_DISARM 8379
#define SPELL_HEROICSTRIKE 29567
#define SPELL_SHIELDBASH 11972
#define SPELL_SHIELDWALL 29390
+
struct TRINITY_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 (!UpdateVictim())
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);
@@ -596,61 +727,76 @@ struct TRINITY_DLL_DECL boss_lord_crispin_ferenceAI : public boss_moroes_guestAI
}else ShieldWall_Timer -= diff;
}
};
+
CreatureAI* GetAI_boss_moroes(Creature* pCreature)
{
return new boss_moroesAI (pCreature);
}
+
CreatureAI* GetAI_baroness_dorothea_millstipe(Creature* pCreature)
{
return new boss_baroness_dorothea_millstipeAI (pCreature);
}
+
CreatureAI* GetAI_baron_rafe_dreuger(Creature* pCreature)
{
return new boss_baron_rafe_dreugerAI (pCreature);
}
+
CreatureAI* GetAI_lady_catriona_von_indi(Creature* pCreature)
{
return new boss_lady_catriona_von_indiAI (pCreature);
}
+
CreatureAI* GetAI_lady_keira_berrybuck(Creature* pCreature)
{
return new boss_lady_keira_berrybuckAI (pCreature);
}
+
CreatureAI* GetAI_lord_robin_daris(Creature* pCreature)
{
return new boss_lord_robin_darisAI (pCreature);
}
+
CreatureAI* GetAI_lord_crispin_ference(Creature* pCreature)
{
return new boss_lord_crispin_ferenceAI (pCreature);
}
+
void AddSC_boss_moroes()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_moroes";
newscript->GetAI = &GetAI_boss_moroes;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_baroness_dorothea_millstipe";
newscript->GetAI = &GetAI_baroness_dorothea_millstipe;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_baron_rafe_dreuger";
newscript->GetAI = &GetAI_baron_rafe_dreuger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lady_catriona_von_indi";
newscript->GetAI = &GetAI_lady_catriona_von_indi;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lady_keira_berrybuck";
newscript->GetAI = &GetAI_lady_keira_berrybuck;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lord_robin_daris";
newscript->GetAI = &GetAI_lord_robin_daris;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lord_crispin_ference";
newscript->GetAI = &GetAI_lord_crispin_ference;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp
index c2d2de25309..b6e3d25c021 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_netherspite.cpp
@@ -13,16 +13,20 @@
* along 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: 90
SDComment: Not sure about timing and portals placing
SDCategory: Karazhan
EndScriptData */
+
#include "precompiled.h"
#include "def_karazhan.h"
+
#define EMOTE_PHASE_PORTAL -1532089
#define EMOTE_PHASE_BANISH -1532090
+
#define SPELL_NETHERBURN_AURA 30522
#define SPELL_VOIDZONE 37063
#define SPELL_NETHER_INFUSION 38688
@@ -31,42 +35,49 @@ EndScriptData */
#define SPELL_BANISH_ROOT 42716
#define SPELL_EMPOWERMENT 38549
#define SPELL_NETHERSPITE_ROAR 38684
+
const float PortalCoord[3][3] =
{
{-11195.353516, -1613.237183, 278.237258}, // Left side
{-11137.846680, -1685.607422, 278.239258}, // Right side
{-11094.493164, -1591.969238, 279.949188} // Back side
};
+
enum Netherspite_Portal{
RED_PORTAL = 0, // Perseverence
GREEN_PORTAL = 1, // Serenity
BLUE_PORTAL = 2 // Dominance
};
+
const uint32 PortalID[3] = {17369, 17367, 17368};
const uint32 PortalVisual[3] = {30487,30490,30491};
const uint32 PortalBeam[3] = {30465,30464,30463};
const uint32 PlayerBuff[3] = {30421,30422,30423};
const uint32 NetherBuff[3] = {30466,30467,30468};
const uint32 PlayerDebuff[3] = {38637,38638,38639};
+
struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
{
boss_netherspiteAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
- for (int i=0; i<3; ++i)
+
+ for(int i=0; i<3; ++i)
{
PortalGUID[i] = 0;
BeamTarget[i] = 0;
BeamerGUID[i] = 0;
}
// need core fix
- for (int i=0; i<3; ++i)
+ for(int i=0; i<3; ++i)
{
if(SpellEntry *spell = (SpellEntry*)GetSpellStore()->LookupEntry(PlayerBuff[i]))
spell->AttributesEx |= SPELL_ATTR_EX_NEGATIVE;
}
}
+
ScriptedInstance* pInstance;
+
bool PortalPhase;
bool Berserk;
uint32 PhaseTimer; // timer for phase switching
@@ -78,10 +89,12 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
uint64 PortalGUID[3]; // guid's of portals
uint64 BeamerGUID[3]; // guid's of auxiliary beaming portals
uint64 BeamTarget[3]; // guid's of portals' current targets
+
bool IsBetween(WorldObject* u1, WorldObject* target, WorldObject* u2) // the in-line checker
{
if(!u1 || !u2 || !target)
return false;
+
float xn, yn, xp, yp, xh, yh;
xn = u1->GetPositionX();
yn = u1->GetPositionY();
@@ -89,25 +102,30 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
yp = u2->GetPositionY();
xh = target->GetPositionX();
yh = target->GetPositionY();
+
// check if target is between (not checking distance from the beam yet)
if(dist(xn,yn,xh,yh)>=dist(xn,yn,xp,yp) || dist(xp,yp,xh,yh)>=dist(xn,yn,xp,yp))
return false;
// check distance from the beam
return (abs((xn-xp)*yh+(yp-yn)*xh-xn*yp+xp*yn)/dist(xn,yn,xp,yp) < 1.5f);
}
+
float dist(float xa, float ya, float xb, float yb) // auxiliary method for distance
{
return sqrt((xa-xb)*(xa-xb) + (ya-yb)*(ya-yb));
}
+
void Reset()
{
Berserk = false;
NetherInfusionTimer = 540000;
VoidZoneTimer = 15000;
NetherbreathTimer = 3000;
+
HandleDoors(true);
DestroyPortals();
}
+
void SummonPortals()
{
uint8 r = rand()%4;
@@ -115,16 +133,18 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
pos[RED_PORTAL] = (r%2 ? (r>1 ? 2: 1): 0);
pos[GREEN_PORTAL] = (r%2 ? 0: (r>1 ? 2: 1));
pos[BLUE_PORTAL] = (r>1 ? 1: 2); // Blue Portal not on the left side (0)
- for (int i=0; i<3; ++i)
+
+ for(int i=0; i<3; ++i)
if(Creature *portal = m_creature->SummonCreature(PortalID[i],PortalCoord[pos[i]][0],PortalCoord[pos[i]][1],PortalCoord[pos[i]][2],0,TEMPSUMMON_TIMED_DESPAWN,60000))
{
PortalGUID[i] = portal->GetGUID();
portal->AddAura(PortalVisual[i], portal);
}
}
+
void DestroyPortals()
{
- for (int i=0; i<3; ++i)
+ for(int i=0; i<3; ++i)
{
if(Creature *portal = Unit::GetCreature(*m_creature, PortalGUID[i]))
portal->DisappearAndDie();
@@ -134,20 +154,23 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
BeamTarget[i] = 0;
}
}
+
void UpdatePortals() // Here we handle the beams' behavior
{
- for (int j=0; j<3; ++j) // j = color
+ for(int j=0; j<3; ++j) // j = color
if(Creature *portal = Unit::GetCreature(*m_creature, PortalGUID[j]))
{
// the one who's been casted upon before
Unit *current = Unit::GetUnit(*portal, BeamTarget[j]);
// temporary store for the best suitable beam reciever
Unit *target = m_creature;
+
if(Map* map = m_creature->GetMap())
{
Map::PlayerList const& players = map->GetPlayers();
+
// get the best suitable target
- for (Map::PlayerList::const_iterator i = players.begin(); i!=players.end(); ++i)
+ for(Map::PlayerList::const_iterator i = players.begin(); i!=players.end(); ++i)
{
Player* p = i->getSource();
if(p && p->isAlive() // alive
@@ -188,6 +211,7 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
m_creature->getThreatManager().addThreat(target, 100000.0f+DoGetThreat(m_creature->getVictim()));
}
}
+
void SwitchToPortalPhase()
{
m_creature->RemoveAurasDueToSpell(SPELL_BANISH_ROOT);
@@ -199,6 +223,7 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
EmpowermentTimer = 10000;
DoScriptText(EMOTE_PHASE_PORTAL,m_creature);
}
+
void SwitchToBanishPhase()
{
m_creature->RemoveAurasDueToSpell(SPELL_EMPOWERMENT);
@@ -209,34 +234,41 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
PhaseTimer = 30000;
PortalPhase = false;
DoScriptText(EMOTE_PHASE_BANISH,m_creature);
- for (int i=0; i<3; ++i)
+
+ for(int i=0; i<3; ++i)
m_creature->RemoveAurasDueToSpell(NetherBuff[i]);
}
+
void HandleDoors(bool open) // Massive Door switcher
{
if(GameObject *Door = GameObject::GetGameObject(*m_creature, pInstance ? pInstance->GetData64(DATA_GO_MASSIVE_DOOR) : 0))
Door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
}
+
void Aggro(Unit *who)
{
HandleDoors(false);
SwitchToPortalPhase();
}
+
void JustDied(Unit* killer)
{
HandleDoors(true);
DestroyPortals();
}
+
void UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
return;
+
// Void Zone
if(VoidZoneTimer < diff)
{
DoCast(SelectTarget(SELECT_TARGET_RANDOM,1,45,true),SPELL_VOIDZONE,true);
VoidZoneTimer = 15000;
}else VoidZoneTimer -= diff;
+
// NetherInfusion Berserk
if(!Berserk && NetherInfusionTimer < diff)
{
@@ -244,6 +276,7 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
DoCast(m_creature, SPELL_NETHERSPITE_ROAR);
Berserk = true;
}else NetherInfusionTimer -= diff;
+
if(PortalPhase) // PORTAL PHASE
{
// Distribute beams and buffs
@@ -252,6 +285,7 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
UpdatePortals();
PortalTimer = 1000;
}else PortalTimer -= diff;
+
// Empowerment & Nether Burn
if(EmpowermentTimer < diff)
{
@@ -259,6 +293,7 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
m_creature->AddAura(SPELL_NETHERBURN_AURA, m_creature);
EmpowermentTimer = 90000;
}else EmpowermentTimer -= diff;
+
if(PhaseTimer < diff)
{
if(!m_creature->IsNonMeleeSpellCasted(false))
@@ -277,6 +312,7 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
DoCast(target,SPELL_NETHERBREATH);
NetherbreathTimer = 5000+rand()%2000;
}else NetherbreathTimer -= diff;
+
if(PhaseTimer < diff)
{
if(!m_creature->IsNonMeleeSpellCasted(false))
@@ -286,16 +322,20 @@ struct TRINITY_DLL_DECL boss_netherspiteAI : public ScriptedAI
}
}else PhaseTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_netherspite(Creature *_Creature)
{
return new boss_netherspiteAI(_Creature);
}
+
void AddSC_boss_netherspite()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_netherspite";
newscript->GetAI = GetAI_boss_netherspite;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp
index b853414448d..0168ce10a35 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_nightbane.cpp
@@ -13,14 +13,17 @@
* along 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: 80
SDComment: SDComment: Timers may incorrect
SDCategory: Karazhan
EndScriptData */
+
#include "precompiled.h"
#include "def_karazhan.h"
+
//phase 1
#define SPELL_BELLOWING_ROAR 39427
#define SPELL_CHARRED_EARTH 30129
@@ -33,12 +36,14 @@ EndScriptData */
#define SPELL_FIREBALL_BARRAGE 30282
#define SPELL_SEARING_CINDERS 30127
#define SPELL_SUMMON_SKELETON 30170
+
#define EMOTE_SUMMON "An ancient being awakens in the distance..."
#define YELL_AGGRO "What fools! I shall bring a quick end to your suffering!"
#define YELL_FLY_PHASE "Miserable vermin. I shall exterminate you from the air!"
#define YELL_LAND_PHASE_1 "Enough! I shall land and crush you myself!"
#define YELL_LAND_PHASE_2 "Insects! Let me show you my strength up close!"
#define EMOTE_BREATH "takes a deep breath."
+
float IntroWay[8][3] =
{
{-11053.37,-1794.48,149},
@@ -50,6 +55,7 @@ float IntroWay[8][3] =
{-11140 , -1915 ,122},
{-11163 , -1903 ,91.473}
};
+
struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
{
boss_nightbaneAI(Creature* c) : ScriptedAI(c)
@@ -57,10 +63,14 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
pInstance = c->GetInstanceData();
Intro = true;
}
+
ScriptedInstance* pInstance;
+
uint32 Phase;
+
bool RainBones;
bool Skeletons;
+
uint32 BellowingRoarTimer;
uint32 CharredEarthTimer;
uint32 DistractingAshTimer;
@@ -70,13 +80,17 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
uint32 SmokingBlastTimer;
uint32 FireballBarrageTimer;
uint32 SearingCindersTimer;
+
uint32 FlyCount;
uint32 FlyTimer;
+
bool Intro;
bool Flying;
bool Movement;
+
uint32 WaitTimer;
uint32 MovePhase;
+
void Reset()
{
BellowingRoarTimer = 30000;
@@ -89,13 +103,16 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
FireballBarrageTimer = 13000;
SearingCindersTimer = 14000;
WaitTimer = 1000;
+
Phase =1;
FlyCount = 0;
MovePhase = 0;
+
m_creature->SetSpeed(MOVE_RUN, 2.0f);
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
m_creature->setActive(true);
+
if (pInstance)
{
if (pInstance->GetData(TYPE_NIGHTBANE) == DONE || pInstance->GetData(TYPE_NIGHTBANE) == IN_PROGRESS)
@@ -103,15 +120,19 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
else
pInstance->SetData(TYPE_NIGHTBANE, NOT_STARTED);
}
+
HandleTerraceDoors(true);
+
Flying = false;
Movement = false;
+
if (!Intro)
{
m_creature->SetHomePosition(IntroWay[7][0],IntroWay[7][1],IntroWay[7][2],0);
m_creature->GetMotionMaster()->MoveTargetedHome();
}
}
+
void HandleTerraceDoors(bool open)
{
if(pInstance)
@@ -120,33 +141,41 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
pInstance->HandleGameObject(pInstance->GetData64(DATA_MASTERS_TERRACE_DOOR_2), open);
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(TYPE_NIGHTBANE, IN_PROGRESS);
+
HandleTerraceDoors(false);
m_creature->MonsterYell(YELL_AGGRO, LANG_UNIVERSAL, NULL);
}
+
void AttackStart(Unit* who)
{
if (!Intro && !Flying)
ScriptedAI::AttackStart(who);
}
+
void JustDied(Unit* killer)
{
if (pInstance)
pInstance->SetData(TYPE_NIGHTBANE, DONE);
+
HandleTerraceDoors(true);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!Intro && !Flying)
ScriptedAI::MoveInLineOfSight(who);
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (Intro)
{
if (id >= 8)
@@ -155,8 +184,10 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
m_creature->SetHomePosition(IntroWay[7][0],IntroWay[7][1],IntroWay[7][2],0);
return;
}
+
WaitTimer = 1;
}
+
if (Flying)
{
if (id == 0)
@@ -166,12 +197,14 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
Phase = 2;
return;
}
+
if (id == 3)
{
MovePhase = 4;
WaitTimer = 1;
return;
}
+
if (id == 8)
{
Flying = false;
@@ -179,28 +212,36 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
Movement = true;
return;
}
+
WaitTimer = 1;
}
}
+
void JustSummoned(Creature *summoned)
{
summoned->AI()->AttackStart(m_creature->getVictim());
}
+
void TakeOff()
{
m_creature->MonsterYell(YELL_FLY_PHASE, LANG_UNIVERSAL, NULL);
+
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF);
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
(*m_creature).GetMotionMaster()->Clear(false);
(*m_creature).GetMotionMaster()->MovePoint(0,IntroWay[2][0],IntroWay[2][1],IntroWay[2][2]);
+
Flying = true;
+
FlyTimer = 45000+rand()%15000; //timer wrong between 45 and 60 seconds
++FlyCount;
+
RainofBonesTimer = 5000; //timer wrong (maybe)
RainBones = false;
Skeletons = false;
}
+
void UpdateAI(const uint32 diff)
{
if (WaitTimer)
@@ -220,6 +261,7 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
++MovePhase;
}
}
+
if (Flying)
{
if (MovePhase >= 7)
@@ -234,12 +276,16 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
++MovePhase;
}
}
+
WaitTimer = 0;
}else WaitTimer -= diff;
+
if (!UpdateVictim())
return;
+
if (Flying)
return;
+
// Phase 1 "GROUND FIGHT"
if (Phase == 1)
{
@@ -248,22 +294,26 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
DoStartMovement(m_creature->getVictim());
Movement = false;
}
+
if (BellowingRoarTimer < diff)
{
DoCast(m_creature->getVictim(),SPELL_BELLOWING_ROAR);
BellowingRoarTimer = 30000+rand()%10000 ; //Timer
}else BellowingRoarTimer -= diff;
+
if (SmolderingBreathTimer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SMOLDERING_BREATH);
SmolderingBreathTimer = 20000;//timer
}else SmolderingBreathTimer -= diff;
+
if (CharredEarthTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target,SPELL_CHARRED_EARTH);
CharredEarthTimer = 20000; //timer
}else CharredEarthTimer -= diff;
+
if (TailSweepTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -271,22 +321,29 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
DoCast(target,SPELL_TAIL_SWEEP);
TailSweepTimer = 15000;//timer
}else TailSweepTimer -= diff;
+
if (SearingCindersTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target,SPELL_SEARING_CINDERS);
SearingCindersTimer = 10000; //timer
}else SearingCindersTimer -= diff;
+
uint32 Prozent;
Prozent = (m_creature->GetHealth()*100) / m_creature->GetMaxHealth();
+
if (Prozent < 75 && FlyCount == 0) // first take off 75%
TakeOff();
+
if (Prozent < 50 && FlyCount == 1) // secound take off 50%
TakeOff();
+
if (Prozent < 25 && FlyCount == 2) // third take off 25%
TakeOff();
+
DoMeleeAttackIfReady();
}
+
//Phase 2 "FLYING FIGHT"
if (Phase == 2)
{
@@ -300,12 +357,14 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
Skeletons = true;
}
}
+
if (RainofBonesTimer < diff && !RainBones) // only once at the beginning of phase 2
{
DoCast(m_creature->getVictim(),SPELL_RAIN_OF_BONES);
RainBones = true;
SmokingBlastTimer = 20000;
}else RainofBonesTimer -= diff;
+
if (DistractingAshTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -313,6 +372,7 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
DistractingAshTimer = 2000;//timer wrong
}else DistractingAshTimer -= diff;
}
+
if (RainBones)
{
if (SmokingBlastTimer < diff)
@@ -321,29 +381,35 @@ struct TRINITY_DLL_DECL boss_nightbaneAI : public ScriptedAI
SmokingBlastTimer = 1500 ; //timer wrong
}else SmokingBlastTimer -= diff;
}
+
if (FireballBarrageTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_FARTHEST, 0))
DoCast(target,SPELL_FIREBALL_BARRAGE);
FireballBarrageTimer = 20000; //Timer
}else FireballBarrageTimer -= diff;
+
if (FlyTimer < diff) //landing
{
if (rand()%2 == 0)
m_creature->MonsterYell(YELL_LAND_PHASE_1, LANG_UNIVERSAL, NULL);
else
m_creature->MonsterYell(YELL_LAND_PHASE_2, LANG_UNIVERSAL, NULL);
+
(*m_creature).GetMotionMaster()->Clear(false);
m_creature->GetMotionMaster()->MovePoint(3,IntroWay[3][0],IntroWay[3][1],IntroWay[3][2]);
+
Flying = true;
}else FlyTimer -= diff;
}
}
};
+
CreatureAI* GetAI_boss_nightbane(Creature* pCreature)
{
return new boss_nightbaneAI (pCreature);
}
+
void AddSC_boss_nightbane()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp
index 89640d64ba0..4f78b626067 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_prince_malchezaar.cpp
@@ -13,14 +13,17 @@
* along 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"
#include "def_karazhan.h"
+
#define SAY_AGGRO -1532091
#define SAY_AXE_TOSS1 -1532092
#define SAY_AXE_TOSS2 -1532093
@@ -33,12 +36,15 @@ EndScriptData */
#define SAY_SUMMON1 -1532100
#define SAY_SUMMON2 -1532101
#define SAY_DEATH -1532102
+
// 18 Coordinates for Infernal spawns
struct InfernalPoint
{
float x,y;
};
+
#define INFERNAL_Z 275.5
+
static InfernalPoint InfernalPoints[] =
{
{-10922.8, -1985.2},
@@ -60,12 +66,16 @@ static InfernalPoint InfernalPoints[] =
{-10932.8, -1979.6},
{-10935.7, -1996.0}
};
+
#define TOTAL_INFERNAL_POINTS 18
+
//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
@@ -77,21 +87,27 @@ static InfernalPoint InfernalPoints[] =
#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 EQUIP_ID_AXE 33542 //Axes info
+
//---------Infernal code first
struct TRINITY_DLL_DECL netherspite_infernalAI : public ScriptedAI
{
netherspite_infernalAI(Creature *c) : ScriptedAI(c) ,
malchezaar(0), HellfireTimer(0), CleanupTimer(0), point(NULL) {}
+
uint32 HellfireTimer;
uint32 CleanupTimer;
uint32 malchezaar;
InfernalPoint *point;
+
void Reset() {}
void EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (HellfireTimer)
@@ -101,6 +117,7 @@ struct TRINITY_DLL_DECL netherspite_infernalAI : public ScriptedAI
HellfireTimer = 0;
}
else HellfireTimer -= diff;
+
if (CleanupTimer)
if (CleanupTimer <= diff)
{
@@ -108,12 +125,14 @@ struct TRINITY_DLL_DECL netherspite_infernalAI : public ScriptedAI
CleanupTimer = 0;
} else CleanupTimer -= diff;
}
+
void KilledUnit(Unit *who)
{
Unit *pMalchezaar = Unit::GetUnit(*m_creature, malchezaar);
if (pMalchezaar)
CAST_CRE(pMalchezaar)->AI()->KilledUnit(who);
}
+
void SpellHit(Unit *who, const SpellEntry *spell)
{
if (spell->Id == SPELL_INFERNAL_RELAY)
@@ -124,19 +143,23 @@ struct TRINITY_DLL_DECL netherspite_infernalAI : public ScriptedAI
CleanupTimer = 170000;
}
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetGUID() != malchezaar)
damage = 0;
}
+
void Cleanup(); //below ...
};
+
struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
{
boss_malchezaarAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 EnfeebleTimer;
uint32 EnfeebleResetTimer;
@@ -148,22 +171,29 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
uint32 InfernalTimer;
uint32 AxesTargetSwitchTimer;
uint32 InfernalCleanupTimer;
+
std::vector<uint64> infernals;
std::vector<InfernalPoint*> positions;
+
uint64 axes[2];
uint64 enfeeble_targets[5];
uint64 enfeeble_health[5];
+
uint32 phase;
+
void Reset()
{
AxesCleanup();
ClearWeapons();
InfernalCleanup();
positions.clear();
- for (uint8 i =0; i < 5; ++i)
+
+ for(uint8 i =0; i < 5; ++i)
enfeeble_targets[i] = 0;
- for (uint8 i = 0; i < TOTAL_INFERNAL_POINTS; ++i)
+
+ for(uint8 i = 0; i < TOTAL_INFERNAL_POINTS; ++i)
positions.push_back(&InfernalPoints[i]);
+
EnfeebleTimer = 30000;
EnfeebleResetTimer = 38000;
ShadowNovaTimer = 35500;
@@ -175,35 +205,44 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
AxesTargetSwitchTimer = 7500 + rand()%12500;
SunderArmorTimer = 5000 + rand()%5000;
phase = 1;
+
if (pInstance)
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_NETHER_DOOR), true);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
AxesCleanup();
ClearWeapons();
InfernalCleanup();
positions.clear();
- for (uint8 i = 0; i < TOTAL_INFERNAL_POINTS; ++i)
+
+ for(uint8 i = 0; i < TOTAL_INFERNAL_POINTS; ++i)
positions.push_back(&InfernalPoints[i]);
+
if (pInstance)
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_NETHER_DOOR), true);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_NETHER_DOOR), false); // Open the door leading further in
}
+
void InfernalCleanup()
{
//Infernal Cleanup
- for (std::vector<uint64>::iterator itr = infernals.begin(); itr!= infernals.end(); ++itr)
+ for(std::vector<uint64>::iterator itr = infernals.begin(); itr!= infernals.end(); ++itr)
{
Unit *pInfernal = Unit::GetUnit(*m_creature, *itr);
if (pInfernal && pInfernal->isAlive())
@@ -214,9 +253,10 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
}
infernals.clear();
}
+
void AxesCleanup()
{
- for (uint8 i=0; i<2; ++i)
+ for(uint8 i=0; i<2; ++i)
{
Unit *axe = Unit::GetUnit(*m_creature, axes[i]);
if (axe && axe->isAlive())
@@ -224,53 +264,64 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
axes[i] = 0;
}
}
+
void ClearWeapons()
{
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
+
//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<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
std::vector<Unit *> targets;
+
if (!t_list.size())
return;
+
//begin + 1 , so we don't target the one with the highest threat
std::list<HostilReference *>::iterator itr = t_list.begin();
std::advance(itr, 1);
- for (; itr!= t_list.end(); ++itr) //store the threat list in a different container
+ 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<Unit *>::iterator iter = targets.begin(); iter!= targets.end(); ++iter, ++i)
+ for(std::vector<Unit *>::iterator iter = targets.begin(); iter!= targets.end(); ++iter, ++i)
{
Unit *target = *iter;
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 (uint8 i = 0; i < 5; ++i)
+ for(uint8 i = 0; i < 5; ++i)
{
Unit *target = Unit::GetUnit(*m_creature, enfeeble_targets[i]);
if (target && target->isAlive())
@@ -279,6 +330,7 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
enfeeble_health[i] = 0;
}
}
+
void SummonInfernal(const uint32 diff)
{
InfernalPoint *point = NULL;
@@ -292,7 +344,9 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
positions.erase(itr);
pos.Relocate(point->x, point->y, INFERNAL_Z);
}
+
Creature *Infernal = m_creature->SummonCreature(NETHERSPITE_INFERNAL, pos, TEMPSUMMON_TIMED_DESPAWN, 180000);
+
if (Infernal)
{
Infernal->SetDisplayId(INFERNAL_MODEL_INVISIBLE);
@@ -300,49 +354,64 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
if (point)
CAST_AI(netherspite_infernalAI, Infernal->AI())->point=point;
CAST_AI(netherspite_infernalAI, Infernal->AI())->malchezaar=m_creature->GetGUID();
+
infernals.push_back(Infernal->GetGUID());
DoCast(Infernal, SPELL_INFERNAL_RELAY);
}
+
DoScriptText(RAND(SAY_SUMMON1,SAY_SUMMON2), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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_STUNNED)) //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
DoScriptText(SAY_AXE_TOSS1, m_creature);
+
//passive thrash aura
m_creature->CastSpell(m_creature, SPELL_THRASH_AURA, true);
+
//models
SetEquipmentSlots(false, EQUIP_ID_AXE, EQUIP_ID_AXE, EQUIP_NO_CHANGE);
+
//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, (m_creature->GetAttackTime(BASE_ATTACK)*150)/100);
}
}
@@ -351,13 +420,18 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
if ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 30)
{
InfernalTimer = 15000;
+
phase = 3;
+
ClearWeapons();
+
//remove thrash
m_creature->RemoveAurasDueToSpell(SPELL_THRASH_AURA);
+
DoScriptText(SAY_AXE_TOSS2, m_creature);
+
Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0);
- for (uint32 i=0; i<2; ++i)
+ 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)
@@ -374,19 +448,25 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
}
}
}
+
if (ShadowNovaTimer > 35000)
ShadowNovaTimer = EnfeebleTimer + 5000;
+
return;
}
+
if (SunderArmorTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SUNDER_ARMOR);
SunderArmorTimer = 10000 + rand()%8000;
+
}else SunderArmorTimer -= diff;
+
if (Cleave_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_CLEAVE);
Cleave_Timer = 6000 + rand()%6000;
+
}else Cleave_Timer -= diff;
}
else
@@ -394,10 +474,11 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
if (AxesTargetSwitchTimer < diff)
{
AxesTargetSwitchTimer = 7500 + rand()%12500 ;
+
Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (target)
{
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
Unit *axe = Unit::GetUnit(*m_creature, axes[i]);
if (axe)
@@ -413,23 +494,27 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
}
}
} 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 ? 14500 : 44500; //15 secs in phase 3, 45 otherwise
}else InfernalTimer -= diff;
+
if (ShadowNovaTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHADOWNOVA);
ShadowNovaTimer = phase == 3 ? 31000 : -1;
}else ShadowNovaTimer -= diff;
+
if (phase != 2)
{
if (SWPainTimer < diff)
@@ -439,11 +524,14 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
target = m_creature->getVictim(); // the tank
else //anyone but the tank
target = SelectUnit(SELECT_TARGET_RANDOM, 1);
+
if (target)
DoCast(target, SPELL_SW_PAIN);
+
SWPainTimer = 20000;
}else SWPainTimer -= diff;
}
+
if (phase != 3)
{
if (EnfeebleTimer < diff)
@@ -454,11 +542,13 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
EnfeebleResetTimer = 9000;
}else EnfeebleTimer -= diff;
}
+
if (phase==2)
DoMeleeAttacksIfReady();
else
DoMeleeAttackIfReady();
}
+
void DoMeleeAttacksIfReady()
{
if (m_creature->IsWithinMeleeRange(m_creature->getVictim()) && !m_creature->IsNonMeleeSpellCasted(false))
@@ -477,31 +567,38 @@ struct TRINITY_DLL_DECL boss_malchezaarAI : public ScriptedAI
}
}
}
+
void Cleanup(Creature *infernal, InfernalPoint *point)
{
- for (std::vector<uint64>::iterator itr = infernals.begin(); itr!= infernals.end(); ++itr)
+ for(std::vector<uint64>::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())
CAST_AI(boss_malchezaarAI, CAST_CRE(pMalchezaar)->AI())->Cleanup(m_creature, point);
}
+
CreatureAI* GetAI_netherspite_infernal(Creature* pCreature)
{
return new netherspite_infernalAI (pCreature);
}
+
CreatureAI* GetAI_boss_malchezaar(Creature* pCreature)
{
return new boss_malchezaarAI (pCreature);
}
+
void AddSC_boss_malchezaar()
{
Script *newscript;
@@ -509,6 +606,7 @@ void AddSC_boss_malchezaar()
newscript->Name = "boss_malchezaar";
newscript->GetAI = &GetAI_boss_malchezaar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "netherspite_infernal";
newscript->GetAI = &GetAI_netherspite_infernal;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp
index c30c7c54500..5bdd4304d33 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_shade_of_aran.cpp
@@ -13,16 +13,19 @@
* along 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.
SDCategory: Karazhan
EndScriptData */
+
#include "precompiled.h"
#include "simple_ai.h"
#include "def_karazhan.h"
#include "GameObject.h"
+
#define SAY_AGGRO1 -1532073
#define SAY_AGGRO2 -1532074
#define SAY_AGGRO3 -1532075
@@ -39,6 +42,7 @@ EndScriptData */
#define SAY_TIMEOVER -1532086
#define SAY_DEATH -1532087
#define SAY_ATIESH -1532088 //Atiesh is equipped by a raid member
+
//Spells
#define SPELL_FROSTBOLT 29954
#define SPELL_FIREBALL 29953
@@ -57,46 +61,58 @@ EndScriptData */
#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 TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
{
boss_aranAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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;
@@ -104,17 +120,23 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
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
@@ -122,47 +144,57 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_LIBRARY_DOOR), true);
}
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL1,SAY_KILL2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(TYPE_ARAN, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_LIBRARY_DOOR), true);
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
+
if (pInstance)
{
pInstance->SetData(TYPE_ARAN, IN_PROGRESS);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_LIBRARY_DOOR), false);
}
}
+
void FlameWreathEffect()
{
std::vector<Unit*> targets;
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
+
if (!t_list.size())
return;
+
//store the threat list in a different container
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::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<Unit*>::iterator itr = targets.begin(); itr!= targets.end(); ++itr)
+ for(std::vector<Unit*>::iterator itr = targets.begin(); itr!= targets.end(); ++itr)
{
if (*itr)
{
@@ -174,10 +206,12 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (CloseDoorTimer)
{
if (CloseDoorTimer <= diff)
@@ -189,6 +223,7 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
}
}else CloseDoorTimer -= diff;
}
+
//Cooldowns for casts
if (ArcaneCooldown)
{
@@ -196,23 +231,28 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
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);
+
DoScriptText(SAY_DRINK, m_creature);
+
if (!DrinkInturrupted)
{
m_creature->CastSpell(m_creature, SPELL_MASS_POLY, true);
@@ -222,6 +262,7 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
DrinkInturruptTimer = 10000;
}
}
+
//Drink Inturrupt
if (Drinking && DrinkInturrupted)
{
@@ -231,6 +272,7 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
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)
@@ -243,9 +285,11 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
DrinkInturrupted = true;
Drinking = false;
}
+
//Don't execute any more code if we are drinking
if (Drinking)
return;
+
//Normal casts
if (NormalCastTimer < diff)
{
@@ -255,8 +299,10 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
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)
{
@@ -273,6 +319,7 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
Spells[AvailableSpells] = SPELL_FROSTBOLT;
AvailableSpells++;
}
+
//If no available spells wait 1 second and try again
if (AvailableSpells)
{
@@ -282,10 +329,12 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
}
NormalCastTimer = 1000;
}else NormalCastTimer -= diff;
+
if (SecondarySpellTimer < diff)
{
switch (rand()%2)
{
+
case 0:
DoCast(m_creature, SPELL_AOE_CS);
break;
@@ -296,9 +345,11 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
}
SecondarySpellTimer = 5000 + (rand()%15000);
}else SecondarySpellTimer -= diff;
+
if (SuperCastTimer < diff)
{
uint8 Available[2];
+
switch (LastSuperSpell)
{
case SUPER_AE:
@@ -314,36 +365,47 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
Available[1] = SUPER_AE;
break;
}
+
LastSuperSpell = Available[rand()%2];
+
switch (LastSuperSpell)
{
case SUPER_AE:
+
if (rand()%2)
DoScriptText(SAY_EXPLOSION1, m_creature);
else
DoScriptText(SAY_EXPLOSION2, m_creature);
+
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)
DoScriptText(SAY_FLAMEWREATH1, m_creature);
else
DoScriptText(SAY_FLAMEWREATH2, m_creature);
+
FlameWreathTimer = 20000;
FlameWreathCheckTime = 500;
+
FlameWreathTarget[0] = 0;
FlameWreathTarget[1] = 0;
FlameWreathTarget[2] = 0;
+
FlameWreathEffect();
break;
+
case SUPER_BLIZZARD:
+
if (rand()%2)
DoScriptText(SAY_BLIZZARD1, m_creature);
else
DoScriptText(SAY_BLIZZARD2, m_creature);
+
if (Creature* pSpawn = m_creature->SummonCreature(CREATURE_ARAN_BLIZZARD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 25000))
{
pSpawn->setFaction(m_creature->getFaction());
@@ -351,11 +413,14 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
}
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)
{
if (Creature* pUnit = m_creature->SummonCreature(CREATURE_WATER_ELEMENTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 90000))
@@ -364,8 +429,10 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
pUnit->setFaction(m_creature->getFaction());
}
}
+
DoScriptText(SAY_ELEMENTALS, m_creature);
}
+
if (BerserkTimer < diff)
{
for (uint32 i = 0; i < 5; ++i)
@@ -376,21 +443,26 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
pUnit->setFaction(m_creature->getFaction());
}
}
+
DoScriptText(SAY_TIMEOVER, m_creature);
+
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->IsWithinDist2d(FWTargPosX[i], FWTargPosY[i], 3))
{
@@ -402,14 +474,17 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
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
@@ -417,10 +492,13 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
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;
@@ -429,19 +507,25 @@ struct TRINITY_DLL_DECL boss_aranAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL water_elementalAI : public ScriptedAI
{
water_elementalAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CastTimer;
+
void Reset()
{
CastTimer = 2000 + (rand()%3000);
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (CastTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_WATERBOLT);
@@ -449,27 +533,34 @@ struct TRINITY_DLL_DECL water_elementalAI : public ScriptedAI
}else CastTimer -= diff;
}
};
+
CreatureAI* GetAI_boss_aran(Creature* pCreature)
{
return new boss_aranAI (pCreature);
}
+
CreatureAI* GetAI_water_elemental(Creature* pCreature)
{
return new water_elementalAI (pCreature);
}
+
// CONVERT TO ACID
CreatureAI* GetAI_shadow_of_aran(Creature* pCreature)
{
outstring_log("TSCR: Convert simpleAI script for Creature Entry %u to ACID", pCreature->GetEntry());
SimpleAI* ai = new SimpleAI (pCreature);
+
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;
@@ -477,10 +568,12 @@ void AddSC_boss_shade_of_aran()
newscript->Name = "boss_shade_of_aran";
newscript->GetAI = &GetAI_boss_aran;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shadow_of_aran";
newscript->GetAI = &GetAI_shadow_of_aran;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_aran_elemental";
newscript->GetAI = &GetAI_water_elemental;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp
index 82684f57181..f60fab0bddb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/boss_terestian_illhoof.cpp
@@ -13,14 +13,17 @@
* along 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: 95
SDComment: Complete! Needs adjustments to use spell though.
SDCategory: Karazhan
EndScriptData */
+
#include "precompiled.h"
#include "def_karazhan.h"
+
#define SAY_SLAY1 -1532065
#define SAY_SLAY2 -1532066
#define SAY_DEATH -1532067
@@ -29,6 +32,7 @@ EndScriptData */
#define SAY_SACRIFICE2 -1532070
#define SAY_SUMMON1 -1532071
#define SAY_SUMMON2 -1532072
+
#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.
@@ -37,29 +41,38 @@ EndScriptData */
#define SPELL_BERSERK 32965 // Increases attack speed by 75%. Periodically casts Shadow Bolt Volley.
#define SPELL_SUMMON_FIENDISIMP 30184 // Summons a Fiendish Imp.
#define SPELL_SUMMON_IMP 30066 // Summons Kil'rek
+
#define SPELL_FIENDISH_PORTAL 30171 // Opens portal and summons Fiendish Portal, 2 sec cast
#define SPELL_FIENDISH_PORTAL_1 30179 // Opens portal and summons Fiendish Portal, instant cast
+
#define SPELL_FIREBOLT 30050 // 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 CREATURE_DEMONCHAINS 17248
#define CREATURE_FIENDISHIMP 17267
#define CREATURE_PORTAL 17265
#define CREATURE_KILREK 17229
+
struct TRINITY_DLL_DECL mob_kilrekAI : public ScriptedAI
{
mob_kilrekAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint64 TerestianGUID;
+
uint32 AmplifyTimer;
+
void Reset()
{
TerestianGUID = 0;
AmplifyTimer = 2000;
}
+
void EnterCombat(Unit *who)
{
if (!pInstance)
@@ -68,6 +81,7 @@ struct TRINITY_DLL_DECL mob_kilrekAI : public ScriptedAI
return;
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
@@ -81,31 +95,40 @@ struct TRINITY_DLL_DECL mob_kilrekAI : public ScriptedAI
}
}else ERROR_INST_DATA(m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (AmplifyTimer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
DoCast(m_creature->getVictim(),SPELL_AMPLIFY_FLAMES);
+
AmplifyTimer = 10000 + rand()%10000;
}else AmplifyTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_demon_chainAI : public ScriptedAI
{
mob_demon_chainAI(Creature *c) : ScriptedAI(c) {}
+
uint64 SacrificeGUID;
+
void Reset()
{
SacrificeGUID = 0;
}
+
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
+
void JustDied(Unit *killer)
{
if (SacrificeGUID)
@@ -116,44 +139,55 @@ struct TRINITY_DLL_DECL mob_demon_chainAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_fiendish_portalAI : public PassiveAI
{
mob_fiendish_portalAI(Creature *c) : PassiveAI(c),summons(m_creature){}
+
SummonList summons;
+
void Reset()
{
summons.DespawnAll();
}
+
void JustSummoned(Creature* summon)
{
summons.Summon(summon);
DoZoneInCombat(summon);
}
+
void DespawnAllImp()
{
summons.DespawnAll();
}
};
+
struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
{
boss_terestianAI(Creature *c) : ScriptedAI(c)
{
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
PortalGUID[i] = 0;
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint64 PortalGUID[2];
uint8 PortalsCount;
+
uint32 SacrificeTimer;
uint32 ShadowboltTimer;
uint32 SummonTimer;
uint32 BerserkTimer;
+
bool SummonedPortals;
bool Berserk;
+
void Reset()
{
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
if (PortalGUID[i])
{
@@ -162,19 +196,25 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
CAST_AI(mob_fiendish_portalAI, pPortal->AI())->DespawnAllImp();
pPortal->ForcedDespawn();
}
+
PortalGUID[i] = 0;
}
}
+
PortalsCount = 0;
SacrificeTimer = 30000;
ShadowboltTimer = 5000;
SummonTimer = 10000;
BerserkTimer = 600000;
+
SummonedPortals = false;
Berserk = false;
+
if (pInstance)
pInstance->SetData(TYPE_TERESTIAN, NOT_STARTED);
+
m_creature->RemoveAurasDueToSpell(SPELL_BROKEN_PACT);
+
if(Minion* Kilrek = m_creature->GetFirstMinion())
{
if(!Kilrek->isAlive())
@@ -185,16 +225,19 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
}
else DoCast(m_creature, SPELL_SUMMON_IMP, true);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustSummoned(Creature* pSummoned)
{
if (pSummoned->GetEntry() == CREATURE_PORTAL)
{
PortalGUID[PortalsCount] = pSummoned->GetGUID();
PortalsCount++;
+
if (pSummoned->GetUInt32Value(UNIT_CREATED_BY_SPELL) == SPELL_FIENDISH_PORTAL_1)
{
DoScriptText(RAND(SAY_SUMMON1,SAY_SUMMON2), m_creature);
@@ -202,29 +245,36 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *killer)
{
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
if (PortalGUID[i])
{
if (Creature* pPortal = Unit::GetCreature((*m_creature), PortalGUID[i]))
pPortal->ForcedDespawn();
+
PortalGUID[i] = 0;
}
}
+
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_TERESTIAN, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (SacrificeTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
@@ -232,6 +282,7 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
{
DoCast(target, SPELL_SACRIFICE, true);
DoCast(target, SPELL_SUMMON_DEMONCHAINS, true);
+
Creature* Chains = m_creature->FindNearestCreature(CREATURE_DEMONCHAINS, 5000);
if (Chains)
{
@@ -242,17 +293,21 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
}
}
}else SacrificeTimer -= diff;
+
if (ShadowboltTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_TOPAGGRO, 0), SPELL_SHADOW_BOLT);
ShadowboltTimer = 10000;
}else ShadowboltTimer -= diff;
+
if (SummonTimer < diff)
{
if(!PortalGUID[0])
DoCast(m_creature->getVictim(), SPELL_FIENDISH_PORTAL, false);
+
if(!PortalGUID[1])
DoCast(m_creature->getVictim(), SPELL_FIENDISH_PORTAL_1, false);
+
if(PortalGUID[0] && PortalGUID[1])
{
if (Creature* pPortal = Unit::GetCreature(*m_creature, PortalGUID[rand()%2]))
@@ -260,6 +315,7 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
SummonTimer = 5000;
}
}else SummonTimer -= diff;
+
if (!Berserk)
{
if (BerserkTimer < diff)
@@ -268,53 +324,69 @@ struct TRINITY_DLL_DECL boss_terestianAI : public ScriptedAI
Berserk = true;
}else BerserkTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
#define SPELL_FIREBOLT 30050 // Blasts a target for 181-209 Fire damage.
+
struct TRINITY_DLL_DECL mob_fiendish_impAI : public ScriptedAI
{
mob_fiendish_impAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FireboltTimer;
+
void Reset()
{
FireboltTimer = 2000;
+
m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true);
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (FireboltTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FIREBOLT);
FireboltTimer = 2200;
}else FireboltTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_kilrek(Creature* pCreature)
{
return new mob_kilrekAI (pCreature);
}
+
CreatureAI* GetAI_mob_fiendish_imp(Creature* pCreature)
{
return new mob_fiendish_impAI (pCreature);
}
+
CreatureAI* GetAI_mob_fiendish_portal(Creature* pCreature)
{
return new mob_fiendish_portalAI (pCreature);
}
+
CreatureAI* GetAI_boss_terestian_illhoof(Creature* pCreature)
{
return new boss_terestianAI (pCreature);
}
+
CreatureAI* GetAI_mob_demon_chain(Creature* pCreature)
{
return new mob_demon_chainAI(pCreature);
}
+
void AddSC_boss_terestian_illhoof()
{
Script *newscript;
@@ -322,18 +394,22 @@ void AddSC_boss_terestian_illhoof()
newscript->Name = "boss_terestian_illhoof";
newscript->GetAI = &GetAI_boss_terestian_illhoof;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_fiendish_imp";
newscript->GetAI = &GetAI_mob_fiendish_imp;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name= "mob_fiendish_portal";
newscript->GetAI = &GetAI_mob_fiendish_portal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_kilrek";
newscript->GetAI = &GetAI_mob_kilrek;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_demon_chain";
newscript->GetAI = &GetAI_mob_demon_chain;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp
index c402bb2bfee..51827bd3fb3 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/bosses_opera.cpp
@@ -13,65 +13,83 @@
* along 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 *****/
/*********************************/
+
#define SAY_DOROTHEE_DEATH -1532025
#define SAY_DOROTHEE_SUMMON -1532026
#define SAY_DOROTHEE_TITO_DEATH -1532027
#define SAY_DOROTHEE_AGGRO -1532028
+
#define SAY_ROAR_AGGRO -1532029
#define SAY_ROAR_DEATH -1532030
#define SAY_ROAR_SLAY -1532031
+
#define SAY_STRAWMAN_AGGRO -1532032
#define SAY_STRAWMAN_DEATH -1532033
#define SAY_STRAWMAN_SLAY -1532034
+
#define SAY_TINHEAD_AGGRO -1532035
#define SAY_TINHEAD_DEATH -1532036
#define SAY_TINHEAD_SLAY -1532037
#define EMOTE_RUST -1532038
+
#define SAY_CRONE_AGGRO -1532039
#define SAY_CRONE_AGGRO2 -1532040
#define SAY_CRONE_DEATH -1532041
#define SAY_CRONE_SLAY -1532042
+
/**** 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
+
void SummonCroneIfReady(ScriptedInstance* pInstance, Creature* pCreature)
{
pInstance->SetData(DATA_OPERA_OZ_DEATHCOUNT, SPECIAL); // Increment DeathCount
+
if (pInstance->GetData(DATA_OPERA_OZ_DEATHCOUNT) == 4)
{
if (Creature* pCrone = pCreature->SummonCreature(CREATURE_CRONE, -10891.96, -1755.95, pCreature->GetPositionZ(), 4.64, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILISECONDS))
@@ -81,55 +99,73 @@ void SummonCroneIfReady(ScriptedInstance* pInstance, Creature* pCreature)
}
}
};
+
struct TRINITY_DLL_DECL boss_dorotheeAI : public ScriptedAI
{
boss_dorotheeAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 AggroTimer;
+
uint32 WaterBoltTimer;
uint32 FearTimer;
uint32 SummonTitoTimer;
+
bool SummonedTito;
bool TitoDied;
+
void Reset()
{
AggroTimer = 500;
+
WaterBoltTimer = 5000;
FearTimer = 15000;
SummonTitoTimer = 47500;
+
SummonedTito = false;
TitoDied = false;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_DOROTHEE_AGGRO, m_creature);
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void SummonTito(); // See below
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DOROTHEE_DEATH, m_creature);
+
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)
@@ -140,38 +176,48 @@ struct TRINITY_DLL_DECL boss_dorotheeAI : public ScriptedAI
AggroTimer = 0;
}else AggroTimer -= diff;
}
+
if (!UpdateVictim())
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 TRINITY_DLL_DECL mob_titoAI : public ScriptedAI
{
mob_titoAI(Creature* c) : ScriptedAI(c) {}
+
uint64 DorotheeGUID;
uint32 YipTimer;
+
void Reset()
{
DorotheeGUID = 0;
YipTimer = 10000;
}
+
void EnterCombat(Unit* who) {}
+
void JustDied(Unit* killer)
{
if (DorotheeGUID)
@@ -184,18 +230,22 @@ struct TRINITY_DLL_DECL mob_titoAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (YipTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_YIPPING);
YipTimer = 10000;
}else YipTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
void boss_dorotheeAI::SummonTito()
{
if (Creature* pTito = m_creature->SummonCreature(CREATURE_TITO, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
@@ -207,42 +257,53 @@ void boss_dorotheeAI::SummonTito()
TitoDied = false;
}
}
+
struct TRINITY_DLL_DECL boss_strawmanAI : public ScriptedAI
{
boss_strawmanAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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 EnterCombat(Unit* who)
{
DoScriptText(SAY_STRAWMAN_AGGRO, m_creature);
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void SpellHit(Unit* caster, const SpellEntry *Spell)
{
if ((Spell->SchoolMask == SPELL_SCHOOL_MASK_FIRE) && (!(rand()%10)))
@@ -251,19 +312,24 @@ struct TRINITY_DLL_DECL boss_strawmanAI : public ScriptedAI
if (not direct damage(aoe,dot))
return;
*/
+
DoCast(m_creature, SPELL_BURNING_STRAW, true);
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_STRAWMAN_DEATH, m_creature);
+
if (pInstance)
SummonCroneIfReady(pInstance, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_STRAWMAN_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (AggroTimer)
@@ -274,69 +340,89 @@ struct TRINITY_DLL_DECL boss_strawmanAI : public ScriptedAI
AggroTimer = 0;
}else AggroTimer -= diff;
}
+
if (!UpdateVictim())
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 TRINITY_DLL_DECL boss_tinheadAI : public ScriptedAI
{
boss_tinheadAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 AggroTimer;
uint32 CleaveTimer;
uint32 RustTimer;
+
uint8 RustCount;
+
void Reset()
{
AggroTimer = 15000;
CleaveTimer = 5000;
RustTimer = 30000;
+
RustCount = 0;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_TINHEAD_AGGRO, m_creature);
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
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)
{
DoScriptText(SAY_TINHEAD_DEATH, m_creature);
+
if (pInstance)
SummonCroneIfReady(pInstance, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_TINHEAD_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (AggroTimer)
@@ -347,13 +433,16 @@ struct TRINITY_DLL_DECL boss_tinheadAI : public ScriptedAI
AggroTimer = 0;
}else AggroTimer -= diff;
}
+
if (!UpdateVictim())
return;
+
if (CleaveTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_CLEAVE);
CleaveTimer = 5000;
}else CleaveTimer -= diff;
+
if (RustCount < 8)
{
if (RustTimer < diff)
@@ -364,20 +453,25 @@ struct TRINITY_DLL_DECL boss_tinheadAI : public ScriptedAI
RustTimer = 6000;
}else RustTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_roarAI : public ScriptedAI
{
boss_roarAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 AggroTimer;
uint32 MangleTimer;
uint32 ShredTimer;
uint32 ScreamTimer;
+
void Reset()
{
AggroTimer = 20000;
@@ -385,36 +479,46 @@ struct TRINITY_DLL_DECL boss_roarAI : public ScriptedAI
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 EnterCombat(Unit* who)
{
DoScriptText(SAY_ROAR_AGGRO, m_creature);
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_ROAR_DEATH, m_creature);
+
if (pInstance)
SummonCroneIfReady(pInstance, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_ROAR_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (AggroTimer)
@@ -425,68 +529,85 @@ struct TRINITY_DLL_DECL boss_roarAI : public ScriptedAI
AggroTimer = 0;
}else AggroTimer -= diff;
}
+
if (!UpdateVictim())
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 TRINITY_DLL_DECL boss_croneAI : public ScriptedAI
{
boss_croneAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 CycloneTimer;
uint32 ChainLightningTimer;
+
void Reset()
{
CycloneTimer = 30000;
ChainLightningTimer = 10000;
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void EnterCombat(Unit* who)
{
DoScriptText(RAND(SAY_CRONE_AGGRO,SAY_CRONE_AGGRO2), m_creature);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_CRONE_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(TYPE_OPERA, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_STAGEDOORLEFT), true);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_STAGEDOORRIGHT), true);
+
if (GameObject* pSideEntrance = pInstance->instance->GetGameObject(pInstance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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);
@@ -494,30 +615,39 @@ struct TRINITY_DLL_DECL boss_croneAI : public ScriptedAI
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 TRINITY_DLL_DECL mob_cycloneAI : public ScriptedAI
{
mob_cycloneAI(Creature* c) : ScriptedAI(c) {}
+
uint32 MoveTimer;
+
void Reset()
{
MoveTimer = 1000;
}
+
void EnterCombat(Unit* who) {}
+
void MoveInLineOfSight(Unit* who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->HasAura(SPELL_KNOCKBACK))
DoCast(m_creature, SPELL_KNOCKBACK, true);
+
if (MoveTimer < diff)
{
Position pos;
@@ -527,112 +657,145 @@ struct TRINITY_DLL_DECL mob_cycloneAI : public ScriptedAI
}else MoveTimer -= diff;
}
};
+
CreatureAI* GetAI_boss_dorothee(Creature* pCreature)
{
return new boss_dorotheeAI(pCreature);
}
+
CreatureAI* GetAI_boss_strawman(Creature* pCreature)
{
return new boss_strawmanAI(pCreature);
}
+
CreatureAI* GetAI_boss_tinhead(Creature* pCreature)
{
return new boss_tinheadAI(pCreature);
}
+
CreatureAI* GetAI_boss_roar(Creature* pCreature)
{
return new boss_roarAI(pCreature);
}
+
CreatureAI* GetAI_boss_crone(Creature* pCreature)
{
return new boss_croneAI(pCreature);
}
+
CreatureAI* GetAI_mob_tito(Creature* pCreature)
{
return new mob_titoAI(pCreature);
}
+
CreatureAI* GetAI_mob_cyclone(Creature* pCreature)
{
return new mob_cycloneAI(pCreature);
}
+
/**************************************/
/**** Opera Red Riding Hood Event ****/
/************************************/
+
/**** Yells for the Wolf ****/
#define SAY_WOLF_AGGRO -1532043
#define SAY_WOLF_SLAY -1532044
#define SAY_WOLF_HOOD -1532045
#define SOUND_WOLF_DEATH 9275 //Only sound on death, no text.
+
/**** Spells For The Wolf ****/
#define SPELL_LITTLE_RED_RIDING_HOOD 30768
#define SPELL_TERRIFYING_HOWL 30752
#define SPELL_WIDE_SWIPE 30761
+
#define GOSSIP_GRANDMA "What phat lewtz you have grandmother?"
+
/**** The Wolf's Entry ****/
#define CREATURE_BIG_BAD_WOLF 17521
+
bool GossipHello_npc_grandmother(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_GRANDMA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(8990, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_grandmother(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
{
if (Creature* pBigBadWolf = pCreature->SummonCreature(CREATURE_BIG_BAD_WOLF, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILISECONDS))
pBigBadWolf->AI()->AttackStart(pPlayer);
+
pCreature->ForcedDespawn();
}
+
return true;
}
+
struct TRINITY_DLL_DECL boss_bigbadwolfAI : public ScriptedAI
{
boss_bigbadwolfAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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 EnterCombat(Unit* who)
{
DoScriptText(SAY_WOLF_AGGRO, m_creature);
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void JustDied(Unit* killer)
{
DoPlaySoundToSet(m_creature, SOUND_WOLF_DEATH);
+
if (pInstance)
{
pInstance->SetData(TYPE_OPERA, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_STAGEDOORLEFT), true);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_STAGEDOORRIGHT), true);
+
if (GameObject* pSideEntrance = pInstance->instance->GetGameObject(pInstance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
+
if (ChaseTimer < diff)
{
if (!IsChasing)
@@ -641,6 +804,7 @@ struct TRINITY_DLL_DECL boss_bigbadwolfAI : public ScriptedAI
if (target && target->GetTypeId() == TYPEID_PLAYER)
{
DoScriptText(SAY_WOLF_HOOD, m_creature);
+
DoCast(target, SPELL_LITTLE_RED_RIDING_HOOD, true);
TempThreat = DoGetThreat(target);
if (TempThreat)
@@ -654,6 +818,7 @@ struct TRINITY_DLL_DECL boss_bigbadwolfAI : public ScriptedAI
else
{
IsChasing = false;
+
if (Unit* target = Unit::GetUnit((*m_creature), HoodGUID))
{
HoodGUID = 0;
@@ -662,30 +827,38 @@ struct TRINITY_DLL_DECL boss_bigbadwolfAI : public ScriptedAI
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()%10000;
}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* pCreature)
{
return new boss_bigbadwolfAI(pCreature);
}
+
/**********************************************/
/******** Opera Romeo and Juliet Event *******/
/********************************************/
+
/**** Speech *****/
#define SAY_JULIANNE_AGGRO -1532046
#define SAY_JULIANNE_ENTER -1532047
@@ -693,35 +866,42 @@ CreatureAI* GetAI_boss_bigbadwolf(Creature* pCreature)
#define SAY_JULIANNE_DEATH02 -1532049
#define SAY_JULIANNE_RESURRECT -1532050
#define SAY_JULIANNE_SLAY -1532051
+
#define SAY_ROMULO_AGGRO -1532052
#define SAY_ROMULO_DEATH -1532053
#define SAY_ROMULO_ENTER -1532054
#define SAY_ROMULO_RESURRECT -1532055
#define SAY_ROMULO_SLAY -1532056
+
/***** 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
+
/*** 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* pCreature)
{
pCreature->InterruptNonMeleeSpells(true);
@@ -732,6 +912,7 @@ void PretendToDie(Creature* pCreature)
pCreature->GetMotionMaster()->MoveIdle();
pCreature->SetStandState(UNIT_STAND_STATE_DEAD);
};
+
void Resurrect(Creature* target)
{
target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -746,6 +927,7 @@ void Resurrect(Creature* target)
else
target->GetMotionMaster()->Initialize();
};
+
struct TRINITY_DLL_DECL boss_julianneAI : public ScriptedAI
{
boss_julianneAI(Creature* c) : ScriptedAI(c)
@@ -755,11 +937,16 @@ struct TRINITY_DLL_DECL boss_julianneAI : public ScriptedAI
AggroYellTimer = 10000;
IsFakingDeath = false;
}
+
ScriptedInstance* pInstance;
+
uint32 EntryYellTimer;
uint32 AggroYellTimer;
+
uint64 RomuloGUID;
+
uint32 Phase;
+
uint32 BlindingPassionTimer;
uint32 DevotionTimer;
uint32 EternalAffectionTimer;
@@ -768,13 +955,16 @@ struct TRINITY_DLL_DECL boss_julianneAI : public ScriptedAI
uint32 ResurrectTimer;
uint32 DrinkPoisonTimer;
uint32 ResurrectSelfTimer;
+
bool IsFakingDeath;
bool SummonedRomulo;
bool RomuloDead;
+
void Reset()
{
RomuloGUID = 0;
Phase = PHASE_JULIANNE;
+
BlindingPassionTimer = 30000;
DevotionTimer = 15000;
EternalAffectionTimer = 25000;
@@ -782,31 +972,40 @@ struct TRINITY_DLL_DECL boss_julianneAI : public ScriptedAI
SummonRomuloTimer = 10000;
DrinkPoisonTimer = 0;
ResurrectSelfTimer = 0;
+
if (IsFakingDeath)
{
Resurrect(m_creature);
IsFakingDeath = false;
}
+
SummonedRomulo = false;
RomuloDead = false;
}
+
void EnterCombat(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 JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void SpellHit(Unit* caster, const SpellEntry *Spell)
{
if (Spell->Id == SPELL_DRINK_POISON)
@@ -815,10 +1014,13 @@ struct TRINITY_DLL_DECL boss_julianneAI : public ScriptedAI
DrinkPoisonTimer = 2500;
}
}
+
void DamageTaken(Unit* done_by, uint32 &damage);
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_JULIANNE_DEATH02, m_creature);
+
if (pInstance)
{
pInstance->SetData(TYPE_OPERA, DONE);
@@ -828,12 +1030,15 @@ struct TRINITY_DLL_DECL boss_julianneAI : public ScriptedAI
pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
}
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_JULIANNE_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff);
};
+
struct TRINITY_DLL_DECL boss_romuloAI : public ScriptedAI
{
boss_romuloAI(Creature* c) : ScriptedAI(c)
@@ -842,9 +1047,12 @@ struct TRINITY_DLL_DECL boss_romuloAI : public ScriptedAI
EntryYellTimer = 8000;
AggroYellTimer = 15000;
}
+
ScriptedInstance* pInstance;
+
uint64 JulianneGUID;
uint32 Phase;
+
uint32 EntryYellTimer;
uint32 AggroYellTimer;
uint32 BackwardLungeTimer;
@@ -852,25 +1060,32 @@ struct TRINITY_DLL_DECL boss_romuloAI : public ScriptedAI
uint32 DeadlySwatheTimer;
uint32 PoisonThrustTimer;
uint32 ResurrectTimer;
+
bool IsFakingDeath;
bool JulianneDead;
+
void Reset()
{
JulianneGUID = 0;
Phase = PHASE_ROMULO;
+
BackwardLungeTimer = 15000;
DaringTimer = 20000;
DeadlySwatheTimer = 25000;
PoisonThrustTimer = 10000;
ResurrectTimer = 10000;
+
IsFakingDeath = false;
JulianneDead = false;
}
+
void JustReachedHome()
{
m_creature->ForcedDespawn();
}
+
void DamageTaken(Unit* done_by, uint32 &damage);
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_ROMULO_AGGRO, m_creature);
@@ -884,53 +1099,68 @@ struct TRINITY_DLL_DECL boss_romuloAI : public ScriptedAI
}
}
}
+
void MoveInLineOfSight(Unit* who)
{
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_ROMULO_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(TYPE_OPERA, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_STAGEDOORLEFT), true);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GO_STAGEDOORRIGHT), true);
+
if (GameObject* pSideEntrance = pInstance->instance->GetGameObject(pInstance->GetData64(DATA_GO_SIDE_ENTRANCE_DOOR)))
pSideEntrance->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_LOCKED);
}
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_ROMULO_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff);
};
+
void boss_julianneAI::DamageTaken(Unit* done_by, uint32 &damage)
{
if (damage < m_creature->GetHealth())
return;
+
//anything below only used if incoming damage will kill
+
if (Phase == PHASE_JULIANNE)
{
damage = 0;
+
//this means already drinking, so return
if (IsFakingDeath)
return;
+
m_creature->InterruptNonMeleeSpells(true);
DoCast(m_creature, SPELL_DRINK_POISON);
+
IsFakingDeath = true;
//IS THIS USEFULL? Creature* Julianne = (Unit::GetCreature((*m_creature), JulianneGUID));
return;
}
+
if (Phase == PHASE_ROMULO)
{
error_log("TSCR: boss_julianneAI: cannot take damage in PHASE_ROMULO, why was i here?");
damage = 0;
return;
}
+
if (Phase == PHASE_BOTH)
{
//if this is true then we have to kill romulo too
@@ -945,8 +1175,10 @@ void boss_julianneAI::DamageTaken(Unit* done_by, uint32 &damage)
Romulo->DeleteThreatList();
Romulo->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
+
return;
}
+
//if not already returned, then romulo is alive and we can pretend die
if (Creature* Romulo = (Unit::GetCreature((*m_creature), RomuloGUID)))
{
@@ -960,25 +1192,31 @@ void boss_julianneAI::DamageTaken(Unit* done_by, uint32 &damage)
}
error_log("TSCR: boss_julianneAI: DamageTaken reach end of code, that should not happen.");
}
+
void boss_romuloAI::DamageTaken(Unit* done_by, uint32 &damage)
{
if (damage < m_creature->GetHealth())
return;
+
//anything below only used if incoming damage will kill
+
if (Phase == PHASE_ROMULO)
{
DoScriptText(SAY_ROMULO_DEATH, m_creature);
PretendToDie(m_creature);
IsFakingDeath = true;
Phase = PHASE_BOTH;
+
if (Creature* Julianne = (Unit::GetCreature((*m_creature), JulianneGUID)))
{
CAST_AI(boss_julianneAI, Julianne->AI())->RomuloDead = true;
CAST_AI(boss_julianneAI, Julianne->AI())->ResurrectSelfTimer = 10000;
}
+
damage = 0;
return;
}
+
if (Phase == PHASE_BOTH)
{
if (JulianneDead)
@@ -994,6 +1232,7 @@ void boss_romuloAI::DamageTaken(Unit* done_by, uint32 &damage)
}
return;
}
+
if (Creature* Julianne = (Unit::GetCreature((*m_creature), JulianneGUID)))
{
PretendToDie(m_creature);
@@ -1004,8 +1243,10 @@ void boss_romuloAI::DamageTaken(Unit* done_by, uint32 &damage)
return;
}
}
+
error_log("TSCR: boss_romuloAI: DamageTaken reach end of code, that should not happen.");
}
+
void boss_julianneAI::UpdateAI(const uint32 diff)
{
if (EntryYellTimer)
@@ -1016,6 +1257,7 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
EntryYellTimer = 0;
}else EntryYellTimer -= diff;
}
+
if (AggroYellTimer)
{
if (AggroYellTimer <= diff)
@@ -1026,6 +1268,7 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
AggroYellTimer = 0;
}else AggroYellTimer -= diff;
}
+
if (DrinkPoisonTimer)
{
//will do this 2secs after spell hit. this is time to display visual as expected
@@ -1037,6 +1280,7 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
DrinkPoisonTimer = 0;
}else DrinkPoisonTimer -= diff;
}
+
if (Phase == PHASE_ROMULO && !SummonedRomulo)
{
if (SummonRomuloTimer < diff)
@@ -1047,11 +1291,13 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
CAST_AI(boss_romuloAI, pRomulo->AI())->JulianneGUID = m_creature->GetGUID();
CAST_AI(boss_romuloAI, pRomulo->AI())->Phase = PHASE_ROMULO;
DoZoneInCombat(pRomulo);
+
pRomulo->setFaction(16);
}
SummonedRomulo = true;
}else SummonRomuloTimer -= diff;
}
+
if (ResurrectSelfTimer)
{
if (ResurrectSelfTimer <= diff)
@@ -1059,14 +1305,18 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
Resurrect(m_creature);
Phase = PHASE_BOTH;
IsFakingDeath = false;
+
if (m_creature->getVictim())
AttackStart(m_creature->getVictim());
+
ResurrectSelfTimer = 0;
ResurrectTimer = 1000;
}else ResurrectSelfTimer -= diff;
}
+
if (!UpdateVictim() || IsFakingDeath)
return;
+
if (RomuloDead)
{
if (ResurrectTimer < diff)
@@ -1082,21 +1332,25 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
}
}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)
@@ -1105,14 +1359,18 @@ void boss_julianneAI::UpdateAI(const uint32 diff)
if (Romulo && Romulo->isAlive() && !RomuloDead)
DoCast(Romulo, SPELL_ETERNAL_AFFECTION);
}else DoCast(m_creature, SPELL_ETERNAL_AFFECTION);
+
EternalAffectionTimer = 45000 + rand()%15000;
}else EternalAffectionTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
void boss_romuloAI::UpdateAI(const uint32 diff)
{
if (!UpdateVictim() || IsFakingDeath)
return;
+
if (JulianneDead)
{
if (ResurrectTimer < diff)
@@ -1128,6 +1386,7 @@ void boss_romuloAI::UpdateAI(const uint32 diff)
}
}else ResurrectTimer -= diff;
}
+
if (BackwardLungeTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
@@ -1137,78 +1396,96 @@ void boss_romuloAI::UpdateAI(const uint32 diff)
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* pCreature)
{
return new boss_julianneAI(pCreature);
}
+
CreatureAI* GetAI_boss_romulo(Creature* pCreature)
{
return new boss_romuloAI(pCreature);
}
+
void AddSC_bosses_opera()
{
Script* newscript;
+
// Oz
newscript = new Script;
newscript->GetAI = &GetAI_boss_dorothee;
newscript->Name = "boss_dorothee";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_strawman;
newscript->Name = "boss_strawman";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_tinhead;
newscript->Name = "boss_tinhead";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_roar;
newscript->Name = "boss_roar";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_crone;
newscript->Name = "boss_crone";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_tito;
newscript->Name = "mob_tito";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_cyclone;
newscript->Name = "mob_cyclone";
newscript->RegisterSelf();
+
// Hood
newscript = new Script;
newscript->pGossipHello = &GossipHello_npc_grandmother;
newscript->pGossipSelect = &GossipSelect_npc_grandmother;
newscript->Name = "npc_grandmother";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_bigbadwolf;
newscript->Name = "boss_bigbadwolf";
newscript->RegisterSelf();
+
// Romeo And Juliet
newscript = new Script;
newscript->GetAI = &GetAI_boss_julianne;
newscript->Name = "boss_julianne";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_romulo;
newscript->Name = "boss_romulo";
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/def_karazhan.h b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/def_karazhan.h
index b79aef32226..1c4c1f37eb0 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/def_karazhan.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/def_karazhan.h
@@ -2,8 +2,10 @@
* 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
+
enum eEnums
{
TYPE_ATTUMEN = 1,
@@ -18,8 +20,10 @@ enum eEnums
TYPE_CHESS = 10,
TYPE_MALCHEZZAR = 11,
TYPE_NIGHTBANE = 12,
+
DATA_OPERA_PERFORMANCE = 13,
DATA_OPERA_OZ_DEATHCOUNT = 14,
+
DATA_KILREK = 15,
DATA_TERESTIAN = 16,
DATA_MOROES = 17,
@@ -31,17 +35,20 @@ enum eEnums
DATA_GO_NETHER_DOOR = 23,
DATA_GO_GAME_DOOR = 24,
DATA_GO_GAME_EXIT_DOOR = 25,
+
DATA_IMAGE_OF_MEDIVH = 26,
DATA_MASTERS_TERRACE_DOOR_1 = 27,
DATA_MASTERS_TERRACE_DOOR_2 = 28,
DATA_GO_SIDE_ENTRANCE_DOOR = 29
};
+
enum OperaEvents
{
EVENT_OZ = 1,
EVENT_HOOD = 2,
EVENT_RAJ = 3
};
+
#define ERROR_INST_DATA(a) error_log("TSCR: 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/eastern_kingdoms/karazhan/instance_karazhan.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp
index 0d8a7cdb251..2ee3f3958cf 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/instance_karazhan.cpp
@@ -13,15 +13,19 @@
* along 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 MAX_ENCOUNTER 12
+
/*
0 - Attumen + Midnight (optional)
1 - Moroes
@@ -36,13 +40,17 @@ EndScriptData */
10 - Prince Malchezzar
11 - Nightbane
*/
+
struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
{
instance_karazhan(Map* pMap) : ScriptedInstance(pMap) {Initialize();}
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string strSaveData;
+
uint32 m_uiOperaEvent;
uint32 m_uiOzDeathCount;
+
uint64 m_uiCurtainGUID;
uint64 m_uiStageDoorLeftGUID;
uint64 m_uiStageDoorRightGUID;
@@ -58,18 +66,23 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
uint64 MastersTerraceDoor[2];
uint64 ImageGUID;
uint64 DustCoveredChest;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
// 1 - OZ, 2 - HOOD, 3 - RAJ, this never gets altered.
m_uiOperaEvent = urand(1,3);
m_uiOzDeathCount = 0;
+
m_uiCurtainGUID = 0;
m_uiStageDoorLeftGUID = 0;
m_uiStageDoorRightGUID = 0;
+
m_uiKilrekGUID = 0;
m_uiTerestianGUID = 0;
m_uiMoroesGUID = 0;
+
m_uiLibraryDoor = 0;
m_uiMassiveDoor = 0;
m_uiSideEntranceDoor = 0;
@@ -81,13 +94,16 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
ImageGUID = 0;
DustCoveredChest = 0;
}
+
bool IsEncounterInProgress() const
{
for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch (pCreature->GetEntry())
@@ -97,6 +113,7 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
case 15687: m_uiMoroesGUID = pCreature->GetGUID(); break;
}
}
+
void SetData(uint32 type, uint32 uiData)
{
switch (type)
@@ -132,18 +149,23 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
m_uiOzDeathCount = 0;
break;
}
+
if (uiData == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " "
<< m_auiEncounter[3] << " " << m_auiEncounter[4] << " " << m_auiEncounter[5] << " " << m_auiEncounter[6] << " "
<< m_auiEncounter[7] << " " << m_auiEncounter[8] << " " << m_auiEncounter[9] << " " << m_auiEncounter[10] << " " << m_auiEncounter[11];
+
strSaveData = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
void SetData64(uint32 identifier, uint64 data)
{
switch(identifier)
@@ -151,6 +173,7 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
case DATA_IMAGE_OF_MEDIVH: ImageGUID = data;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -182,21 +205,26 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
break;
case 185119: DustCoveredChest = pGo->GetGUID(); break;
}
+
switch(m_uiOperaEvent)
{
//TODO: Set Object visibilities for Opera based on performance
case EVENT_OZ:
break;
+
case EVENT_HOOD:
break;
+
case EVENT_RAJ:
break;
}
}
+
std::string GetSaveData()
{
return strSaveData;
}
+
uint32 GetData(uint32 uiData)
{
switch (uiData)
@@ -217,8 +245,10 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
case DATA_OPERA_OZ_DEATHCOUNT: return m_uiOzDeathCount;
case DATA_IMAGE_OF_MEDIVH: return ImageGUID;
}
+
return 0;
}
+
uint64 GetData64(uint32 uiData)
{
switch (uiData)
@@ -238,8 +268,10 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
case DATA_MASTERS_TERRACE_DOOR_1: return MastersTerraceDoor[0];
case DATA_MASTERS_TERRACE_DOOR_2: return MastersTerraceDoor[1];
}
+
return 0;
}
+
void Load(const char* chrIn)
{
if (!chrIn)
@@ -247,21 +279,25 @@ struct TRINITY_DLL_DECL instance_karazhan : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(chrIn);
std::istringstream loadStream(chrIn);
+
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]
>> m_auiEncounter[4] >> m_auiEncounter[5] >> m_auiEncounter[6] >> m_auiEncounter[7]
>> m_auiEncounter[8] >> m_auiEncounter[9] >> m_auiEncounter[10] >> m_auiEncounter[11];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead.
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_karazhan(Map* pMap)
{
return new instance_karazhan(pMap);
}
+
void AddSC_instance_karazhan()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/karazhan.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/karazhan.cpp
index a315ca3287c..f392c50ca13 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/karazhan.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/karazhan/karazhan.cpp
@@ -13,39 +13,49 @@
* along 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), Support for Quest 9645.
SDCategory: Karazhan
EndScriptData */
+
/* ContentData
npc_barnes
npc_berthold
npc_image_of_medivh
EndContentData */
+
#include "precompiled.h"
#include "def_karazhan.h"
#include "escort_ai.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."
+
#define OZ_GM_GOSSIP1 "[GM] Change event to EVENT_OZ"
#define OZ_GM_GOSSIP2 "[GM] Change event to EVENT_HOOD"
#define OZ_GM_GOSSIP3 "[GM] Change event to EVENT_RAJ"
+
struct Dialogue
{
int32 textid;
uint32 timer;
};
+
static Dialogue OzDialogue[]=
{
{-1532103, 6000},
@@ -53,6 +63,7 @@ static Dialogue OzDialogue[]=
{-1532105, 9000},
{-1532106, 15000}
};
+
static Dialogue HoodDialogue[]=
{
{-1532107, 6000},
@@ -60,6 +71,7 @@ static Dialogue HoodDialogue[]=
{-1532109, 14000},
{-1532110, 15000}
};
+
static Dialogue RAJDialogue[]=
{
{-1532111, 5000},
@@ -67,6 +79,7 @@ static Dialogue RAJDialogue[]=
{-1532113, 14000},
{-1532114, 14000}
};
+
// Entries and spawn locations for creatures in Oz event
float Spawns[6][2]=
{
@@ -77,12 +90,16 @@ float Spawns[6][2]=
{17603, -10892}, // Grandmother
{17534, -10900}, // Julianne
};
+
#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 TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
{
npc_barnesAI(Creature* c) : npc_escortAI(c)
@@ -91,39 +108,54 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
m_uiEventId = 0;
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint64 m_uiSpotlightGUID;
+
uint32 TalkCount;
uint32 TalkTimer;
uint32 WipeTimer;
uint32 m_uiEventId;
+
bool PerformanceReady;
bool RaidWiped;
+
void Reset()
{
m_uiSpotlightGUID = 0;
+
TalkCount = 0;
TalkTimer = 2000;
WipeTimer = 5000;
+
PerformanceReady = false;
+
if (pInstance)
m_uiEventId = pInstance->GetData(DATA_OPERA_PERFORMANCE);
}
+
void StartEvent()
{
if (!pInstance)
return;
+
pInstance->SetData(TYPE_OPERA, IN_PROGRESS);
+
//resets count for this event, in case earlier failed
if (m_uiEventId == EVENT_OZ)
pInstance->SetData(DATA_OPERA_OZ_DEATHCOUNT, IN_PROGRESS);
+
Start(false, false);
}
+
void EnterCombat(Unit* who) {}
+
void WaypointReached(uint32 i)
{
if (!pInstance)
return;
+
switch(i)
{
case 0:
@@ -133,6 +165,7 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
case 4:
TalkCount = 0;
SetEscortPaused(true);
+
if (Creature* pSpotlight = m_creature->SummonCreature(CREATURE_SPOTLIGHT,
m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0.0f,
TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000))
@@ -152,9 +185,11 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
break;
}
}
+
void Talk(uint32 count)
{
int32 text = 0;
+
switch(m_uiEventId)
{
case EVENT_OZ:
@@ -163,12 +198,14 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
if (OzDialogue[count].timer)
TalkTimer = OzDialogue[count].timer;
break;
+
case EVENT_HOOD:
if (HoodDialogue[count].textid)
text = HoodDialogue[count].textid;
if (HoodDialogue[count].timer)
TalkTimer = HoodDialogue[count].timer;
break;
+
case EVENT_RAJ:
if (RAJDialogue[count].textid)
text = RAJDialogue[count].textid;
@@ -176,14 +213,17 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
TalkTimer = RAJDialogue[count].timer;
break;
}
+
if (text)
DoScriptText(text, m_creature);
}
+
void PrepareEncounter()
{
debug_log("TSCR: Barnes Opera Event - Introduction complete - preparing encounter %d", m_uiEventId);
uint8 index = 0;
uint8 count = 0;
+
switch(m_uiEventId)
{
case EVENT_OZ:
@@ -199,10 +239,12 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
count = index+1;
break;
}
- for (; index < count; ++index)
+
+ for(; index < count; ++index)
{
uint32 entry = ((uint32)Spawns[index][0]);
float PosX = Spawns[index][1];
+
if (Creature* pCreature = m_creature->SummonCreature(entry, PosX, SPAWN_Y, SPAWN_Z, SPAWN_O, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, HOUR*2*IN_MILISECONDS))
{
// In case database has bad flags
@@ -210,11 +252,14 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
}
+
RaidWiped = false;
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
+
if (HasEscortState(STATE_ESCORT_PAUSED))
{
if (TalkTimer < diff)
@@ -223,15 +268,18 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
{
if (Creature* pSpotlight = Unit::GetCreature(*m_creature, m_uiSpotlightGUID))
pSpotlight->ForcedDespawn();
+
SetEscortPaused(false);
return;
}
+
Talk(TalkCount);
++TalkCount;
}
else
TalkTimer -= diff;
}
+
if (PerformanceReady)
{
if (!RaidWiped)
@@ -240,11 +288,13 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
{
Map* pMap = m_creature->GetMap();
if (!pMap->IsDungeon()) return;
+
Map::PlayerList const &PlayerList = pMap->GetPlayers();
if (PlayerList.isEmpty())
return;
+
RaidWiped = true;
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin();i != PlayerList.end(); ++i)
{
if (i->getSource()->isAlive() && !i->getSource()->isGameMaster())
{
@@ -252,22 +302,27 @@ struct TRINITY_DLL_DECL npc_barnesAI : public npc_escortAI
break;
}
}
+
if (RaidWiped)
{
RaidWiped = true;
EnterEvadeMode();
return;
}
+
WipeTimer = 15000;
}else WipeTimer -= diff;
}
+
}
}
};
+
CreatureAI* GetAI_npc_barnesAI(Creature* pCreature)
{
return new npc_barnesAI(pCreature);
}
+
bool GossipHello_npc_barnes(Player* pPlayer, Creature* pCreature)
{
if (ScriptedInstance* pInstance = pCreature->GetInstanceData())
@@ -276,28 +331,34 @@ bool GossipHello_npc_barnes(Player* pPlayer, Creature* pCreature)
if (pInstance->GetData(TYPE_MOROES) == DONE && pInstance->GetData(TYPE_OPERA) != DONE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, OZ_GOSSIP1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
if (pPlayer->isGameMaster())
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, OZ_GM_GOSSIP1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, OZ_GM_GOSSIP2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_DOT, OZ_GM_GOSSIP3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
}
+
if (npc_barnesAI* pBarnesAI = CAST_AI(npc_barnesAI,pCreature->AI()))
{
if (!pBarnesAI->RaidWiped)
pPlayer->SEND_GOSSIP_MENU(8970, pCreature->GetGUID());
else
pPlayer->SEND_GOSSIP_MENU(8975, pCreature->GetGUID());
+
return true;
}
}
}
+
pPlayer->SEND_GOSSIP_MENU(8978, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_barnes(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
npc_barnesAI* pBarnesAI = CAST_AI(npc_barnesAI, pCreature->AI());
+
switch(uiAction)
{
case GOSSIP_ACTION_INFO_DEF+1:
@@ -324,16 +385,21 @@ bool GossipSelect_npc_barnes(Player* pPlayer, Creature* pCreature, uint32 uiSend
outstring_log("TSCR: player (GUID %i) manually set Opera event to EVENT_RAJ",pPlayer->GetGUID());
break;
}
+
return true;
}
+
/*###
# npc_berthold
####*/
+
enum eBerthold
{
SPELL_TELEPORT = 39567
};
+
#define GOSSIP_ITEM_TELEPORT "Teleport me to the Guardian's Library"
+
bool GossipHello_npc_berthold(Player* pPlayer, Creature* pCreature)
{
if (ScriptedInstance* pInstance = pCreature->GetInstanceData())
@@ -342,19 +408,24 @@ bool GossipHello_npc_berthold(Player* pPlayer, Creature* pCreature)
if (pInstance->GetData(TYPE_ARAN) == DONE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_berthold(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
pPlayer->CastSpell(pPlayer, SPELL_TELEPORT, true);
+
pPlayer->CLOSE_GOSSIP_MENU();
return true;
}
+
/*###
# npc_image_of_medivh
####*/
+
#define SAY_DIALOG_MEDIVH_1 "You've got my attention, dragon. You'll find I'm not as easily scared as the villagers below."
#define SAY_DIALOG_ARCANAGOS_2 "Your dabbling in the arcane has gone too far, Medivh. You've attracted the attention of powers beyond your understanding. You must leave Karazhan at once!"
#define SAY_DIALOG_MEDIVH_3 "You dare challenge me at my own dwelling? Your arrogance is astounding, even for a dragon!"
@@ -364,29 +435,38 @@ bool GossipSelect_npc_berthold(Player* pPlayer, Creature* pCreature, uint32 uiSe
#define EMOTE_DIALOG_MEDIVH_7 "begins to cast a spell of great power, weaving his own essence into the magic."
#define SAY_DIALOG_ARCANAGOS_8 "What have you done, wizard? This cannot be! I'm burning from... within!"
#define SAY_DIALOG_MEDIVH_9 "He should not have angered me. I must go... recover my strength now..."
+
#define MOB_ARCANAGOS 17652
#define SPELL_FIRE_BALL 30967
#define SPELL_UBER_FIREBALL 30971
#define SPELL_CONFLAGRATION_BLAST 30977
#define SPELL_MANA_SHIELD 31635
+
static float MedivPos[4] = {-11161.49,-1902.24,91.48,1.94};
static float ArcanagosPos[4] = {-11169.75,-1881.48,95.39,4.83};
+
struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
{
npc_image_of_medivhAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint64 ArcanagosGUID;
+
uint32 YellTimer;
uint32 Step;
uint32 FireMedivhTimer;
uint32 FireArcanagosTimer;
+
bool EventStarted;
+
void Reset()
{
ArcanagosGUID = 0;
+
if (pInstance && pInstance->GetData64(DATA_IMAGE_OF_MEDIVH) == 0)
{
pInstance->SetData64(DATA_IMAGE_OF_MEDIVH, m_creature->GetGUID());
@@ -399,6 +479,7 @@ struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
}
}
void EnterCombat(Unit* who){}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
@@ -410,6 +491,7 @@ struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
m_creature->SetOrientation(MedivPos[3]);
}
}
+
void StartEvent()
{
Step = 1;
@@ -425,6 +507,7 @@ struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
YellTimer = 10000;
}
+
uint32 NextStep(uint32 Step)
{
Unit* arca = Unit::GetUnit((*m_creature),ArcanagosGUID);
@@ -483,6 +566,7 @@ struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
case 14:
m_creature->SetVisibility(VISIBILITY_OFF);
m_creature->ClearInCombat();
+
if (pMap->IsDungeon())
{
InstanceMap::PlayerList const &PlayerList = pMap->GetPlayers();
@@ -501,9 +585,12 @@ struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
return 5000;
default : return 9999999;
}
+
}
+
void UpdateAI(const uint32 diff)
{
+
if (YellTimer < diff)
{
if (EventStarted)
@@ -511,42 +598,51 @@ struct TRINITY_DLL_DECL npc_image_of_medivhAI : public ScriptedAI
YellTimer = NextStep(Step++);
}
}else YellTimer -= diff;
+
if (Step >= 7 && Step <= 12)
{
Unit* arca = Unit::GetUnit((*m_creature),ArcanagosGUID);
+
if (FireArcanagosTimer < diff)
{
if (arca)
arca->CastSpell(m_creature, SPELL_FIRE_BALL, false);
FireArcanagosTimer = 6000;
}else FireArcanagosTimer -= diff;
+
if (FireMedivhTimer < diff)
{
if (arca)
DoCast(arca, SPELL_FIRE_BALL);
FireMedivhTimer = 5000;
}else FireMedivhTimer -= diff;
+
}
}
};
+
CreatureAI* GetAI_npc_image_of_medivh(Creature* pCreature)
{
return new npc_image_of_medivhAI(pCreature);
}
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_berthold";
newscript->pGossipHello = &GossipHello_npc_berthold;
newscript->pGossipSelect = &GossipSelect_npc_berthold;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_image_of_medivh";
newscript->GetAI = &GetAI_npc_image_of_medivh;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/loch_modan.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/loch_modan.cpp
index c379a6ab590..80e1d3ffceb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/loch_modan.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/loch_modan.cpp
@@ -13,35 +13,46 @@
* along 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
SDCategory: Loch Modan
EndScriptData */
+
/* ContentData
npc_mountaineer_pebblebitty
EndContentData */
+
#include "precompiled.h"
+
/*######
## npc_mountaineer_pebblebitty
######*/
+
#define GOSSIP_MP "Open the gate please, i need to get to Searing Gorge"
+
#define GOSSIP_MP1 "But i need to get there, now open the gate!"
#define GOSSIP_MP2 "Ok, so what is this other way?"
#define GOSSIP_MP3 "Doesn't matter, i'm invulnerable."
#define GOSSIP_MP4 "Yes..."
#define GOSSIP_MP5 "Ok, i'll try to remember that."
#define GOSSIP_MP6 "A key? Ok!"
+
bool GossipHello_npc_mountaineer_pebblebitty(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (!pPlayer->GetQuestRewardStatus(3181) == 1)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_MP, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_mountaineer_pebblebitty(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -76,9 +87,11 @@ bool GossipSelect_npc_mountaineer_pebblebitty(Player* pPlayer, Creature* pCreatu
}
return true;
}
+
void AddSC_loch_modan()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_mountaineer_pebblebitty";
newscript->pGossipHello = &GossipHello_npc_mountaineer_pebblebitty;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp
index 721dd8f2fc5..3fc8e2dc4d1 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_felblood_kaelthas.cpp
@@ -13,15 +13,18 @@
* along 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.
SDCategory: Magisters' Terrace
EndScriptData */
+
#include "precompiled.h"
#include "def_magisters_terrace.h"
#include "WorldPacket.h"
+
#define SAY_AGGRO -1585023 //This yell should be done when the room is cleared. For now, set it as a movelineofsight yell.
#define SAY_PHOENIX -1585024
#define SAY_FLAMESTRIKE -1585025
@@ -29,19 +32,25 @@ EndScriptData */
#define SAY_TIRED -1585027
#define SAY_RECAST_GRAVITY -1585028
#define SAY_DEATH -1585029
+
/*** 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 44197 // A spell Phoenix uses to damage everything around
#define SPELL_REBIRTH_DMG 44196 // DMG if a Phoenix rebirth happen
+
#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
@@ -50,10 +59,12 @@ EndScriptData */
#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
+
/** Locations **/
float KaelLocations[3][2]=
{
@@ -61,7 +72,9 @@ float KaelLocations[3][2]=
{140.823883, 195.403046},
{156.574188, 195.650482},
};
+
#define LOCATION_Z -16.727455
+
struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
{
boss_felblood_kaelthasAI(Creature* c) : ScriptedAI(c)
@@ -69,13 +82,17 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
pInstance = c->GetInstanceData();
Heroic = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
+
uint32 FireballTimer;
uint32 PhoenixTimer;
uint32 FlameStrikeTimer;
uint32 CombatPulseTimer;
+
//Heroic only
uint32 PyroblastTimer;
+
uint32 GravityLapseTimer;
uint32 GravityLapsePhase;
// 0 = No Gravity Lapse
@@ -83,13 +100,16 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
// 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;
bool HasTaunted;
+
uint8 Phase;
// 0 = Not started
// 1 = Fireball; Summon Phoenix; Flamestrike
// 2 = Gravity Lapses
+
void Reset()
{
// TODO: Timers
@@ -97,12 +117,17 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
PhoenixTimer = 10000;
FlameStrikeTimer = 25000;
CombatPulseTimer = 0;
+
PyroblastTimer = 60000;
+
GravityLapseTimer = 0;
GravityLapsePhase = 0;
+
FirstGravityLapse = true;
HasTaunted = false;
+
Phase = 0;
+
if (pInstance)
{
pInstance->SetData(DATA_KAELTHAS_EVENT, NOT_STARTED);
@@ -111,26 +136,33 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
// Small door opened after event are expected to be closed by default
}
}
+
void JustDied(Unit *killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (!pInstance)
return;
+
pInstance->HandleGameObject(pInstance->GetData64(DATA_KAEL_DOOR), true);
// Open the encounter door
}
+
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 EnterCombat(Unit *who)
{
if (!pInstance)
return;
+
pInstance->HandleGameObject(pInstance->GetData64(DATA_KAEL_DOOR), false);
//Close the encounter door, open it in JustDied/Reset
}
+
void MoveInLineOfSight(Unit *who)
{
if (!HasTaunted && m_creature->IsWithinDistInMap(who, 40.0))
@@ -138,15 +170,18 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
DoScriptText(SAY_AGGRO, m_creature);
HasTaunted = true;
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void SetThreatList(Creature* SummonedUnit)
{
if (!SummonedUnit)
return;
+
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator i = m_threatlist.begin();
- for (i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
+ for(i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid());
if (pUnit && pUnit->isAlive())
@@ -156,6 +191,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
}
}
}
+
void TeleportPlayersToSelf()
{
float x = KaelLocations[0][0];
@@ -171,6 +207,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
}
DoCast(m_creature, SPELL_TELEPORT_CENTER, true);
}
+
void CastGravityLapseKnockUp()
{
std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
@@ -182,6 +219,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
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<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
@@ -201,6 +239,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
}
}
}
+
void RemoveGravityLapse()
{
std::list<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
@@ -211,6 +250,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
{
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());
@@ -219,11 +259,13 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
switch(Phase)
{
case 0:
@@ -240,17 +282,22 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
PyroblastTimer = 60000;
}else PyroblastTimer -= diff;
}
+
if (FireballTimer < diff)
{
DoCast(m_creature->getVictim(), Heroic ? SPELL_FIREBALL_HEROIC : SPELL_FIREBALL_NORMAL);
FireballTimer = 2000 + rand()%4000;
}else FireballTimer -= diff;
+
if (PhoenixTimer < diff)
{
+
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1);
+
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)
{
@@ -258,9 +305,12 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
SetThreatList(Phoenix);
Phoenix->AI()->AttackStart(target);
}
+
DoScriptText(SAY_PHOENIX, m_creature);
+
PhoenixTimer = 60000;
}else PhoenixTimer -= diff;
+
if (FlameStrikeTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -272,6 +322,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
}
FlameStrikeTimer = 15000 + rand()%10000;
}else FlameStrikeTimer -= diff;
+
// Below 50%
if (m_creature->GetMaxHealth() * 0.5 > m_creature->GetHealth())
{
@@ -283,9 +334,11 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
GravityLapsePhase = 0;
Phase = 1;
}
+
DoMeleeAttackIfReady();
}
break;
+
case 1:
{
if (GravityLapseTimer < diff)
@@ -297,6 +350,7 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
{
DoScriptText(SAY_GRAVITY_LAPSE, m_creature);
FirstGravityLapse = false;
+
if (pInstance)
{
pInstance->HandleGameObject(pInstance->GetData64(DATA_KAEL_STATUE_LEFT), true);
@@ -306,29 +360,35 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
{
DoScriptText(SAY_RECAST_GRAVITY, m_creature);
}
+
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)
+
+ for(uint8 i = 0; i < 3; ++i)
{
Unit* target = NULL;
target = SelectUnit(SELECT_TARGET_RANDOM,0);
+
Creature* Orb = DoSpawnCreature(CREATURE_ARCANE_SPHERE, 5, 5, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
if (Orb && target)
{
@@ -336,9 +396,12 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
Orb->AddThreat(target, 1.0f);
Orb->AI()->AttackStart(target);
}
+
}
+
DoCast(m_creature, SPELL_GRAVITY_LAPSE_CHANNEL);
break;
+
case 4:
m_creature->InterruptNonMeleeSpells(false);
DoScriptText(SAY_TIRED, m_creature);
@@ -354,21 +417,27 @@ struct TRINITY_DLL_DECL boss_felblood_kaelthasAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_felkael_flamestrikeAI : public ScriptedAI
{
mob_felkael_flamestrikeAI(Creature *c) : ScriptedAI(c)
{
Heroic = c->GetMap()->IsHeroic();
}
+
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 EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
void UpdateAI(const uint32 diff)
@@ -380,17 +449,20 @@ struct TRINITY_DLL_DECL mob_felkael_flamestrikeAI : public ScriptedAI
} else FlameStrikeTimer -= diff;
}
};
+
struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
{
mob_felkael_phoenixAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
uint32 BurnTimer;
uint32 Death_Timer;
bool Rebirth;
bool FakeDeath;
+
void Reset()
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE + UNIT_FLAG_NON_ATTACKABLE);
@@ -401,16 +473,20 @@ struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
Rebirth = false;
FakeDeath = false;
}
+
void EnterCombat(Unit* who) {}
+
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 all phases of Kael'Thas
if (pInstance && pInstance->GetData(DATA_KAELTHAS_EVENT) == 0)
@@ -418,6 +494,7 @@ struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
//prevent death
damage = 0;
FakeDeath = true;
+
m_creature->InterruptNonMeleeSpells(false);
m_creature->SetHealth(0);
m_creature->StopMoving();
@@ -431,14 +508,19 @@ struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
+
}
+
}
+
void JustDied(Unit* slayer)
{
m_creature->SummonCreature(CREATURE_PHOENIX_EGG, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000);
}
+
void UpdateAI(const uint32 diff)
{
+
//If we are fake death, we cast revbirth and after that we kill the phoenix to spawn the egg.
if (FakeDeath)
{
@@ -447,8 +529,10 @@ struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
DoCast(m_creature, SPELL_REBIRTH_DMG);
Rebirth = true;
}
+
if (Rebirth)
{
+
if (Death_Timer < diff)
{
m_creature->SummonCreature(CREATURE_PHOENIX_EGG, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000);
@@ -458,9 +542,12 @@ struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
}else Death_Timer -= diff;
}
+
}
+
if (!UpdateVictim())
return;
+
if (BurnTimer < diff)
{
//spell Burn should possible do this, but it doesn't, so do this for now.
@@ -468,19 +555,26 @@ struct TRINITY_DLL_DECL mob_felkael_phoenixAI : public ScriptedAI
m_creature->DealDamage(m_creature, dmg, 0, DOT, SPELL_SCHOOL_MASK_FIRE, NULL, false);
BurnTimer += 2000;
} BurnTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_felkael_phoenix_eggAI : public ScriptedAI
{
mob_felkael_phoenix_eggAI(Creature *c) : ScriptedAI(c) {}
+
uint32 HatchTimer;
+
void Reset()
{
HatchTimer = 10000;
+
}
+
void EnterCombat(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (HatchTimer < diff)
@@ -488,31 +582,40 @@ struct TRINITY_DLL_DECL mob_felkael_phoenix_eggAI : public ScriptedAI
m_creature->SummonCreature(CREATURE_PHOENIX, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 60000);
m_creature->Kill(m_creature);
} else HatchTimer -= diff;
+
}
};
+
struct TRINITY_DLL_DECL mob_arcane_sphereAI : public ScriptedAI
{
mob_arcane_sphereAI(Creature *c) : ScriptedAI(c) {Reset();}
+
uint32 DespawnTimer;
uint32 ChangeTargetTimer;
+
void Reset()
{
DespawnTimer = 30000;
ChangeTargetTimer = urand(6000,12000);
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
m_creature->setFaction(14);
DoCast(m_creature, SPELL_ARCANE_SPHERE_PASSIVE, true);
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (DespawnTimer < diff)
m_creature->Kill(m_creature);
else DespawnTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (ChangeTargetTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
@@ -521,49 +624,61 @@ struct TRINITY_DLL_DECL mob_arcane_sphereAI : public ScriptedAI
m_creature->TauntApply(target);
AttackStart(target);
}
+
ChangeTargetTimer = urand(5000,15000);
} 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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_arcane_sphere";
newscript->GetAI = &GetAI_mob_arcane_sphere;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_felkael_phoenix";
newscript->GetAI = &GetAI_mob_felkael_phoenix;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_felkael_phoenix_egg";
newscript->GetAI = &GetAI_mob_felkael_phoenix_egg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_felkael_flamestrike";
newscript->GetAI = &GetAI_mob_felkael_flamestrike;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp
index ebf73da0f63..546f42cae19 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_priestess_delrissa.cpp
@@ -13,18 +13,22 @@
* along 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: 65
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"
+
struct Speech
{
int32 id;
};
+
static Speech LackeyDeath[]=
{
{-1585013},
@@ -32,6 +36,7 @@ static Speech LackeyDeath[]=
{-1585015},
{-1585016},
};
+
static Speech PlayerDeath[]=
{
{-1585017},
@@ -40,10 +45,12 @@ static Speech PlayerDeath[]=
{-1585020},
{-1585021},
};
+
enum eEnums
{
SAY_AGGRO = -1585012,
SAY_DEATH = -1585022,
+
SPELL_DISPEL_MAGIC = 27609,
SPELL_FLASH_HEAL = 17843,
SPELL_SW_PAIN_NORMAL = 14032,
@@ -51,10 +58,13 @@ enum eEnums
SPELL_SHIELD = 44291,
SPELL_RENEW_NORMAL = 44174,
SPELL_RENEW_HEROIC = 46192,
+
MAX_ACTIVE_LACKEY = 4
};
+
const float fOrientation = 4.98;
const float fZLocation = -19.921;
+
float LackeyLocations[4][2]=
{
{123.77, 17.6007},
@@ -62,6 +72,7 @@ float LackeyLocations[4][2]=
{121.563, 15.6213},
{129.988, 17.2355},
};
+
const uint32 m_auiAddEntries[] =
{
24557, //Kagani Nightstrike
@@ -73,6 +84,7 @@ const uint32 m_auiAddEntries[] =
24553, //Apoko
24556, //Zelfan
};
+
struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
{
boss_priestess_delrissaAI(Creature* c) : ScriptedAI(c)
@@ -82,38 +94,48 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
memset(&m_auiLackeyGUID, 0, sizeof(m_auiLackeyGUID));
LackeyEntryList.clear();
}
+
ScriptedInstance* pInstance;
bool Heroic;
+
std::vector<uint32> LackeyEntryList;
uint64 m_auiLackeyGUID[MAX_ACTIVE_LACKEY];
+
uint8 PlayersKilled;
+
uint32 HealTimer;
uint32 RenewTimer;
uint32 ShieldTimer;
uint32 SWPainTimer;
uint32 DispelTimer;
uint32 ResetTimer;
+
void Reset()
{
PlayersKilled = 0;
+
HealTimer = 15000;
RenewTimer = 10000;
ShieldTimer = 2000;
SWPainTimer = 5000;
DispelTimer = 7500;
ResetTimer = 5000;
+
InitializeLackeys();
}
+
//this mean she at some point evaded
void JustReachedHome()
{
if (pInstance)
pInstance->SetData(DATA_DELRISSA_EVENT, FAIL);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
- for (uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
+
+ for(uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
{
if (Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUID[i]))
{
@@ -124,39 +146,48 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_DELRISSA_EVENT, IN_PROGRESS);
}
+
void InitializeLackeys()
{
//can be called if Creature are dead, so avoid
if (!m_creature->isAlive())
return;
+
uint8 j = 0;
+
//it's empty, so first time
if (LackeyEntryList.empty())
{
//pre-allocate size for speed
LackeyEntryList.resize((sizeof(m_auiAddEntries) / sizeof(uint32)));
+
//fill vector array with entries from Creature array
- for (uint8 i = 0; i < LackeyEntryList.size(); ++i)
+ for(uint8 i = 0; i < LackeyEntryList.size(); ++i)
LackeyEntryList[i] = m_auiAddEntries[i];
+
//remove random entries
while(LackeyEntryList.size() > MAX_ACTIVE_LACKEY)
LackeyEntryList.erase(LackeyEntryList.begin() + rand()%LackeyEntryList.size());
+
//summon all the remaining in vector
- for (std::vector<uint32>::iterator itr = LackeyEntryList.begin(); itr != LackeyEntryList.end(); ++itr)
+ for(std::vector<uint32>::iterator itr = LackeyEntryList.begin(); itr != LackeyEntryList.end(); ++itr)
{
if (Creature* pAdd = m_creature->SummonCreature((*itr), LackeyLocations[j][0], LackeyLocations[j][1], fZLocation, fOrientation, TEMPSUMMON_CORPSE_DESPAWN, 0))
m_auiLackeyGUID[j] = pAdd->GetGUID();
+
++j;
}
}
else
{
- for (std::vector<uint32>::iterator itr = LackeyEntryList.begin(); itr != LackeyEntryList.end(); ++itr)
+ for(std::vector<uint32>::iterator itr = LackeyEntryList.begin(); itr != LackeyEntryList.end(); ++itr)
{
Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUID[j]);
+
//object already removed, not exist
if (!pAdd)
{
@@ -167,19 +198,25 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit* victim)
{
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
+
DoScriptText(PlayerDeath[PlayersKilled].id, m_creature);
+
if (PlayersKilled < 4)
++PlayersKilled;
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (!pInstance)
return;
+
if (pInstance->GetData(DATA_DELRISSA_DEATH_COUNT) == MAX_ACTIVE_LACKEY)
pInstance->SetData(DATA_DELRISSA_EVENT, DONE);
else
@@ -188,10 +225,12 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (ResetTimer < diff)
{
float x, y, z, o;
@@ -203,11 +242,12 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
}
ResetTimer = 5000;
}else ResetTimer -= diff;
+
if (HealTimer < diff)
{
uint32 health = m_creature->GetHealth();
Unit* target = m_creature;
- for (uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
+ for(uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
{
if (Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUID[i]))
{
@@ -215,12 +255,15 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
target = pAdd;
}
}
+
DoCast(target, SPELL_FLASH_HEAL);
HealTimer = 15000;
}else HealTimer -= diff;
+
if (RenewTimer < diff)
{
Unit* target = m_creature;
+
if (rand()%2 == 1)
{
if (Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUID[rand()%MAX_ACTIVE_LACKEY]))
@@ -232,9 +275,11 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
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)
{
if (Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUID[rand()%MAX_ACTIVE_LACKEY]))
@@ -243,18 +288,22 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
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
@@ -266,27 +315,35 @@ struct TRINITY_DLL_DECL boss_priestess_delrissaAI : public ScriptedAI
}
}
}
+
if (target)
DoCast(target, SPELL_DISPEL_MAGIC);
+
DispelTimer = 12000;
}else DispelTimer -= diff;
+
if (SWPainTimer < diff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, Heroic ? SPELL_SW_PAIN_HEROIC : SPELL_SW_PAIN_NORMAL);
+
SWPainTimer = 10000;
}else SWPainTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_priestess_delrissa(Creature* pCreature)
{
return new boss_priestess_delrissaAI(pCreature);
}
+
enum eHealingPotion
{
SPELL_HEALING_POTION = 15503
};
+
//all 8 possible lackey use this common
struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
{
@@ -296,18 +353,24 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
memset(&m_auiLackeyGUIDs, 0, sizeof(m_auiLackeyGUIDs));
AcquireGUIDs();
}
+
ScriptedInstance* pInstance;
+
uint64 m_auiLackeyGUIDs[MAX_ACTIVE_LACKEY];
uint32 ResetThreatTimer;
+
bool UsedPotion;
+
void Reset()
{
UsedPotion = false;
+
// These guys does not follow normal threat system rules
// For later development, some alternative threat system should be made
// We do not know what this system is based upon, but one theory is class (healers=high threat, dps=medium, etc)
// We reset their threat frequently as an alternative until such a system exist
ResetThreatTimer = 5000 + rand()%15000;
+
// in case she is not alive and Reset was for some reason called, respawn her (most likely party wipe after killing her)
if (Creature* pDelrissa = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_DELRISSA) : 0))
{
@@ -315,13 +378,15 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
pDelrissa->Respawn();
}
}
+
void EnterCombat(Unit* pWho)
{
if (!pWho)
return;
+
if (pInstance)
{
- for (uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
+ for(uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
{
if (Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUIDs[i]))
{
@@ -332,6 +397,7 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
}
}
}
+
if (Creature* pDelrissa = Unit::GetCreature(*m_creature, pInstance->GetData64(DATA_DELRISSA)))
{
if (pDelrissa->isAlive() && !pDelrissa->getVictim())
@@ -342,19 +408,26 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* killer)
{
if (!pInstance)
return;
+
Creature* pDelrissa = Unit::GetCreature(*m_creature, pInstance->GetData64(DATA_DELRISSA));
uint32 uiLackeyDeathCount = pInstance->GetData(DATA_DELRISSA_DEATH_COUNT);
+
if (!pDelrissa)
return;
+
//should delrissa really yell if dead?
DoScriptText(LackeyDeath[uiLackeyDeathCount].id, pDelrissa);
+
pInstance->SetData(DATA_DELRISSA_DEATH_COUNT, SPECIAL);
+
//increase local var, since we now may have four dead
++uiLackeyDeathCount;
+
if (uiLackeyDeathCount == MAX_ACTIVE_LACKEY)
{
//time to make her lootable and complete event if she died before lackeys
@@ -362,27 +435,33 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
{
if (!pDelrissa->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE))
pDelrissa->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+
pInstance->SetData(DATA_DELRISSA_EVENT, DONE);
}
}
}
+
void KilledUnit(Unit* victim)
{
if (!pInstance)
return;
+
if (Creature* Delrissa = (Unit::GetCreature(*m_creature, pInstance->GetData64(DATA_DELRISSA))))
Delrissa->AI()->KilledUnit(victim);
}
+
void AcquireGUIDs()
{
if (!pInstance)
return;
+
if (Creature* Delrissa = (Unit::GetCreature(*m_creature, pInstance->GetData64(DATA_DELRISSA))))
{
- for (uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
+ for(uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
m_auiLackeyGUIDs[i] = CAST_AI(boss_priestess_delrissaAI, Delrissa->AI())->m_auiLackeyGUID[i];
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UsedPotion && ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25))
@@ -390,6 +469,7 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
DoCast(m_creature, SPELL_HEALING_POTION);
UsedPotion = true;
}
+
if (ResetThreatTimer < diff)
{
DoResetThreat();
@@ -397,6 +477,7 @@ struct TRINITY_DLL_DECL boss_priestess_lackey_commonAI : public ScriptedAI
}else ResetThreatTimer -= diff;
}
};
+
enum eRogueSpells
{
SPELL_KIDNEY_SHOT = 27615,
@@ -406,16 +487,19 @@ enum eRogueSpells
SPELL_BACKSTAB = 15657,
SPELL_EVISCERATE = 27611
};
+
struct TRINITY_DLL_DECL boss_kagani_nightstrikeAI : public boss_priestess_lackey_commonAI
{
//Rogue
boss_kagani_nightstrikeAI(Creature *c) : boss_priestess_lackey_commonAI(c) {}
+
uint32 Gouge_Timer;
uint32 Kick_Timer;
uint32 Vanish_Timer;
uint32 Eviscerate_Timer;
uint32 Wait_Timer;
bool InVanish;
+
void Reset()
{
Gouge_Timer = 5500;
@@ -425,24 +509,33 @@ struct TRINITY_DLL_DECL boss_kagani_nightstrikeAI : public boss_priestess_lackey
Wait_Timer = 5000;
InVanish = false;
m_creature->SetVisibility(VISIBILITY_ON);
+
boss_priestess_lackey_commonAI::Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::UpdateAI(diff);
+
if (Vanish_Timer < diff)
{
DoCast(m_creature, SPELL_VANISH);
+
Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0);
+
DoResetThreat();
+
if (pUnit)
m_creature->AddThreat(pUnit, 1000.0f);
+
InVanish = true;
Vanish_Timer = 30000;
Wait_Timer = 10000;
}else Vanish_Timer -= diff;
+
if (InVanish)
{
if (Wait_Timer < diff)
@@ -453,29 +546,35 @@ struct TRINITY_DLL_DECL boss_kagani_nightstrikeAI : public boss_priestess_lackey
InVanish = false;
}else Wait_Timer -= diff;
}
+
if (Gouge_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_GOUGE);
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();
}
};
+
CreatureAI* GetAI_boss_kagani_nightstrike(Creature* pCreature)
{
return new boss_kagani_nightstrikeAI(pCreature);
}
+
enum eWarlockSpells
{
SPELL_IMMOLATE = 44267,
@@ -486,15 +585,18 @@ enum eWarlockSpells
SPELL_IMP_FIREBALL = 44164,
SPELL_SUMMON_IMP = 44163
};
+
struct TRINITY_DLL_DECL boss_ellris_duskhallowAI : public boss_priestess_lackey_commonAI
{
//Warlock
boss_ellris_duskhallowAI(Creature *c) : boss_priestess_lackey_commonAI(c) {}
+
uint32 Immolate_Timer;
uint32 Shadow_Bolt_Timer;
uint32 Seed_of_Corruption_Timer;
uint32 Curse_of_Agony_Timer;
uint32 Fear_Timer;
+
void Reset()
{
Immolate_Timer = 6000;
@@ -502,91 +604,117 @@ struct TRINITY_DLL_DECL boss_ellris_duskhallowAI : public boss_priestess_lackey_
Seed_of_Corruption_Timer = 2000;
Curse_of_Agony_Timer = 1000;
Fear_Timer = 10000;
+
boss_priestess_lackey_commonAI::Reset();
}
+
void Aggro(Unit* pWho)
{
DoCast(m_creature,SPELL_SUMMON_IMP);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::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)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, SPELL_SEED_OF_CORRUPTION);
+
Seed_of_Corruption_Timer = 10000;
}else Seed_of_Corruption_Timer -= diff;
+
if (Curse_of_Agony_Timer < diff)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, SPELL_CURSE_OF_AGONY);
+
Curse_of_Agony_Timer = 13000;
}else Curse_of_Agony_Timer -= diff;
+
if (Fear_Timer < diff)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, SPELL_FEAR);
+
Fear_Timer = 10000;
}else Fear_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_ellris_duskhallow(Creature* pCreature)
{
return new boss_ellris_duskhallowAI(pCreature);
}
+
enum eKickDown
{
SPELL_KNOCKDOWN = 11428,
SPELL_SNAP_KICK = 46182
};
+
struct TRINITY_DLL_DECL boss_eramas_brightblazeAI : public boss_priestess_lackey_commonAI
{
//Monk
boss_eramas_brightblazeAI(Creature *c) : boss_priestess_lackey_commonAI(c) {}
+
uint32 Knockdown_Timer;
uint32 Snap_Kick_Timer;
+
void Reset()
{
Knockdown_Timer = 6000;
Snap_Kick_Timer = 4500;
+
boss_priestess_lackey_commonAI::Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::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();
}
};
+
CreatureAI* GetAI_eramas_brightblaze(Creature* pCreature)
{
return new boss_eramas_brightblazeAI(pCreature);
}
+
enum eMageSpells
{
SPELL_POLYMORPH = 13323,
@@ -597,11 +725,14 @@ enum eMageSpells
SPELL_FROSTBOLT = 15043,
SPELL_BLINK = 14514
};
+
struct TRINITY_DLL_DECL boss_yazzaiAI : public boss_priestess_lackey_commonAI
{
//Mage
boss_yazzaiAI(Creature *c) : boss_priestess_lackey_commonAI(c) {}
+
bool HasIceBlocked;
+
uint32 Polymorph_Timer;
uint32 Ice_Block_Timer;
uint32 Wait_Timer;
@@ -610,9 +741,11 @@ struct TRINITY_DLL_DECL boss_yazzaiAI : public boss_priestess_lackey_commonAI
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;
@@ -621,13 +754,17 @@ struct TRINITY_DLL_DECL boss_yazzaiAI : public boss_priestess_lackey_commonAI
Cone_of_Cold_Timer = 10000;
Frostbolt_Timer = 3000;
Blink_Timer = 8000;
+
boss_priestess_lackey_commonAI::Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::UpdateAI(diff);
+
if (Polymorph_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -636,37 +773,44 @@ struct TRINITY_DLL_DECL boss_yazzaiAI : public boss_priestess_lackey_commonAI
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)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, 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<HostilReference*>& t_list = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
if (Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()))
{
@@ -678,18 +822,23 @@ struct TRINITY_DLL_DECL boss_yazzaiAI : public boss_priestess_lackey_commonAI
}
}
}
+
//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();
}
};
+
CreatureAI* GetAI_yazzai(Creature* pCreature)
{
return new boss_yazzaiAI(pCreature);
}
+
enum eWarriorSpells
{
SPELL_INTERCEPT_STUN = 27577,
@@ -700,16 +849,19 @@ enum eWarriorSpells
SPELL_BATTLE_SHOUT = 27578,
SPELL_MORTAL_STRIKE = 44268
};
+
struct TRINITY_DLL_DECL boss_warlord_salarisAI : public boss_priestess_lackey_commonAI
{
//Warrior
boss_warlord_salarisAI(Creature *c) : boss_priestess_lackey_commonAI(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;
@@ -718,22 +870,27 @@ struct TRINITY_DLL_DECL boss_warlord_salarisAI : public boss_priestess_lackey_co
Frightening_Shout_Timer = 18000;
Hamstring_Timer = 4500;
Mortal_Strike_Timer = 8000;
+
boss_priestess_lackey_commonAI::Reset();
}
+
void EnterCombat(Unit* who)
{
DoCast(m_creature, SPELL_BATTLE_SHOUT);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::UpdateAI(diff);
+
if (Intercept_Stun_Timer < diff)
{
bool InMeleeRange = false;
std::list<HostilReference*>& t_list = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
if (Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()))
{
@@ -745,46 +902,56 @@ struct TRINITY_DLL_DECL boss_warlord_salarisAI : public boss_priestess_lackey_co
}
}
}
+
//if nobody is in melee range than try to use Intercept
if (!InMeleeRange)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, 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();
}
};
+
CreatureAI* GetAI_warlord_salaris(Creature* pCreature)
{
return new boss_warlord_salarisAI(pCreature);
}
+
enum eHunterSpells
{
SPELL_AIMED_SHOT = 44271,
@@ -793,19 +960,24 @@ enum eHunterSpells
SPELL_MULTI_SHOT = 31942,
SPELL_WING_CLIP = 44286,
SPELL_FREEZING_TRAP = 44136,
+
NPC_SLIVER = 24552
};
+
struct TRINITY_DLL_DECL boss_garaxxasAI : public boss_priestess_lackey_commonAI
{
//Hunter
boss_garaxxasAI(Creature *c) : boss_priestess_lackey_commonAI(c) { m_uiPetGUID = 0; }
+
uint64 m_uiPetGUID;
+
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()
{
Aimed_Shot_Timer = 6000;
@@ -814,20 +986,26 @@ struct TRINITY_DLL_DECL boss_garaxxasAI : public boss_priestess_lackey_commonAI
Multi_Shot_Timer = 10000;
Wing_Clip_Timer = 4000;
Freezing_Trap_Timer = 15000;
+
Unit* pPet = Unit::GetUnit(*m_creature,m_uiPetGUID);
if (!pPet)
m_creature->SummonCreature(NPC_SLIVER, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
boss_priestess_lackey_commonAI::Reset();
}
+
void JustSummoned(Creature* pSummoned)
{
m_uiPetGUID = pSummoned->GetGUID();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::UpdateAI(diff);
+
if (m_creature->IsWithinDistInMap(m_creature->getVictim(), ATTACK_DISTANCE))
{
if (Wing_Clip_Timer < diff)
@@ -835,10 +1013,12 @@ struct TRINITY_DLL_DECL boss_garaxxasAI : public boss_priestess_lackey_commonAI
DoCast(m_creature->getVictim(), SPELL_WING_CLIP);
Wing_Clip_Timer = 4000;
}else Wing_Clip_Timer -= diff;
+
if (Freezing_Trap_Timer < diff)
{
//attempt find go summoned from spell (casted by m_creature)
GameObject* pGo = m_creature->GetGameObject(SPELL_FREEZING_TRAP);
+
//if we have a pGo, we need to wait (only one trap at a time)
if (pGo)
Freezing_Trap_Timer = 2500;
@@ -849,6 +1029,7 @@ struct TRINITY_DLL_DECL boss_garaxxasAI : public boss_priestess_lackey_commonAI
Freezing_Trap_Timer = 15000;
}
}else Freezing_Trap_Timer -= diff;
+
DoMeleeAttackIfReady();
}
else
@@ -858,16 +1039,19 @@ struct TRINITY_DLL_DECL boss_garaxxasAI : public boss_priestess_lackey_commonAI
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);
@@ -876,10 +1060,12 @@ struct TRINITY_DLL_DECL boss_garaxxasAI : public boss_priestess_lackey_commonAI
}
}
};
+
CreatureAI* GetAI_garaxxas(Creature* pCreature)
{
return new boss_garaxxasAI(pCreature);
}
+
enum Spells
{
SPELL_WINDFURY_TOTEM = 27621,
@@ -890,16 +1076,19 @@ enum Spells
SPELL_FIRE_NOVA_TOTEM = 44257,
SPELL_EARTHBIND_TOTEM = 15786
};
+
struct TRINITY_DLL_DECL boss_apokoAI : public boss_priestess_lackey_commonAI
{
//Shaman
boss_apokoAI(Creature *c) : boss_priestess_lackey_commonAI(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;
@@ -908,35 +1097,44 @@ struct TRINITY_DLL_DECL boss_apokoAI : public boss_priestess_lackey_commonAI
Purge_Timer = 8000;
Healing_Wave_Timer = 5000;
Frost_Shock_Timer = 7000;
+
boss_priestess_lackey_commonAI::Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::UpdateAI(diff);
+
if (Totem_Timer < diff)
{
DoCast(m_creature, RAND(SPELL_WINDFURY_TOTEM,SPELL_FIRE_NOVA_TOTEM,SPELL_EARTHBIND_TOTEM));
++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)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, 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<Add*>::iterator itr = Group.begin() + rand()%Group.size();
@@ -951,13 +1149,16 @@ struct TRINITY_DLL_DECL boss_apokoAI : public boss_priestess_lackey_commonAI
// }
// }
} else Healing_Wave_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_apoko(Creature* pCreature)
{
return new boss_apokoAI(pCreature);
}
+
enum eEngineerSpells
{
SPELL_GOBLIN_DRAGON_GUN = 44272,
@@ -967,15 +1168,18 @@ enum eEngineerSpells
SPELL_FEL_IRON_BOMB = 46024,
SPELL_SHEEP_EXPLOSION = 44279
};
+
struct TRINITY_DLL_DECL boss_zelfanAI : public boss_priestess_lackey_commonAI
{
//Engineer
boss_zelfanAI(Creature *c) : boss_priestess_lackey_commonAI(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;
@@ -983,31 +1187,38 @@ struct TRINITY_DLL_DECL boss_zelfanAI : public boss_priestess_lackey_commonAI
Recombobulate_Timer = 4000;
High_Explosive_Sheep_Timer = 10000;
Fel_Iron_Bomb_Timer = 15000;
+
boss_priestess_lackey_commonAI::Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
boss_priestess_lackey_commonAI::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 < MAX_ACTIVE_LACKEY; ++i)
+ for(uint8 i = 0; i < MAX_ACTIVE_LACKEY; ++i)
{
if (Unit* pAdd = Unit::GetUnit(*m_creature, m_auiLackeyGUIDs[i]))
{
@@ -1020,18 +1231,22 @@ struct TRINITY_DLL_DECL boss_zelfanAI : public boss_priestess_lackey_commonAI
}
Recombobulate_Timer = 2000;
}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();
}
};
+
CreatureAI* GetAI_zelfan(Creature* pCreature)
{
return new boss_zelfanAI(pCreature);
}
+
//struct TRINITY_DLL_DECL mob_high_explosive_sheepAI : public ScriptedAI
//{
// mob_high_explosive_sheepAI(Creature *c) : ScriptedAI(c) {}
@@ -1056,49 +1271,61 @@ CreatureAI* GetAI_zelfan(Creature* pCreature)
// Explosion_Timer -= diff;
// }
//};
+
//CreatureAI* GetAI_mob_high_explosive_sheep(Creature* pCreature)
//{
// return new mob_high_explosive_sheepAI (pCreature);
//};
+
void AddSC_boss_priestess_delrissa()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_priestess_delrissa";
newscript->GetAI = &GetAI_boss_priestess_delrissa;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_kagani_nightstrike";
newscript->GetAI = &GetAI_boss_kagani_nightstrike;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_ellris_duskhallow";
newscript->GetAI = &GetAI_ellris_duskhallow;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_eramas_brightblaze";
newscript->GetAI = &GetAI_eramas_brightblaze;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_yazzai";
newscript->GetAI = &GetAI_yazzai;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_warlord_salaris";
newscript->GetAI = &GetAI_warlord_salaris;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_garaxxas";
newscript->GetAI = &GetAI_garaxxas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_apoko";
newscript->GetAI = &GetAI_apoko;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_zelfan";
newscript->GetAI = &GetAI_zelfan;
newscript->RegisterSelf();
+
/*newscript = new Script;
newscript->Name = "mob_high_explosive_sheep";
newscript->GetAI = &GetAI_mob_high_explosive_sheep;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp
index 22e1ad968d0..080e2ad6f68 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_selin_fireheart.cpp
@@ -13,14 +13,17 @@
* along 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: 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 -1585000
#define SAY_ENERGY -1585001
#define SAY_EMPOWERED -1585002
@@ -28,29 +31,36 @@ EndScriptData */
#define SAY_KILL_2 -1585004
#define SAY_DEATH -1585005
#define EMOTE_CRYSTAL -1585006
+
//Crystal effect 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 TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
{
boss_selin_fireheartAI(Creature* c) : ScriptedAI(c)
{
pInstance = 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)
+ for(uint8 i = 0; i < size; ++i)
{
uint64 guid = pInstance->GetData64(DATA_FEL_CRYSTAL);
debug_log("TSCR: Selin: Adding Fel Crystal %u to list", guid);
@@ -59,23 +69,29 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
}
Heroic = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool Heroic;
+
std::list<uint64> Crystals;
+
uint32 DrainLifeTimer;
uint32 DrainManaTimer;
uint32 FelExplosionTimer;
uint32 DrainCrystalTimer;
uint32 EmpowerTimer;
+
bool IsDraining;
bool DrainingCrystal;
+
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<uint64>::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr)
+ //for(uint8 i = 0; i < CRYSTALS_NUMBER; ++i)
+ for(std::list<uint64>::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr)
{
//Unit* pUnit = Unit::GetUnit(*m_creature, FelCrystals[i]);
Unit* pUnit = Unit::GetUnit(*m_creature, *itr);
@@ -83,36 +99,42 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
{
if (!pUnit->isAlive())
CAST_CRE(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);
}
}
+
pInstance->HandleGameObject(pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR), true);
// Open the big encounter door. Close it in Aggro and open it only in JustDied(and here)
// Small door opened after event are expected to be closed by default
// Set Inst data for encounter
pInstance->SetData(DATA_SELIN_EVENT, NOT_STARTED);
}else error_log(ERROR_INST_DATA);
+
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<uint64>::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr)
+ //for(uint8 i = 0; i < CRYSTALS_NUMBER; ++i)
+ for(std::list<uint64>::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr)
{
pCrystal = NULL;
//pCrystal = Unit::GetUnit(*m_creature, FelCrystals[i]);
@@ -131,20 +153,25 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
{
DoScriptText(SAY_ENERGY, m_creature);
DoScriptText(EMOTE_CRYSTAL, m_creature);
+
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<uint64>::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr)
+
+ //for(uint8 i = 0; i < CRYSTALS_NUMBER; ++i)
+ for(std::list<uint64>::iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr)
{
//Creature* pCrystal = (Unit::GetCreature(*m_creature, FelCrystals[i]));
Creature* pCrystal = Unit::GetCreature(*m_creature, *itr);
@@ -152,17 +179,21 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
pCrystal->Kill(pCrystal);
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->HandleGameObject(pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR), false);
//Close the encounter door, open it in JustDied/Reset
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type == POINT_MOTION_TYPE && id == 1)
@@ -184,20 +215,25 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (!pInstance)
return;
+
pInstance->SetData(DATA_SELIN_EVENT, DONE); // Encounter complete!
pInstance->HandleGameObject(pInstance->GetData64(DATA_SELIN_ENCOUNTER_DOOR), true); // Open the encounter door
pInstance->HandleGameObject(pInstance->GetData64(DATA_SELIN_DOOR), true); // Open the door leading further in
ShatterRemainingCrystals();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (!DrainingCrystal)
{
uint32 maxPowerMana = m_creature->GetMaxPower(POWER_MANA);
@@ -208,6 +244,7 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DRAIN_LIFE);
DrainLifeTimer = 10000;
} else DrainLifeTimer -= diff;
+
// Heroic only
if (Heroic)
{
@@ -218,6 +255,7 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
} else DrainManaTimer -= diff;
}
}
+
if (FelExplosionTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -226,6 +264,7 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
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))
@@ -237,6 +276,7 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
else DrainCrystalTimer = 20000 + rand()%5000;
} else DrainCrystalTimer -= diff;
}
+
}else
{
if (IsDraining)
@@ -245,32 +285,41 @@ struct TRINITY_DLL_DECL boss_selin_fireheartAI : public ScriptedAI
{
IsDraining = false;
DrainingCrystal = false;
+
DoScriptText(SAY_EMPOWERED, m_creature);
+
Unit* CrystalChosen = Unit::GetUnit(*m_creature, CrystalGUID);
if (CrystalChosen && CrystalChosen->isAlive())
// Use Deal Damage to kill it, not setDeathState.
CrystalChosen->Kill(CrystalChosen);
+
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* pCreature)
{
return new boss_selin_fireheartAI (pCreature);
};
+
struct TRINITY_DLL_DECL mob_fel_crystalAI : public ScriptedAI
{
mob_fel_crystalAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff) {}
+
void JustDied(Unit* killer)
{
if (ScriptedInstance* pInstance = m_creature->GetInstanceData())
@@ -294,17 +343,21 @@ struct TRINITY_DLL_DECL mob_fel_crystalAI : public ScriptedAI
} else error_log(ERROR_INST_DATA);
}
};
+
CreatureAI* GetAI_mob_fel_crystal(Creature* pCreature)
{
return new mob_fel_crystalAI (pCreature);
};
+
void AddSC_boss_selin_fireheart()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_selin_fireheart";
newscript->GetAI = &GetAI_boss_selin_fireheart;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_fel_crystal";
newscript->GetAI = &GetAI_mob_fel_crystal;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp
index a90f435aa9c..82b5980ca44 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/boss_vexallus.cpp
@@ -13,14 +13,17 @@
* along 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"
+
enum eEnums
{
SAY_AGGRO = -1585007,
@@ -28,25 +31,32 @@ enum eEnums
SAY_OVERLOAD = -1585009,
SAY_KILL = -1585010,
EMOTE_DISCHARGE_ENERGY = -1585011,
+
//is this text for real?
//#define SAY_DEATH "What...happen...ed."
+
//Pure energy spell info
SPELL_ENERGY_BOLT = 46156,
SPELL_ENERGY_FEEDBACK = 44335,
+
//Vexallus spell info
SPELL_CHAIN_LIGHTNING = 44318,
SPELL_H_CHAIN_LIGHTNING = 46380, //heroic spell
SPELL_OVERLOAD = 44353,
SPELL_ARCANE_SHOCK = 44319,
SPELL_H_ARCANE_SHOCK = 46381, //heroic spell
+
SPELL_SUMMON_PURE_ENERGY = 44322, //mod scale -10
H_SPELL_SUMMON_PURE_ENERGY1 = 46154, //mod scale -5
H_SPELL_SUMMON_PURE_ENERGY2 = 46159, //mod scale -5
+
//Creatures
NPC_PURE_ENERGY = 24745,
+
INTERVAL_MODIFIER = 15,
INTERVAL_SWITCH = 6
};
+
struct TRINITY_DLL_DECL boss_vexallusAI : public ScriptedAI
{
boss_vexallusAI(Creature *c) : ScriptedAI(c)
@@ -54,13 +64,16 @@ struct TRINITY_DLL_DECL boss_vexallusAI : public ScriptedAI
pInstance = c->GetInstanceData();
Heroic = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool Heroic;
+
uint32 ChainLightningTimer;
uint32 ArcaneShockTimer;
uint32 OverloadTimer;
uint32 IntervalHealthAmount;
bool Enraged;
+
void Reset()
{
ChainLightningTimer = 8000;
@@ -68,35 +81,44 @@ struct TRINITY_DLL_DECL boss_vexallusAI : public ScriptedAI
OverloadTimer = 1200;
IntervalHealthAmount = 1;
Enraged = false;
+
if (pInstance)
pInstance->SetData(DATA_VEXALLUS_EVENT, NOT_STARTED);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
pInstance->SetData(DATA_VEXALLUS_EVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_VEXALLUS_EVENT, IN_PROGRESS);
}
+
void JustSummoned(Creature *summoned)
{
if (Unit *temp = SelectUnit(SELECT_TARGET_RANDOM, 0))
summoned->GetMotionMaster()->MoveFollow(temp,0,0);
+
//spells are SUMMON_TYPE_GUARDIAN, so using setOwner should be ok
summoned->CastSpell(summoned,SPELL_ENERGY_BOLT,false,0,0,m_creature->GetGUID());
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (!Enraged)
{
//used for check, when Vexallus cast adds 85%, 70%, 55%, 40%, 25%
@@ -110,8 +132,10 @@ struct TRINITY_DLL_DECL boss_vexallusAI : public ScriptedAI
}
else
++IntervalHealthAmount;
+
DoScriptText(SAY_ENERGY, m_creature);
DoScriptText(EMOTE_DISCHARGE_ENERGY, m_creature);
+
if (Heroic)
{
m_creature->CastSpell(m_creature,H_SPELL_SUMMON_PURE_ENERGY1,false);
@@ -119,22 +143,28 @@ struct TRINITY_DLL_DECL boss_vexallusAI : public ScriptedAI
}
else
m_creature->CastSpell(m_creature,SPELL_SUMMON_PURE_ENERGY,false);
+
//below are workaround summons, remove when summoning spells w/implicitTarget 73 implemented in Mangos
m_creature->SummonCreature(NPC_PURE_ENERGY, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (Heroic)
m_creature->SummonCreature(NPC_PURE_ENERGY, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0);
}
+
if (ChainLightningTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, Heroic ? SPELL_H_CHAIN_LIGHTNING : SPELL_CHAIN_LIGHTNING);
+
ChainLightningTimer = 8000;
}else ChainLightningTimer -= diff;
+
if (ArcaneShockTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
if (target)
DoCast(target, Heroic ? SPELL_H_ARCANE_SHOCK : SPELL_ARCANE_SHOCK);
+
ArcaneShockTimer = 8000;
}else ArcaneShockTimer -= diff;
}
@@ -143,20 +173,26 @@ struct TRINITY_DLL_DECL boss_vexallusAI : public ScriptedAI
if (OverloadTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_OVERLOAD);
+
OverloadTimer = 2000;
}else OverloadTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_vexallus(Creature* pCreature)
{
return new boss_vexallusAI (pCreature);
};
+
struct TRINITY_DLL_DECL mob_pure_energyAI : public ScriptedAI
{
mob_pure_energyAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
+
void JustDied(Unit* slayer)
{
if (Unit *temp = m_creature->GetOwner())
@@ -165,21 +201,26 @@ struct TRINITY_DLL_DECL mob_pure_energyAI : public ScriptedAI
slayer->CastSpell(slayer, SPELL_ENERGY_FEEDBACK, true, 0, 0, temp->GetGUID());
}
}
+
void EnterCombat(Unit *who) { }
void MoveInLineOfSight(Unit *who) { }
void AttackStart(Unit *who) { }
};
+
CreatureAI* GetAI_mob_pure_energy(Creature* pCreature)
{
return new mob_pure_energyAI (pCreature);
};
+
void AddSC_boss_vexallus()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_vexallus";
newscript->GetAI = &GetAI_boss_vexallus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_pure_energy";
newscript->GetAI = &GetAI_mob_pure_energy;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/def_magisters_terrace.h b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/def_magisters_terrace.h
index ac7aee1f887..d6419ea409c 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/def_magisters_terrace.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/def_magisters_terrace.h
@@ -1,24 +1,31 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef 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_DOOR 13
#define DATA_KAEL_STATUE_LEFT 14
#define DATA_KAEL_STATUE_RIGHT 15
+
#define DATA_DELRISSA_DEATH_COUNT 16
+
#define ERROR_INST_DATA "TSCR 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/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp
index 7874123a4f4..4b7a68b9a61 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/instance_magisters_terrace.cpp
@@ -13,28 +13,36 @@
* along 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: 60
SDComment: Designed only for Selin Fireheart
SDCategory: Magister's Terrace
EndScriptData */
+
#include "precompiled.h"
#include "def_magisters_terrace.h"
+
#define MAX_ENCOUNTER 4
+
/*
0 - Selin Fireheart
1 - Vexallus
2 - Priestess Delrissa
3 - Kael'thas Sunstrider
*/
+
struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
{
instance_magisters_terrace(Map* pMap) : ScriptedInstance(pMap) {Initialize();}
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint32 DelrissaDeathCount;
+
std::list<uint64> FelCrystals;
std::list<uint64>::iterator CrystalItr;
+
uint64 SelinGUID;
uint64 DelrissaGUID;
uint64 VexallusDoorGUID;
@@ -43,12 +51,17 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
uint64 DelrissaDoorGUID;
uint64 KaelDoorGUID;
uint64 KaelStatue[2];
+
bool InitializedItr;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
FelCrystals.clear();
+
DelrissaDeathCount = 0;
+
SelinGUID = 0;
DelrissaGUID = 0;
VexallusDoorGUID = 0;
@@ -58,15 +71,18 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
KaelDoorGUID = 0;
KaelStatue[0] = 0;
KaelStatue[1] = 0;
+
InitializedItr = false;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
return false;
}
+
uint32 GetData(uint32 identifier)
{
switch(identifier)
@@ -80,6 +96,7 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 identifier, uint32 data)
{
switch(identifier)
@@ -98,6 +115,7 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
m_auiEncounter[2] = data;
break;
case DATA_KAELTHAS_EVENT: m_auiEncounter[3] = data; break;
+
case DATA_DELRISSA_DEATH_COUNT:
if (data == SPECIAL)
++DelrissaDeathCount;
@@ -106,6 +124,7 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -115,6 +134,7 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
case 24722: FelCrystals.push_back(pCreature->GetGUID()); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -130,6 +150,7 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
case 188166: KaelStatue[1] = pGo->GetGUID(); break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -143,6 +164,7 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
case DATA_KAEL_DOOR: return KaelDoorGUID;
case DATA_KAEL_STATUE_LEFT: return KaelStatue[0];
case DATA_KAEL_STATUE_RIGHT: return KaelStatue[1];
+
case DATA_FEL_CRYSTAL:
{
if (FelCrystals.empty())
@@ -150,11 +172,13 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
error_log("TSCR: Magisters Terrace: No Fel Crystals loaded in Inst Data");
return 0;
}
+
if (!InitializedItr)
{
CrystalItr = FelCrystals.begin();
InitializedItr = true;
}
+
uint64 guid = *CrystalItr;
++CrystalItr;
return guid;
@@ -163,13 +187,16 @@ struct TRINITY_DLL_DECL instance_magisters_terrace : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_magisters_terrace(Map* pMap)
{
return new instance_magisters_terrace(pMap);
}
+
void AddSC_instance_magisters_terrace()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "instance_magisters_terrace";
newscript->GetInstanceData = &GetInstanceData_instance_magisters_terrace;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp
index 542569c8392..b23d5e72186 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/magisters_terrace/magisters_terrace.cpp
@@ -13,19 +13,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Magisters_Terrace
SD%Complete: 100
SDComment: Quest support: 11490(post-event)
SDCategory: Magisters Terrace
EndScriptData */
+
/* ContentData
npc_kalecgos
EndContentData */
+
#include "precompiled.h"
+
/*######
## npc_kalecgos
######*/
+
enum eEnums
{
SPELL_TRANSFORM_TO_KAEL = 44670,
@@ -33,43 +38,57 @@ enum eEnums
NPC_KAEL = 24848, //human form entry
POINT_ID_LAND = 1
};
+
const float afKaelLandPoint[] = {225.045, -276.236, -5.434};
+
#define GOSSIP_ITEM_KAEL_1 "Who are you?"
#define GOSSIP_ITEM_KAEL_2 "What can we do to assist you?"
#define GOSSIP_ITEM_KAEL_3 "What brings you to the Sunwell?"
#define GOSSIP_ITEM_KAEL_4 "You're not alone here?"
#define GOSSIP_ITEM_KAEL_5 "What would Kil'jaeden want with a mortal woman?"
+
// This is friendly keal that appear after used Orb.
// If we assume DB handle summon, summon appear somewhere outside the platform where Orb is
struct TRINITY_DLL_DECL npc_kalecgosAI : public ScriptedAI
{
npc_kalecgosAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
uint32 m_uiTransformTimer;
+
void Reset()
{
m_uiTransformTimer = 0;
+
// we must assume he appear as dragon somewhere outside the platform of orb, and then move directly to here
if (m_creature->GetEntry() != NPC_KAEL)
m_creature->GetMotionMaster()->MovePoint(POINT_ID_LAND, afKaelLandPoint[0], afKaelLandPoint[1], afKaelLandPoint[2]);
}
+
void MovementInform(uint32 uiType, uint32 uiPointId)
{
if (uiType != POINT_MOTION_TYPE)
return;
+
if (uiPointId == POINT_ID_LAND)
m_uiTransformTimer = MINUTE*IN_MILISECONDS;
}
+
// some targeting issues with the spell, so use this workaround as temporary solution
void DoWorkaroundForQuestCredit()
{
Map* pMap = m_creature->GetMap();
+
if (!pMap || pMap->IsHeroic())
return;
+
Map::PlayerList const &lList = pMap->GetPlayers();
+
if (lList.isEmpty())
return;
+
SpellEntry const* pSpell = GetSpellStore()->LookupEntry(SPELL_ORB_KILL_CREDIT);
- for (Map::PlayerList::const_iterator i = lList.begin(); i != lList.end(); ++i)
+
+ for(Map::PlayerList::const_iterator i = lList.begin(); i != lList.end(); ++i)
{
if (Player* pPlayer = i->getSource())
{
@@ -78,6 +97,7 @@ struct TRINITY_DLL_DECL npc_kalecgosAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (m_uiTransformTimer)
@@ -86,26 +106,33 @@ struct TRINITY_DLL_DECL npc_kalecgosAI : public ScriptedAI
{
m_creature->CastSpell(m_creature,SPELL_ORB_KILL_CREDIT,false);
DoWorkaroundForQuestCredit();
+
// Transform and update entry, now ready for quest/read gossip
m_creature->CastSpell(m_creature,SPELL_TRANSFORM_TO_KAEL,false);
m_creature->UpdateEntry(NPC_KAEL);
+
m_uiTransformTimer = 0;
}else m_uiTransformTimer -= uiDiff;
}
}
};
+
CreatureAI* GetAI_npc_kalecgos(Creature* pCreature)
{
return new npc_kalecgosAI(pCreature);
}
+
bool GossipHello_npc_kalecgos(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KAEL_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(12498, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_kalecgos(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -130,11 +157,14 @@ bool GossipSelect_npc_kalecgos(Player* pPlayer, Creature* pCreature, uint32 uiSe
pPlayer->SEND_GOSSIP_MENU(12608, pCreature->GetGUID());
break;
}
+
return true;
}
+
void AddSC_magisters_terrace()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_kalecgos";
newscript->GetAI = &GetAI_npc_kalecgos;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp
index c32f85e2797..1a6ead66319 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_baron_geddon.cpp
@@ -13,37 +13,47 @@
* along 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 EMOTE_SERVICE -1409000
+
#define SPELL_INFERNO 19695
#define SPELL_IGNITEMANA 19659
#define SPELL_LIVINGBOMB 20475
#define SPELL_ARMAGEDDOM 20479
+
struct TRINITY_DLL_DECL boss_baron_geddonAI : public ScriptedAI
{
boss_baron_geddonAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//If we are <2% hp cast Armageddom
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 2)
{
@@ -52,26 +62,32 @@ struct TRINITY_DLL_DECL boss_baron_geddonAI : public ScriptedAI
DoScriptText(EMOTE_SERVICE, m_creature);
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)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_IGNITEMANA);
+
IgniteMana_Timer = 30000;
}else IgniteMana_Timer -= diff;
+
//LivingBomb_Timer
if (LivingBomb_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_LIVINGBOMB);
+
LivingBomb_Timer = 35000;
}else LivingBomb_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -79,6 +95,7 @@ CreatureAI* GetAI_boss_baron_geddon(Creature* pCreature)
{
return new boss_baron_geddonAI (pCreature);
}
+
void AddSC_boss_baron_geddon()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_garr.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_garr.cpp
index 89336c33166..bfdc20a251a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_garr.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_garr.cpp
@@ -13,78 +13,98 @@
* along 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 TRINITY_DLL_DECL boss_garrAI : public ScriptedAI
{
boss_garrAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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 TRINITY_DLL_DECL mob_fireswornAI : public ScriptedAI
{
mob_fireswornAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Immolate_Timer;
+
void Reset()
{
Immolate_Timer = 4000; //These times are probably wrong
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Immolate_Timer
if (Immolate_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
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)
{
@@ -92,6 +112,7 @@ struct TRINITY_DLL_DECL mob_fireswornAI : public ScriptedAI
m_creature->setDeathState(JUST_DIED);
m_creature->RemoveCorpse();
}
+
DoMeleeAttackIfReady();
}
};
@@ -99,17 +120,21 @@ CreatureAI* GetAI_boss_garr(Creature* pCreature)
{
return new boss_garrAI (pCreature);
}
+
CreatureAI* GetAI_mob_firesworn(Creature* pCreature)
{
return new mob_fireswornAI (pCreature);
}
+
void AddSC_boss_garr()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_garr";
newscript->GetAI = &GetAI_boss_garr;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_firesworn";
newscript->GetAI = &GetAI_mob_firesworn;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp
index 43df768573f..d33e9a92ba3 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_gehennas.cpp
@@ -13,33 +13,42 @@
* along 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 TRINITY_DLL_DECL boss_gehennasAI : public ScriptedAI
{
boss_gehennasAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowBolt_Timer;
uint32 RainOfFire_Timer;
uint32 GehennasCurse_Timer;
+
void Reset()
{
ShadowBolt_Timer = 6000;
RainOfFire_Timer = 10000;
GehennasCurse_Timer = 12000;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//ShadowBolt_Timer
if (ShadowBolt_Timer < diff)
{
@@ -47,19 +56,23 @@ struct TRINITY_DLL_DECL boss_gehennasAI : public ScriptedAI
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();
}
};
@@ -67,6 +80,7 @@ CreatureAI* GetAI_boss_gehennas(Creature* pCreature)
{
return new boss_gehennasAI (pCreature);
}
+
void AddSC_boss_gehennas()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp
index 1f9375bb82b..3fedad28827 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_golemagg.cpp
@@ -13,14 +13,17 @@
* along 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: Timers need to be confirmed, Golemagg's Trust need to be checked
SDCategory: Molten Core
EndScriptData */
+
#include "precompiled.h"
#include "def_molten_core.h"
+
enum eEnums
{
SPELL_MAGMASPLASH = 13879,
@@ -28,53 +31,65 @@ enum eEnums
SPELL_EARTHQUAKE = 19798,
SPELL_ENRAGE = 19953,
SPELL_GOLEMAGG_TRUST = 20553,
+
// Core Rager
EMOTE_LOWHP = -1409002,
SPELL_MANGLE = 19820
};
+
struct TRINITY_DLL_DECL boss_golemaggAI : public ScriptedAI
{
boss_golemaggAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 m_uiPyroblastTimer;
uint32 m_uiEarthquakeTimer;
uint32 m_uiBuffTimer;
bool m_bEnraged;
+
void Reset()
{
m_uiPyroblastTimer = 7*IN_MILISECONDS; // These timers are probably wrong
m_uiEarthquakeTimer = 3*IN_MILISECONDS;
m_uiBuffTimer = 2.5*IN_MILISECONDS;
m_bEnraged = false;
+
m_creature->CastSpell(m_creature, SPELL_MAGMASPLASH, true);
}
+
void JustDied(Unit* pKiller)
{
if (m_pInstance)
m_pInstance->SetData(DATA_GOLEMAGG_DEATH, 0);
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//Pyroblast
if (m_uiPyroblastTimer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_PYROBLAST);
+
m_uiPyroblastTimer = 7*IN_MILISECONDS;
}
else
m_uiPyroblastTimer -= uiDiff;
+
// Enrage
if (!m_bEnraged && m_creature->GetHealth()*100 < m_creature->GetMaxHealth()*10)
{
DoCast(m_creature, SPELL_ENRAGE);
m_bEnraged = true;
}
+
// Earthquake
if (m_bEnraged)
{
@@ -86,6 +101,7 @@ struct TRINITY_DLL_DECL boss_golemaggAI : public ScriptedAI
else
m_uiEarthquakeTimer -= uiDiff;
}
+
/*
// Golemagg's Trust
if (m_uiBuffTimer < uiDiff)
@@ -96,21 +112,27 @@ struct TRINITY_DLL_DECL boss_golemaggAI : public ScriptedAI
else
m_uiBuffTimer -= uiDiff;
*/
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_core_ragerAI : public ScriptedAI
{
mob_core_ragerAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 m_uiMangleTimer;
+
void Reset()
{
m_uiMangleTimer = 7*IN_MILISECONDS; // These times are probably wrong
}
+
void DamageTaken(Unit* pDoneBy, uint32& uiDamage)
{
if (m_creature->GetHealth()*100 < m_creature->GetMaxHealth()*50)
@@ -130,10 +152,12 @@ struct TRINITY_DLL_DECL mob_core_ragerAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
// Mangle
if (m_uiMangleTimer < uiDiff)
{
@@ -142,24 +166,30 @@ struct TRINITY_DLL_DECL mob_core_ragerAI : public ScriptedAI
}
else
m_uiMangleTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_golemagg(Creature* pCreature)
{
return new boss_golemaggAI (pCreature);
}
+
CreatureAI* GetAI_mob_core_rager(Creature* pCreature)
{
return new mob_core_ragerAI (pCreature);
}
+
void AddSC_boss_golemagg()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "boss_golemagg";
newscript->GetAI = &GetAI_boss_golemagg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_core_rager";
newscript->GetAI = &GetAI_mob_core_rager;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp
index a6ffcb657c0..509faaa6fd4 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_lucifron.cpp
@@ -13,53 +13,65 @@
* along 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 TRINITY_DLL_DECL boss_lucifronAI : public ScriptedAI
{
boss_lucifronAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -67,6 +79,7 @@ CreatureAI* GetAI_boss_lucifron(Creature* pCreature)
{
return new boss_lucifronAI (pCreature);
}
+
void AddSC_boss_lucifron()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp
index c43e9eaada7..ce6348f005e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_magmadar.cpp
@@ -13,39 +13,50 @@
* along 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 EMOTE_FRENZY -1409001
+
#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 TRINITY_DLL_DECL boss_magmadarAI : public ScriptedAI
{
boss_magmadarAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Frenzy_Timer
if (Frenzy_Timer < diff)
{
@@ -53,19 +64,23 @@ struct TRINITY_DLL_DECL boss_magmadarAI : public ScriptedAI
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();
}
};
@@ -73,6 +88,7 @@ CreatureAI* GetAI_boss_magmadar(Creature* pCreature)
{
return new boss_magmadarAI (pCreature);
}
+
void AddSC_boss_magmadar()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp
index 6d9c06af2b3..905edf27ea8 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_majordomo_executus.cpp
@@ -13,86 +13,108 @@
* along 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 SAY_AGGRO -1409003
#define SAY_SPAWN -1409004
#define SAY_SLAY -1409005
#define SAY_SPECIAL -1409006
#define SAY_DEFEAT -1409007
+
#define SAY_SUMMON_MAJ -1409008
#define SAY_ARRIVAL1_RAG -1409009
#define SAY_ARRIVAL2_MAJ -1409010
#define SAY_ARRIVAL3_RAG -1409011
#define SAY_ARRIVAL5_RAG -1409012
+
#define SPAWN_RAG_X 838.51
#define SPAWN_RAG_Y -829.84
#define SPAWN_RAG_Z -232.00
#define SPAWN_RAG_O 1.70
+
#define SPELL_MAGIC_REFLECTION 20619
#define SPELL_DAMAGE_REFLECTION 21075
+
#define SPELL_BLASTWAVE 20229
#define SPELL_AEGIS 20620 //This is self casted whenever we are below 50%
#define SPELL_TELEPORT 20618
#define SPELL_SUMMON_RAGNAROS 19774
+
#define ENTRY_FLAMEWALKER_HEALER 11663
#define ENTRY_FLAMEWALKER_ELITE 11664
+
struct TRINITY_DLL_DECL boss_majordomoAI : public ScriptedAI
{
boss_majordomoAI(Creature *c) : ScriptedAI(c) {}
+
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;
+
DoScriptText(SAY_SLAY, m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -100,6 +122,7 @@ CreatureAI* GetAI_boss_majordomo(Creature* pCreature)
{
return new boss_majordomoAI (pCreature);
}
+
void AddSC_boss_majordomo()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp
index 3e97982f047..9376250e3e8 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_ragnaros.cpp
@@ -13,67 +13,83 @@
* along 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 SAY_REINFORCEMENTS1 -1409013
#define SAY_REINFORCEMENTS2 -1409014
#define SAY_HAND -1409015
#define SAY_WRATH -1409016
#define SAY_KILL -1409017
#define SAY_MAGMABURST -1409018
+
#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 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 TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
{
boss_ragnarosAI(Creature *c) : ScriptedAI(c)
{
SetCombatMovement(false);
}
+
uint32 WrathOfRagnaros_Timer;
uint32 HandOfRagnaros_Timer;
uint32 LavaBurst_Timer;
@@ -87,6 +103,7 @@ struct TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
bool HasSubmergedOnce;
bool WasBanished;
bool HasAura;
+
void Reset()
{
WrathOfRagnaros_Timer = 30000;
@@ -100,18 +117,23 @@ struct TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
HasYelledMagmaBurst = false;
HasSubmergedOnce = false;
WasBanished = false;
+
m_creature->CastSpell(m_creature,SPELL_MELTWEAPON,true);
HasAura = true;
}
+
void KilledUnit(Unit* victim)
{
if (rand()%5)
return;
+
DoScriptText(SAY_KILL, m_creature);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (WasBanished && Attack_Timer < diff)
@@ -127,64 +149,78 @@ struct TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
//Do nothing while banished
return;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//WrathOfRagnaros_Timer
if (WrathOfRagnaros_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_WRATHOFRAGNAROS);
+
if (rand()%2 == 0)
{
DoScriptText(SAY_WRATH, m_creature);
}
+
WrathOfRagnaros_Timer = 30000;
}else WrathOfRagnaros_Timer -= diff;
+
//HandOfRagnaros_Timer
if (HandOfRagnaros_Timer < diff)
{
DoCast(m_creature,SPELL_HANDOFRAGNAROS);
+
if (rand()%2==0)
{
DoScriptText(SAY_HAND, m_creature);
}
+
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);
+
if (!HasSubmergedOnce)
{
DoScriptText(SAY_REINFORCEMENTS1, m_creature);
+
// summon 10 elementals
- for (uint8 i = 0; i < 9; ++i)
+ for(uint8 i = 0; i < 9; ++i)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM,0))
{
@@ -192,15 +228,18 @@ struct TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
pSummoned->AI()->AttackStart(pTarget);
}
}
+
HasSubmergedOnce = true;
WasBanished = true;
DoCast(m_creature,SPELL_RAGSUBMERGE);
Attack_Timer = 90000;
+
}
else
{
DoScriptText(SAY_REINFORCEMENTS2, m_creature);
- for (uint8 i = 0; i < 9; ++i)
+
+ for(uint8 i = 0; i < 9; ++i)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM,0))
{
@@ -208,12 +247,15 @@ struct TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
pSummoned->AI()->AttackStart(pTarget);
}
}
+
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->IsWithinMeleeRange(m_creature->getVictim()))
{
@@ -230,12 +272,14 @@ struct TRINITY_DLL_DECL boss_ragnarosAI : public ScriptedAI
if (MagmaBurst_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_MAGMABURST);
+
if (!HasYelledMagmaBurst)
{
//Say our dialog
DoScriptText(SAY_MAGMABURST, m_creature);
HasYelledMagmaBurst = true;
}
+
MagmaBurst_Timer = 2500;
}else MagmaBurst_Timer -= diff;
}
@@ -245,6 +289,7 @@ CreatureAI* GetAI_boss_ragnaros(Creature* pCreature)
{
return new boss_ragnarosAI (pCreature);
}
+
void AddSC_boss_ragnaros()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp
index efb042d6711..772a7452214 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_shazzrah.cpp
@@ -13,25 +13,31 @@
* along 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 TRINITY_DLL_DECL boss_shazzrahAI : public ScriptedAI
{
boss_shazzrahAI(Creature *c) : ScriptedAI(c) {}
+
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
@@ -40,39 +46,47 @@ struct TRINITY_DLL_DECL boss_shazzrahAI : public ScriptedAI
Countspell_Timer = 15000;
Blink_Timer = 30000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -80,14 +94,17 @@ struct TRINITY_DLL_DECL boss_shazzrahAI : public ScriptedAI
// Blink is not working cause of LoS System we need to do this hardcoded.
Unit* target = NULL;
target = SelectUnit(SELECT_TARGET_RANDOM,0);
+
if (target)
{
DoTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
DoCast(target,SPELL_ARCANEEXPLOSION);
DoResetThreat();
}
+
Blink_Timer = 45000;
}else Blink_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -95,6 +112,7 @@ CreatureAI* GetAI_boss_shazzrah(Creature* pCreature)
{
return new boss_shazzrahAI (pCreature);
}
+
void AddSC_boss_shazzrah()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp
index a0aaa76c0be..a01fb32c46c 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/boss_sulfuron_harbinger.cpp
@@ -13,31 +13,38 @@
* along 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 TRINITY_DLL_DECL boss_sulfuronAI : public ScriptedAI
{
boss_sulfuronAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Darkstrike_Timer;
uint32 DemoralizingShout_Timer;
uint32 Inspire_Timer;
uint32 Knockdown_Timer;
uint32 Flamespear_Timer;
+
void Reset()
{
Darkstrike_Timer=10000; //These times are probably wrong
@@ -46,19 +53,23 @@ struct TRINITY_DLL_DECL boss_sulfuronAI : public ScriptedAI
Knockdown_Timer = 6000;
Flamespear_Timer = 2000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -70,96 +81,122 @@ struct TRINITY_DLL_DECL boss_sulfuronAI : public ScriptedAI
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 TRINITY_DLL_DECL mob_flamewaker_priestAI : public ScriptedAI
{
mob_flamewaker_priestAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Heal_Timer;
uint32 ShadowWordPain_Timer;
uint32 Immolate_Timer;
+
void Reset()
{
Heal_Timer = 15000+rand()%15000;
ShadowWordPain_Timer = 2000;
Immolate_Timer = 8000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new boss_sulfuronAI (pCreature);
}
+
CreatureAI* GetAI_mob_flamewaker_priest(Creature* pCreature)
{
return new mob_flamewaker_priestAI (pCreature);
}
+
void AddSC_boss_sulfuron()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_sulfuron";
newscript->GetAI = &GetAI_boss_sulfuron;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_flamewaker_priest";
newscript->GetAI = &GetAI_mob_flamewaker_priest;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/def_molten_core.h b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/def_molten_core.h
index 40bbcb1226e..5874d8b9408 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/def_molten_core.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/def_molten_core.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_MOLTEN_CORE_H
#define DEF_MOLTEN_CORE_H
+
#define DATA_FLAMEWAKERPRIEST 1
#define DATA_GARRISDEAD 2
#define DATA_GEDDONISDEAD 3
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp
index e5c0a0351e2..6e7df410a98 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/instance_molten_core.cpp
@@ -13,15 +13,19 @@
* along 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 MAX_ENCOUNTER 9
+
#define ID_LUCIFRON 12118
#define ID_MAGMADAR 11982
#define ID_GEHENNAS 12259
@@ -33,17 +37,23 @@ EndScriptData */
#define ID_DOMO 12018
#define ID_RAGNAROS 11502
#define ID_FLAMEWAKERPRIEST 11662
+
struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
{
instance_molten_core(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 Lucifron, Magmadar, Gehennas, Garr, Geddon, Shazzrah, Sulfuron, Golemagg, Domo, Ragnaros, FlamewakerPriest;
uint64 RuneKoro, RuneZeth, RuneMazj, RuneTheri, RuneBlaz, RuneKress, RuneMohn, m_uiFirelordCacheGUID;;
+
//If all Bosses are dead.
bool IsBossDied[9];
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
Lucifron = 0;
Magmadar = 0;
Gehennas = 0;
@@ -55,6 +65,7 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
Domo = 0;
Ragnaros = 0;
FlamewakerPriest = 0;
+
RuneKoro = 0;
RuneZeth = 0;
RuneMazj = 0;
@@ -62,7 +73,9 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
RuneBlaz = 0;
RuneKress = 0;
RuneMohn = 0;
+
m_uiFirelordCacheGUID = 0;
+
IsBossDied[0] = false;
IsBossDied[1] = false;
IsBossDied[2] = false;
@@ -70,14 +83,17 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
IsBossDied[4] = false;
IsBossDied[5] = false;
IsBossDied[6] = false;
+
IsBossDied[7] = false;
IsBossDied[8] = false;
}
+
bool IsEncounterInProgress() const
{
return false;
};
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -109,6 +125,7 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch (pCreature->GetEntry())
@@ -116,38 +133,49 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
case ID_LUCIFRON:
Lucifron = pCreature->GetGUID();
break;
+
case ID_MAGMADAR:
Magmadar = pCreature->GetGUID();
break;
+
case ID_GEHENNAS:
Gehennas = pCreature->GetGUID();
break;
+
case ID_GARR:
Garr = pCreature->GetGUID();
break;
+
case ID_GEDDON:
Geddon = pCreature->GetGUID();
break;
+
case ID_SHAZZRAH:
Shazzrah = pCreature->GetGUID();
break;
+
case ID_SULFURON:
Sulfuron = pCreature->GetGUID();
break;
+
case ID_GOLEMAGG:
Golemagg = pCreature->GetGUID();
break;
+
case ID_DOMO:
Domo = pCreature->GetGUID();
break;
+
case ID_RAGNAROS:
Ragnaros = pCreature->GetGUID();
break;
+
case ID_FLAMEWAKERPRIEST:
FlamewakerPriest = pCreature->GetGUID();
break;
}
}
+
uint64 GetData64 (uint32 identifier)
{
switch(identifier)
@@ -156,11 +184,14 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
return Sulfuron;
case DATA_GOLEMAGG:
return Golemagg;
+
case DATA_FLAMEWAKERPRIEST:
return FlamewakerPriest;
}
+
return 0;
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -169,51 +200,63 @@ struct TRINITY_DLL_DECL instance_molten_core : public ScriptedInstance
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* pMap)
{
return new instance_molten_core (pMap);
}
+
void AddSC_instance_molten_core()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/molten_core.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/molten_core.cpp
index a6be8e4f346..833f67f5fc7 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/molten_core.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/molten_core/molten_core.cpp
@@ -13,19 +13,24 @@
* along 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 "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
@@ -33,32 +38,41 @@ EndContentData */
#define SPELL_WITHERING_HEAT 19367
#define SPELL_ANCIENT_DESPAIR 19369
#define SPELL_ANCIENT_HYSTERIA 19372
+
CreatureAI* GetAI_mob_ancient_core_hound(Creature* pCreature)
{
SimpleAI *ai = new SimpleAI(pCreature);
+
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 = RAND(SPELL_GROUND_STOMP,SPELL_ANCIENT_DREAD,SPELL_CAUTERIZING_FLAMES,
SPELL_WITHERING_HEAT,SPELL_ANCIENT_DESPAIR,SPELL_ANCIENT_HYSTERIA);
+
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;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp
index 4fd9d5ef933..449ed462a44 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter1.cpp
@@ -15,39 +15,48 @@
* 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"
#include "Vehicle.h"
#include "ObjectMgr.h"
#include "escort_ai.h"
+
/*######
##Quest 12848
######*/
+
#define GCD_CAST 1
+
enum eDeathKnightSpells
{
SPELL_SOUL_PRISON_CHAIN_SELF = 54612,
SPELL_SOUL_PRISON_CHAIN = 54613,
SPELL_DK_INITIATE_VISUAL = 51519,
+
SPELL_ICY_TOUCH = 52372,
SPELL_PLAGUE_STRIKE = 52373,
SPELL_BLOOD_STRIKE = 52374,
SPELL_DEATH_COIL = 52375
};
+
#define EVENT_ICY_TOUCH 1
#define EVENT_PLAGUE_STRIKE 2
#define EVENT_BLOOD_STRIKE 3
#define EVENT_DEATH_COIL 4
+
//used by 29519,29520,29565,29566,29567 but signed for 29519
int32 say_event_start[8] =
{
-1609000,-1609001,-1609002,-1609003,
-1609004,-1609005,-1609006,-1609007
};
+
int32 say_event_attack[9] =
{
-1609008,-1609009,-1609010,-1609011,-1609012,
-1609013,-1609014,-1609015,-1609016
};
+
uint32 acherus_soul_prison[12] =
{
191577,
@@ -63,6 +72,7 @@ uint32 acherus_soul_prison[12] =
191589,
191590
};
+
uint32 acherus_unworthy_initiate[5] =
{
29519,
@@ -71,6 +81,7 @@ uint32 acherus_unworthy_initiate[5] =
29566,
29567
};
+
enum UnworthyInitiatePhase
{
PHASE_CHAINED,
@@ -79,6 +90,7 @@ enum UnworthyInitiatePhase
PHASE_TO_ATTACK,
PHASE_ATTACKING,
};
+
struct TRINITY_DLL_DECL npc_unworthy_initiateAI : public ScriptedAI
{
npc_unworthy_initiateAI(Creature *c) : ScriptedAI(c)
@@ -89,12 +101,15 @@ struct TRINITY_DLL_DECL npc_unworthy_initiateAI : public ScriptedAI
if (info->equipmentId)
const_cast<CreatureInfo*>(me->GetCreatureInfo())->equipmentId = info->equipmentId;
}
+
uint64 playerGUID;
UnworthyInitiatePhase phase;
uint32 wait_timer;
float anchorX, anchorY;
uint64 anchorGUID;
+
EventMap events;
+
void Reset()
{
anchorGUID = 0;
@@ -105,6 +120,7 @@ struct TRINITY_DLL_DECL npc_unworthy_initiateAI : public ScriptedAI
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 8);
me->LoadEquipment(0, true);
}
+
void EnterCombat(Unit *who)
{
events.ScheduleEvent(EVENT_ICY_TOUCH, 1000, GCD_CAST);
@@ -112,48 +128,63 @@ struct TRINITY_DLL_DECL npc_unworthy_initiateAI : public ScriptedAI
events.ScheduleEvent(EVENT_BLOOD_STRIKE, 2000, GCD_CAST);
events.ScheduleEvent(EVENT_DEATH_COIL, 5000, GCD_CAST);
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id == 1)
{
wait_timer = 5000;
me->CastSpell(me, SPELL_DK_INITIATE_VISUAL, true);
+
if(Player* starter = Unit::GetPlayer(playerGUID))
DoScriptText(say_event_attack[rand()%9], me, starter);
+
phase = PHASE_TO_ATTACK;
}
}
+
void EventStart(Creature* anchor, Player* target)
{
wait_timer = 5000;
phase = PHASE_TO_EQUIP;
+
me->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
me->RemoveAurasDueToSpell(SPELL_SOUL_PRISON_CHAIN_SELF);
me->RemoveAurasDueToSpell(SPELL_SOUL_PRISON_CHAIN);
+
float z;
anchor->GetContactPoint(me, anchorX, anchorY, z, 1.0f);
+
playerGUID = target->GetGUID();
DoScriptText(say_event_start[rand()%8], me, target);
}
+
void UpdateAI(const uint32 diff);
};
+
CreatureAI* GetAI_npc_unworthy_initiate(Creature* pCreature)
{
return new npc_unworthy_initiateAI(pCreature);
}
+
struct TRINITY_DLL_DECL npc_unworthy_initiate_anchorAI : public PassiveAI
{
npc_unworthy_initiate_anchorAI(Creature *c) : PassiveAI(c), prisonerGUID(0) {}
+
uint64 prisonerGUID;
+
void SetGUID(const uint64 &guid, int32 id)
{
if (!prisonerGUID)
prisonerGUID = guid;
}
+
uint64 GetGUID(int32 id) { return prisonerGUID; }
};
+
void npc_unworthy_initiateAI::UpdateAI(const uint32 diff)
{
switch(phase)
@@ -169,9 +200,11 @@ void npc_unworthy_initiateAI::UpdateAI(const uint32 diff)
}
else
error_log("npc_unworthy_initiateAI: unable to find anchor!");
+
float dist = 99.0f;
GameObject *prison = NULL;
- for (uint8 i = 0; i < 12; ++i)
+
+ for(uint8 i = 0; i < 12; ++i)
{
if (GameObject* temp_prison = me->FindNearestGameObject(acherus_soul_prison[i],30))
{
@@ -182,6 +215,7 @@ void npc_unworthy_initiateAI::UpdateAI(const uint32 diff)
}
}
}
+
if (prison)
prison->ResetDoorOrButton();
else
@@ -212,6 +246,7 @@ void npc_unworthy_initiateAI::UpdateAI(const uint32 diff)
me->setFaction(14);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
phase = PHASE_ATTACKING;
+
if (Player* target = Unit::GetPlayer(playerGUID))
me->AI()->AttackStart(target);
wait_timer = 0;
@@ -221,7 +256,9 @@ void npc_unworthy_initiateAI::UpdateAI(const uint32 diff)
case PHASE_ATTACKING:
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -248,25 +285,32 @@ void npc_unworthy_initiateAI::UpdateAI(const uint32 diff)
break;
}
}
+
DoMeleeAttackIfReady();
}
}
+
CreatureAI* GetAI_npc_unworthy_initiate_anchor(Creature* pCreature)
{
return new npc_unworthy_initiate_anchorAI(pCreature);
}
+
bool GOHello_go_acherus_soul_prison(Player* pPlayer, GameObject* pGo)
{
if (Creature *anchor = pGo->FindNearestCreature(29521, 15))
if (uint64 prisonerGUID = anchor->AI()->GetGUID())
if (Creature* prisoner = Creature::GetCreature(*pPlayer, prisonerGUID))
CAST_AI(npc_unworthy_initiateAI, prisoner->AI())->EventStart(anchor, pPlayer);
+
return false;
}
+
/*######
## npc_death_knight_initiate
######*/
+
#define GOSSIP_ACCEPT_DUEL "I challenge you, death knight!"
+
enum eDuelEnums
{
SAY_DUEL_A = -1609080,
@@ -278,37 +322,46 @@ enum eDuelEnums
SAY_DUEL_G = -1609086,
SAY_DUEL_H = -1609087,
SAY_DUEL_I = -1609088,
+
SPELL_DUEL = 52996,
//SPELL_DUEL_TRIGGERED = 52990,
SPELL_DUEL_VICTORY = 52994,
SPELL_DUEL_FLAG = 52991,
+
QUEST_DEATH_CHALLENGE = 12733,
FACTION_HOSTILE = 2068
};
+
int32 m_auiRandomSay[] =
{
SAY_DUEL_A, SAY_DUEL_B, SAY_DUEL_C, SAY_DUEL_D, SAY_DUEL_E, SAY_DUEL_F, SAY_DUEL_G, SAY_DUEL_H, SAY_DUEL_I
};
+
struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
{
npc_death_knight_initiateAI(Creature* pCreature) : CombatAI(pCreature)
{
m_bIsDuelInProgress = false;
}
+
bool lose;
uint64 m_uiDuelerGUID;
uint32 m_uiDuelTimer;
bool m_bIsDuelInProgress;
+
void Reset()
{
lose = false;
me->RestoreFaction();
CombatAI::Reset();
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15);
+
m_uiDuelerGUID = 0;
m_uiDuelTimer = 5000;
m_bIsDuelInProgress = false;
}
+
void SpellHit(Unit* pCaster, const SpellEntry* pSpell)
{
if (!m_bIsDuelInProgress && pSpell->Id == SPELL_DUEL)
@@ -317,6 +370,7 @@ struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
m_bIsDuelInProgress = true;
}
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (m_bIsDuelInProgress && pDoneBy->IsControlledByPlayer())
@@ -326,6 +380,7 @@ struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
else if (uiDamage >= m_creature->GetHealth())
{
uiDamage = 0;
+
if (!lose)
{
pDoneBy->RemoveGameObject(SPELL_DUEL_FLAG, true);
@@ -338,6 +393,7 @@ struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
@@ -347,6 +403,7 @@ struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
if (m_uiDuelTimer < uiDiff)
{
m_creature->setFaction(FACTION_HOSTILE);
+
if (Unit* pUnit = Unit::GetUnit(*m_creature, m_uiDuelerGUID))
AttackStart(pUnit);
}
@@ -355,6 +412,7 @@ struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
}
return;
}
+
if (m_bIsDuelInProgress)
{
if (lose)
@@ -372,59 +430,76 @@ struct TRINITY_DLL_DECL npc_death_knight_initiateAI : public CombatAI
return;
}
}
+
// TODO: spells
+
CombatAI::UpdateAI(uiDiff);
}
};
+
CreatureAI* GetAI_npc_death_knight_initiate(Creature* pCreature)
{
return new npc_death_knight_initiateAI(pCreature);
}
+
bool GossipHello_npc_death_knight_initiate(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_DEATH_CHALLENGE) == QUEST_STATUS_INCOMPLETE && pCreature->GetHealth() == pCreature->GetMaxHealth())
{
if (pPlayer->GetHealth() * 10 < pPlayer->GetMaxHealth())
return true;
+
if (pPlayer->isInCombat() || pCreature->isInCombat())
return true;
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ACCEPT_DUEL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(),pCreature->GetGUID());
}
return true;
}
+
bool GossipSelect_npc_death_knight_initiate(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
{
pPlayer->CLOSE_GOSSIP_MENU();
+
if (pPlayer->isInCombat() || pCreature->isInCombat())
return true;
+
if (npc_death_knight_initiateAI* pInitiateAI = CAST_AI(npc_death_knight_initiateAI, pCreature->AI()))
{
if (pInitiateAI->m_bIsDuelInProgress)
return true;
}
+
pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_UNK_15);
+
int32 uiSayId = rand()% (sizeof(m_auiRandomSay)/sizeof(int32));
DoScriptText(m_auiRandomSay[uiSayId], pCreature, pPlayer);
+
pPlayer->CastSpell(pCreature, SPELL_DUEL, false);
pPlayer->CastSpell(pPlayer, SPELL_DUEL_FLAG, true);
}
return true;
}
+
/*######
## npc_dark_rider_of_acherus
######*/
+
#define DESPAWN_HORSE 52267
#define SAY_DARK_RIDER "The realm of shadows awaits..."
+
struct TRINITY_DLL_DECL npc_dark_rider_of_acherusAI : public ScriptedAI
{
npc_dark_rider_of_acherusAI(Creature *c) : ScriptedAI(c) {}
+
uint32 PhaseTimer;
uint32 Phase;
bool Intro;
Unit *Target;
+
void Reset()
{
PhaseTimer = 4000;
@@ -432,10 +507,12 @@ struct TRINITY_DLL_DECL npc_dark_rider_of_acherusAI : public ScriptedAI
Intro = false;
Target = NULL;
}
+
void UpdateAI(const uint32 diff)
{
if (!Intro)
return;
+
if (PhaseTimer < diff)
{
switch(Phase)
@@ -462,11 +539,14 @@ struct TRINITY_DLL_DECL npc_dark_rider_of_acherusAI : public ScriptedAI
break;
}
}else PhaseTimer -= diff;
+
}
+
void InitDespawnHorse(Unit *who)
{
if (!who)
return;
+
Target = who;
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
m_creature->SetSpeed(MOVE_RUN, 0.4f);
@@ -474,14 +554,18 @@ struct TRINITY_DLL_DECL npc_dark_rider_of_acherusAI : public ScriptedAI
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Target->GetGUID());
Intro = true;
}
+
};
+
CreatureAI* GetAI_npc_dark_rider_of_acherus(Creature* pCreature)
{
return new npc_dark_rider_of_acherusAI(pCreature);
}
+
/*######
## npc_salanar_the_horseman
######*/
+
enum eSalanar
{
REALM_OF_SHADOWS = 52693,
@@ -490,9 +574,11 @@ enum eSalanar
CALL_DARK_RIDER = 52266,
SPELL_EFFECT_OVERTAKE = 52349
};
+
struct TRINITY_DLL_DECL npc_salanar_the_horsemanAI : public ScriptedAI
{
npc_salanar_the_horsemanAI(Creature *c) : ScriptedAI(c) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == DELIVER_STOLEN_HORSE)
@@ -511,9 +597,11 @@ struct TRINITY_DLL_DECL npc_salanar_the_horsemanAI : public ScriptedAI
}
}
}
+
void MoveInLineOfSight(Unit *who)
{
ScriptedAI::MoveInLineOfSight(who);
+
if (who->GetTypeId() == TYPEID_UNIT && who->IsVehicle() && me->IsWithinDistInMap(who, 5.0f))
{
if (Unit *charmer = who->GetCharmer())
@@ -528,6 +616,7 @@ struct TRINITY_DLL_DECL npc_salanar_the_horsemanAI : public ScriptedAI
CAST_CRE(who)->ForcedDespawn();
//CAST_CRE(who)->Respawn(true);
}
+
if (CAST_PLR(charmer)->HasAura(REALM_OF_SHADOWS))
charmer->RemoveAurasDueToSpell(REALM_OF_SHADOWS);
}
@@ -535,20 +624,25 @@ struct TRINITY_DLL_DECL npc_salanar_the_horsemanAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_salanar_the_horseman(Creature* pCreature)
{
return new npc_salanar_the_horsemanAI(pCreature);
}
+
/*######
## npc_ros_dark_rider
######*/
+
struct TRINITY_DLL_DECL npc_ros_dark_riderAI : public ScriptedAI
{
npc_ros_dark_riderAI(Creature *c) : ScriptedAI(c) {}
+
void EnterCombat(Unit *who)
{
me->ExitVehicle();
}
+
void Reset()
{
Creature* deathcharger = me->FindNearestCreature(28782, 30);
@@ -559,6 +653,7 @@ struct TRINITY_DLL_DECL npc_ros_dark_riderAI : public ScriptedAI
if (!me->GetVehicle() && deathcharger->IsVehicle() && deathcharger->GetVehicleKit()->HasEmptySeat(0))
me->EnterVehicle(deathcharger);
}
+
void JustDied(Unit *killer)
{
Creature* deathcharger = me->FindNearestCreature(28782, 30);
@@ -571,10 +666,12 @@ struct TRINITY_DLL_DECL npc_ros_dark_riderAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_ros_dark_rider(Creature* pCreature)
{
return new npc_ros_dark_riderAI(pCreature);
}
+
// correct way: 52312 52314 52555 ...
enum SG
{
@@ -584,9 +681,11 @@ enum SG
struct TRINITY_DLL_DECL npc_dkc1_gothikAI : public ScriptedAI
{
npc_dkc1_gothikAI(Creature *c) : ScriptedAI(c) {}
+
void MoveInLineOfSight(Unit *who)
{
ScriptedAI::MoveInLineOfSight(who);
+
if (who->GetEntry() == GHOULS && me->IsWithinDistInMap(who, 10.0f))
{
if (Unit *owner = who->GetOwner())
@@ -602,6 +701,7 @@ struct TRINITY_DLL_DECL npc_dkc1_gothikAI : public ScriptedAI
// stand next to Gothik and be commanded into the pit
// and dig into the ground.
CAST_CRE(who)->ForcedDespawn();
+
if (CAST_PLR(owner)->GetQuestStatus(12698) == QUEST_STATUS_COMPLETE)
owner->RemoveAllMinionsByEntry(GHOULS);
}
@@ -609,10 +709,12 @@ struct TRINITY_DLL_DECL npc_dkc1_gothikAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_dkc1_gothik(Creature* pCreature)
{
return new npc_dkc1_gothikAI(pCreature);
}
+
struct TRINITY_DLL_DECL npc_scarlet_ghoulAI : public ScriptedAI
{
npc_scarlet_ghoulAI(Creature *c) : ScriptedAI(c)
@@ -623,13 +725,15 @@ struct TRINITY_DLL_DECL npc_scarlet_ghoulAI : public ScriptedAI
//m_creature->MonsterSay("Mommy?",LANG_UNIVERSAL,0);
m_creature->SetReactState(REACT_DEFENSIVE);
}
+
void FindMinions(Unit *owner)
{
std::list<Creature*> MinionList;
owner->GetAllMinionsByEntry(MinionList,GHOULS);
+
if (!MinionList.empty())
{
- for (std::list<Creature*>::iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
+ for(std::list<Creature*>::iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
{
if (CAST_CRE(*itr)->GetOwner()->GetGUID() == m_creature->GetOwner()->GetGUID())
{
@@ -641,6 +745,7 @@ struct TRINITY_DLL_DECL npc_scarlet_ghoulAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isInCombat())
@@ -660,8 +765,10 @@ struct TRINITY_DLL_DECL npc_scarlet_ghoulAI : public ScriptedAI
}
}
}
+
if (!UpdateVictim())
return;
+
//ScriptedAI::UpdateAI(diff);
//Check if we have a current target
if (m_creature->getVictim()->GetEntry() == GHOSTS)
@@ -678,15 +785,19 @@ struct TRINITY_DLL_DECL npc_scarlet_ghoulAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_scarlet_ghoul(Creature* pCreature)
{
return new npc_scarlet_ghoulAI(pCreature);
}
+
/*####
## npc_scarlet_miner_cart
####*/
+
#define SPELL_CART_CHECK 54173
#define SPELL_CART_DRAG 52465
+
struct TRINITY_DLL_DECL npc_scarlet_miner_cartAI : public PassiveAI
{
npc_scarlet_miner_cartAI(Creature *c) : PassiveAI(c), minerGUID(0)
@@ -694,11 +805,14 @@ struct TRINITY_DLL_DECL npc_scarlet_miner_cartAI : public PassiveAI
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
me->SetDisplayId(me->GetCreatureInfo()->Modelid1); // H0 is horse
}
+
uint64 minerGUID;
+
void SetGUID(const uint64 &guid, int32 id)
{
minerGUID = guid;
}
+
void DoAction(const int32 param)
{
if(Creature *miner = Unit::GetCreature(*me, minerGUID))
@@ -710,6 +824,7 @@ struct TRINITY_DLL_DECL npc_scarlet_miner_cartAI : public PassiveAI
me->GetMotionMaster()->MoveFollow(miner, 1.0f, 0);
}
}
+
void PassengerBoarded(Unit *who, int8 seatId, bool apply)
{
if(!apply)
@@ -717,30 +832,37 @@ struct TRINITY_DLL_DECL npc_scarlet_miner_cartAI : public PassiveAI
miner->DisappearAndDie();
}
};
+
CreatureAI* GetAI_npc_scarlet_miner_cart(Creature *_Creature)
{
return new npc_scarlet_miner_cartAI(_Creature);
}
+
/*####
## npc_scarlet_miner
####*/
+
#define SAY_SCARLET_MINER1 "Where'd this come from? I better get this down to the ships before the foreman sees it!"
#define SAY_SCARLET_MINER2 "Now I can have a rest!"
+
struct TRINITY_DLL_DECL npc_scarlet_minerAI : public npc_escortAI
{
npc_scarlet_minerAI(Creature *c) : npc_escortAI(c)
{
me->SetReactState(REACT_PASSIVE);
}
+
uint32 IntroTimer;
uint32 IntroPhase;
uint64 carGUID;
+
void Reset()
{
carGUID = 0;
IntroTimer = 0;
IntroPhase = 0;
}
+
void InitWaypoint()
{
AddWaypoint(1, 2389.03, -5902.74, 109.014, 5000);
@@ -755,6 +877,7 @@ struct TRINITY_DLL_DECL npc_scarlet_minerAI : public npc_escortAI
AddWaypoint(10, 2217.265625, -6028.959473, 7.675705 );
AddWaypoint(11, 2202.595947, -6061.325684, 5.882018 );
AddWaypoint(12, 2188.974609, -6080.866699, 3.370027 );
+
if(rand()%2)
{
AddWaypoint(13, 2176.483887, -6110.407227, 1.855181 );
@@ -772,6 +895,7 @@ struct TRINITY_DLL_DECL npc_scarlet_minerAI : public npc_escortAI
AddWaypoint(17, 2271.739014, -6195.401855, 13.3542, 10000);
}
}
+
void InitCartQuest(Player *who)
{
carGUID = who->GetVehicleBase()->GetGUID();
@@ -779,6 +903,7 @@ struct TRINITY_DLL_DECL npc_scarlet_minerAI : public npc_escortAI
Start(false, false, who->GetGUID());
SetDespawnAtFar(false);
}
+
void WaypointReached(uint32 i)
{
switch (i)
@@ -811,6 +936,7 @@ struct TRINITY_DLL_DECL npc_scarlet_minerAI : public npc_escortAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (IntroPhase)
@@ -835,14 +961,18 @@ struct TRINITY_DLL_DECL npc_scarlet_minerAI : public npc_escortAI
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_scarlet_miner(Creature *_Creature)
{
return new npc_scarlet_minerAI(_Creature);
}
+
/*######
## go_inconspicuous_mine_car
######*/
+
#define SPELL_CART_SUMM 52463
+
bool GOHello_go_inconspicuous_mine_car(Player* pPlayer, GameObject* pGO)
{
if (pPlayer->GetQuestStatus(12701) == QUEST_STATUS_INCOMPLETE)
@@ -863,23 +993,29 @@ bool GOHello_go_inconspicuous_mine_car(Player* pPlayer, GameObject* pGO)
}
return true;
}
+
// npc 28912 quest 17217 boss 29001 mob 29007 go 191092
+
void AddSC_the_scarlet_enclave_c1()
{
Script *newscript;
+
// 12848 The Endless Hunger
newscript = new Script;
newscript->Name = "npc_unworthy_initiate";
newscript->GetAI = &GetAI_npc_unworthy_initiate;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_unworthy_initiate_anchor";
newscript->GetAI = &GetAI_npc_unworthy_initiate_anchor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_acherus_soul_prison";
newscript->pGOHello = &GOHello_go_acherus_soul_prison;
newscript->RegisterSelf();
+
// Death's Challenge
newscript = new Script;
newscript->Name = "npc_death_knight_initiate";
@@ -887,43 +1023,52 @@ void AddSC_the_scarlet_enclave_c1()
newscript->pGossipHello = &GossipHello_npc_death_knight_initiate;
newscript->pGossipSelect = &GossipSelect_npc_death_knight_initiate;
newscript->RegisterSelf();
+
// 12680 Grand Theft Palomino
newscript = new Script;
newscript->Name = "npc_salanar_the_horseman";
newscript->GetAI = &GetAI_npc_salanar_the_horseman;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_dark_rider_of_acherus";
newscript->GetAI = &GetAI_npc_dark_rider_of_acherus;
newscript->RegisterSelf();
+
// 12687 Into the Realm of Shadows
newscript = new Script;
newscript->Name = "npc_ros_dark_rider";
newscript->GetAI = &GetAI_npc_ros_dark_rider;
newscript->RegisterSelf();
+
// 12698 The Gift That Keeps On Giving
newscript = new Script;
newscript->Name = "npc_dkc1_gothik";
newscript->GetAI = &GetAI_npc_dkc1_gothik;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_scarlet_ghoul";
newscript->GetAI = &GetAI_npc_scarlet_ghoul;
newscript->RegisterSelf();
+
// Massacre At Light's Point
newscript = new Script;
newscript->Name = "npc_scarlet_miner";
newscript->GetAI = &GetAI_npc_scarlet_miner;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_scarlet_miner_cart";
newscript->GetAI = &GetAI_npc_scarlet_miner_cart;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_inconspicuous_mine_car";
newscript->pGOHello = &GOHello_go_inconspicuous_mine_car;
newscript->RegisterSelf();
}
+
/*
DELETE FROM `script_texts` WHERE `entry` IN(-1609301, -1609302);
INSERT INTO `script_texts` (`entry`,`content_default`,`type`,`language`,`emote`,`comment`) VALUES
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp
index b9f8ed31bf8..a592b0cc9b0 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter2.cpp
@@ -15,8 +15,10 @@
* 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"
#include "escort_ai.h"
+
//How to win friends and influence enemies
// texts signed for creature 28939 but used for 28939,28940,28610
enum win_friends
@@ -42,12 +44,15 @@ enum win_friends
SAY_PERSUADED6 = -1609519,
SPELL_PERSUASIVE_STRIKE = 52781
};
+
struct TRINITY_DLL_DECL npc_crusade_persuadedAI : public ScriptedAI
{
npc_crusade_persuadedAI(Creature *pCreature) : ScriptedAI(pCreature) {}
+
uint32 uiSpeech_timer;
uint32 uiSpeech_counter;
uint64 uiPlayerGUID;
+
void Reset()
{
uiSpeech_timer = 0;
@@ -56,6 +61,7 @@ struct TRINITY_DLL_DECL npc_crusade_persuadedAI : public ScriptedAI
me->SetReactState(REACT_AGGRESSIVE);
me->RestoreFaction();
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_PERSUASIVE_STRIKE && caster->GetTypeId() == TYPEID_PLAYER && me->isAlive() && !uiSpeech_counter)
@@ -70,14 +76,17 @@ struct TRINITY_DLL_DECL npc_crusade_persuadedAI : public ScriptedAI
me->GetMotionMaster()->MoveIdle();
me->SetReactState(REACT_PASSIVE);
DoCastAOE(58111, true);
+
DoScriptText(RAND(SAY_PERSUADE1,SAY_PERSUADE2,SAY_PERSUADE3,
SAY_PERSUADE4,SAY_PERSUADE5,SAY_PERSUADE6,
SAY_PERSUADE7), caster);
+
DoScriptText(RAND(SAY_CRUSADER1,SAY_CRUSADER2,SAY_CRUSADER3,
SAY_CRUSADER4,SAY_CRUSADER5,SAY_CRUSADER6), me);
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (uiSpeech_counter)
@@ -90,6 +99,7 @@ struct TRINITY_DLL_DECL npc_crusade_persuadedAI : public ScriptedAI
EnterEvadeMode();
return;
}
+
switch(uiSpeech_counter)
{
case 1: DoScriptText(SAY_PERSUADED1, me); uiSpeech_timer = 8000; break;
@@ -105,23 +115,30 @@ struct TRINITY_DLL_DECL npc_crusade_persuadedAI : public ScriptedAI
pPlayer->GroupEventHappens(12720, me);
return;
}
+
++uiSpeech_counter;
DoCastAOE(58111, true);
}else uiSpeech_timer -= diff;
+
return;
}
+
if(!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_crusade_persuaded(Creature* pCreature)
{
return new npc_crusade_persuadedAI (pCreature);
}
+
/*######
## npc_koltira_deathweaver
######*/
+
enum eKoltira
{
SAY_BREAKOUT1 = -1609561,
@@ -134,25 +151,32 @@ enum eKoltira
SAY_BREAKOUT8 = -1609568,
SAY_BREAKOUT9 = -1609569,
SAY_BREAKOUT10 = -1609570,
+
SPELL_KOLTIRA_TRANSFORM = 52899,
SPELL_ANTI_MAGIC_ZONE = 52894,
+
QUEST_BREAKOUT = 12727,
+
NPC_CRIMSON_ACOLYTE = 29007,
NPC_HIGH_INQUISITOR_VALROTH = 29001,
NPC_KOLTIRA_ALT = 28447,
+
//not sure about this id
//NPC_DEATH_KNIGHT_MOUNT = 29201,
MODEL_DEATH_KNIGHT_MOUNT = 25278
};
+
struct TRINITY_DLL_DECL npc_koltira_deathweaverAI : public npc_escortAI
{
npc_koltira_deathweaverAI(Creature *pCreature) : npc_escortAI(pCreature)
{
me->SetReactState(REACT_DEFENSIVE);
}
+
uint32 m_uiWave;
uint32 m_uiWave_Timer;
uint64 m_uiValrothGUID;
+
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
@@ -165,6 +189,7 @@ struct TRINITY_DLL_DECL npc_koltira_deathweaverAI : public npc_escortAI
me->RemoveAura(SPELL_ANTI_MAGIC_ZONE);
}
}
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -199,25 +224,31 @@ struct TRINITY_DLL_DECL npc_koltira_deathweaverAI : public npc_escortAI
break;
}
}
+
void JustSummoned(Creature* pSummoned)
{
if (Player* pPlayer = GetPlayerForEscort())
{
pSummoned->AI()->AttackStart(pPlayer);
}
+
if (pSummoned->GetEntry() == NPC_HIGH_INQUISITOR_VALROTH)
m_uiValrothGUID = pSummoned->GetGUID();
+
pSummoned->AddThreat(me, 0.0f);
pSummoned->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
+
void SummonAcolyte(uint32 uiAmount)
{
- for (uint32 i = 0; i < uiAmount; ++i)
+ for(uint32 i = 0; i < uiAmount; ++i)
me->SummonCreature(NPC_CRIMSON_ACOLYTE, 1642.329, -6045.818, 127.583, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
}
+
void UpdateAI(const uint32 uiDiff)
{
npc_escortAI::UpdateAI(uiDiff);
+
if (HasEscortState(STATE_ESCORT_PAUSED))
{
if (m_uiWave_Timer < uiDiff)
@@ -247,6 +278,7 @@ struct TRINITY_DLL_DECL npc_koltira_deathweaverAI : public npc_escortAI
case 4:
{
Creature* pTemp = Unit::GetCreature(*me, m_uiValrothGUID);
+
if (!pTemp || !pTemp->isAlive())
{
DoScriptText(SAY_BREAKOUT8, me);
@@ -270,6 +302,7 @@ struct TRINITY_DLL_DECL npc_koltira_deathweaverAI : public npc_escortAI
SetEscortPaused(false);
break;
}
+
++m_uiWave;
}
else
@@ -277,20 +310,24 @@ struct TRINITY_DLL_DECL npc_koltira_deathweaverAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_npc_koltira_deathweaver(Creature* pCreature)
{
return new npc_koltira_deathweaverAI(pCreature);
}
+
bool QuestAccept_npc_koltira_deathweaver(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_BREAKOUT)
{
pCreature->SetStandState(UNIT_STAND_STATE_STAND);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_koltira_deathweaverAI,pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID());
}
return true;
}
+
//Scarlet courier
enum ScarletCourierEnum
{
@@ -300,30 +337,37 @@ enum ScarletCourierEnum
GO_INCONSPICUOUS_TREE = 191144,
NPC_SCARLET_COURIER = 29076
};
+
struct TRINITY_DLL_DECL mob_scarlet_courierAI : public ScriptedAI
{
mob_scarlet_courierAI(Creature *pCreature) : ScriptedAI(pCreature) {}
+
uint32 uiStage;
uint32 uiStage_timer;
+
void Reset()
{
me->Mount(14338); // not sure about this id
uiStage = 1;
uiStage_timer = 3000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_TREE2, me);
me->Unmount();
uiStage = 0;
}
+
void MovementInform(uint32 type, uint32 id)
{
if(type != POINT_MOTION_TYPE)
return;
+
if(id == 1)
uiStage = 2;
}
+
void UpdateAI(const uint32 diff)
{
if(uiStage && !me->isInCombat())
@@ -352,16 +396,21 @@ struct TRINITY_DLL_DECL mob_scarlet_courierAI : public ScriptedAI
uiStage = 0;
}else uiStage_timer -= diff;
}
+
if(!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_scarlet_courier(Creature* pCreature)
{
return new mob_scarlet_courierAI (pCreature);
}
+
//Koltira & Valroth- Breakout
+
enum valroth
{
SAY_VALROTH1 = -1609581,
@@ -375,23 +424,28 @@ enum valroth
SPELL_VALROTH_SMITE = 52926,
SPELL_SUMMON_VALROTH_REMAINS = 52929
};
+
struct TRINITY_DLL_DECL mob_high_inquisitor_valrothAI : public ScriptedAI
{
mob_high_inquisitor_valrothAI(Creature *pCreature) : ScriptedAI(pCreature) {}
+
uint32 uiRenew_timer;
uint32 uiInquisitor_Penance_timer;
uint32 uiValroth_Smite_timer;
+
void Reset()
{
uiRenew_timer = 1000;
uiInquisitor_Penance_timer = 2000;
uiValroth_Smite_timer = 1000;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_VALROTH2, me);
DoCast(who, SPELL_VALROTH_SMITE);
}
+
void UpdateAI(const uint32 diff)
{
if (uiRenew_timer < diff)
@@ -400,36 +454,43 @@ struct TRINITY_DLL_DECL mob_high_inquisitor_valrothAI : public ScriptedAI
DoCast(me, SPELL_RENEW);
uiRenew_timer = 1000 + rand()%5000;
}else uiRenew_timer -= diff;
+
if (uiInquisitor_Penance_timer < diff)
{
Shout();
DoCast(me->getVictim(), SPELL_INQUISITOR_PENANCE);
uiInquisitor_Penance_timer = 2000 + rand()%5000;
}else uiInquisitor_Penance_timer -= diff;
+
if (uiValroth_Smite_timer < diff)
{
Shout();
DoCast(me->getVictim(), SPELL_VALROTH_SMITE);
uiValroth_Smite_timer = 1000 + rand()%5000;
}else uiValroth_Smite_timer -= diff;
+
DoMeleeAttackIfReady();
}
+
void Shout()
{
if(rand()%100 < 15)
DoScriptText(RAND(SAY_VALROTH3,SAY_VALROTH4,SAY_VALROTH5), me);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_VALROTH6, me);
killer->CastSpell(me, SPELL_SUMMON_VALROTH_REMAINS, true);
}
};
+
CreatureAI* GetAI_mob_high_inquisitor_valroth(Creature* pCreature)
{
return new mob_high_inquisitor_valrothAI (pCreature);
}
+
/*######
## npc_a_special_surprise
######*/
@@ -490,21 +551,27 @@ enum SpecialSurprise
SAY_EXEC_TIME_10 = -1609076,
SAY_EXEC_WAITING = -1609077,
EMOTE_DIES = -1609078,
+
NPC_PLAGUEFIST = 29053
};
+
struct TRINITY_DLL_DECL npc_a_special_surpriseAI : public ScriptedAI
{
npc_a_special_surpriseAI(Creature *pCreature) : ScriptedAI(pCreature) {}
+
uint32 ExecuteSpeech_Timer;
uint32 ExecuteSpeech_Counter;
uint64 PlayerGUID;
+
void Reset()
{
ExecuteSpeech_Timer = 0;
ExecuteSpeech_Counter = 0;
PlayerGUID = 0;
+
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
+
bool MeetQuestCondition(Unit* pPlayer)
{
switch(me->GetEntry())
@@ -550,15 +617,19 @@ struct TRINITY_DLL_DECL npc_a_special_surpriseAI : public ScriptedAI
return true;
break;
}
+
return false;
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (PlayerGUID || pWho->GetTypeId() != TYPEID_PLAYER || !pWho->IsWithinDist(me, INTERACTION_DISTANCE))
return;
+
if (MeetQuestCondition(pWho))
PlayerGUID = pWho->GetGUID();
}
+
void UpdateAI(const uint32 diff)
{
if (PlayerGUID && !me->getVictim() && me->isAlive())
@@ -566,12 +637,15 @@ struct TRINITY_DLL_DECL npc_a_special_surpriseAI : public ScriptedAI
if (ExecuteSpeech_Timer < diff)
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
if (!pPlayer)
{
Reset();
return;
}
+
//TODO: simplify text's selection
+
switch(pPlayer->getRace())
{
case RACE_HUMAN:
@@ -855,10 +929,12 @@ struct TRINITY_DLL_DECL npc_a_special_surpriseAI : public ScriptedAI
}
break;
}
+
if (ExecuteSpeech_Counter >= 9)
ExecuteSpeech_Timer = 15000;
else
ExecuteSpeech_Timer = 7000;
+
++ExecuteSpeech_Counter;
}
else
@@ -866,39 +942,47 @@ struct TRINITY_DLL_DECL npc_a_special_surpriseAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_a_special_surprise(Creature* pCreature)
{
return new npc_a_special_surpriseAI(pCreature);
}
+
void AddSC_the_scarlet_enclave_c2()
{
Script *newscript;
+
// How to win friends and influence enemies
newscript = new Script;
newscript->Name = "npc_crusade_persuaded";
newscript->GetAI = &GetAI_npc_crusade_persuaded;
newscript->RegisterSelf();
+
// Ambush At The Overlook
newscript = new Script;
newscript->Name = "mob_scarlet_courier";
newscript->GetAI = &GetAI_mob_scarlet_courier;
newscript->RegisterSelf();
+
// 12727 Bloody Breakout
newscript = new Script;
newscript->Name = "npc_koltira_deathweaver";
newscript->GetAI = &GetAI_npc_koltira_deathweaver;
newscript->pQuestAccept = &QuestAccept_npc_koltira_deathweaver;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_high_inquisitor_valroth";
newscript->GetAI = &GetAI_mob_high_inquisitor_valroth;
newscript->RegisterSelf();
+
// A Special Suprise
newscript = new Script;
newscript->Name = "npc_a_special_surprise";
newscript->GetAI = &GetAI_npc_a_special_surprise;
newscript->RegisterSelf();
}
+
/*
-- Bloody Breakout
UPDATE `creature_template` SET `ScriptName`='npc_koltira_deathweaver' WHERE `entry`='28912';
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp
index 2d42374f84c..0f64371f528 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/chapter5.cpp
@@ -15,8 +15,10 @@
* 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"
#include "escort_ai.h"
+
#define LESS_MOB // if you do not have a good server and do not want it to be laggy as hell
//Light of Dawn
enum mograine
@@ -42,9 +44,11 @@ enum mograine
#endif
ENCOUNTER_TOTAL_DAWN = 300, // Total number
ENCOUNTER_TOTAL_SCOURGE = 10000,
+
WORLD_STATE_REMAINS = 3592,
WORLD_STATE_COUNTDOWN = 3603,
WORLD_STATE_EVENT_BEGIN = 3605,
+
SAY_LIGHT_OF_DAWN01 = -1609201, // pre text
SAY_LIGHT_OF_DAWN02 = -1609202,
SAY_LIGHT_OF_DAWN03 = -1609203,
@@ -113,6 +117,7 @@ enum mograine
SAY_LIGHT_OF_DAWN66 = -1609266, // Highlord Tirion Fordring
SAY_LIGHT_OF_DAWN67 = -1609267, // Highlord Tirion Fordring
SAY_LIGHT_OF_DAWN68 = -1609268, // Highlord Darion Mograine
+
EMOTE_LIGHT_OF_DAWN01 = -1609269, // Emotes
EMOTE_LIGHT_OF_DAWN02 = -1609270,
EMOTE_LIGHT_OF_DAWN03 = -1609271,
@@ -131,8 +136,10 @@ enum mograine
EMOTE_LIGHT_OF_DAWN16 = -1609284,
EMOTE_LIGHT_OF_DAWN17 = -1609285,
EMOTE_LIGHT_OF_DAWN18 = -1609286,
+
GO_LIGHT_OF_DAWN = 191330,
SPELL_THE_LIGHT_OF_DAWN_Q = 53606, // quest credit
+
// ---- Dark Knight npc --------------------
// Highlord Darion Mograine
NPC_HIGHLORD_DARION_MOGRAINE = 29173,
@@ -146,6 +153,7 @@ enum mograine
SPELL_ALEXANDROS_MOGRAINE_SPAWN = 53667,
SPELL_MOGRAINE_CHARGE = 53679,
SPELL_ASHBRINGER = 53701,
+
// Koltira Deathweaver & Orbaz Bloodbane are using the same abilities
NPC_KOLTIRA_DEATHWEAVER = 29199,
NPC_ORBAZ_BLOODBANE = 29204, // this guy fleed
@@ -155,6 +163,7 @@ enum mograine
SPELL_ICY_TOUCH2 = 52372,
SPELL_PLAGUE_STRIKE1 = 50668,
// all do SPELL_HERO_AGGRO_AURA 53627
+
// Lich King
NPC_THE_LICH_KING = 29183, // show up at end
SPELL_APOCALYPSE = 53210,
@@ -163,20 +172,26 @@ enum mograine
SPELL_SOUL_FEAST_TIRION = 53685, // on Tirion
SPELL_ICEBOUND_VISAGE = 53274, // not sure what is it for
SPELL_REBUKE = 53680,
+
// others
NPC_RAMPAGING_ABOMINATION = 29186,
SPELL_CLEAVE1 = 53633,
SPELL_SCOURGE_HOOK = 50335,
SPELL_SCOURGE_AGGRO_AURA = 53624,
+
NPC_FLESH_BEHEMOTH = 29190, // giant guy
SPELL_STOMP = 53634,
SPELL_THUNDERCLAP = 36706,
SPELL_HERO_AGGRO_AURA = 53627,
+
NPC_ACHERUS_GHOUL = 29219, // just ghoul....
SPELL_GHOULPLOSION = 53632,
+
NPC_WARRIOR_OF_THE_FROZEN_WASTES = 29206, // use SPELL_CLEAVE 53631
+
NPC_HIGHLORD_ALEXANDROS_MOGRAINE = 29227, // ghost
NPC_DARION_MOGRAINE = 29228, // ghost
+
// ---- Dawn npc --------------------
// Highlord Tirion Fordring
NPC_HIGHLORD_TIRION_FORDRING = 29175,
@@ -185,46 +200,56 @@ enum mograine
SPELL_REBIRTH_OF_THE_ASHBRINGER = 53702,
SPELL_TIRION_CHARGE = 53705,
SPELL_TIRION_CHARGE_VISUAL = 53706,
+
// others
NPC_KORFAX_CHAMPION_OF_THE_LIGHT = 29176,
SPELL_CLEAVE = 53631,
SPELL_HEROIC_LEAP = 53625,
+
NPC_LORD_MAXWELL_TYROSUS = 29178,
NPC_LEONID_BARTHALOMEW_THE_REVERED = 29179,
NPC_DUKE_NICHOLAS_ZVERENHOFF = 29180,
+
NPC_COMMANDER_ELIGOR_DAWNBRINGER = 29177,
SPELL_HOLY_LIGHT2 = 37979,
+
NPC_RAYNE = 29181,
SPELL_REJUVENATION = 20664,
SPELL_STARFALL = 20678,
SPELL_TRANQUILITY = 25817,
SPELL_WRATH = 21807,
+
NPC_DEFENDER_OF_THE_LIGHT = 29174, // also does SPELL_HEROIC_LEAP 53625
SPELL_HOLY_LIGHT1 = 29427,
SPELL_HOLY_STRIKE = 53643,
SPELL_HOLY_WRATH = 53638,
SPELL_UPPERCUT = 53629,
+
NPC_RIMBLAT_EARTHSHATTER = 29182,
SPELL_CHAIN_HEAL = 33642,
SPELL_THUNDER = 53630
};
+
struct Locations
{
float x, y, z, o;
uint32 id;
};
+
void UpdateWorldState(Map *map, uint32 id, uint32 state)
{
Map::PlayerList const& players = map->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* pPlayer = itr->getSource())
pPlayer->SendUpdateWorldState(id,state);
}
}
}
+
static Locations LightofDawnLoc[]=
{
{2281.335, -5300.409, 85.170, 0}, // 0 Tirion Fordring loc
@@ -258,12 +283,14 @@ static Locations LightofDawnLoc[]=
{2272.709, -5255.552, 78.226, 0}, // 28 Lich king kicked
{2273.972, -5257.676, 78.862, 0} // 29 Lich king moves forward
};
+
struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
{
npc_highlord_darion_mograineAI(Creature *pCreature) : npc_escortAI(pCreature)
{
Reset();
}
+
bool bIsBattle;
uint32 uiStep;
uint32 uiPhase_timer;
@@ -271,6 +298,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uint32 uiTotal_dawn;
uint32 uiTotal_scourge;
uint32 uiSummon_counter;
+
// Darion Mograine
uint32 uiAnti_magic_zone;
uint32 uiDeath_strike;
@@ -280,6 +308,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uint32 uiFight_speech;
uint32 uiSpawncheck;
uint32 uiTargetcheck;
+
// Dawn
uint64 uiTirionGUID;
uint64 uiAlexandrosGUID;
@@ -290,6 +319,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uint64 uiRayneGUID;
uint64 uiDefenderGUID[ENCOUNTER_DEFENDER_NUMBER];
uint64 uiEarthshatterGUID[ENCOUNTER_EARTHSHATTER_NUMBER];
+
// Death
uint64 uiKoltiraGUID;
uint64 uiOrbazGUID;
@@ -299,6 +329,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uint64 uiBehemothGUID[ENCOUNTER_BEHEMOTH_NUMBER];
uint64 uiGhoulGUID[ENCOUNTER_GHOUL_NUMBER];
uint64 uiWarriorGUID[ENCOUNTER_WARRIOR_NUMBER];
+
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
@@ -310,19 +341,24 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiTotal_dawn = ENCOUNTER_TOTAL_DAWN;
uiTotal_scourge = ENCOUNTER_TOTAL_SCOURGE;
uiSummon_counter = 0;
+
uiAnti_magic_zone = 1000 + rand()%5000;
uiDeath_strike = 5000 + rand()%5000;
uiDeath_embrace = 5000 + rand()%5000;
uiIcy_touch = 5000 + rand()%5000;
uiUnholy_blight = 5000 + rand()%5000;
+
uiFight_speech = 15000;
uiSpawncheck = 1000;
uiTargetcheck = 10000;
+
me->SetStandState(UNIT_STAND_STATE_STAND);
me->Mount(25279);
+
UpdateWorldState(me->GetMap(), WORLD_STATE_REMAINS, 0);
//UpdateWorldState(me->GetMap(), WORLD_STATE_COUNTDOWN, 0);
UpdateWorldState(me->GetMap(), WORLD_STATE_EVENT_BEGIN, 0);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
pTemp->setDeathState(JUST_DIED);
if (Creature* pTemp = Unit::GetCreature(*me, uiKorfaxGUID))
@@ -333,23 +369,26 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->setDeathState(JUST_DIED);
if (Creature* pTemp = Unit::GetCreature(*me, uiRayneGUID))
pTemp->setDeathState(JUST_DIED);
+
uiTirionGUID = NULL;
uiKorfaxGUID = NULL;
uiMaxwellGUID = NULL;
uiEligorGUID = NULL;
uiRayneGUID = NULL;
- for (uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i)
+
+ for(uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i)
{
if (Creature* pTemp = Unit::GetCreature(*me, uiDefenderGUID[i]))
pTemp->setDeathState(JUST_DIED);
uiDefenderGUID[i] = 0;
}
- for (uint8 i = 0; i < ENCOUNTER_EARTHSHATTER_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_EARTHSHATTER_NUMBER; ++i)
{
if (Creature* pTemp = Unit::GetCreature(*me, uiEarthshatterGUID[i]))
pTemp->setDeathState(JUST_DIED);
uiEarthshatterGUID[i] = 0;
}
+
if (Creature* pTemp = Unit::GetCreature(*me, uiKoltiraGUID))
pTemp->setDeathState(JUST_DIED);
if (Creature* pTemp = Unit::GetCreature(*me, uiOrbazGUID))
@@ -358,29 +397,30 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->setDeathState(JUST_DIED);
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
pTemp->setDeathState(JUST_DIED);
+
uiKoltiraGUID = NULL;
uiOrbazGUID = NULL;
uiThassarianGUID = NULL;
uiLichKingGUID = NULL;
- for (uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
{
if (Creature* pTemp = Unit::GetCreature(*me, uiAbominationGUID[i]))
pTemp->setDeathState(JUST_DIED);
uiAbominationGUID[i] = 0;
}
- for (uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
{
if (Creature* pTemp = Unit::GetCreature(*me, uiBehemothGUID[i]))
pTemp->setDeathState(JUST_DIED);
uiBehemothGUID[i] = 0;
}
- for (uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
{
if (Creature* pTemp = Unit::GetCreature(*me, uiGhoulGUID[i]))
pTemp->setDeathState(JUST_DIED);
uiGhoulGUID[i] = 0;
}
- for (uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
{
if (Creature* pTemp = Unit::GetCreature(*me, uiWarriorGUID[i]))
pTemp->setDeathState(JUST_DIED);
@@ -388,12 +428,15 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
}
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (who == me)
return;
+
if (me->Attack(who, true))
{
me->AddThreat(who, 0.0f);
@@ -402,18 +445,22 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
DoStartMovement(who);
}
}
+
void MoveInLineOfSight(Unit* who)
{
if (!who)
return;
+
if (who->isTargetableForAttack() && me->IsHostileTo(who))
if (me->IsWithinDistInMap(who, 20) && me->IsWithinLOSInMap(who))
AttackStart(who);
}
+
void SetHoldState(bool bOnHold)
{
SetEscortPaused(bOnHold);
}
+
void WaypointReached(uint32 i)
{
switch(i)
@@ -424,28 +471,33 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
break;
case 1:
SetHoldState(true);
+
SpawnNPC();
if (Creature* pTemp = Unit::GetCreature(*me, uiKorfaxGUID))
DoScriptText(SAY_LIGHT_OF_DAWN07, pTemp);
if (Creature* pTemp = Unit::GetCreature(*me, uiMaxwellGUID))
DoScriptText(SAY_LIGHT_OF_DAWN08, pTemp);
- for (uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
+
+ for(uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
NPCChangeTarget(uiGhoulGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
NPCChangeTarget(uiWarriorGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
NPCChangeTarget(uiAbominationGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
NPCChangeTarget(uiBehemothGUID[i]);
NPCChangeTarget(uiKoltiraGUID);
NPCChangeTarget(uiOrbazGUID);
NPCChangeTarget(uiThassarianGUID);
+
me->Unmount();
me->CastSpell(me, SPELL_THE_MIGHT_OF_MOGRAINE, true); // need to fix, on player only
+
if (Creature* pTemp = Unit::GetCreature(*me, uiKoltiraGUID))
pTemp->Unmount();
if (Creature* pTemp = Unit::GetCreature(*me, uiThassarianGUID))
pTemp->Unmount();
+
bIsBattle = true;
break;
case 2:
@@ -455,6 +507,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
case 3:
{
Unit* pTirion = Unit::GetCreature(*me, uiTirionGUID);
+
DoScriptText(EMOTE_LIGHT_OF_DAWN05, me);
if (me->HasAura(SPELL_THE_LIGHT_OF_DAWN, 0))
me->RemoveAurasDueToSpell(SPELL_THE_LIGHT_OF_DAWN);
@@ -493,6 +546,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
case 4:
DoScriptText(SAY_LIGHT_OF_DAWN27, me);
me->SetStandState(UNIT_STAND_STATE_KNEEL);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiKoltiraGUID))
pTemp->SetStandState(UNIT_STAND_STATE_KNEEL);
if (Creature* pTemp = Unit::GetCreature(*me, uiThassarianGUID))
@@ -521,9 +575,11 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
+
if (!bIsBattle)
{
if (uiPhase_timer < diff)
@@ -534,6 +590,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
case 0: // countdown
//UpdateWorldState(me->GetMap(), WORLD_STATE_COUNTDOWN, 1);
break;
+
case 1: // just delay
//UpdateWorldState(me->GetMap(), WORLD_STATE_REMAINS, 1);
UpdateWorldState(me->GetMap(), WORLD_STATE_COUNTDOWN, 0);
@@ -541,6 +598,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
JumpToNextStep(3000);
break;
+
case 2:
DoScriptText(SAY_LIGHT_OF_DAWN04, me);
if (Creature* pKoltira = GetClosestCreatureWithEntry(me, NPC_KOLTIRA_DEATHWEAVER, 50.0f))
@@ -551,10 +609,12 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiThassarianGUID = pThassarian->GetGUID();
JumpToNextStep(10000);
break;
+
case 3: // rise
DoScriptText(SAY_LIGHT_OF_DAWN05, me);
JumpToNextStep(3000);
break;
+
case 4: // summon ghoul
// Dunno whats the summon spell, so workaround
DoCast(me, 33271); // shack effect
@@ -573,6 +633,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiStep++;
}
break;
+
case 5: // summon abomination
DoCast(me, 33271); // shack effect
uiPhase_timer = 500;
@@ -590,6 +651,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiStep++;
}
break;
+
case 6: // summon warrior
DoCast(me, 33271); // shack effect
uiPhase_timer = 500;
@@ -607,6 +669,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiStep++;
}
break;
+
case 7: // summon warrior
DoCast(me, 33271); // shack effect
uiPhase_timer = 500;
@@ -624,10 +687,12 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiStep++;
}
break;
+
case 8: // summon announce
DoScriptText(SAY_LIGHT_OF_DAWN06, me);
JumpToNextStep(5000);
break;
+
case 9: // charge begins
SetHoldState(false);
if (Creature* pTemp = Unit::GetCreature(*me, uiKoltiraGUID))
@@ -645,41 +710,46 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].x+rand()%30, LightofDawnLoc[0].y+rand()%30, LightofDawnLoc[0].z);
}
- for (uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
if (Creature* pTemp = Unit::GetCreature(*me, uiAbominationGUID[i]))
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].x+rand()%30, LightofDawnLoc[0].y+rand()%30, LightofDawnLoc[0].z);
- for (uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
if (Creature* pTemp = Unit::GetCreature(*me, uiBehemothGUID[i]))
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].x+rand()%30, LightofDawnLoc[0].y+rand()%30, LightofDawnLoc[0].z);
- for (uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
if (Creature* pTemp = Unit::GetCreature(*me, uiGhoulGUID[i]))
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].x+rand()%30, LightofDawnLoc[0].y+rand()%30, LightofDawnLoc[0].z);
- for (uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
if (Creature* pTemp = Unit::GetCreature(*me, uiWarriorGUID[i]))
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[0].x+rand()%30, LightofDawnLoc[0].y+rand()%30, LightofDawnLoc[0].z);
JumpToNextStep(5000);
break;
+
// ******* After battle *****************************************************************
case 11: // Tirion starts to speak
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN28, pTemp);
JumpToNextStep(21000);
break;
+
case 12:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN29, pTemp);
JumpToNextStep(13000);
break;
+
case 13:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN30, pTemp);
JumpToNextStep(13000);
break;
+
case 14:
me->SetStandState(UNIT_STAND_STATE_STAND);
DoScriptText(SAY_LIGHT_OF_DAWN31, me);
JumpToNextStep(7000);
break;
+
case 15: // summon gate
if (Unit* pTemp = me->SummonCreature(NPC_HIGHLORD_ALEXANDROS_MOGRAINE, LightofDawnLoc[22].x, LightofDawnLoc[22].y, LightofDawnLoc[22].z, LightofDawnLoc[22].o, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 300000))
{
@@ -690,6 +760,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(4000);
break;
+
case 16: // Alexandros out
if (Creature* pTemp = Unit::GetCreature(*me, uiAlexandrosGUID))
{
@@ -700,11 +771,13 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
SetHoldState(false); // makes darion turns back
JumpToNextStep(5000);
break;
+
case 17:
me->SetStandState(UNIT_STAND_STATE_KNEEL);
DoScriptText(SAY_LIGHT_OF_DAWN34, me);
JumpToNextStep(5000);
break;
+
case 18: // Darion's spirit out
if (Unit* pTemp = me->SummonCreature(NPC_DARION_MOGRAINE, LightofDawnLoc[24].x, LightofDawnLoc[24].y, LightofDawnLoc[24].z, LightofDawnLoc[24].o, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 300000))
{
@@ -714,6 +787,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(4000);
break;
+
case 19: // runs to father
if (Creature* pTemp = Unit::GetCreature(*me, uiDarionGUID))
{
@@ -722,53 +796,65 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(4000);
break;
+
case 20:
if (Creature* pTemp = Unit::GetCreature(*me, uiDarionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN36, pTemp);
JumpToNextStep(4000);
break;
+
case 21:
if (Creature* pTemp = Unit::GetCreature(*me, uiDarionGUID))
DoScriptText(EMOTE_LIGHT_OF_DAWN08, pTemp);
JumpToNextStep(4000);
break;
+
case 22:
if (Creature* pTemp = Unit::GetCreature(*me, uiAlexandrosGUID))
DoScriptText(SAY_LIGHT_OF_DAWN37, pTemp);
JumpToNextStep(8000);
break;
+
case 23:
if (Creature* pTemp = Unit::GetCreature(*me, uiDarionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN38, pTemp);
JumpToNextStep(8000);
break;
+
case 24:
if (Creature* pTemp = Unit::GetCreature(*me, uiAlexandrosGUID))
DoScriptText(SAY_LIGHT_OF_DAWN39, pTemp);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID)) // Tirion moves forward here
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[1].x, LightofDawnLoc[1].y, LightofDawnLoc[1].z);
+
JumpToNextStep(15000);
break;
+
case 25:
if (Creature* pTemp = Unit::GetCreature(*me, uiDarionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN40, pTemp);
JumpToNextStep(11000);
break;
+
case 26:
if (Creature* pTemp = Unit::GetCreature(*me, uiAlexandrosGUID))
DoScriptText(SAY_LIGHT_OF_DAWN41, pTemp);
JumpToNextStep(5000);
break;
+
case 27:
if (Creature* pTemp = Unit::GetCreature(*me, uiDarionGUID))
pTemp->setDeathState(JUST_DIED);
JumpToNextStep(24000);
break;
+
case 28:
if (Creature* pTemp = Unit::GetCreature(*me, uiAlexandrosGUID))
DoScriptText(SAY_LIGHT_OF_DAWN42, pTemp);
JumpToNextStep(6000);
break;
+
case 29: // lich king spawns
if (Unit* pTemp = me->SummonCreature(NPC_THE_LICH_KING, LightofDawnLoc[26].x, LightofDawnLoc[26].y, LightofDawnLoc[26].z, LightofDawnLoc[26].o, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 300000))
{
@@ -779,6 +865,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(2000);
break;
+
case 30:
if (Creature* pTemp = Unit::GetCreature(*me, uiAlexandrosGUID)) // just hide him
{
@@ -792,17 +879,20 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(3000);
break;
+
case 31:
me->SetStandState(UNIT_STAND_STATE_STAND);
DoScriptText(EMOTE_LIGHT_OF_DAWN10, me);
DoScriptText(SAY_LIGHT_OF_DAWN44, me);
JumpToNextStep(3000);
break;
+
case 32:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[27].x, LightofDawnLoc[27].y, LightofDawnLoc[27].z);
JumpToNextStep(6000);
break;
+
case 33: // Darion supports to jump to lich king here
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoCast(me, SPELL_MOGRAINE_CHARGE); // jumping charge
@@ -812,6 +902,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
SetHoldState(false);
JumpToNextStep(0);
break;
+
case 35: // Lich king counterattacks
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
{
@@ -823,25 +914,30 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
SetHoldState(false); // Darion got kicked by lich king
JumpToNextStep(0);
break;
+
case 37: // Lich king counterattacks
me->SetStandState(UNIT_STAND_STATE_KNEEL);
JumpToNextStep(3000);
break;
+
case 38:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN47, pTemp);
JumpToNextStep(8000);
break;
+
case 39:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(SAY_LIGHT_OF_DAWN48, pTemp);
JumpToNextStep(15000);
break;
+
case 40:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(SAY_LIGHT_OF_DAWN49, pTemp);
JumpToNextStep(17000);
break;
+
case 41: // Lich king - Apocalypse
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
{
@@ -857,6 +953,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(2000);
break;
+
case 42: // Maxwell yells for attack
{
float fLichPositionX, fLichPositionY, fLichPositionZ;
@@ -866,6 +963,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
fLichPositionY = pTemp->GetPositionY();
fLichPositionZ = pTemp->GetPositionZ();
}
+
if (fLichPositionX && fLichPositionY)
{
Unit* pTemp;
@@ -876,6 +974,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->setFaction(me->getFaction());
pTemp->GetMotionMaster()->MovePoint(0, fLichPositionX, fLichPositionY, fLichPositionZ);
uiDefenderGUID[0] = pTemp->GetGUID();
+
pTemp = me->SummonCreature(NPC_RIMBLAT_EARTHSHATTER, LightofDawnLoc[0].x+rand()%10, LightofDawnLoc[0].y+rand()%10, LightofDawnLoc[0].z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 10000);
pTemp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_ATTACK_UNARMED);
pTemp->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
@@ -910,9 +1009,11 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(4500);
break;
+
case 43: // They all got kicked
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(EMOTE_LIGHT_OF_DAWN13, pTemp);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiMaxwellGUID))
{
pTemp->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
@@ -948,6 +1049,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(3000);
break;
+
case 44: // make them stand up
if (Creature* pTemp = Unit::GetCreature(*me, uiMaxwellGUID))
pTemp->SetStandState(UNIT_STAND_STATE_STAND);
@@ -957,10 +1059,12 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->SetStandState(UNIT_STAND_STATE_STAND);
JumpToNextStep(1000);
break;
+
case 45:
DoScriptText(SAY_LIGHT_OF_DAWN52, me);
JumpToNextStep(5000);
break;
+
case 46: // Darion stand up, "not today"
me->SetSpeed(MOVE_RUN, 1.0f);
me->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
@@ -969,6 +1073,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
SetHoldState(false); // Darion throws sword
JumpToNextStep(7000);
break;
+
case 47: // Ashbringer rebirth
me->SetStandState(UNIT_STAND_STATE_KNEEL);
DoScriptText(EMOTE_LIGHT_OF_DAWN15, me);
@@ -980,6 +1085,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(1000);
break;
+
case 48: // Show the cleansing effect (dawn of light)
//if (GameObject* pGo = me->GetMap()->GetGameObject(uiDawnofLightGUID))
// pGo->SetPhaseMask(128, true);
@@ -995,21 +1101,25 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->InterruptNonMeleeSpells(false);
JumpToNextStep(2500);
break;
+
case 49:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN54, pTemp);
JumpToNextStep(4000);
break;
+
case 50:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(SAY_LIGHT_OF_DAWN55, pTemp);
JumpToNextStep(5000);
break;
+
case 51:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN56, pTemp);
JumpToNextStep(1000);
break;
+
case 52: // Tiron charges
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
{
@@ -1024,11 +1134,13 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(1500);
break;
+
case 53:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(SAY_LIGHT_OF_DAWN57, pTemp);
JumpToNextStep(1000);
break;
+
case 54:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
{
@@ -1038,26 +1150,31 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(4000);
break;
+
case 55:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
pTemp->SetStandState(UNIT_STAND_STATE_KNEEL);
JumpToNextStep(2000);
break;
+
case 56:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
pTemp->SetStandState(UNIT_STAND_STATE_STAND);
JumpToNextStep(1500);
break;
+
case 57:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(SAY_LIGHT_OF_DAWN58, pTemp);
JumpToNextStep(10000);
break;
+
case 58:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
DoScriptText(SAY_LIGHT_OF_DAWN59, pTemp);
JumpToNextStep(10000);
break;
+
case 59:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID))
pTemp->CastSpell(pTemp, SPELL_TELEPORT_VISUAL, false);
@@ -1069,6 +1186,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(2500);
break;
+
case 60:
if (Creature* pTemp = Unit::GetCreature(*me, uiLichKingGUID)) // Lich king disappears here
{
@@ -1077,11 +1195,13 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(10000);
break;
+
case 61:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN60, pTemp);
JumpToNextStep(3000);
break;
+
case 62:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
{
@@ -1090,6 +1210,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(5500);
break;
+
case 63:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
{
@@ -1098,44 +1219,53 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
JumpToNextStep(15000);
break;
+
case 64:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN62, pTemp);
JumpToNextStep(7000);
break;
+
case 65:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN63, pTemp);
JumpToNextStep(10000);
break;
+
case 66:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN64, pTemp);
JumpToNextStep(11000);
break;
+
case 67:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN65, pTemp);
JumpToNextStep(10000);
break;
+
case 68:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN66, pTemp);
JumpToNextStep(8000);
break;
+
case 69:
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN67, pTemp);
JumpToNextStep(10000);
break;
+
case 70:
me->SetStandState(UNIT_STAND_STATE_STAND);
DoScriptText(SAY_LIGHT_OF_DAWN68, me);
JumpToNextStep(10000);
break;
+
case 71:
//if (GameObject* pGo = me->GetMap()->GetGameObject(uiDawnofLightGUID)) // Turn off dawn of light
// pGo->SetPhaseMask(0, true);
+
{
Map *map = me->GetMap(); // search players with in 50 yards for quest credit
Map::PlayerList const &PlayerList = map->GetPlayers();
@@ -1150,13 +1280,16 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
me->SummonCreature(NPC_HIGHLORD_DARION_MOGRAINE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 180000);
JumpToNextStep(1000);
break;
+
case 72:
SetHoldState(false); // Escort ends
JumpToNextStep(0);
break;
}
+
}else uiPhase_timer -= diff;
}
+
// ******* During battle *****************************************************************
else
{
@@ -1165,26 +1298,31 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
DoCast(me, SPELL_ANTI_MAGIC_ZONE1);
uiAnti_magic_zone = 25000 + rand()%5000;
} else uiAnti_magic_zone -= diff;
+
if (uiDeath_strike < diff)
{
DoCast(me->getVictim(), SPELL_DEATH_STRIKE);
uiDeath_strike = 5000 + rand()%5000;
} else uiDeath_strike -= diff;
+
if (uiDeath_embrace < diff)
{
DoCast(me->getVictim(), SPELL_DEATH_EMBRACE);
uiDeath_embrace = 5000 + rand()%5000;
} else uiDeath_embrace -= diff;
+
if (uiIcy_touch < diff)
{
DoCast(me->getVictim(), SPELL_ICY_TOUCH1);
uiIcy_touch = 5000 + rand()%5000;
} else uiIcy_touch -= diff;
+
if (uiUnholy_blight < diff)
{
DoCast(me->getVictim(), SPELL_UNHOLY_BLIGHT);
uiUnholy_blight = 5000 + rand()%5000;
} else uiUnholy_blight -= diff;
+
if (uiFight_speech < diff)
{
DoScriptText(RAND(SAY_LIGHT_OF_DAWN09,SAY_LIGHT_OF_DAWN10,SAY_LIGHT_OF_DAWN11,
@@ -1195,28 +1333,32 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
SAY_LIGHT_OF_DAWN24), me);
uiFight_speech = 15000 + rand()%5000;
} else uiFight_speech -= diff;
+
// Check spawns
if (uiSpawncheck < diff)
{
SpawnNPC();
uiSpawncheck = 1000;
} else uiSpawncheck -= diff;
+
// Check targets
if (uiTargetcheck < diff)
{
- for (uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
NPCChangeTarget(uiGhoulGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
NPCChangeTarget(uiWarriorGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
NPCChangeTarget(uiAbominationGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
NPCChangeTarget(uiBehemothGUID[i]);
NPCChangeTarget(uiKoltiraGUID);
NPCChangeTarget(uiOrbazGUID);
NPCChangeTarget(uiThassarianGUID);
+
uiTargetcheck = 10000;
} else uiTargetcheck -= diff;
+
// Battle end
if (uiFight_duration < diff + 5000)
{
@@ -1233,6 +1375,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
{
bIsBattle = false;
uiFight_duration = 300000;
+
if (me->HasAura(SPELL_THE_MIGHT_OF_MOGRAINE, 0))
me->RemoveAurasDueToSpell(SPELL_THE_MIGHT_OF_MOGRAINE);
me->RemoveAllAuras();
@@ -1240,18 +1383,20 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
me->CombatStop(true);
me->InterruptNonMeleeSpells(false);
me->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
- for (uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i)
+
+ for(uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i)
DespawnNPC(uiDefenderGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_EARTHSHATTER_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_EARTHSHATTER_NUMBER; ++i)
DespawnNPC(uiEarthshatterGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
DespawnNPC(uiAbominationGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
DespawnNPC(uiBehemothGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
DespawnNPC(uiGhoulGUID[i]);
- for (uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
DespawnNPC(uiWarriorGUID[i]);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiKorfaxGUID))
{
pTemp->RemoveAllAuras();
@@ -1262,6 +1407,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[9].x, LightofDawnLoc[9].y, LightofDawnLoc[9].z);
}
+
if (Creature* pTemp = Unit::GetCreature(*me, uiMaxwellGUID))
{
pTemp->RemoveAllAuras();
@@ -1272,6 +1418,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[12].x, LightofDawnLoc[12].y, LightofDawnLoc[12].z);
}
+
if (Creature* pTemp = Unit::GetCreature(*me, uiEligorGUID))
{
pTemp->RemoveAllAuras();
@@ -1283,6 +1430,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[15].x, LightofDawnLoc[15].y, LightofDawnLoc[15].z);
}
DespawnNPC(uiRayneGUID);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiKoltiraGUID))
{
pTemp->RemoveAllAuras();
@@ -1294,8 +1442,10 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[18].x, LightofDawnLoc[18].y, LightofDawnLoc[18].z);
pTemp->CastSpell(pTemp, SPELL_THE_LIGHT_OF_DAWN, false);
}
+
if (Creature* pTemp = Unit::GetCreature(*me, uiOrbazGUID))
DoScriptText(EMOTE_LIGHT_OF_DAWN04, pTemp);
+
if (Creature* pTemp = Unit::GetCreature(*me, uiThassarianGUID))
{
pTemp->RemoveAllAuras();
@@ -1307,18 +1457,24 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
pTemp->GetMotionMaster()->MovePoint(0, LightofDawnLoc[20].x, LightofDawnLoc[20].y, LightofDawnLoc[20].z);
pTemp->CastSpell(pTemp, SPELL_THE_LIGHT_OF_DAWN, false);
}
+
if (Creature* pTemp = Unit::GetCreature(*me, uiTirionGUID))
DoScriptText(SAY_LIGHT_OF_DAWN26, pTemp);
+
SetHoldState(false);
+
} else uiFight_duration -= diff;
+
DoMeleeAttackIfReady();
}
}
+
void JumpToNextStep(uint32 uiTimer)
{
uiPhase_timer = uiTimer;
uiStep++;
}
+
void NPCChangeTarget(uint64 ui_GUID)
{
if (Creature* pTemp = Unit::GetCreature(*me, ui_GUID))
@@ -1334,11 +1490,13 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
// pTemp->GetMotionMaster()->MoveChase(pTarger, 20.0f);
}
}
+
void SpawnNPC()
{
Unit* pTemp = NULL;
+
// Death
- for (uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_GHOUL_NUMBER; ++i)
{
if (!(pTemp = Unit::GetCreature(*me, uiGhoulGUID[i])))
{
@@ -1347,7 +1505,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiGhoulGUID[i] = pTemp->GetGUID();
}
}
- for (uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_ABOMINATION_NUMBER; ++i)
{
if (!(pTemp = Unit::GetCreature(*me, uiAbominationGUID[i])))
{
@@ -1356,7 +1514,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiAbominationGUID[i] = pTemp->GetGUID();
}
}
- for (uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_WARRIOR_NUMBER; ++i)
{
if (!(pTemp = Unit::GetCreature(*me, uiWarriorGUID[i])))
{
@@ -1365,7 +1523,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiWarriorGUID[i] = pTemp->GetGUID();
}
}
- for (uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_BEHEMOTH_NUMBER; ++i)
{
if (!(pTemp = Unit::GetCreature(*me, uiBehemothGUID[i])))
{
@@ -1374,8 +1532,9 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiBehemothGUID[i] = pTemp->GetGUID();
}
}
+
// Dawn
- for (uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_DEFENDER_NUMBER; ++i)
{
if (!(pTemp = Unit::GetCreature(*me, uiDefenderGUID[i])))
{
@@ -1385,7 +1544,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiDefenderGUID[i] = pTemp->GetGUID();
}
}
- for (uint8 i = 0; i < ENCOUNTER_EARTHSHATTER_NUMBER; ++i)
+ for(uint8 i = 0; i < ENCOUNTER_EARTHSHATTER_NUMBER; ++i)
{
if (!(pTemp = Unit::GetCreature(*me, uiEarthshatterGUID[i])))
{
@@ -1424,6 +1583,7 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
uiRayneGUID = pTemp->GetGUID();
}
}
+
void DespawnNPC(uint64 pGUID)
{
if (Creature* pTemp = Unit::GetCreature(*me, pGUID))
@@ -1434,15 +1594,20 @@ struct TRINITY_DLL_DECL npc_highlord_darion_mograineAI : public npc_escortAI
}
}
};
+
bool GossipHello_npc_highlord_darion_mograine(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu( pCreature->GetGUID() );
+
if (pPlayer->GetQuestStatus(12801) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM( 0, "I am ready.", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_highlord_darion_mograine(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -1455,6 +1620,7 @@ bool GossipSelect_npc_highlord_darion_mograine(Player* pPlayer, Creature* pCreat
}
return true;
}
+
/*######
## npc the lich king in dawn of light
######*/
@@ -1466,23 +1632,28 @@ struct TRINITY_DLL_DECL npc_the_lich_king_tirion_dawnAI : public ScriptedAI
void UpdateAI(const uint32 diff) { return; }
void JustDied(Unit* killer) {}
};
+
CreatureAI* GetAI_npc_highlord_darion_mograine(Creature* pCreature)
{
return new npc_highlord_darion_mograineAI(pCreature);
}
+
CreatureAI* GetAI_npc_the_lich_king_tirion_dawn(Creature* pCreature)
{
return new npc_the_lich_king_tirion_dawnAI (pCreature);
}
+
void AddSC_the_scarlet_enclave_c5()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_highlord_darion_mograine";
newscript->GetAI = &GetAI_npc_highlord_darion_mograine;
newscript->pGossipHello = &GossipHello_npc_highlord_darion_mograine;
newscript->pGossipSelect = &GossipSelect_npc_highlord_darion_mograine;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_the_lich_king_tirion_dawn";
newscript->GetAI = &GetAI_npc_the_lich_king_tirion_dawn;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp
index 2a728d5d2da..049c4b72cde 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_enclave/the_scarlet_enclave.cpp
@@ -15,18 +15,23 @@
* 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"
+
/*####
## npc_valkyr_battle_maiden
####*/
#define SPELL_REVIVE 51918
#define VALK_WHISPER "It is not yet your time, champion. Rise! Rise and fight once more!"
+
struct TRINITY_DLL_DECL npc_valkyr_battle_maidenAI : public PassiveAI
{
npc_valkyr_battle_maidenAI(Creature *c) : PassiveAI(c) {}
+
uint32 FlyBackTimer;
float x, y, z;
uint32 phase;
+
void Reset()
{
m_creature->SetVisibility(VISIBILITY_OFF);
@@ -34,11 +39,13 @@ struct TRINITY_DLL_DECL npc_valkyr_battle_maidenAI : public PassiveAI
m_creature->SetFlying(true);
FlyBackTimer = 500;
phase = 0;
+
m_creature->GetPosition(x, y, z);
z += 4; x -= 3.5; y -= 5;
m_creature->GetMotionMaster()->Clear(false);
m_creature->GetMap()->CreatureRelocation(m_creature, x, y, z, 0.0f);
}
+
void UpdateAI(const uint32 diff)
{
if(FlyBackTimer < diff)
@@ -48,8 +55,10 @@ struct TRINITY_DLL_DECL npc_valkyr_battle_maidenAI : public PassiveAI
if(Unit *summoner = CAST_SUM(me)->GetSummoner())
if(summoner->GetTypeId() == TYPEID_PLAYER)
plr = CAST_PLR(summoner);
+
if(!plr)
phase = 3;
+
switch(phase)
{
case 0:
@@ -85,17 +94,21 @@ struct TRINITY_DLL_DECL npc_valkyr_battle_maidenAI : public PassiveAI
}else FlyBackTimer-=diff;
}
};
+
CreatureAI* GetAI_npc_valkyr_battle_maiden(Creature* pCreature)
{
return new npc_valkyr_battle_maidenAI (pCreature);
}
+
void AddSC_the_scarlet_enclave()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_valkyr_battle_maiden";
newscript->GetAI = &GetAI_npc_valkyr_battle_maiden;
newscript->RegisterSelf();
+
// Chapter 3: Scarlet Armies Approach... - An End To All Things...
// Chapter 4: An End To All Things... - An End To All Things...
}
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp
index be5d73b4009..51a8d6efc8d 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_arcanist_doan.cpp
@@ -13,31 +13,38 @@
* along 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"
+
enum eEnums
{
SAY_AGGRO = -1189019,
SAY_SPECIALAE = -1189020,
+
SPELL_POLYMORPH = 13323,
SPELL_AOESILENCE = 8988,
SPELL_ARCANEEXPLOSION = 9433,
SPELL_FIREAOE = 9435,
SPELL_ARCANEBUBBLE = 9438,
};
+
struct TRINITY_DLL_DECL boss_arcanist_doanAI : public ScriptedAI
{
boss_arcanist_doanAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Polymorph_Timer;
uint32 AoESilence_Timer;
uint32 ArcaneExplosion_Timer;
bool bCanDetonate;
bool bShielded;
+
void Reset()
{
Polymorph_Timer = 20000;
@@ -46,50 +53,62 @@ struct TRINITY_DLL_DECL boss_arcanist_doanAI : public ScriptedAI
bCanDetonate = false;
bShielded = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (bShielded && bCanDetonate)
{
DoCast(m_creature,SPELL_FIREAOE);
bCanDetonate = false;
}
+
if (m_creature->HasAura(SPELL_ARCANEBUBBLE))
return;
+
//If we are <50% hp cast Arcane Bubble
if (!bShielded && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50)
{
//wait if we already casting
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
DoScriptText(SAY_SPECIALAE, m_creature);
DoCast(m_creature,SPELL_ARCANEBUBBLE);
+
bCanDetonate = true;
bShielded = true;
}
+
if (Polymorph_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1))
DoCast(target,SPELL_POLYMORPH);
+
Polymorph_Timer = 20000;
}else Polymorph_Timer -= diff;
+
//AoESilence_Timer
if (AoESilence_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_AOESILENCE);
AoESilence_Timer = 15000 + rand()%5000;
}else AoESilence_Timer -= diff;
+
//ArcaneExplosion_Timer
if (ArcaneExplosion_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ARCANEEXPLOSION);
ArcaneExplosion_Timer = 8000;
}else ArcaneExplosion_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -97,6 +116,7 @@ CreatureAI* GetAI_boss_arcanist_doan(Creature* pCreature)
{
return new boss_arcanist_doanAI (pCreature);
}
+
void AddSC_boss_arcanist_doan()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp
index 4b2a045cb7a..1409b2ff693 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_azshir_the_sleepless.cpp
@@ -13,35 +13,44 @@
* along 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 TRINITY_DLL_DECL boss_azshir_the_sleeplessAI : public ScriptedAI
{
boss_azshir_the_sleeplessAI(Creature *c) : ScriptedAI(c) {}
+
uint32 SoulSiphon_Timer;
uint32 CallOftheGrave_Timer;
uint32 Terrify_Timer;
+
void Reset()
{
SoulSiphon_Timer = 1;
CallOftheGrave_Timer = 30000;
Terrify_Timer = 20000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//If we are <50% hp cast Soul Siphon rank 1
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50 && !m_creature->IsNonMeleeSpellCasted(false))
{
@@ -50,28 +59,34 @@ struct TRINITY_DLL_DECL boss_azshir_the_sleeplessAI : public ScriptedAI
{
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* pCreature)
{
return new boss_azshir_the_sleeplessAI (pCreature);
}
+
void AddSC_boss_azshir_the_sleepless()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp
index a3b67ee7e71..b7cba1ae97a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_bloodmage_thalnos.cpp
@@ -13,31 +13,38 @@
* along 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"
+
enum eEnums
{
SAY_AGGRO = -1189016,
SAY_HEALTH = -1189017,
SAY_KILL = -1189018,
+
SPELL_FLAMESHOCK = 8053,
SPELL_SHADOWBOLT = 1106,
SPELL_FLAMESPIKE = 8814,
SPELL_FIRENOVA = 16079,
};
+
struct TRINITY_DLL_DECL boss_bloodmage_thalnosAI : public ScriptedAI
{
boss_bloodmage_thalnosAI(Creature *c) : ScriptedAI(c) {}
+
bool HpYell;
uint32 FlameShock_Timer;
uint32 ShadowBolt_Timer;
uint32 FlameSpike_Timer;
uint32 FireNova_Timer;
+
void Reset()
{
HpYell = false;
@@ -46,55 +53,66 @@ struct TRINITY_DLL_DECL boss_bloodmage_thalnosAI : public ScriptedAI
FlameSpike_Timer = 8000;
FireNova_Timer = 40000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit* Victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//If we are <35% hp
if (!HpYell && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 35))
{
DoScriptText(SAY_HEALTH, m_creature);
HpYell = true;
}
+
//FlameShock_Timer
if (FlameShock_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_FLAMESHOCK);
FlameShock_Timer = 10000 + rand()%5000;
}else FlameShock_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 = 40000;
}else FireNova_Timer -= diff;
+
//ShadowBolt_Timer
if (ShadowBolt_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT);
ShadowBolt_Timer = 2000;
}else ShadowBolt_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_bloodmage_thalnos(Creature* pCreature)
{
return new boss_bloodmage_thalnosAI (pCreature);
}
+
void AddSC_boss_bloodmage_thalnos()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp
index bece0e692bd..bce58b42632 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_headless_horseman.cpp
@@ -13,15 +13,18 @@
* along 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_Headless_Horseman
SD%Complete:
SDComment:
SDCategory: Scarlet Monastery
EndScriptData */
+
#include "precompiled.h"
#include "SpellMgr.h"
#include "def_scarlet_monastery.h"
+
//this texts are already used by 3975 and 3976
#define SAY_ENTRANCE -1189001
#define SAY_REJOINED -1189002
@@ -30,7 +33,9 @@ EndScriptData */
#define SAY_SPROUTING_PUMPKINS -1189005
#define SAY_PLAYER_DEATH -1189006
#define SAY_DEATH -1189007
+
uint32 RandomLaugh[] = {11965, 11975, 11976};
+
// Entryes
#define HH_MOUNTED 23682
#define HH_UNHORSED 23800
@@ -39,23 +44,28 @@ uint32 RandomLaugh[] = {11965, 11975, 11976};
#define PUMPKIN_FIEND 23545
#define HELPER 23686
#define WISP_INVIS 24034
+
//Spells
#define SPELL_CLEAVE 42587
#define SPELL_CONFLAGRATION 42380 //Phase 2, can't find real spell(Dim Fire?)
//#define SPELL_CONFL_SPEED 22587 //8% increase speed, value 22587 from SPELL_CONFLAGRATION mains that spell?
#define SPELL_SUMMON_PUMPKIN 42394
+
#define SPELL_WHIRLWIND 43116
#define SPELL_IMMUNE 42556
#define SPELL_BODY_REGEN 42403
#define SPELL_CONFUSE 43105
+
#define SPELL_FLYING_HEAD 42399 //visual flying head
#define SPELL_HEAD 42413 //visual buff, "head"
#define SPELL_HEAD_IS_DEAD 42428 //at killing head, Phase 3
+
#define SPELL_PUMPKIN_AURA 42280
#define SPELL_PUMPKIN_AURA_GREEN 42294
#define SPELL_SQUASH_SOUL 42514
#define SPELL_SPROUTING 42281
#define SPELL_SPROUT_BODY 42285
+
//Effects
#define SPELL_RHYME_BIG 42909
//#define SPELL_RHYME_SMALL 42910
@@ -69,10 +79,12 @@ uint32 RandomLaugh[] = {11965, 11975, 11976};
//#define SPELL_WISP_INVIS 42823
#define SPELL_SMOKE 42355
#define SPELL_DEATH 42566 //not correct spell
+
struct Locations
{
float x, y, z;
};
+
static Locations FlightPoint[]=
{
{1754.00,1346.00,17.50},
@@ -97,15 +109,18 @@ static Locations FlightPoint[]=
{1757.80,1378.20,29.00},
{1758.00,1367.00,19.51}
};
+
static Locations Spawn[]=
{
{1776.27,1348.74,19.20}, //spawn point for pumpkin shrine mob
{1765.28,1347.46,17.55} //spawn point for smoke
};
+
struct Summon
{
const std::string text;
};
+
static Summon Text[]=
{
{"Horseman rise..."},
@@ -113,7 +128,9 @@ static Summon Text[]=
{"You felt death once..."},
{"Now, know demise!"}
};
+
#define EMOTE_LAUGHS "laughs"
+
struct TRINITY_DLL_DECL mob_wisp_invisAI : public ScriptedAI
{
mob_wisp_invisAI(Creature *c) : ScriptedAI(c)
@@ -127,6 +144,7 @@ struct TRINITY_DLL_DECL mob_wisp_invisAI : public ScriptedAI
if (port)
port->rangeIndex = 6;
}
+
uint32 Creaturetype;
uint32 delay;
uint32 spell;
@@ -157,18 +175,22 @@ struct TRINITY_DLL_DECL mob_wisp_invisAI : public ScriptedAI
if (spell)
DoCast(m_creature,spell);
}
+
void SpellHit(Unit* caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_WISP_FLIGHT_PORT && Creaturetype == 4)
m_creature->SetDisplayId(2027);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || Creaturetype != 1 || !who->isTargetableForAttack())
return;
+
if (m_creature->IsWithinDist(who, 0.1, false) && !who->HasAura(SPELL_SQUASH_SOUL))
DoCast(who,SPELL_SQUASH_SOUL);
}
+
void UpdateAI(const uint32 diff)
{
if (delay)
@@ -183,15 +205,20 @@ struct TRINITY_DLL_DECL mob_wisp_invisAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_headAI : public ScriptedAI
{
mob_headAI(Creature *c) : ScriptedAI(c) {}
+
uint64 bodyGUID;
+
uint32 Phase;
uint32 laugh;
uint32 wait;
+
bool withbody;
bool die;
+
void Reset()
{
Phase = 0;
@@ -201,6 +228,7 @@ struct TRINITY_DLL_DECL mob_headAI : public ScriptedAI
wait = 1000;
laugh = urand(15000,30000);
}
+
void EnterCombat(Unit *who) {}
void SaySound(int32 textEntry, Unit *target = 0)
{
@@ -211,10 +239,12 @@ struct TRINITY_DLL_DECL mob_headAI : public ScriptedAI
speaker->CastSpell(speaker,SPELL_HEAD_SPEAKS,false);
laugh += 3000;
}
+
void DamageTaken(Unit* done_by,uint32 &damage)
{
if (withbody)
return;
+
switch(Phase)
{
case 1:
@@ -240,10 +270,12 @@ struct TRINITY_DLL_DECL mob_headAI : public ScriptedAI
break;
}
}
+
void SpellHit(Unit *caster, const SpellEntry* spell)
{
if (!withbody)
return;
+
if (spell->Id == SPELL_FLYING_HEAD)
{
if (Phase < 3) ++Phase;
@@ -272,6 +304,7 @@ struct TRINITY_DLL_DECL mob_headAI : public ScriptedAI
m_creature->GetMotionMaster()->Clear(false);
m_creature->GetMotionMaster()->MoveFleeing(m_creature->getVictim());
} else wait -= diff;
+
if (laugh < diff)
{
laugh = urand(15000,30000);
@@ -298,6 +331,7 @@ struct TRINITY_DLL_DECL mob_headAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
{
boss_headless_horsemanAI(Creature *c) : ScriptedAI(c)
@@ -313,6 +347,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
/*
if (SpellEntry *confl = GET_SPELL(SPELL_CONFLAGRATION))
confl->EffectTriggerSpell[1] = 22587;
+
if (SpellEntry *speed = GET_SPELL(22587))
{
speed->Effect[1] = SPELL_EFFECT_APPLY_AURA;
@@ -321,13 +356,17 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
*/
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint64 headGUID;
uint64 PlayerGUID;
+
uint32 Phase;
uint32 id;
uint32 count;
uint32 say_timer;
+
uint32 conflagrate;
uint32 summonadds;
uint32 cleave;
@@ -335,11 +374,13 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
uint32 whirlwind;
uint32 laugh;
uint32 burn;
+
bool withhead;
bool returned;
bool IsFlying;
bool wp_reached;
bool burned;
+
void Reset()
{
Phase = 1;
@@ -351,6 +392,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
burn = 6000;
count = 0;
say_timer = 3000;
+
withhead = true;
returned = true;
burned = false;
@@ -360,11 +402,14 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
{
if (Creature* Head = Unit::GetCreature((*m_creature), headGUID))
Head->DisappearAndDie();
+
headGUID = 0;
}
+
if (pInstance)
pInstance->SetData(DATA_HORSEMAN_EVENT, NOT_STARTED);
}
+
void FlyMode()
{
m_creature->SetVisibility(VISIBILITY_OFF);
@@ -377,11 +422,14 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
id = 0;
Phase = 0;
}
+
void MovementInform(uint32 type, uint32 i)
{
if (type != POINT_MOTION_TYPE || !IsFlying || i != id)
return;
+
wp_reached = true;
+
switch (id)
{
case 0:
@@ -415,6 +463,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
}
++id;
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -438,24 +487,30 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
CAST_AI(mob_headAI, Head->AI())->SaySound(SAY_PLAYER_DEATH);
}
}
+
void SaySound(int32 textEntry, Unit *target = 0)
{
DoScriptText(textEntry, m_creature, target);
laugh += 4000;
}
+
Player* SelectRandomPlayer(float range = 0.0f, bool checkLoS = true)
{
Map* pMap = m_creature->GetMap();
if (!pMap->IsDungeon()) return NULL;
+
Map::PlayerList const &PlayerList = pMap->GetPlayers();
Map::PlayerList::const_iterator i;
if (PlayerList.isEmpty()) return NULL;
+
std::list<Player*> temp;
std::list<Player*>::iterator j;
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if ((m_creature->IsWithinLOSInMap(i->getSource()) || !checkLoS) && m_creature->getVictim() != i->getSource() &&
m_creature->IsWithinDistInMap(i->getSource(), range) && i->getSource()->isAlive())
temp.push_back(i->getSource());
+
if (temp.size())
{
j = temp.begin();
@@ -464,11 +519,13 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
}
return NULL;
}
+
void SpellHitTarget(Unit* unit, const SpellEntry* spell)
{
if (spell->Id == SPELL_CONFLAGRATION && unit->HasAura(SPELL_CONFLAGRATION))
SaySound(SAY_CONFLAGRATION,unit);
}
+
void JustDied(Unit* killer)
{
m_creature->StopMoving();
@@ -481,10 +538,12 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_HORSEMAN_EVENT, DONE);
}
+
void SpellHit(Unit *caster, const SpellEntry* spell)
{
if (withhead)
return;
+
if (spell->Id == SPELL_FLYING_HEAD)
{
if (Phase < 3)
@@ -501,7 +560,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
caster->GetMotionMaster()->MoveFollow(m_creature,6,urand(0,5));
//DoResetThreat();//not sure if need
std::list<HostilReference*>::iterator itr;
- for (itr = caster->getThreatManager().getThreatList().begin(); itr != caster->getThreatManager().getThreatList().end(); ++itr)
+ for(itr = caster->getThreatManager().getThreatList().begin(); itr != caster->getThreatManager().getThreatList().end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if (pUnit && pUnit->isAlive() && pUnit != caster)
@@ -509,6 +568,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
}
}
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= m_creature->GetHealth() && withhead)
@@ -518,6 +578,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
damage = m_creature->GetHealth() - m_creature->GetMaxHealth()/100;
m_creature->RemoveAllAuras();
m_creature->SetName("Headless Horseman, Unhorsed");
+
if (!headGUID)
headGUID = DoSpawnCreature(HEAD,rand()%6,rand()%6,0,0,TEMPSUMMON_DEAD_DESPAWN,0)->GetGUID();
Unit* Head = Unit::GetUnit((*m_creature), headGUID);
@@ -536,6 +597,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (withhead)
@@ -610,12 +672,14 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
} else summonadds -= diff;
break;
}
+
if (laugh < diff)
{
laugh = urand(11000,22000);
m_creature->MonsterTextEmote(EMOTE_LAUGHS,NULL);
DoPlaySoundToSet(m_creature, RandomLaugh[rand()%3]);
} else laugh -= diff;
+
if (UpdateVictim())
{
DoMeleeAttackIfReady();
@@ -647,6 +711,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
}
}
else regen -= diff;
+
if (whirlwind < diff)
{
whirlwind = urand(4000,8000);
@@ -661,6 +726,7 @@ struct TRINITY_DLL_DECL boss_headless_horsemanAI : public ScriptedAI
}
}
};
+
void mob_headAI::Disappear()
{
if (withbody)
@@ -682,11 +748,14 @@ void mob_headAI::Disappear()
}
}
}
+
struct TRINITY_DLL_DECL mob_pulsing_pumpkinAI : public ScriptedAI
{
mob_pulsing_pumpkinAI(Creature *c) : ScriptedAI(c) {}
+
bool sprouted;
uint64 debuffGUID;
+
void Reset()
{
float x, y, z;
@@ -706,7 +775,9 @@ struct TRINITY_DLL_DECL mob_pulsing_pumpkinAI : public ScriptedAI
DoCast(m_creature,SPELL_SPROUTING);
m_creature->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_STUNNED);
}
+
void EnterCombat(Unit *who){}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_SPROUTING)
@@ -719,6 +790,7 @@ struct TRINITY_DLL_DECL mob_pulsing_pumpkinAI : public ScriptedAI
DoStartMovement(m_creature->getVictim());
}
}
+
void Despawn()
{
if (!debuffGUID) return;
@@ -727,21 +799,26 @@ struct TRINITY_DLL_DECL mob_pulsing_pumpkinAI : public ScriptedAI
debuff->SetVisibility(VISIBILITY_OFF);
debuffGUID = 0;
}
+
void JustDied(Unit *killer) { if (!sprouted) Despawn(); }
+
void MoveInLineOfSight(Unit *who)
{
if (!who || !who->isTargetableForAttack() || !m_creature->IsHostileTo(who) || m_creature->getVictim())
return;
+
m_creature->AddThreat(who,0.0f);
if (sprouted)
DoStartMovement(who);
}
+
void UpdateAI(const uint32 diff)
{
if (sprouted && UpdateVictim())
DoMeleeAttackIfReady();
}
};
+
bool GOHello_go_loosely_turned_soil(Player* pPlayer, GameObject* soil)
{
/* if (soil->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER && plr->getLevel() > 64)
@@ -760,41 +837,51 @@ bool GOHello_go_loosely_turned_soil(Player* pPlayer, GameObject* soil)
//}
return true;
}
+
CreatureAI* GetAI_mob_head(Creature* pCreature)
{
return new mob_headAI (pCreature);
}
+
CreatureAI* GetAI_boss_headless_horseman(Creature* pCreature)
{
return new boss_headless_horsemanAI (pCreature);
}
+
CreatureAI* GetAI_mob_pulsing_pumpkin(Creature* pCreature)
{
return new mob_pulsing_pumpkinAI (pCreature);
}
+
CreatureAI* GetAI_mob_wisp_invis(Creature* pCreature)
{
return new mob_wisp_invisAI (pCreature);
}
+
void AddSC_boss_headless_horseman()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_headless_horseman";
newscript->GetAI = &GetAI_boss_headless_horseman;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_head";
newscript->GetAI = &GetAI_mob_head;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_pulsing_pumpkin";
newscript->GetAI = &GetAI_mob_pulsing_pumpkin;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_wisp_invis";
newscript->GetAI = &GetAI_mob_wisp_invis;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_loosely_turned_soil";
newscript->pGOHello = &GOHello_go_loosely_turned_soil;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp
index 5e4a3d31d70..fc02cad4a2f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_herod.cpp
@@ -13,56 +13,70 @@
* along 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: 95
SDComment: Should in addition spawn Myrmidons in the hallway outside
SDCategory: Scarlet Monastery
EndScriptData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
#define SAY_AGGRO -1189000
#define SAY_WHIRLWIND -1189001
#define SAY_ENRAGE -1189002
#define SAY_KILL -1189003
#define EMOTE_ENRAGE -1189004
+
#define SPELL_RUSHINGCHARGE 8260
#define SPELL_CLEAVE 15496
#define SPELL_WHIRLWIND 8989
#define SPELL_FRENZY 8269
+
#define ENTRY_SCARLET_TRAINEE 6575
#define ENTRY_SCARLET_MYRMIDON 4295
+
struct TRINITY_DLL_DECL boss_herodAI : public ScriptedAI
{
boss_herodAI(Creature *c) : ScriptedAI(c) {}
+
bool Enrage;
+
uint32 Cleave_Timer;
uint32 Whirlwind_Timer;
+
void Reset()
{
Enrage = false;
Cleave_Timer = 12000;
Whirlwind_Timer = 60000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoCast(m_creature,SPELL_RUSHINGCHARGE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void JustDied(Unit* killer)
{
- for (uint8 i = 0; i < 20; ++i)
+ for(uint8 i = 0; i < 20; ++i)
m_creature->SummonCreature(ENTRY_SCARLET_TRAINEE, 1939.18, -431.58, 17.09, 6.22, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 600000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//If we are <30% hp goes Enraged
if (!Enrage && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 30 && !m_creature->IsNonMeleeSpellCasted(false))
{
@@ -71,12 +85,14 @@ struct TRINITY_DLL_DECL boss_herodAI : public ScriptedAI
DoCast(m_creature,SPELL_FRENZY);
Enrage = true;
}
+
//Cleave_Timer
if (Cleave_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CLEAVE);
Cleave_Timer = 12000;
}else Cleave_Timer -= diff;
+
// Whirlwind_Timer
if (Whirlwind_Timer < diff)
{
@@ -84,23 +100,29 @@ struct TRINITY_DLL_DECL boss_herodAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_WHIRLWIND);
Whirlwind_Timer = 30000;
}else Whirlwind_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_herod(Creature* pCreature)
{
return new boss_herodAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_scarlet_traineeAI : public npc_escortAI
{
mob_scarlet_traineeAI(Creature *c) : npc_escortAI(c)
{
Start_Timer = urand(1000,6000);
}
+
uint32 Start_Timer;
+
void Reset() { }
void WaypointReached(uint32 uiPoint) { }
void EnterCombat(Unit* who) { }
+
void UpdateAI(const uint32 diff)
{
if (Start_Timer)
@@ -111,13 +133,16 @@ struct TRINITY_DLL_DECL mob_scarlet_traineeAI : public npc_escortAI
Start_Timer = 0;
}else Start_Timer -= diff;
}
+
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_mob_scarlet_trainee(Creature* pCreature)
{
return new mob_scarlet_traineeAI(pCreature);
}
+
void AddSC_boss_herod()
{
Script *newscript;
@@ -125,6 +150,7 @@ void AddSC_boss_herod()
newscript->Name = "boss_herod";
newscript->GetAI = &GetAI_boss_herod;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_scarlet_trainee";
newscript->GetAI = &GetAI_mob_scarlet_trainee;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp
index 6522510fc13..13e9ef4af53 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_high_inquisitor_fairbanks.cpp
@@ -13,13 +13,16 @@
* along 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_Fairbanks
SD%Complete: 100
SDComment: TODO: if this guy not involved in some special event, remove (and let ACID script)
SDCategory: Scarlet Monastery
EndScriptData */
+
#include "precompiled.h"
+
enum eSpells
{
SPELL_CURSEOFBLOOD = 8282,
@@ -29,9 +32,11 @@ enum eSpells
SPELL_POWERWORDSHIELD = 11647,
SPELL_SLEEP = 8399
};
+
struct TRINITY_DLL_DECL boss_high_inquisitor_fairbanksAI : public ScriptedAI
{
boss_high_inquisitor_fairbanksAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CurseOfBlood_Timer;
uint32 DispelMagic_Timer;
uint32 Fear_Timer;
@@ -39,6 +44,7 @@ struct TRINITY_DLL_DECL boss_high_inquisitor_fairbanksAI : public ScriptedAI
uint32 Sleep_Timer;
uint32 Dispel_Timer;
bool PowerWordShield;
+
void Reset()
{
CurseOfBlood_Timer = 10000;
@@ -49,59 +55,73 @@ struct TRINITY_DLL_DECL boss_high_inquisitor_fairbanksAI : public ScriptedAI
Dispel_Timer = 20000;
PowerWordShield = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//If we are <25% hp cast Heal
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 25 && !m_creature->IsNonMeleeSpellCasted(false) && Heal_Timer < diff)
{
DoCast(m_creature,SPELL_HEAL);
Heal_Timer = 30000;
}else Heal_Timer -= diff;
+
//Fear_Timer
if (Fear_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1))
DoCast(target,SPELL_FEAR);
+
Fear_Timer = 40000;
}else Fear_Timer -= diff;
+
//Sleep_Timer
if (Sleep_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO,0))
DoCast(target,SPELL_SLEEP);
+
Sleep_Timer = 30000;
}else Sleep_Timer -= diff;
+
//PowerWordShield_Timer
if (!PowerWordShield && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 25)
{
DoCast(m_creature,SPELL_POWERWORDSHIELD);
PowerWordShield = true;
}
+
//Dispel_Timer
if (Dispel_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target, SPELL_DISPELMAGIC);
+
DispelMagic_Timer = 30000;
}else DispelMagic_Timer -= diff;
+
//CurseOfBlood_Timer
if (CurseOfBlood_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CURSEOFBLOOD);
CurseOfBlood_Timer = 25000;
}else CurseOfBlood_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_high_inquisitor_fairbanks(Creature* pCreature)
{
return new boss_high_inquisitor_fairbanksAI (pCreature);
}
+
void AddSC_boss_high_inquisitor_fairbanks()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp
index c431cde008b..2e79f2449d0 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_houndmaster_loksey.cpp
@@ -13,47 +13,59 @@
* along 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"
+
enum eEnums
{
SAY_AGGRO = -1189021,
SPELL_SUMMONSCARLETHOUND = 17164,
SPELL_BLOODLUST = 6742
};
+
struct TRINITY_DLL_DECL boss_houndmaster_lokseyAI : public ScriptedAI
{
boss_houndmaster_lokseyAI(Creature *c) : ScriptedAI(c) {}
+
uint32 BloodLust_Timer;
+
void Reset()
{
BloodLust_Timer = 20000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (BloodLust_Timer < diff)
{
DoCast(m_creature,SPELL_BLOODLUST);
BloodLust_Timer = 20000;
}else BloodLust_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_houndmaster_loksey(Creature* pCreature)
{
return new boss_houndmaster_lokseyAI (pCreature);
}
+
void AddSC_boss_houndmaster_loksey()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp
index b50c1a6a023..c0b02a2c370 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_interrogator_vishas.cpp
@@ -13,14 +13,17 @@
* along 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"
#include "def_scarlet_monastery.h"
+
enum eEnums
{
SAY_AGGRO = -1189011,
@@ -28,66 +31,82 @@ enum eEnums
SAY_HEALTH2 = -1189013,
SAY_KILL = -1189014,
SAY_TRIGGER_VORREL = -1189015,
+
SPELL_SHADOWWORDPAIN = 2767,
};
+
struct TRINITY_DLL_DECL boss_interrogator_vishasAI : public ScriptedAI
{
boss_interrogator_vishasAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
bool Yell30;
bool Yell60;
uint32 ShadowWordPain_Timer;
+
void Reset()
{
ShadowWordPain_Timer = 5000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit* Victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void JustDied(Unit* Killer)
{
if (!pInstance)
return;
+
//Any other actions to do with vorrel? setStandState?
if (Unit *vorrel = Unit::GetUnit(*m_creature,pInstance->GetData64(DATA_VORREL)))
DoScriptText(SAY_TRIGGER_VORREL, vorrel);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//If we are low on hp Do sayings
if (!Yell60 && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 60))
{
DoScriptText(SAY_HEALTH1, m_creature);
Yell60 = true;
}
+
if (!Yell30 && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 30))
{
DoScriptText(SAY_HEALTH2, m_creature);
Yell30 = true;
}
+
//ShadowWordPain_Timer
if (ShadowWordPain_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SHADOWWORDPAIN);
ShadowWordPain_Timer = 5000 + rand()%10000;;
}else ShadowWordPain_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_interrogator_vishas(Creature* pCreature)
{
return new boss_interrogator_vishasAI (pCreature);
}
+
void AddSC_boss_interrogator_vishas()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp
index 742b2709e2b..9859f3b8bd7 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_mograine_and_whitemane.cpp
@@ -13,29 +13,35 @@
* along 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_Mograine_And_Whitemane
SD%Complete: 90
SDComment:
SDCategory: Scarlet Monastery
EndScriptData */
+
#include "precompiled.h"
#include "def_scarlet_monastery.h"
+
enum eEnums
{
//Mograine says
SAY_MO_AGGRO = -1189005,
SAY_MO_KILL = -1189006,
SAY_MO_RESSURECTED = -1189007,
+
//Whitemane says
SAY_WH_INTRO = -1189008,
SAY_WH_KILL = -1189009,
SAY_WH_RESSURECT = -1189010,
+
//Mograine Spells
SPELL_CRUSADERSTRIKE = 14518,
SPELL_HAMMEROFJUSTICE = 5589,
SPELL_LAYONHANDS = 9257,
SPELL_RETRIBUTIONAURA = 8990,
+
//Whitemanes Spells
SPELL_DEEPSLEEP = 9256,
SPELL_SCARLETRESURRECTION = 9232,
@@ -44,33 +50,42 @@ enum eEnums
SPELL_HEAL = 12039,
SPELL_POWERWORDSHIELD = 22187
};
+
struct TRINITY_DLL_DECL boss_scarlet_commander_mograineAI : public ScriptedAI
{
boss_scarlet_commander_mograineAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 m_uiCrusaderStrike_Timer;
uint32 m_uiHammerOfJustice_Timer;
+
bool m_bHasDied;
bool m_bHeal;
bool m_bFakeDeath;
+
void Reset()
{
m_uiCrusaderStrike_Timer = 10000;
m_uiHammerOfJustice_Timer = 10000;
+
//Incase wipe during phase that mograine fake death
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
if (m_pInstance)
if (m_creature->isAlive())
m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT,NOT_STARTED);
+
m_bHasDied = false;
m_bHeal = false;
m_bFakeDeath = false;
}
+
void JustReachedHome()
{
if (m_pInstance)
@@ -79,42 +94,57 @@ struct TRINITY_DLL_DECL boss_scarlet_commander_mograineAI : public ScriptedAI
m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT, FAIL);
}
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_MO_AGGRO, m_creature);
DoCast(m_creature,SPELL_RETRIBUTIONAURA);
+
m_creature->CallForHelp(VISIBLE_RANGE);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(SAY_MO_KILL, m_creature);
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (uiDamage < m_creature->GetHealth() || m_bHasDied || m_bFakeDeath)
return;
+
if (!m_pInstance)
return;
+
//On first death, fake death and open door, as well as initiate whitemane if exist
if (Unit* Whitemane = Unit::GetUnit((*m_creature), m_pInstance->GetData64(DATA_WHITEMANE)))
{
m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT, IN_PROGRESS);
+
Whitemane->GetMotionMaster()->MovePoint(1,1163.113370,1398.856812,32.527786);
+
m_creature->GetMotionMaster()->MovementExpired();
m_creature->GetMotionMaster()->MoveIdle();
+
m_creature->SetHealth(0);
+
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
m_creature->ClearComboPointHolders();
m_creature->RemoveAllAuras();
m_creature->ClearAllReactives();
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
+
m_bHasDied = true;
m_bFakeDeath = true;
+
uiDamage = 0;
}
}
+
void SpellHit(Unit* pWho, const SpellEntry* pSpell)
{
//When hit with ressurection say text
@@ -122,15 +152,18 @@ struct TRINITY_DLL_DECL boss_scarlet_commander_mograineAI : public ScriptedAI
{
DoScriptText(SAY_MO_RESSURECTED, m_creature);
m_bFakeDeath = false;
+
if (m_pInstance)
m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT, SPECIAL);
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
if (m_bHasDied && !m_bHeal && m_pInstance && m_pInstance->GetData(TYPE_MOGRAINE_AND_WHITE_EVENT) == SPECIAL)
{
//On ressurection, stop fake death and heal whitemane and resume fight
@@ -139,74 +172,94 @@ struct TRINITY_DLL_DECL boss_scarlet_commander_mograineAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
DoCast(Whitemane, SPELL_LAYONHANDS);
+
m_uiCrusaderStrike_Timer = 10000;
m_uiHammerOfJustice_Timer = 10000;
+
if (m_creature->getVictim())
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
+
m_bHeal = true;
}
}
+
//This if-check to make sure mograine does not attack while fake death
if (m_bFakeDeath)
return;
+
//m_uiCrusaderStrike_Timer
if (m_uiCrusaderStrike_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_CRUSADERSTRIKE);
m_uiCrusaderStrike_Timer = 10000;
}else m_uiCrusaderStrike_Timer -= uiDiff;
+
//m_uiHammerOfJustice_Timer
if (m_uiHammerOfJustice_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_HAMMEROFJUSTICE);
m_uiHammerOfJustice_Timer = 60000;
}else m_uiHammerOfJustice_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_high_inquisitor_whitemaneAI : public ScriptedAI
{
boss_high_inquisitor_whitemaneAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 m_uiHeal_Timer;
uint32 m_uiPowerWordShield_Timer;
uint32 m_uiHolySmite_Timer;
uint32 m_uiWait_Timer;
+
bool m_bCanResurrectCheck;
bool m_bCanResurrect;
+
void Reset()
{
m_uiWait_Timer = 7000;
m_uiHeal_Timer = 10000;
m_uiPowerWordShield_Timer = 15000;
m_uiHolySmite_Timer = 6000;
+
m_bCanResurrectCheck = false;
m_bCanResurrect = false;
+
if (m_pInstance)
if (m_creature->isAlive())
m_pInstance->SetData(TYPE_MOGRAINE_AND_WHITE_EVENT, NOT_STARTED);
}
+
void AttackStart(Unit* pWho)
{
if (m_pInstance && m_pInstance->GetData(TYPE_MOGRAINE_AND_WHITE_EVENT) == NOT_STARTED)
return;
+
ScriptedAI::AttackStart(pWho);
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_WH_INTRO, m_creature);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(SAY_WH_KILL, m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
if (m_bCanResurrect)
{
//When casting resuruction make sure to delay so on rez when reinstate battle deepsleep runs out
@@ -221,25 +274,31 @@ struct TRINITY_DLL_DECL boss_high_inquisitor_whitemaneAI : public ScriptedAI
}
else m_uiWait_Timer -= uiDiff;
}
+
//Cast Deep sleep when health is less than 50%
if (!m_bCanResurrectCheck && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50)
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
DoCast(m_creature->getVictim(), SPELL_DEEPSLEEP);
m_bCanResurrectCheck = true;
m_bCanResurrect = true;
return;
}
+
//while in "resurrect-mode", don't do anything
if (m_bCanResurrect)
return;
+
//If we are <75% hp cast healing spells at self or Mograine
if (m_uiHeal_Timer < uiDiff)
{
Creature* pTarget = NULL;
+
if (m_creature->GetHealth() <= m_creature->GetMaxHealth()*0.75f)
pTarget = m_creature;
+
if (m_pInstance)
{
if (Creature* pMograine = Unit::GetCreature((*m_creature), m_pInstance->GetData64(DATA_MOGRAINE)))
@@ -249,40 +308,50 @@ struct TRINITY_DLL_DECL boss_high_inquisitor_whitemaneAI : public ScriptedAI
pTarget = pMograine;
}
}
+
if (pTarget)
DoCast(pTarget, SPELL_HEAL);
+
m_uiHeal_Timer = 13000;
}else m_uiHeal_Timer -= uiDiff;
+
//m_uiPowerWordShield_Timer
if (m_uiPowerWordShield_Timer < uiDiff)
{
DoCast(m_creature,SPELL_POWERWORDSHIELD);
m_uiPowerWordShield_Timer = 15000;
}else m_uiPowerWordShield_Timer -= uiDiff;
+
//m_uiHolySmite_Timer
if (m_uiHolySmite_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_HOLYSMITE);
m_uiHolySmite_Timer = 6000;
}else m_uiHolySmite_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_scarlet_commander_mograine(Creature* pCreature)
{
return new boss_scarlet_commander_mograineAI (pCreature);
}
+
CreatureAI* GetAI_boss_high_inquisitor_whitemane(Creature* pCreature)
{
return new boss_high_inquisitor_whitemaneAI (pCreature);
}
+
void AddSC_boss_mograine_and_whitemane()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_scarlet_commander_mograine";
newscript->GetAI = &GetAI_boss_scarlet_commander_mograine;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_high_inquisitor_whitemane";
newscript->GetAI = &GetAI_boss_high_inquisitor_whitemane;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp
index 9d5d99a9a83..0b0b5114f92 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/boss_scorn.cpp
@@ -13,24 +13,30 @@
* along 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 TRINITY_DLL_DECL boss_scornAI : public ScriptedAI
{
boss_scornAI(Creature *c) : ScriptedAI(c) {}
+
uint32 LichSlap_Timer;
uint32 FrostboltVolley_Timer;
uint32 MindFlay_Timer;
uint32 FrostNova_Timer;
+
void Reset()
{
LichSlap_Timer = 45000;
@@ -38,37 +44,44 @@ struct TRINITY_DLL_DECL boss_scornAI : public ScriptedAI
MindFlay_Timer = 30000;
FrostNova_Timer = 30000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -76,6 +89,7 @@ CreatureAI* GetAI_boss_scorn(Creature* pCreature)
{
return new boss_scornAI (pCreature);
}
+
void AddSC_boss_scorn()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/def_scarlet_monastery.h b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/def_scarlet_monastery.h
index e92d0cd9cad..2b6399ae3e4 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/def_scarlet_monastery.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/def_scarlet_monastery.h
@@ -1,14 +1,18 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SCARLET_M
#define DEF_SCARLET_M
+
#define TYPE_MOGRAINE_AND_WHITE_EVENT 1
#define DATA_MOGRAINE 2
#define DATA_WHITEMANE 3
#define DATA_DOOR_WHITEMANE 4
+
#define DATA_HORSEMAN_EVENT 5
#define GAMEOBJECT_PUMPKIN_SHRINE 6
+
#define DATA_VORREL 7
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp
index 6b0e0098f80..492493e2390 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scarlet_monastery/instance_scarlet_monastery.cpp
@@ -13,44 +13,56 @@
* along 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_Scarlet_Monastery
SD%Complete: 50
SDComment:
SDCategory: Scarlet Monastery
EndScriptData */
+
#include "precompiled.h"
#include "def_scarlet_monastery.h"
#include "sc_creature.h"
+
#define ENTRY_PUMPKIN_SHRINE 186267
#define ENTRY_HORSEMAN 23682
#define ENTRY_HEAD 23775
#define ENTRY_PUMPKIN 23694
+
#define MAX_ENCOUNTER 1
+
struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
{
instance_scarlet_monastery(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 PumpkinShrineGUID;
uint64 HorsemanGUID;
uint64 HeadGUID;
std::set<uint64> HorsemanAdds;
+
uint64 MograineGUID;
uint64 WhitemaneGUID;
uint64 VorrelGUID;
uint64 DoorHighInquisitorGUID;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
PumpkinShrineGUID = 0;
HorsemanGUID = 0;
HeadGUID = 0;
HorsemanAdds.clear();
+
MograineGUID = 0;
WhitemaneGUID = 0;
VorrelGUID = 0;
DoorHighInquisitorGUID = 0;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -59,6 +71,7 @@ struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
case 104600: DoorHighInquisitorGUID = pGo->GetGUID(); break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -71,6 +84,7 @@ struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
case 3981: VorrelGUID = pCreature->GetGUID(); break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -80,6 +94,7 @@ struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
DoUseDoorOrButton(DoorHighInquisitorGUID);
if (data == FAIL)
DoUseDoorOrButton(DoorHighInquisitorGUID);
+
m_auiEncounter[0] = data;
break;
case GAMEOBJECT_PUMPKIN_SHRINE:
@@ -88,7 +103,7 @@ struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
case DATA_HORSEMAN_EVENT:
if (data == DONE)
{
- for (std::set<uint64>::iterator itr = HorsemanAdds.begin(); itr != HorsemanAdds.end(); ++itr)
+ for(std::set<uint64>::iterator itr = HorsemanAdds.begin(); itr != HorsemanAdds.end(); ++itr)
{
Creature* add = instance->GetCreature(*itr);
if (add && add->isAlive())
@@ -100,6 +115,7 @@ struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
break;
}
}
+
uint64 GetData64(uint32 type)
{
switch(type)
@@ -114,17 +130,21 @@ struct TRINITY_DLL_DECL instance_scarlet_monastery : public ScriptedInstance
}
return 0;
}
+
uint32 GetData(uint32 type)
{
if (type == TYPE_MOGRAINE_AND_WHITE_EVENT)
return m_auiEncounter[0];
+
return 0;
}
};
+
InstanceData* GetInstanceData_instance_scarlet_monastery(Map* pMap)
{
return new instance_scarlet_monastery(pMap);
}
+
void AddSC_instance_scarlet_monastery()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp
index 7ad9a5a67bf..b064a1d9e10 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_darkmaster_gandling.cpp
@@ -13,45 +13,57 @@
* along 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: 75
SDComment: Doors missing in instance script.
SDCategory: Scholomance
EndScriptData */
+
#include "precompiled.h"
#include "def_scholomance.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 TRINITY_DLL_DECL boss_darkmaster_gandlingAI : public ScriptedAI
{
boss_darkmaster_gandlingAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 ArcaneMissiles_Timer;
uint32 ShadowShield_Timer;
uint32 Curse_Timer;
uint32 Teleport_Timer;
+
Creature *Summoned;
+
void Reset()
{
ArcaneMissiles_Timer = 4500;
@@ -59,36 +71,43 @@ struct TRINITY_DLL_DECL boss_darkmaster_gandlingAI : public ScriptedAI
Curse_Timer = 2000;
Teleport_Timer = 16000;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustDied(Unit *killer)
{
if (pInstance)
pInstance->SetData(TYPE_GANDLING, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
@@ -101,6 +120,7 @@ struct TRINITY_DLL_DECL boss_darkmaster_gandlingAI : public ScriptedAI
{
if (DoGetThreat(target))
DoModifyThreatPercent(target, -100);
+
switch(rand()%6)
{
case 0:
@@ -186,6 +206,7 @@ struct TRINITY_DLL_DECL boss_darkmaster_gandlingAI : public ScriptedAI
Teleport_Timer = 20000 + rand()%15000;
}else Teleport_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
@@ -193,6 +214,7 @@ CreatureAI* GetAI_boss_darkmaster_gandling(Creature* pCreature)
{
return new boss_darkmaster_gandlingAI (pCreature);
}
+
void AddSC_boss_darkmaster_gandling()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp
index bab59bf0638..9144542ae7e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_death_knight_darkreaver.cpp
@@ -13,19 +13,24 @@
* along 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 TRINITY_DLL_DECL boss_death_knight_darkreaverAI : public ScriptedAI
{
boss_death_knight_darkreaverAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (m_creature->GetHealth() <= damage)
@@ -33,6 +38,7 @@ struct TRINITY_DLL_DECL boss_death_knight_darkreaverAI : public ScriptedAI
m_creature->CastSpell(m_creature,23261,true); //Summon Darkreaver's Fallen Charger
}
}
+
void EnterCombat(Unit *who)
{
}
@@ -41,9 +47,11 @@ CreatureAI* GetAI_boss_death_knight_darkreaver(Creature* pCreature)
{
return new boss_death_knight_darkreaverAI (pCreature);
}
+
void AddSC_boss_death_knight_darkreaver()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_death_knight_darkreaver";
newscript->GetAI = &GetAI_boss_death_knight_darkreaver;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp
index 0ec2492eb39..51ace6a6e21 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_doctor_theolen_krastinov.cpp
@@ -13,47 +13,58 @@
* along 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"
+
enum eEnums
{
EMOTE_GENERIC_FRENZY_KILL = -1000001,
+
SPELL_REND = 16509,
SPELL_BACKHAND = 18103,
SPELL_FRENZY = 8269
};
+
struct TRINITY_DLL_DECL boss_theolenkrastinovAI : public ScriptedAI
{
boss_theolenkrastinovAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiRend_Timer;
uint32 m_uiBackhand_Timer;
uint32 m_uiFrenzy_Timer;
+
void Reset()
{
m_uiRend_Timer = 8000;
m_uiBackhand_Timer = 9000;
m_uiFrenzy_Timer = 1000;
}
+
void JustDied(Unit* pKiller)
{
ScriptedInstance* pInstance = m_creature->GetInstanceData();
if (pInstance)
{
pInstance->SetData(DATA_DOCTORTHEOLENKRASTINOV_DEATH, 0);
+
if (pInstance->GetData(TYPE_GANDLING) == IN_PROGRESS)
m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//Rend_Timer
if (m_uiRend_Timer < uiDiff)
{
@@ -62,6 +73,7 @@ struct TRINITY_DLL_DECL boss_theolenkrastinovAI : public ScriptedAI
}
else
m_uiRend_Timer -= uiDiff;
+
//Backhand_Timer
if (m_uiBackhand_Timer < uiDiff)
{
@@ -70,6 +82,7 @@ struct TRINITY_DLL_DECL boss_theolenkrastinovAI : public ScriptedAI
}
else
m_uiBackhand_Timer -= uiDiff;
+
//Frenzy_Timer
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 26)
{
@@ -77,18 +90,22 @@ struct TRINITY_DLL_DECL boss_theolenkrastinovAI : public ScriptedAI
{
DoCast(m_creature,SPELL_FRENZY);
DoScriptText(EMOTE_GENERIC_FRENZY_KILL, m_creature);
+
m_uiFrenzy_Timer = 120000;
}
else
m_uiFrenzy_Timer -= uiDiff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_theolenkrastinov(Creature* pCreature)
{
return new boss_theolenkrastinovAI (pCreature);
}
+
void AddSC_boss_theolenkrastinov()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp
index d37a8668a89..c6bb471f6f0 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_illucia_barov.cpp
@@ -13,25 +13,31 @@
* along 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 TRINITY_DLL_DECL boss_illuciabarovAI : public ScriptedAI
{
boss_illuciabarovAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CurseOfAgony_Timer;
uint32 ShadowShock_Timer;
uint32 Silence_Timer;
uint32 Fear_Timer;
+
void Reset()
{
CurseOfAgony_Timer = 18000;
@@ -39,49 +45,59 @@ struct TRINITY_DLL_DECL boss_illuciabarovAI : public ScriptedAI
Silence_Timer = 5000;
Fear_Timer = 30000;
}
+
void JustDied(Unit *killer)
{
ScriptedInstance *pInstance = m_creature->GetInstanceData();
if (pInstance)
{
pInstance->SetData(DATA_LADYILLUCIABAROV_DEATH, 0);
+
if (pInstance->GetData(TYPE_GANDLING) == IN_PROGRESS)
m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -89,6 +105,7 @@ CreatureAI* GetAI_boss_illuciabarov(Creature* pCreature)
{
return new boss_illuciabarovAI (pCreature);
}
+
void AddSC_boss_illuciabarov()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp
index 2b8f7ac2a41..866a2f4dfbc 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_instructor_malicia.cpp
@@ -13,22 +13,27 @@
* along 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 TRINITY_DLL_DECL boss_instructormaliciaAI : public ScriptedAI
{
boss_instructormaliciaAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CallOfGraves_Timer;
uint32 Corruption_Timer;
uint32 FlashHeal_Timer;
@@ -36,6 +41,7 @@ struct TRINITY_DLL_DECL boss_instructormaliciaAI : public ScriptedAI
uint32 HealingTouch_Timer;
uint32 FlashCounter;
uint32 TouchCounter;
+
void Reset()
{
CallOfGraves_Timer = 4000;
@@ -46,47 +52,57 @@ struct TRINITY_DLL_DECL boss_instructormaliciaAI : public ScriptedAI
FlashCounter = 0;
TouchCounter = 0;
}
+
void JustDied(Unit *killer)
{
ScriptedInstance *pInstance = m_creature->GetInstanceData();
if (pInstance)
{
pInstance->SetData(DATA_INSTRUCTORMALICIA_DEATH, 0);
+
if (pInstance->GetData(TYPE_GANDLING) == IN_PROGRESS)
m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -99,10 +115,12 @@ struct TRINITY_DLL_DECL boss_instructormaliciaAI : public ScriptedAI
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)
{
@@ -115,6 +133,7 @@ struct TRINITY_DLL_DECL boss_instructormaliciaAI : public ScriptedAI
HealingTouch_Timer = 30000;
}
}else HealingTouch_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -122,6 +141,7 @@ CreatureAI* GetAI_boss_instructormalicia(Creature* pCreature)
{
return new boss_instructormaliciaAI (pCreature);
}
+
void AddSC_boss_instructormalicia()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp
index 328cf01be4f..7d304445fdc 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_jandice_barov.cpp
@@ -13,20 +13,26 @@
* along 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 TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
{
boss_jandicebarovAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CurseOfBlood_Timer;
uint32 Illusion_Timer;
//uint32 Illusioncounter;
@@ -36,6 +42,7 @@ struct TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
int RandX;
int RandY;
Creature* Summoned;
+
void Reset()
{
CurseOfBlood_Timer = 15000;
@@ -43,9 +50,11 @@ struct TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
Invisible_Timer = 3000; //Too much too low?
Invisible = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void SummonIllusions(Unit* victim)
{
Rand = rand()%10;
@@ -66,6 +75,7 @@ struct TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
if (Summoned)
(Summoned->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
if (Invisible && Invisible_Timer < diff)
@@ -81,29 +91,35 @@ struct TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
//Do nothing while invisible
return;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//CurseOfBlood_Timer
if (CurseOfBlood_Timer < diff)
{
//Cast
DoCast(m_creature->getVictim(),SPELL_CURSEOFBLOOD);
+
//45 seconds
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);
m_creature->SetDisplayId(11686); // Invisible Model
DoModifyThreatPercent(m_creature->getVictim(),-99);
+
//Summon 10 Illusions attacking random gamers
Unit* target = NULL;
- for (uint8 i = 0; i < 10; ++i)
+ for(uint8 i = 0; i < 10; ++i)
{
target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (target)
@@ -111,10 +127,12 @@ struct TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
}
Invisible = true;
Invisible_Timer = 3000;
+
//25 seconds until we should cast this agian
Illusion_Timer = 25000;
}else Illusion_Timer -= diff;
+
// //Illusion_Timer
// if (Illusion_Timer < diff)
// {
@@ -134,48 +152,61 @@ struct TRINITY_DLL_DECL boss_jandicebarovAI : public ScriptedAI
// }
//
// }else Illusion_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
// Illusion of Jandice Barov Script
+
struct TRINITY_DLL_DECL mob_illusionofjandicebarovAI : public ScriptedAI
{
mob_illusionofjandicebarovAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Cleave_Timer;
+
void Reset()
{
Cleave_Timer = 2000 + rand()%6000;
m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_MAGIC, true);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Cleave_Timer
if (Cleave_Timer < diff)
{
//Cast
DoCast(m_creature->getVictim(),SPELL_CLEAVE);
+
//5-8 seconds
Cleave_Timer = 5000 + rand()%3000;
}else Cleave_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_jandicebarov(Creature* pCreature)
{
return new boss_jandicebarovAI (pCreature);
}
+
CreatureAI* GetAI_mob_illusionofjandicebarov(Creature* pCreature)
{
return new mob_illusionofjandicebarovAI (pCreature);
}
+
void AddSC_boss_jandicebarov()
{
Script *newscript;
@@ -183,6 +214,7 @@ void AddSC_boss_jandicebarov()
newscript->Name = "boss_jandice_barov";
newscript->GetAI = &GetAI_boss_jandicebarov;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_illusionofjandicebarov";
newscript->GetAI = &GetAI_mob_illusionofjandicebarov;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp
index d5793b24e98..753f1969f7f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_kormok.cpp
@@ -13,18 +13,23 @@
* along 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 TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
{
boss_kormokAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowVolley_Timer;
uint32 BoneShield_Timer;
uint32 Minion_Timer;
@@ -38,6 +43,7 @@ struct TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
int Rand2Y;
Creature* SummonedMinions;
Creature* SummonedMages;
+
void Reset()
{
ShadowVolley_Timer = 10000;
@@ -46,9 +52,11 @@ struct TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
Mage_Timer = 0;
Mages = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void SummonMinion(Unit* victim)
{
Rand1 = rand()%8;
@@ -69,6 +77,7 @@ struct TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
if (SummonedMinions)
(SummonedMinions->AI())->AttackStart(victim);
}
+
void SummonMages(Unit* victim)
{
Rand2 = rand()%10;
@@ -89,22 +98,26 @@ struct TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
if (SummonedMages)
(SummonedMages->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -113,8 +126,10 @@ struct TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
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)
{
@@ -123,6 +138,7 @@ struct TRINITY_DLL_DECL boss_kormokAI : public ScriptedAI
SummonMages(m_creature->getVictim());
Mages = true;
}
+
DoMeleeAttackIfReady();
}
};
@@ -130,6 +146,7 @@ CreatureAI* GetAI_boss_kormok(Creature* pCreature)
{
return new boss_kormokAI (pCreature);
}
+
void AddSC_boss_kormok()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp
index 5b777130b32..41f771471db 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lord_alexei_barov.cpp
@@ -13,58 +13,73 @@
* along 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 TRINITY_DLL_DECL boss_lordalexeibarovAI : public ScriptedAI
{
boss_lordalexeibarovAI(Creature *c) : ScriptedAI(c) {}
+
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();
if (pInstance)
{
pInstance->SetData(DATA_LORDALEXEIBAROV_DEATH, 0);
+
if (pInstance->GetData(TYPE_GANDLING) == IN_PROGRESS)
m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -72,6 +87,7 @@ CreatureAI* GetAI_boss_lordalexeibarov(Creature* pCreature)
{
return new boss_lordalexeibarovAI (pCreature);
}
+
void AddSC_boss_lordalexeibarov()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp
index a2b9e017eb1..695d907fca5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_lorekeeper_polkelt.cpp
@@ -13,25 +13,31 @@
* along 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 TRINITY_DLL_DECL boss_lorekeeperpolkeltAI : public ScriptedAI
{
boss_lorekeeperpolkeltAI(Creature *c) : ScriptedAI(c) {}
+
uint32 VolatileInfection_Timer;
uint32 Darkplague_Timer;
uint32 CorrosiveAcid_Timer;
uint32 NoxiousCatalyst_Timer;
+
void Reset()
{
VolatileInfection_Timer = 38000;
@@ -39,47 +45,56 @@ struct TRINITY_DLL_DECL boss_lorekeeperpolkeltAI : public ScriptedAI
CorrosiveAcid_Timer = 45000;
NoxiousCatalyst_Timer = 35000;
}
+
void JustDied(Unit *killer)
{
ScriptedInstance *pInstance = m_creature->GetInstanceData();
if (pInstance)
{
pInstance->SetData(DATA_LOREKEEPERPOLKELT_DEATH, 0);
+
if (pInstance->GetData(TYPE_GANDLING) == IN_PROGRESS)
m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -87,6 +102,7 @@ CreatureAI* GetAI_boss_lorekeeperpolkelt(Creature* pCreature)
{
return new boss_lorekeeperpolkeltAI (pCreature);
}
+
void AddSC_boss_lorekeeperpolkelt()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp
index 2a2e34e137a..0659ab2da4d 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_ras_frostwhisper.cpp
@@ -13,28 +13,34 @@
* along 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 TRINITY_DLL_DECL boss_rasfrostAI : public ScriptedAI
{
boss_rasfrostAI(Creature *c) : ScriptedAI(c) {}
+
uint32 IceArmor_Timer;
uint32 Frostbolt_Timer;
uint32 Freeze_Timer;
uint32 Fear_Timer;
uint32 ChillNova_Timer;
uint32 FrostVolley_Timer;
+
void Reset()
{
IceArmor_Timer = 2000;
@@ -43,53 +49,64 @@ struct TRINITY_DLL_DECL boss_rasfrostAI : public ScriptedAI
Freeze_Timer = 18000;
FrostVolley_Timer = 24000;
Fear_Timer = 45000;
+
m_creature->CastSpell(m_creature,SPELL_ICEARMOR,true);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -97,6 +114,7 @@ CreatureAI* GetAI_boss_rasfrost(Creature* pCreature)
{
return new boss_rasfrostAI (pCreature);
}
+
void AddSC_boss_rasfrost()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp
index 01479e575cb..aae06c8d27a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_the_ravenian.cpp
@@ -13,26 +13,32 @@
* along 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
+
struct TRINITY_DLL_DECL boss_theravenianAI : public ScriptedAI
{
boss_theravenianAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Trample_Timer;
uint32 Cleave_Timer;
uint32 SunderingCleave_Timer;
uint32 KnockAway_Timer;
bool HasYelled;
+
void Reset()
{
Trample_Timer = 24000;
@@ -41,54 +47,65 @@ struct TRINITY_DLL_DECL boss_theravenianAI : public ScriptedAI
KnockAway_Timer = 32000;
HasYelled = false;
}
+
void JustDied(Unit *killer)
{
ScriptedInstance *pInstance = m_creature->GetInstanceData();
if (pInstance)
{
pInstance->SetData(DATA_THERAVENIAN_DEATH, 0);
+
if (pInstance->GetData(TYPE_GANDLING) == IN_PROGRESS)
m_creature->SummonCreature(1853, 180.73, -9.43856, 75.507, 1.61399, TEMPSUMMON_DEAD_DESPAWN, 0);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new boss_theravenianAI (pCreature);
}
+
void AddSC_boss_theravenian()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp
index e20ecf20b2f..f3eef33d24a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/boss_vectus.cpp
@@ -13,37 +13,46 @@
* along 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"
+
enum eEnums
{
EMOTE_GENERIC_FRENZY_KILL = -1000001,
+
SPELL_FLAMESTRIKE = 18399,
SPELL_BLAST_WAVE = 16046,
SPELL_FIRESHIELD = 19626,
SPELL_FRENZY = 8269 //28371,
};
+
struct TRINITY_DLL_DECL boss_vectusAI : public ScriptedAI
{
boss_vectusAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiFireShield_Timer;
uint32 m_uiBlastWave_Timer;
uint32 m_uiFrenzy_Timer;
+
void Reset()
{
m_uiFireShield_Timer = 2000;
m_uiBlastWave_Timer = 14000;
m_uiFrenzy_Timer = 0;
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//FireShield_Timer
if (m_uiFireShield_Timer < uiDiff)
{
@@ -52,6 +61,7 @@ struct TRINITY_DLL_DECL boss_vectusAI : public ScriptedAI
}
else
m_uiFireShield_Timer -= uiDiff;
+
//BlastWave_Timer
if (m_uiBlastWave_Timer < uiDiff)
{
@@ -60,6 +70,7 @@ struct TRINITY_DLL_DECL boss_vectusAI : public ScriptedAI
}
else
m_uiBlastWave_Timer -= uiDiff;
+
//Frenzy_Timer
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 25)
{
@@ -67,18 +78,22 @@ struct TRINITY_DLL_DECL boss_vectusAI : public ScriptedAI
{
DoCast(m_creature, SPELL_FRENZY);
DoScriptText(EMOTE_GENERIC_FRENZY_KILL, m_creature);
+
m_uiFrenzy_Timer = 24000;
}
else
m_uiFrenzy_Timer -= uiDiff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_vectus(Creature* pCreature)
{
return new boss_vectusAI (pCreature);
}
+
void AddSC_boss_vectus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/def_scholomance.h b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/def_scholomance.h
index beaae92bee8..83ce26c9687 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/def_scholomance.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/def_scholomance.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SCHOLOMANCE_H
#define DEF_SCHOLOMANCE_H
+
#define TYPE_GANDLING 1
#define DATA_DOCTORTHEOLENKRASTINOV_DEATH 2
#define DATA_INSTRUCTORMALICIA_DEATH 3
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp
index 4a56bf215f1..282d6e9705a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp
@@ -13,14 +13,17 @@
* along 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"
+
#define GO_GATE_KIRTONOS 175570
#define GO_GATE_GANDLING 177374
#define GO_GATE_MALICIA 177375
@@ -29,13 +32,17 @@ EndScriptData */
#define GO_GATE_RAVENIAN 177372
#define GO_GATE_BAROV 177373
#define GO_GATE_ILLUCIA 177371
+
#define MAX_ENCOUNTER 2
+
struct TRINITY_DLL_DECL instance_scholomance : public ScriptedInstance
{
instance_scholomance(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
//Lord Alexei Barov, Doctor Theolen Krastinov, The Ravenian, Lorekeeper Polkelt, Instructor Malicia and the Lady Illucia Barov.
bool IsBossDied[6];
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
uint64 GateKirtonosGUID;
uint64 GateGandlingGUID;
uint64 GateMiliciaGUID;
@@ -44,9 +51,11 @@ struct TRINITY_DLL_DECL instance_scholomance : public ScriptedInstance
uint64 GateRavenianGUID;
uint64 GateBarovGUID;
uint64 GateIlluciaGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
GateKirtonosGUID = 0;
GateGandlingGUID = 0;
GateMiliciaGUID = 0;
@@ -55,9 +64,11 @@ struct TRINITY_DLL_DECL instance_scholomance : public ScriptedInstance
GateRavenianGUID = 0;
GateBarovGUID = 0;
GateIlluciaGUID = 0;
- for (uint8 i = 0; i < 6; ++i)
+
+ for(uint8 i = 0; i < 6; ++i)
IsBossDied[i] = false;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -72,6 +83,7 @@ struct TRINITY_DLL_DECL instance_scholomance : public ScriptedInstance
case GO_GATE_ILLUCIA: GateIlluciaGUID = pGo->GetGUID(); break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -102,6 +114,7 @@ struct TRINITY_DLL_DECL instance_scholomance : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 type)
{
if (type == TYPE_GANDLING)
@@ -112,13 +125,16 @@ struct TRINITY_DLL_DECL instance_scholomance : public ScriptedInstance
return IN_PROGRESS;
}
}
+
return 0;
}
};
+
InstanceData* GetInstanceData_instance_scholomance(Map* pMap)
{
return new instance_scholomance(pMap);
}
+
void AddSC_instance_scholomance()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/searing_gorge.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/searing_gorge.cpp
index a2797da6dbe..c6e18761ded 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/searing_gorge.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/searing_gorge.cpp
@@ -13,33 +13,43 @@
* along 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
######*/
+
#define GOSSIP_HELLO_KW "Tell me what drives this vengance?"
#define GOSSIP_SELECT_KW1 "Continue please"
#define GOSSIP_SELECT_KW2 "Let me confer with my colleagues"
+
bool GossipHello_npc_kalaran_windblade(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(3441) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_KW, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_kalaran_windblade(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -59,19 +69,26 @@ bool GossipSelect_npc_kalaran_windblade(Player* pPlayer, Creature* pCreature, ui
}
return true;
}
+
/*######
## npc_lothos_riftwaker
######*/
+
#define GOSSIP_HELLO_LR "Teleport me to the Molten Core"
+
bool GossipHello_npc_lothos_riftwaker(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestRewardStatus(7487) || pPlayer->GetQuestRewardStatus(7848))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_LR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lothos_riftwaker(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -79,23 +96,31 @@ bool GossipSelect_npc_lothos_riftwaker(Player* pPlayer, Creature* pCreature, uin
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->TeleportTo(409, 1096, -467, -104.6, 3.64);
}
+
return true;
}
+
/*######
## npc_zamael_lunthistle
######*/
+
#define GOSSIP_HELLO_ZL "Tell me your story"
#define GOSSIP_SELECT_ZL1 "Please continue..."
#define GOSSIP_SELECT_ZL2 "Goodbye"
+
bool GossipHello_npc_zamael_lunthistle(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(3377) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_ZL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(1920, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_zamael_lunthistle(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -115,22 +140,27 @@ bool GossipSelect_npc_zamael_lunthistle(Player* pPlayer, Creature* pCreature, ui
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lothos_riftwaker";
newscript->pGossipHello = &GossipHello_npc_lothos_riftwaker;
newscript->pGossipSelect = &GossipSelect_npc_lothos_riftwaker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_zamael_lunthistle";
newscript->pGossipHello = &GossipHello_npc_zamael_lunthistle;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/def_shadowfang_keep.h b/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/def_shadowfang_keep.h
index d3a2dce833e..8383a5c3950 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/def_shadowfang_keep.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/def_shadowfang_keep.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SHADOWFANG_H
#define DEF_SHADOWFANG_H
+
#define TYPE_FREE_NPC 1
#define TYPE_RETHILGORE 2
#define TYPE_FENRUS 3
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp
index b5381c1e685..29a8d04a1cb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/instance_shadowfang_keep.cpp
@@ -13,44 +13,58 @@
* along 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: 90
SDComment:
SDCategory: Shadowfang Keep
EndScriptData */
+
#include "precompiled.h"
#include "def_shadowfang_keep.h"
+
#define MAX_ENCOUNTER 4
+
enum eEnums
{
SAY_BOSS_DIE_AD = -1033007,
SAY_BOSS_DIE_AS = -1033008,
+
NPC_ASH = 3850,
NPC_ADA = 3849,
+
GO_COURTYARD_DOOR = 18895, //door to open when talking to NPC's
GO_SORCERER_DOOR = 18972, //door to open when Fenrus the Devourer
GO_ARUGAL_DOOR = 18971 //door to open when Wolf Master Nandos
};
+
struct TRINITY_DLL_DECL instance_shadowfang_keep : public ScriptedInstance
{
instance_shadowfang_keep(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
+
uint64 uiAshGUID;
uint64 uiAdaGUID;
+
uint64 DoorCourtyardGUID;
uint64 DoorSorcererGUID;
uint64 DoorArugalGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
uiAshGUID = 0;
uiAdaGUID = 0;
+
DoorCourtyardGUID = 0;
DoorSorcererGUID = 0;
DoorArugalGUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -59,6 +73,7 @@ struct TRINITY_DLL_DECL instance_shadowfang_keep : public ScriptedInstance
case NPC_ADA: uiAdaGUID = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -80,16 +95,19 @@ struct TRINITY_DLL_DECL instance_shadowfang_keep : public ScriptedInstance
break;
}
}
+
void DoSpeech()
{
Creature* pAda = instance->GetCreature(uiAdaGUID);
Creature* pAsh = instance->GetCreature(uiAshGUID);
+
if (pAda && pAda->isAlive() && pAsh && pAsh->isAlive())
{
DoScriptText(SAY_BOSS_DIE_AD,pAda);
DoScriptText(SAY_BOSS_DIE_AS,pAsh);
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -115,16 +133,21 @@ struct TRINITY_DLL_DECL instance_shadowfang_keep : public ScriptedInstance
m_auiEncounter[3] = data;
break;
}
+
if (data == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " " << m_auiEncounter[3];
+
str_data = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -140,10 +163,12 @@ struct TRINITY_DLL_DECL instance_shadowfang_keep : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -151,21 +176,27 @@ struct TRINITY_DLL_DECL instance_shadowfang_keep : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
{
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
}
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_shadowfang_keep(Map* pMap)
{
return new instance_shadowfang_keep(pMap);
}
+
void AddSC_instance_shadowfang_keep()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp
index 6c3f0197466..dddf80ee4bb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/shadowfang_keep/shadowfang_keep.cpp
@@ -13,21 +13,26 @@
* along 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 "escort_ai.h"
#include "def_shadowfang_keep.h"
+
/*######
## npc_shadowfang_prisoner
######*/
+
enum eEnums
{
SAY_FREE_AS = -1033000,
@@ -37,10 +42,13 @@ enum eEnums
SAY_OPEN_DOOR_AD = -1033004,
SAY_POST1_DOOR_AD = -1033005,
SAY_POST2_DOOR_AD = -1033006,
+
SPELL_UNLOCK = 6421,
NPC_ASH = 3850
};
+
#define GOSSIP_ITEM_DOOR "Thanks, I'll follow you to the door."
+
struct TRINITY_DLL_DECL npc_shadowfang_prisonerAI : public npc_escortAI
{
npc_shadowfang_prisonerAI(Creature *c) : npc_escortAI(c)
@@ -48,8 +56,10 @@ struct TRINITY_DLL_DECL npc_shadowfang_prisonerAI : public npc_escortAI
pInstance = c->GetInstanceData();
uiNpcEntry = c->GetEntry();
}
+
ScriptedInstance *pInstance;
uint32 uiNpcEntry;
+
void WaypointReached(uint32 uiPoint)
{
switch(uiPoint)
@@ -75,6 +85,7 @@ struct TRINITY_DLL_DECL npc_shadowfang_prisonerAI : public npc_escortAI
DoScriptText(SAY_POST_DOOR_AS, m_creature);
else
DoScriptText(SAY_POST1_DOOR_AD, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_FREE_NPC, DONE);
break;
@@ -84,34 +95,44 @@ struct TRINITY_DLL_DECL npc_shadowfang_prisonerAI : public npc_escortAI
break;
}
}
+
void Reset() {}
void EnterCombat(Unit* who) {}
};
+
CreatureAI* GetAI_npc_shadowfang_prisoner(Creature* pCreature)
{
return new npc_shadowfang_prisonerAI(pCreature);
}
+
bool GossipHello_npc_shadowfang_prisoner(Player* pPlayer, Creature* pCreature)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
+
if (pInstance && pInstance->GetData(TYPE_FREE_NPC) != DONE && pInstance->GetData(TYPE_RETHILGORE) == DONE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_DOOR, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_shadowfang_prisoner(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
{
pPlayer->CLOSE_GOSSIP_MENU();
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_shadowfang_prisonerAI, pCreature->AI()))
pEscortAI->Start(false, false);
}
return true;
}
+
void AddSC_shadowfang_keep()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_shadowfang_prisoner";
newscript->pGossipHello = &GossipHello_npc_shadowfang_prisoner;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/silvermoon_city.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/silvermoon_city.cpp
index e69f8dfc71e..eb06008ed75 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/silvermoon_city.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/silvermoon_city.cpp
@@ -13,28 +13,37 @@
* along 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 -1000334
+
#define QUEST_REDEEMING_THE_DEAD 9685
#define SPELL_SHIMMERING_VESSEL 31225
#define SPELL_REVIVE_SELF 32343
+
struct TRINITY_DLL_DECL npc_blood_knight_stillbladeAI : public ScriptedAI
{
npc_blood_knight_stillbladeAI(Creature *c) : ScriptedAI(c) {}
+
uint32 lifeTimer;
bool spellHit;
+
void Reset()
{
lifeTimer = 120000;
@@ -42,13 +51,16 @@ struct TRINITY_DLL_DECL npc_blood_knight_stillbladeAI : public ScriptedAI
m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1,7); // lay down
spellHit = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void MoveInLineOfSight(Unit *who)
{
return;
}
+
void UpdateAI(const uint32 diff)
{
if (m_creature->IsStandState())
@@ -59,6 +71,7 @@ struct TRINITY_DLL_DECL npc_blood_knight_stillbladeAI : public ScriptedAI
lifeTimer -= diff;
}
}
+
void SpellHit(Unit *Hitter, const SpellEntry *Spellkind)
{
if ((Spellkind->Id == SPELL_SHIMMERING_VESSEL) && !spellHit &&
@@ -74,10 +87,12 @@ struct TRINITY_DLL_DECL npc_blood_knight_stillbladeAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_blood_knight_stillblade(Creature* pCreature)
{
return new npc_blood_knight_stillbladeAI (pCreature);
}
+
void AddSC_silvermoon_city()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/silverpine_forest.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/silverpine_forest.cpp
index 453656b94c4..7a70d646fae 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/silverpine_forest.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/silverpine_forest.cpp
@@ -13,49 +13,63 @@
* along 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, 435
SDCategory: Silverpine Forest
EndScriptData */
+
/* ContentData
npc_astor_hadren
npc_deathstalker_erland
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_astor_hadren
######*/
+
#define GOSSIP_HAH "You're Astor Hadren, right?"
#define GOSSIP_SAH "You've got something I need, Astor. And I'll be taking it now."
+
struct TRINITY_DLL_DECL npc_astor_hadrenAI : public ScriptedAI
{
npc_astor_hadrenAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
m_creature->setFaction(68);
}
+
void EnterCombat(Unit* who)
{
}
+
void JustDied(Unit *who)
{
m_creature->setFaction(68);
}
};
+
CreatureAI* GetAI_npc_astor_hadren(Creature* pCreature)
{
return new npc_astor_hadrenAI(pCreature);
}
+
bool GossipHello_npc_astor_hadren(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(1886) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HAH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
pPlayer->SEND_GOSSIP_MENU(623, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_astor_hadren(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -73,9 +87,11 @@ bool GossipSelect_npc_astor_hadren(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*######
## npc_deathstalker_erland
######*/
+
enum eErland
{
SAY_QUESTACCEPT = -1000335,
@@ -83,25 +99,32 @@ enum eErland
SAY_AGGRO_1 = -1000337,
SAY_AGGRO_2 = -1000338,
SAY_LAST = -1000339,
+
SAY_THANKS = -1000340,
SAY_RANE = -1000341,
SAY_ANSWER = -1000342,
SAY_MOVE_QUINN = -1000343,
+
SAY_GREETINGS = -1000344,
SAY_QUINN = -1000345,
SAY_ON_BYE = -1000346,
+
QUEST_ESCORTING = 435,
NPC_RANE = 1950,
NPC_QUINN = 1951
};
+
struct TRINITY_DLL_DECL npc_deathstalker_erlandAI : public npc_escortAI
{
npc_deathstalker_erlandAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 1: DoScriptText(SAY_START, m_creature, pPlayer);break;
@@ -123,40 +146,51 @@ struct TRINITY_DLL_DECL npc_deathstalker_erlandAI : public npc_escortAI
DoScriptText(SAY_QUINN, Quinn);
break;}
case 26: DoScriptText(SAY_ON_BYE, m_creature, NULL);break;
+
}
}
+
void Reset() {}
+
void EnterCombat(Unit* who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2), m_creature, who);
}
};
+
bool QuestAccept_npc_deathstalker_erland(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_ESCORTING)
{
DoScriptText(SAY_QUESTACCEPT, pCreature, pPlayer);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_deathstalker_erlandAI, pCreature->AI()))
pEscortAI->Start(true, false, pPlayer->GetGUID());
}
+
return true;
}
+
CreatureAI* GetAI_npc_deathstalker_erlandAI(Creature* pCreature)
{
return new npc_deathstalker_erlandAI(pCreature);
}
+
/*######
## 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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_deathstalker_erland";
newscript->GetAI = &GetAI_npc_deathstalker_erlandAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stormwind_city.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stormwind_city.cpp
index e0f190b5a7b..5e495d6e1e6 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stormwind_city.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stormwind_city.cpp
@@ -13,32 +13,42 @@
* along 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
SDCategory: Stormwind City
EndScriptData */
+
/* ContentData
npc_archmage_malin
npc_bartleby
npc_dashel_stonefist
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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(11223) == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(11223))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_MALIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_archmage_malin(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -46,48 +56,60 @@ bool GossipSelect_npc_archmage_malin(Player* pPlayer, Creature* pCreature, uint3
pPlayer->CLOSE_GOSSIP_MENU();
pCreature->CastSpell(pPlayer, 42711, true);
}
+
return true;
}
+
/*######
## npc_bartleby
######*/
+
enum eBartleby
{
FACTION_ENEMY = 168,
QUEST_BEAT = 1640
};
+
struct TRINITY_DLL_DECL npc_bartlebyAI : public ScriptedAI
{
npc_bartlebyAI(Creature *c) : ScriptedAI(c)
{
m_uiNormalFaction = c->getFaction();
}
+
uint32 m_uiNormalFaction;
+
void Reset()
{
if (m_creature->getFaction() != m_uiNormalFaction)
m_creature->setFaction(m_uiNormalFaction);
}
+
void AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim())
return;
+
if (m_creature->IsFriendlyTo(pAttacker))
return;
+
AttackStart(pAttacker);
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage)*100 / m_creature->GetMaxHealth() < 15))
{
//Take 0 damage
uiDamage = 0;
+
if (pDoneBy->GetTypeId() == TYPEID_PLAYER)
CAST_PLR(pDoneBy)->AreaExploredOrEventHappens(QUEST_BEAT);
EnterEvadeMode();
}
}
};
+
bool QuestAccept_npc_bartleby(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
if (pQuest->GetQuestId() == QUEST_BEAT)
@@ -97,49 +119,62 @@ bool QuestAccept_npc_bartleby(Player* pPlayer, Creature* pCreature, Quest const*
}
return true;
}
+
CreatureAI* GetAI_npc_bartleby(Creature* pCreature)
{
return new npc_bartlebyAI(pCreature);
}
+
/*######
## npc_dashel_stonefist
######*/
+
enum eDashel
{
QUEST_MISSING_DIPLO_PT8 = 1447,
FACTION_HOSTILE = 168
};
+
struct TRINITY_DLL_DECL npc_dashel_stonefistAI : public ScriptedAI
{
npc_dashel_stonefistAI(Creature *c) : ScriptedAI(c)
{
m_uiNormalFaction = c->getFaction();
}
+
uint32 m_uiNormalFaction;
+
void Reset()
{
if (m_creature->getFaction() != m_uiNormalFaction)
m_creature->setFaction(m_uiNormalFaction);
}
+
void AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim())
return;
+
if (m_creature->IsFriendlyTo(pAttacker))
return;
+
AttackStart(pAttacker);
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage)*100 / m_creature->GetMaxHealth() < 15))
{
uiDamage = 0;
+
if (pDoneBy->GetTypeId() == TYPEID_PLAYER)
CAST_PLR(pDoneBy)->AreaExploredOrEventHappens(QUEST_MISSING_DIPLO_PT8);
+
EnterEvadeMode();
}
}
};
+
bool QuestAccept_npc_dashel_stonefist(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
if (pQuest->GetQuestId() == QUEST_MISSING_DIPLO_PT8)
@@ -149,26 +184,34 @@ bool QuestAccept_npc_dashel_stonefist(Player* pPlayer, Creature* pCreature, Ques
}
return true;
}
+
CreatureAI* GetAI_npc_dashel_stonefist(Creature* pCreature)
{
return new npc_dashel_stonefistAI(pCreature);
}
+
/*######
## 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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(4185) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KAT_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(2693, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lady_katrana_prestor(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -192,24 +235,29 @@ bool GossipSelect_npc_lady_katrana_prestor(Player* pPlayer, Creature* pCreature,
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_bartleby";
newscript->GetAI = &GetAI_npc_bartleby;
newscript->pQuestAccept = &QuestAccept_npc_bartleby;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_dashel_stonefist";
newscript->GetAI = &GetAI_npc_dashel_stonefist;
newscript->pQuestAccept = &QuestAccept_npc_dashel_stonefist;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lady_katrana_prestor";
newscript->pGossipHello = &GossipHello_npc_lady_katrana_prestor;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stranglethorn_vale.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stranglethorn_vale.cpp
index c8a43c7d5e9..6f2c787f950 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stranglethorn_vale.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stranglethorn_vale.cpp
@@ -13,32 +13,40 @@
* along 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 TRINITY_DLL_DECL mob_yennikuAI : public ScriptedAI
{
mob_yennikuAI(Creature *c) : ScriptedAI(c)
{
bReset = false;
}
+
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)
@@ -50,13 +58,16 @@ struct TRINITY_DLL_DECL mob_yennikuAI : public ScriptedAI
m_creature->CombatStop(); //stop combat
m_creature->DeleteThreatList(); //unsure of this
m_creature->setFaction(83); //horde generic
+
bReset = true;
Reset_Timer = 60000;
}
}
return;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (bReset)
@@ -69,6 +80,7 @@ struct TRINITY_DLL_DECL mob_yennikuAI : public ScriptedAI
return;
}
else Reset_Timer -= diff;
+
if (m_creature->isInCombat() && m_creature->getVictim())
{
if (m_creature->getVictim()->GetTypeId() == TYPEID_PLAYER)
@@ -82,9 +94,11 @@ struct TRINITY_DLL_DECL mob_yennikuAI : public ScriptedAI
}
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
@@ -92,12 +106,15 @@ CreatureAI* GetAI_mob_yenniku(Creature* pCreature)
{
return new mob_yennikuAI (pCreature);
}
+
/*######
##
######*/
+
void AddSC_stranglethorn_vale()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_yenniku";
newscript->GetAI = &GetAI_mob_yenniku;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp
index aaf4f754f35..e5f33d7a086 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baron_rivendare.cpp
@@ -13,68 +13,84 @@
* along 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: 70
SDComment: aura applied/defined in database
SDCategory: Stratholme
EndScriptData */
+
#include "precompiled.h"
#include "def_stratholme.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 17393
#define SPELL_CLEAVE 15284
#define SPELL_MORTALSTRIKE 15708
+
#define SPELL_UNHOLY_AURA 17467
#define SPELL_RAISEDEAD 17473 //triggers death pact (17471)
+
#define SPELL_RAISE_DEAD1 17475
#define SPELL_RAISE_DEAD2 17476
#define SPELL_RAISE_DEAD3 17477
#define SPELL_RAISE_DEAD4 17478
#define SPELL_RAISE_DEAD5 17479
#define SPELL_RAISE_DEAD6 17480
+
struct TRINITY_DLL_DECL boss_baron_rivendareAI : public ScriptedAI
{
boss_baron_rivendareAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 ShadowBolt_Timer;
uint32 Cleave_Timer;
uint32 MortalStrike_Timer;
// uint32 RaiseDead_Timer;
uint32 SummonSkeletons_Timer;
Creature *Summoned;
+
void Reset()
{
ShadowBolt_Timer = 5000;
@@ -85,33 +101,40 @@ struct TRINITY_DLL_DECL boss_baron_rivendareAI : public ScriptedAI
if (pInstance && pInstance->GetData(TYPE_RAMSTEIN) == DONE)
pInstance->SetData(TYPE_BARON,NOT_STARTED);
}
+
void AttackStart(Unit* who)
{
- if (pInstance) //can't use entercombat(), boss' dmg aura sets near players in combat, before entering the room's door
+ if (pInstance)//can't use entercombat(), boss' dmg aura sets near players in combat, before entering the room's door
pInstance->SetData(TYPE_BARON,IN_PROGRESS);
ScriptedAI::AttackStart(who);
}
+
void JustSummoned(Creature* summoned)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
summoned->AI()->AttackStart(target);
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(TYPE_BARON,DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//ShadowBolt
if (ShadowBolt_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT);
+
ShadowBolt_Timer = 10000;
}else ShadowBolt_Timer -= diff;
+
//Cleave
if (Cleave_Timer < diff)
{
@@ -119,18 +142,21 @@ struct TRINITY_DLL_DECL boss_baron_rivendareAI : public ScriptedAI
//13 seconds until we should cast this again
Cleave_Timer = 7000 + (rand()%10000);
}else Cleave_Timer -= diff;
+
//MortalStrike
if (MortalStrike_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_MORTALSTRIKE);
MortalStrike_Timer = 10000 + (rand()%15000);
}else MortalStrike_Timer -= diff;
+
//RaiseDead
// if (RaiseDead_Timer < diff)
// {
// DoCast(m_creature,SPELL_RAISEDEAD);
// RaiseDead_Timer = 45000;
// }else RaiseDead_Timer -= diff;
+
//SummonSkeletons
if (SummonSkeletons_Timer < diff)
{
@@ -140,16 +166,20 @@ struct TRINITY_DLL_DECL boss_baron_rivendareAI : public ScriptedAI
m_creature->SummonCreature(11197,ADD_4X,ADD_4Y,ADD_4Z,ADD_4O,TEMPSUMMON_TIMED_DESPAWN,29000);
m_creature->SummonCreature(11197,ADD_5X,ADD_5Y,ADD_5Z,ADD_5O,TEMPSUMMON_TIMED_DESPAWN,29000);
m_creature->SummonCreature(11197,ADD_6X,ADD_6Y,ADD_6Z,ADD_6O,TEMPSUMMON_TIMED_DESPAWN,29000);
+
//34 seconds until we should cast this again
SummonSkeletons_Timer = 40000;
}else SummonSkeletons_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_baron_rivendare(Creature* pCreature)
{
return new boss_baron_rivendareAI (pCreature);
}
+
void AddSC_boss_baron_rivendare()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp
index 897a2aef8e5..ba5c2ec0521 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_baroness_anastari.cpp
@@ -13,29 +13,36 @@
* along 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"
#include "def_stratholme.h"
+
#define SPELL_BANSHEEWAIL 16565
#define SPELL_BANSHEECURSE 16867
#define SPELL_SILENCE 18327
//#define SPELL_POSSESS 17244
+
struct TRINITY_DLL_DECL boss_baroness_anastariAI : public ScriptedAI
{
boss_baroness_anastariAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 BansheeWail_Timer;
uint32 BansheeCurse_Timer;
uint32 Silence_Timer;
//uint32 Possess_Timer;
+
void Reset()
{
BansheeWail_Timer = 1000;
@@ -43,18 +50,22 @@ struct TRINITY_DLL_DECL boss_baroness_anastariAI : public ScriptedAI
Silence_Timer = 13000;
//Possess_Timer = 35000;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(TYPE_BARONESS,IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//BansheeWail
if (BansheeWail_Timer < diff)
{
@@ -63,6 +74,7 @@ struct TRINITY_DLL_DECL boss_baroness_anastariAI : public ScriptedAI
//4 seconds until we should cast this again
BansheeWail_Timer = 4000;
}else BansheeWail_Timer -= diff;
+
//BansheeCurse
if (BansheeCurse_Timer < diff)
{
@@ -71,6 +83,7 @@ struct TRINITY_DLL_DECL boss_baroness_anastariAI : public ScriptedAI
//18 seconds until we should cast this again
BansheeCurse_Timer = 18000;
}else BansheeCurse_Timer -= diff;
+
//Silence
if (Silence_Timer < diff)
{
@@ -79,6 +92,7 @@ struct TRINITY_DLL_DECL boss_baroness_anastariAI : public ScriptedAI
//13 seconds until we should cast this again
Silence_Timer = 13000;
}else Silence_Timer -= diff;
+
//Possess
/* if (Possess_Timer < diff)
{
@@ -93,6 +107,7 @@ struct TRINITY_DLL_DECL boss_baroness_anastariAI : public ScriptedAI
Possess_Timer = 50000;
}else Possess_Timer -= diff;
*/
+
DoMeleeAttackIfReady();
}
};
@@ -100,6 +115,7 @@ CreatureAI* GetAI_boss_baroness_anastari(Creature* pCreature)
{
return new boss_baroness_anastariAI (pCreature);
}
+
void AddSC_boss_baroness_anastari()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp
index 5586693df80..69ff09067f5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_cannon_master_willey.cpp
@@ -13,13 +13,16 @@
* along 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
@@ -65,17 +68,21 @@ EndScriptData */
#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 16496
//#define SPELL_SUMMONCRIMSONRIFLEMAN 17279
+
struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
{
boss_cannon_master_willeyAI(Creature *c) : ScriptedAI(c) {}
+
uint32 KnockAway_Timer;
uint32 Pummel_Timer;
uint32 Shoot_Timer;
uint32 SummonRifleman_Timer;
+
void Reset()
{
Shoot_Timer = 1000;
@@ -83,6 +90,7 @@ struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
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);
@@ -93,15 +101,18 @@ struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Pummel
if (Pummel_Timer < diff)
{
@@ -113,6 +124,7 @@ struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
//12 seconds until we should cast this again
Pummel_Timer = 12000;
}else Pummel_Timer -= diff;
+
//KnockAway
if (KnockAway_Timer < diff)
{
@@ -124,6 +136,7 @@ struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
//14 seconds until we should cast this again
KnockAway_Timer = 14000;
}else KnockAway_Timer -= diff;
+
//Shoot
if (Shoot_Timer < diff)
{
@@ -132,6 +145,7 @@ struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
//1 seconds until we should cast this again
Shoot_Timer = 1000;
}else Shoot_Timer -= diff;
+
//SummonRifleman
if (SummonRifleman_Timer < diff)
{
@@ -187,6 +201,7 @@ struct TRINITY_DLL_DECL boss_cannon_master_willeyAI : public ScriptedAI
//30 seconds until we should cast this again
SummonRifleman_Timer = 30000;
}else SummonRifleman_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -194,6 +209,7 @@ CreatureAI* GetAI_boss_cannon_master_willey(Creature* pCreature)
{
return new boss_cannon_master_willeyAI (pCreature);
}
+
void AddSC_boss_cannon_master_willey()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp
index 86978181dd7..91477d3fef5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_dathrohan_balnazzar.cpp
@@ -13,49 +13,60 @@
* along 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_Dathrohan_Balnazzar
SD%Complete: 95
SDComment: Possibly need to fix/improve summons after death
SDCategory: Stratholme
EndScriptData */
+
#include "precompiled.h"
+
enum eEnums
{
//Dathrohan spells
SPELL_CRUSADERSHAMMER = 17286, //AOE stun
SPELL_CRUSADERSTRIKE = 17281,
SPELL_HOLYSTRIKE = 17284, //weapon dmg +3
+
//Transform
SPELL_BALNAZZARTRANSFORM = 17288, //restore full HP/mana, trigger spell Balnazzar Transform Stun
+
//Balnazzar spells
SPELL_SHADOWSHOCK = 17399,
SPELL_MINDBLAST = 17287,
SPELL_PSYCHICSCREAM = 13704,
SPELL_SLEEP = 12098,
SPELL_MINDCONTROL = 15690,
+
NPC_DATHROHAN = 10812,
NPC_BALNAZZAR = 10813,
NPC_ZOMBIE = 10698 //probably incorrect
};
+
struct SummonDef
{
float m_fX, m_fY, m_fZ, m_fOrient;
};
+
SummonDef m_aSummonPoint[]=
{
{3444.156, -3090.626, 135.002, 2.240}, //G1 front, left
{3449.123, -3087.009, 135.002, 2.240}, //G1 front, right
{3446.246, -3093.466, 135.002, 2.240}, //G1 back left
{3451.160, -3089.904, 135.002, 2.240}, //G1 back, right
+
{3457.995, -3080.916, 135.002, 3.784}, //G2 front, left
{3454.302, -3076.330, 135.002, 3.784}, //G2 front, right
{3460.975, -3078.901, 135.002, 3.784}, //G2 back left
{3457.338, -3073.979, 135.002, 3.784} //G2 back, right
};
+
struct TRINITY_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI
{
boss_dathrohan_balnazzarAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiCrusadersHammer_Timer;
uint32 m_uiCrusaderStrike_Timer;
uint32 m_uiMindBlast_Timer;
@@ -65,6 +76,7 @@ struct TRINITY_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI
uint32 m_uiDeepSleep_Timer;
uint32 m_uiMindControl_Timer;
bool m_bTransformed;
+
void Reset()
{
m_uiCrusadersHammer_Timer = 8000;
@@ -76,24 +88,30 @@ struct TRINITY_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI
m_uiDeepSleep_Timer = 20000;
m_uiMindControl_Timer = 10000;
m_bTransformed = false;
+
if (m_creature->GetEntry() == NPC_BALNAZZAR)
m_creature->UpdateEntry(NPC_DATHROHAN);
}
+
void JustDied(Unit* Victim)
{
static uint32 uiCount = sizeof(m_aSummonPoint)/sizeof(SummonDef);
+
for (uint8 i=0; i<uiCount; ++i)
m_creature->SummonCreature(NPC_ZOMBIE,
m_aSummonPoint[i].m_fX, m_aSummonPoint[i].m_fY, m_aSummonPoint[i].m_fZ, m_aSummonPoint[i].m_fOrient,
TEMPSUMMON_TIMED_DESPAWN, HOUR*IN_MILISECONDS);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//START NOT TRANSFORMED
if (!m_bTransformed)
{
@@ -103,29 +121,34 @@ struct TRINITY_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_MINDBLAST);
m_uiMindBlast_Timer = 15000 + rand()%5000;
}else m_uiMindBlast_Timer -= uiDiff;
+
//CrusadersHammer
if (m_uiCrusadersHammer_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_CRUSADERSHAMMER);
m_uiCrusadersHammer_Timer = 12000;
}else m_uiCrusadersHammer_Timer -= uiDiff;
+
//CrusaderStrike
if (m_uiCrusaderStrike_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_CRUSADERSTRIKE);
m_uiCrusaderStrike_Timer = 15000;
}else m_uiCrusaderStrike_Timer -= uiDiff;
+
//HolyStrike
if (m_uiHolyStrike_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_HOLYSTRIKE);
m_uiHolyStrike_Timer = 15000;
}else m_uiHolyStrike_Timer -= uiDiff;
+
//BalnazzarTransform
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 40)
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
//restore hp, mana and stun
DoCast(m_creature,SPELL_BALNAZZARTRANSFORM);
m_creature->UpdateEntry(NPC_BALNAZZAR);
@@ -140,26 +163,32 @@ struct TRINITY_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_MINDBLAST);
m_uiMindBlast_Timer = 15000 + rand()%5000;
}else m_uiMindBlast_Timer -= uiDiff;
+
//ShadowShock
if (m_uiShadowShock_Timer < uiDiff)
{
DoCast(m_creature->getVictim(),SPELL_SHADOWSHOCK);
m_uiShadowShock_Timer = 11000;
}else m_uiShadowShock_Timer -= uiDiff;
+
//PsychicScream
if (m_uiPsychicScream_Timer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(pTarget,SPELL_PSYCHICSCREAM);
+
m_uiPsychicScream_Timer = 20000;
}else m_uiPsychicScream_Timer -= uiDiff;
+
//DeepSleep
if (m_uiDeepSleep_Timer < uiDiff)
{
if (Unit *pTarget = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(pTarget,SPELL_SLEEP);
+
m_uiDeepSleep_Timer = 15000;
}else m_uiDeepSleep_Timer -= uiDiff;
+
//MindControl
if (m_uiMindControl_Timer < uiDiff)
{
@@ -167,13 +196,16 @@ struct TRINITY_DLL_DECL boss_dathrohan_balnazzarAI : public ScriptedAI
m_uiMindControl_Timer = 15000;
}else m_uiMindControl_Timer -= uiDiff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_dathrohan_balnazzar(Creature* pCreature)
{
return new boss_dathrohan_balnazzarAI (pCreature);
}
+
void AddSC_boss_dathrohan_balnazzar()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp
index 439e104b8ef..c1fe3220b9e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_magistrate_barthilas.cpp
@@ -13,28 +13,35 @@
* along 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: 70
SDComment:
SDCategory: Stratholme
EndScriptData */
+
#include "precompiled.h"
#include "def_stratholme.h"
+
#define SPELL_DRAININGBLOW 16793
#define SPELL_CROWDPUMMEL 10887
#define SPELL_MIGHTYBLOW 14099
#define SPELL_FURIOUS_ANGER 16791
+
#define MODEL_NORMAL 10433
#define MODEL_HUMAN 3637
+
struct TRINITY_DLL_DECL boss_magistrate_barthilasAI : public ScriptedAI
{
boss_magistrate_barthilasAI(Creature *c) : ScriptedAI(c) {}
+
uint32 DrainingBlow_Timer;
uint32 CrowdPummel_Timer;
uint32 MightyBlow_Timer;
uint32 FuriousAnger_Timer;
uint32 AngerCount;
+
void Reset()
{
DrainingBlow_Timer = 20000;
@@ -42,54 +49,66 @@ struct TRINITY_DLL_DECL boss_magistrate_barthilasAI : public ScriptedAI
MightyBlow_Timer = 10000;
FuriousAnger_Timer = 5000;
AngerCount = 0;
+
if (m_creature->isAlive())
m_creature->SetDisplayId(MODEL_NORMAL);
else
m_creature->SetDisplayId(MODEL_HUMAN);
}
+
void MoveInLineOfSight(Unit *who)
{
//nothing to see here yet
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void JustDied(Unit* Killer)
{
m_creature->SetDisplayId(MODEL_HUMAN);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (FuriousAnger_Timer < diff)
{
FuriousAnger_Timer = 4000;
if (AngerCount > 25)
return;
+
++AngerCount;
m_creature->CastSpell(m_creature,SPELL_FURIOUS_ANGER,false);
}else FuriousAnger_Timer -= diff;
+
//DrainingBlow
if (DrainingBlow_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_DRAININGBLOW);
DrainingBlow_Timer = 15000;
}else DrainingBlow_Timer -= diff;
+
//CrowdPummel
if (CrowdPummel_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CROWDPUMMEL);
CrowdPummel_Timer = 15000;
}else CrowdPummel_Timer -= diff;
+
//MightyBlow
if (MightyBlow_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_MIGHTYBLOW);
MightyBlow_Timer = 20000;
}else MightyBlow_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -97,6 +116,7 @@ CreatureAI* GetAI_boss_magistrate_barthilas(Creature* pCreature)
{
return new boss_magistrate_barthilasAI (pCreature);
}
+
void AddSC_boss_magistrate_barthilas()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp
index 1af71d380da..e5e31e20704 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_maleki_the_pallid.cpp
@@ -13,47 +13,58 @@
* along 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"
#include "def_stratholme.h"
+
#define SPELL_FROSTBOLT 17503
#define SPELL_DRAINLIFE 20743
#define SPELL_DRAIN_MANA 17243
#define SPELL_ICETOMB 16869
+
struct TRINITY_DLL_DECL boss_maleki_the_pallidAI : public ScriptedAI
{
boss_maleki_the_pallidAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 Frostbolt_Timer;
uint32 IceTomb_Timer;
uint32 DrainLife_Timer;
+
void Reset()
{
Frostbolt_Timer = 1000;
IceTomb_Timer = 16000;
DrainLife_Timer = 31000;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(TYPE_PALLID,IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Frostbolt
if (Frostbolt_Timer < diff)
{
@@ -61,6 +72,7 @@ struct TRINITY_DLL_DECL boss_maleki_the_pallidAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_FROSTBOLT);
Frostbolt_Timer = 3500;
}else Frostbolt_Timer -= diff;
+
//IceTomb
if (IceTomb_Timer < diff)
{
@@ -68,6 +80,7 @@ struct TRINITY_DLL_DECL boss_maleki_the_pallidAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_ICETOMB);
IceTomb_Timer = 28000;
}else IceTomb_Timer -= diff;
+
//DrainLife
if (DrainLife_Timer < diff)
{
@@ -75,6 +88,7 @@ struct TRINITY_DLL_DECL boss_maleki_the_pallidAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_DRAINLIFE);
DrainLife_Timer = 31000;
}else DrainLife_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -82,6 +96,7 @@ CreatureAI* GetAI_boss_maleki_the_pallid(Creature* pCreature)
{
return new boss_maleki_the_pallidAI (pCreature);
}
+
void AddSC_boss_maleki_the_pallid()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp
index 465b7c4a272..f051208c287 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_nerubenkan.cpp
@@ -13,25 +13,31 @@
* along 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: 70
SDComment:
SDCategory: Stratholme
EndScriptData */
+
#include "precompiled.h"
#include "def_stratholme.h"
+
#define SPELL_ENCASINGWEBS 4962
#define SPELL_PIERCEARMOR 6016
#define SPELL_CRYPT_SCARABS 31602
#define SPELL_RAISEUNDEADSCARAB 17235
+
struct TRINITY_DLL_DECL boss_nerubenkanAI : public ScriptedAI
{
boss_nerubenkanAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 EncasingWebs_Timer;
uint32 PierceArmor_Timer;
uint32 CryptScarabs_Timer;
@@ -40,6 +46,7 @@ struct TRINITY_DLL_DECL boss_nerubenkanAI : public ScriptedAI
int RandX;
int RandY;
Creature* Summoned;
+
void Reset()
{
CryptScarabs_Timer = 3000;
@@ -47,14 +54,17 @@ struct TRINITY_DLL_DECL boss_nerubenkanAI : public ScriptedAI
PierceArmor_Timer = 19000;
RaiseUndeadScarab_Timer = 3000;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(TYPE_NERUB,IN_PROGRESS);
}
+
void RaiseUndeadScarab(Unit* victim)
{
Rand = rand()%10;
@@ -75,16 +85,19 @@ struct TRINITY_DLL_DECL boss_nerubenkanAI : public ScriptedAI
if (Summoned)
(Summoned->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//EncasingWebs
if (EncasingWebs_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ENCASINGWEBS);
EncasingWebs_Timer = 30000;
}else EncasingWebs_Timer -= diff;
+
//PierceArmor
if (PierceArmor_Timer < diff)
{
@@ -92,18 +105,21 @@ struct TRINITY_DLL_DECL boss_nerubenkanAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_PIERCEARMOR);
PierceArmor_Timer = 35000;
}else PierceArmor_Timer -= diff;
+
//CryptScarabs_Timer
if (CryptScarabs_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CRYPT_SCARABS);
CryptScarabs_Timer = 20000;
}else CryptScarabs_Timer -= diff;
+
//RaiseUndeadScarab
if (RaiseUndeadScarab_Timer < diff)
{
RaiseUndeadScarab(m_creature->getVictim());
RaiseUndeadScarab_Timer = 16000;
}else RaiseUndeadScarab_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -111,6 +127,7 @@ CreatureAI* GetAI_boss_nerubenkan(Creature* pCreature)
{
return new boss_nerubenkanAI (pCreature);
}
+
void AddSC_boss_nerubenkan()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp
index 63e0da6143b..4cbbe273a65 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_order_of_silver_hand.cpp
@@ -13,41 +13,51 @@
* along 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 TRINITY_DLL_DECL boss_silver_hand_bossesAI : public ScriptedAI
{
boss_silver_hand_bossesAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 HolyLight_Timer;
uint32 DivineShield_Timer;
+
void Reset()
{
HolyLight_Timer = 20000;
DivineShield_Timer = 20000;
+
if (pInstance)
{
switch(m_creature->GetEntry())
@@ -70,9 +80,11 @@ struct TRINITY_DLL_DECL boss_silver_hand_bossesAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit* who)
{
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
@@ -99,11 +111,13 @@ struct TRINITY_DLL_DECL boss_silver_hand_bossesAI : public ScriptedAI
CAST_PLR(Killer)->KilledMonsterCredit(SH_QUEST_CREDIT,m_creature->GetGUID());
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (HolyLight_Timer < diff)
{
if (m_creature->GetHealth()*5 < m_creature->GetMaxHealth())
@@ -112,6 +126,7 @@ struct TRINITY_DLL_DECL boss_silver_hand_bossesAI : public ScriptedAI
HolyLight_Timer = 20000;
}
}else HolyLight_Timer -= diff;
+
if (DivineShield_Timer < diff)
{
if (m_creature->GetHealth()*20 < m_creature->GetMaxHealth())
@@ -120,16 +135,20 @@ struct TRINITY_DLL_DECL boss_silver_hand_bossesAI : public ScriptedAI
DivineShield_Timer = 40000;
}
}else DivineShield_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
};
CreatureAI* GetAI_boss_silver_hand_bossesAI(Creature* pCreature)
{
return new boss_silver_hand_bossesAI (pCreature);
}
+
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;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp
index e2c6fcb3312..ef43c7774bc 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_postmaster_malown.cpp
@@ -13,30 +13,38 @@
* along 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 TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
{
boss_postmaster_malownAI(Creature *c) : ScriptedAI(c) {}
+
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
@@ -46,14 +54,17 @@ struct TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
CallOfTheGrave_Timer = 25000;
HasYelled = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//WailingDead
if (WailingDead_Timer < diff)
{
@@ -65,6 +76,7 @@ struct TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
//19 seconds until we should cast this again
WailingDead_Timer = 19000;
}else WailingDead_Timer -= diff;
+
//Backhand
if (Backhand_Timer < diff)
{
@@ -76,6 +88,7 @@ struct TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
//8 seconds until we should cast this again
Backhand_Timer = 8000;
}else Backhand_Timer -= diff;
+
//CurseOfWeakness
if (CurseOfWeakness_Timer < diff)
{
@@ -87,6 +100,7 @@ struct TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
//20 seconds until we should cast this again
CurseOfWeakness_Timer = 20000;
}else CurseOfWeakness_Timer -= diff;
+
//CurseOfTongues
if (CurseOfTongues_Timer < diff)
{
@@ -98,6 +112,7 @@ struct TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
//22 seconds until we should cast this again
CurseOfTongues_Timer = 22000;
}else CurseOfTongues_Timer -= diff;
+
//CallOfTheGrave
if (CallOfTheGrave_Timer < diff)
{
@@ -109,6 +124,7 @@ struct TRINITY_DLL_DECL boss_postmaster_malownAI : public ScriptedAI
//25 seconds until we should cast this again
CallOfTheGrave_Timer = 25000;
}else CallOfTheGrave_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -116,6 +132,7 @@ CreatureAI* GetAI_boss_postmaster_malown(Creature* pCreature)
{
return new boss_postmaster_malownAI (pCreature);
}
+
void AddSC_boss_postmaster_malown()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp
index a65022ac629..bac18320293 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_ramstein_the_gorger.cpp
@@ -13,61 +13,76 @@
* along 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: 70
SDComment:
SDCategory: Stratholme
EndScriptData */
+
#include "precompiled.h"
#include "def_stratholme.h"
+
#define SPELL_TRAMPLE 5568
#define SPELL_KNOCKOUT 17307
+
#define C_MINDLESS_UNDEAD 11030
+
struct TRINITY_DLL_DECL boss_ramstein_the_gorgerAI : public ScriptedAI
{
boss_ramstein_the_gorgerAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 Trample_Timer;
uint32 Knockout_Timer;
+
void Reset()
{
Trample_Timer = 3000;
Knockout_Timer = 12000;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustDied(Unit* Killer)
{
- for (uint8 i = 0; i < 30; ++i)
+ for(uint8 i = 0; i < 30; ++i)
{
if (Creature* mob = m_creature->SummonCreature(C_MINDLESS_UNDEAD,3969.35+irand(-10,10),-3391.87+irand(-10,10),119.11,5.91,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,1800000))
mob->AI()->AttackStart(m_creature->SelectNearestTarget(500));
}
+
if (pInstance)
pInstance->SetData(TYPE_RAMSTEIN,DONE);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Trample
if (Trample_Timer < diff)
{
DoCast(m_creature,SPELL_TRAMPLE);
Trample_Timer = 7000;
}else Trample_Timer -= diff;
+
//Knockout
if (Knockout_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_KNOCKOUT);
Knockout_Timer = 10000;
}else Knockout_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -75,6 +90,7 @@ CreatureAI* GetAI_boss_ramstein_the_gorger(Creature* pCreature)
{
return new boss_ramstein_the_gorgerAI (pCreature);
}
+
void AddSC_boss_ramstein_the_gorger()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp
index 140b8725841..e6e96c3a776 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/boss_timmy_the_cruel.cpp
@@ -13,25 +13,33 @@
* along 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 TRINITY_DLL_DECL boss_timmy_the_cruelAI : public ScriptedAI
{
boss_timmy_the_cruelAI(Creature *c) : ScriptedAI(c) {}
+
uint32 RavenousClaw_Timer;
bool HasYelled;
+
void Reset()
{
RavenousClaw_Timer = 10000;
HasYelled = false;
}
+
void EnterCombat(Unit *who)
{
if (!HasYelled)
@@ -40,11 +48,13 @@ struct TRINITY_DLL_DECL boss_timmy_the_cruelAI : public ScriptedAI
HasYelled = true;
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//RavenousClaw
if (RavenousClaw_Timer < diff)
{
@@ -53,6 +63,7 @@ struct TRINITY_DLL_DECL boss_timmy_the_cruelAI : public ScriptedAI
//15 seconds until we should cast this again
RavenousClaw_Timer = 15000;
}else RavenousClaw_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -60,6 +71,7 @@ CreatureAI* GetAI_boss_timmy_the_cruel(Creature* pCreature)
{
return new boss_timmy_the_cruelAI (pCreature);
}
+
void AddSC_boss_timmy_the_cruel()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/def_stratholme.h b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/def_stratholme.h
index 2f05f55f556..b9246091a7c 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/def_stratholme.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/def_stratholme.h
@@ -1,22 +1,27 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_STRATHOLME_H
#define DEF_STRATHOLME_H
+
#define TYPE_BARON_RUN 1
#define TYPE_BARONESS 2
#define TYPE_NERUB 3
#define TYPE_PALLID 4
#define TYPE_RAMSTEIN 5
#define TYPE_BARON 6
+
#define DATA_BARON 10
#define DATA_YSIDA_TRIGGER 11
+
#define TYPE_SH_QUEST 20
#define TYPE_SH_CATHELA 21
#define TYPE_SH_GREGOR 22
#define TYPE_SH_NEMAS 23
#define TYPE_SH_VICAR 24
#define TYPE_SH_AELMAR 25
+
#define QUEST_DEAD_MAN_PLEA 8945
#define SPELL_BARON_ULTIMATUM 27861
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp
index 6a56d2ca865..d475e116039 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/instance_stratholme.cpp
@@ -13,14 +13,17 @@
* along 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: 50
SDComment: In progress. Undead side 75% implemented. Save/load not implemented.
SDCategory: Stratholme
EndScriptData */
+
#include "precompiled.h"
#include "def_stratholme.h"
+
#define GO_SERVICE_ENTRANCE 175368
#define GO_GAUNTLET_GATE1 175357
#define GO_ZIGGURAT1 175380 //baroness
@@ -31,22 +34,30 @@ EndScriptData */
#define GO_PORT_GAUNTLET 175374 //port from gauntlet to slaugther
#define GO_PORT_SLAUGTHER 175373 //port at slaugther
#define GO_PORT_ELDERS 175377 //port at elders square
+
#define C_CRYSTAL 10415 //three ziggurat crystals
#define C_BARON 10440
#define C_YSIDA_TRIGGER 16100
+
#define C_RAMSTEIN 10439
#define C_ABOM_BILE 10416
#define C_ABOM_VENOM 10417
#define C_BLACK_GUARD 10394
#define C_YSIDA 16031
+
#define MAX_ENCOUNTER 6
+
struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
{
instance_stratholme(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 Encounter[MAX_ENCOUNTER];
+
bool IsSilverHandDead[5];
+
uint32 BaronRun_Timer;
uint32 SlaugtherSquare_Timer;
+
uint64 serviceEntranceGUID;
uint64 gauntletGate1GUID;
uint64 ziggurat1GUID;
@@ -57,18 +68,23 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
uint64 portGauntletGUID;
uint64 portSlaugtherGUID;
uint64 portElderGUID;
+
uint64 baronGUID;
uint64 ysidaTriggerGUID;
std::set<uint64> crystalsGUID;
std::set<uint64> abomnationGUID;
+
void Initialize()
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
Encounter[i] = NOT_STARTED;
- for (uint8 i = 0; i < 5; ++i)
+
+ for(uint8 i = 0; i < 5; ++i)
IsSilverHandDead[i] = false;
+
BaronRun_Timer = 0;
SlaugtherSquare_Timer = 0;
+
serviceEntranceGUID = 0;
gauntletGate1GUID = 0;
ziggurat1GUID = 0;
@@ -79,11 +95,13 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
portGauntletGUID = 0;
portSlaugtherGUID = 0;
portElderGUID = 0;
+
baronGUID = 0;
ysidaTriggerGUID = 0;
crystalsGUID.clear();
abomnationGUID.clear();
}
+
bool StartSlaugtherSquare()
{
//change to DONE when crystals implemented
@@ -93,14 +111,17 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
UpdateGoState(portSlaugtherGUID,0,false);
return true;
}
+
debug_log("TSCR: Instance Stratholme: Cannot open slaugther square yet.");
return false;
}
+
//if withRestoreTime true, then newState will be ignored and GO should be restored to original state after 10 seconds
void UpdateGoState(uint64 goGuid, uint32 newState, bool withRestoreTime)
{
if (!goGuid)
return;
+
if (GameObject* pGo = instance->GetGameObject(goGuid))
{
if (withRestoreTime)
@@ -109,6 +130,7 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
pGo->SetGoState((GOState)newState);
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -120,6 +142,7 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
case C_ABOM_VENOM: abomnationGUID.insert(pCreature->GetGUID()); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -148,6 +171,7 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
case GO_PORT_ELDERS: portElderGUID = pGo->GetGUID(); break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -200,8 +224,9 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
{
if (Encounter[4] != IN_PROGRESS)
UpdateGoState(portGauntletGUID,GO_STATE_READY,false);
+
uint32 count = abomnationGUID.size();
- for (std::set<uint64>::iterator i = abomnationGUID.begin(); i != abomnationGUID.end(); ++i)
+ for(std::set<uint64>::iterator i = abomnationGUID.begin(); i != abomnationGUID.end(); ++i)
{
if (Creature* pAbom = instance->GetCreature(*i))
{
@@ -209,6 +234,7 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
--count;
}
}
+
if (!count)
{
//a bit itchy, it should close the door after 10 secs, but it doesn't. skipping it for now.
@@ -235,19 +261,22 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
if (GetData(TYPE_BARON_RUN) == IN_PROGRESS)
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* pPlayer = itr->getSource())
{
if (pPlayer->HasAura(SPELL_BARON_ULTIMATUM))
pPlayer->RemoveAurasDueToSpell(SPELL_BARON_ULTIMATUM);
+
if (pPlayer->GetQuestStatus(QUEST_DEAD_MAN_PLEA) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(QUEST_DEAD_MAN_PLEA);
}
}
}
+
SetData(TYPE_BARON_RUN,DONE);
}
}
@@ -276,15 +305,19 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
}
if (data == DONE)SaveToDB();
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << Encounter[0] << " " << Encounter[1] << " " << Encounter[2] << " "
<< Encounter[3] << " " << Encounter[4] << " " << Encounter[5];
+
OUT_SAVE_INST_DATA_COMPLETE;
return saveStream.str();
}
+
void Load(const char* in)
{
if (!in)
@@ -292,15 +325,20 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> Encounter[0] >> Encounter[1] >> Encounter[2] >> Encounter[3]
>> Encounter[4] >> Encounter[5];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (Encounter[i] == IN_PROGRESS)
Encounter[i] = NOT_STARTED;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -324,6 +362,7 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -335,6 +374,7 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
}
return 0;
}
+
void Update(uint32 diff)
{
if (BaronRun_Timer)
@@ -347,14 +387,16 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
debug_log("TSCR: Instance Stratholme: Baron run event reached end. Event has state %u.",GetData(TYPE_BARON_RUN));
}else BaronRun_Timer -= diff;
}
+
if (SlaugtherSquare_Timer)
{
if (SlaugtherSquare_Timer <= diff)
{
if (Creature* pBaron = instance->GetCreature(baronGUID))
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
pBaron->SummonCreature(C_BLACK_GUARD,4032.84,-3390.24,119.73,4.71,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,1800000);
+
UpdateGoState(ziggurat4GUID,GO_STATE_ACTIVE,false);
UpdateGoState(ziggurat5GUID,GO_STATE_ACTIVE,false);
debug_log("TSCR: Instance Stratholme: Black guard sentries spawned. Opening gates to baron.");
@@ -364,10 +406,12 @@ struct TRINITY_DLL_DECL instance_stratholme : public ScriptedInstance
}
}
};
+
InstanceData* GetInstanceData_instance_stratholme(Map* pMap)
{
return new instance_stratholme(pMap);
}
+
void AddSC_instance_stratholme()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/stratholme.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/stratholme.cpp
index c091ab5562e..1d0473e1d9b 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/stratholme.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/stratholme/stratholme.cpp
@@ -13,37 +13,46 @@
* along 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: Misc mobs for instance. pGo-script to apply aura and start event for quest 8945
SDCategory: Stratholme
EndScriptData */
+
/* ContentData
go_gauntlet_gate
mob_freed_soul
mob_restless_soul
mobs_spectral_ghostly_citizen
EndContentData */
+
#include "precompiled.h"
#include "def_stratholme.h"
+
/*######
## go_gauntlet_gate (this is the _first_ of the gauntlet gates, two exist)
######*/
+
bool GOHello_go_gauntlet_gate(Player* pPlayer, GameObject* pGo)
{
ScriptedInstance* pInstance = pGo->GetInstanceData();
+
if (!pInstance)
return false;
+
if (pInstance->GetData(TYPE_BARON_RUN) != NOT_STARTED)
return false;
+
if (Group *pGroup = pPlayer->GetGroup())
{
- for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* pGroupie = itr->getSource();
if (!pGroupie)
continue;
+
if (pGroupie->GetQuestStatus(QUEST_DEAD_MAN_PLEA) == QUEST_STATUS_INCOMPLETE &&
!pGroupie->HasAura(SPELL_BARON_ULTIMATUM) &&
pGroupie->GetMap() == pGo->GetMap())
@@ -53,51 +62,65 @@ bool GOHello_go_gauntlet_gate(Player* pPlayer, GameObject* pGo)
!pPlayer->HasAura(SPELL_BARON_ULTIMATUM) &&
pPlayer->GetMap() == pGo->GetMap())
pPlayer->CastSpell(pPlayer,SPELL_BARON_ULTIMATUM,true);
+
pInstance->SetData(TYPE_BARON_RUN,IN_PROGRESS);
return false;
}
+
/*######
## mob_freed_soul
######*/
+
//Possibly more of these quotes around.
#define SAY_ZAPPED0 -1329000
#define SAY_ZAPPED1 -1329001
#define SAY_ZAPPED2 -1329002
#define SAY_ZAPPED3 -1329003
+
struct TRINITY_DLL_DECL mob_freed_soulAI : public ScriptedAI
{
mob_freed_soulAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
DoScriptText(RAND(SAY_ZAPPED0,SAY_ZAPPED1,SAY_ZAPPED2,SAY_ZAPPED3), m_creature);
}
+
void EnterCombat(Unit* who) { }
};
+
CreatureAI* GetAI_mob_freed_soul(Creature* pCreature)
{
return new mob_freed_soulAI (pCreature);
}
+
/*######
## mob_restless_soul
######*/
+
#define SPELL_EGAN_BLASTER 17368
#define SPELL_SOUL_FREED 17370
#define QUEST_RESTLESS_SOUL 5282
#define ENTRY_RESTLESS 11122
#define ENTRY_FREED 11136
+
struct TRINITY_DLL_DECL mob_restless_soulAI : public ScriptedAI
{
mob_restless_soulAI(Creature *c) : ScriptedAI(c) {}
+
uint64 Tagger;
uint32 Die_Timer;
bool Tagged;
+
void Reset()
{
Tagger = 0;
Die_Timer = 5000;
Tagged = false;
}
+
void EnterCombat(Unit* who) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (caster->GetTypeId() == TYPEID_PLAYER)
@@ -109,15 +132,18 @@ struct TRINITY_DLL_DECL mob_restless_soulAI : public ScriptedAI
}
}
}
+
void JustSummoned(Creature *summoned)
{
summoned->CastSpell(summoned,SPELL_SOUL_FREED,false);
}
+
void JustDied(Unit* Killer)
{
if (Tagged)
m_creature->SummonCreature(ENTRY_FREED, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000);
}
+
void UpdateAI(const uint32 diff)
{
if (Tagged)
@@ -130,39 +156,48 @@ struct TRINITY_DLL_DECL mob_restless_soulAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mob_restless_soul(Creature* pCreature)
{
return new mob_restless_soulAI (pCreature);
}
+
/*######
## mobs_spectral_ghostly_citizen
######*/
+
enum eGhostlyCitizenSpells
{
SPELL_HAUNTING_PHANTOM = 16336,
SPELL_SLAP = 6754
};
+
struct TRINITY_DLL_DECL mobs_spectral_ghostly_citizenAI : public ScriptedAI
{
mobs_spectral_ghostly_citizenAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Die_Timer;
bool Tagged;
+
void Reset()
{
Die_Timer = 5000;
Tagged = false;
}
+
void EnterCombat(Unit* who) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (!Tagged && spell->Id == SPELL_EGAN_BLASTER)
Tagged = true;
}
+
void JustDied(Unit* Killer)
{
if (Tagged)
{
- for (uint32 i = 1; i <= 4; ++i)
+ for(uint32 i = 1; i <= 4; ++i)
{
//100%, 50%, 33%, 25% chance to spawn
if (urand(1,i) == 1)
@@ -170,6 +205,7 @@ struct TRINITY_DLL_DECL mobs_spectral_ghostly_citizenAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (Tagged)
@@ -178,10 +214,13 @@ struct TRINITY_DLL_DECL mobs_spectral_ghostly_citizenAI : public ScriptedAI
m_creature->Kill(m_creature);
else Die_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void ReceiveEmote(Player* pPlayer, uint32 emote)
{
switch(emote)
@@ -207,25 +246,31 @@ struct TRINITY_DLL_DECL mobs_spectral_ghostly_citizenAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mobs_spectral_ghostly_citizen(Creature* pCreature)
{
return new mobs_spectral_ghostly_citizenAI (pCreature);
}
+
void AddSC_stratholme()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_gauntlet_gate";
newscript->pGOHello = &GOHello_go_gauntlet_gate;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_freed_soul";
newscript->GetAI = &GetAI_mob_freed_soul;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_restless_soul";
newscript->GetAI = &GetAI_mob_restless_soul;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mobs_spectral_ghostly_citizen";
newscript->GetAI = &GetAI_mobs_spectral_ghostly_citizen;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/def_sunken_temple.h b/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/def_sunken_temple.h
index bc191af0697..82245095c31 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/def_sunken_temple.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/def_sunken_temple.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 kb_z
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SUNKEN_TEMPLE_H
#define DEF_SUNKEN_TEMPLE_H
+
#define TROLLBOSS1_DEATH 1
#define TROLLBOSS2_DEATH 2
#define TROLLBOSS3_DEATH 3
@@ -14,6 +16,7 @@
#define HAZZAS_DEATH 9
#define ERANIKUS_DEATH 10
#define ATALALARION_DEATH 11 //optional
+
#define EVENT_STATE 1
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp
index 4e7981c7969..42ec8d5d784 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/instance_sunken_temple.cpp
@@ -13,14 +13,17 @@
* along 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_Sunken_Temple
SD%Complete: 100
SDComment:Place Holder
SDCategory: Sunken Temple
EndScriptData */
+
#include "precompiled.h"
#include "def_sunken_temple.h"
+
#define GO_ATALAI_STATUE1 148830
#define GO_ATALAI_STATUE2 148831
#define GO_ATALAI_STATUE3 148832
@@ -28,15 +31,19 @@ EndScriptData */
#define GO_ATALAI_STATUE5 148834
#define GO_ATALAI_STATUE6 148835
#define GO_ATALAI_IDOL 148836
+
#define GO_ATALAI_LIGHT1 148883
#define GO_ATALAI_LIGHT2 148937
+
#define NPC_MALFURION_STORMRAGE 15362
+
struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
{
instance_sunken_temple(Map* pMap) : ScriptedInstance(pMap)
{
Initialize();
};
+
GameObject* AtalaiStatue1;
GameObject* AtalaiStatue2;
GameObject* AtalaiStatue3;
@@ -44,6 +51,7 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
GameObject* AtalaiStatue5;
GameObject* AtalaiStatue6;
GameObject* AtalaiIdol;
+
uint64 GOAtalaiStatue1;
uint64 GOAtalaiStatue2;
uint64 GOAtalaiStatue3;
@@ -52,12 +60,14 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
uint64 GOAtalaiStatue6;
uint64 GOAtalaiIdol;
uint32 State;
+
bool s1;
bool s2;
bool s3;
bool s4;
bool s5;
bool s6;
+
void Initialize()
{
GOAtalaiStatue1 = 0;
@@ -82,6 +92,7 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
s5 = false;
s6 = false;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -117,6 +128,7 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
}
};
+
virtual void Update(uint32 diff) // correct order goes form 1-6
{
switch(State)
@@ -171,6 +183,7 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
break;
}
};
+
void UseStatue(GameObject* pGo)
{
pGo->SummonGameObject(GO_ATALAI_LIGHT1,pGo->GetPositionX(),pGo->GetPositionY(),pGo->GetPositionZ(),0,0,0,0,0,0);
@@ -186,11 +199,13 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
AtalaiStatue6->SummonGameObject(GO_ATALAI_LIGHT2,AtalaiStatue6->GetPositionX(),AtalaiStatue6->GetPositionY(),AtalaiStatue6->GetPositionZ(),0,0,0,0,0,100000);
pGo->SummonGameObject(148838,-488.997,96.61,-189.019,-1.52,0,0,0,0,100000);
}
+
void SetData(uint32 type, uint32 data)
{
if (type == EVENT_STATE)
State = data;
}
+
uint32 GetData(uint32 type)
{
if (type == EVENT_STATE)
@@ -198,10 +213,12 @@ struct TRINITY_DLL_DECL instance_sunken_temple : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_sunken_temple(Map* pMap)
{
return new instance_sunken_temple(pMap);
}
+
void AddSC_instance_sunken_temple()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp
index 47af3f53cb0..d4da9a68e53 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunken_temple/sunken_temple.cpp
@@ -13,20 +13,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Sunken_Temple
SD%Complete: 100
SDComment: Area Trigger + Puzzle event support
SDCategory: Sunken Temple
EndScriptData */
+
/* ContentData
at_malfurion_Stormrage_trigger
EndContentData */
+
#include "precompiled.h"
#include "def_sunken_temple.h"
+
/*#####
# at_malfurion_Stormrage_trigger
#####*/
+
bool AreaTrigger_at_malfurion_stormrage(Player* pPlayer, AreaTriggerEntry *at)
{
if (ScriptedInstance* pInstance = pPlayer->GetInstanceData())
@@ -40,6 +45,7 @@ return false;
/*#####
# go_atalai_statue
#####*/
+
bool GOHello_go_atalai_statue(Player* pPlayer, GameObject* pGo)
{
ScriptedInstance* pInstance = pPlayer->GetInstanceData();
@@ -48,13 +54,16 @@ bool GOHello_go_atalai_statue(Player* pPlayer, GameObject* pGo)
pInstance->SetData(EVENT_STATE,pGo->GetEntry());
return false;
}
+
void AddSC_sunken_temple()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "at_malfurion_stormrage";
newscript->pAreaTrigger = &AreaTrigger_at_malfurion_stormrage;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_atalai_statue";
newscript->pGOHello = &GOHello_go_atalai_statue;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp
index d63ca9061a7..cfa03ab6086 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_brutallus.cpp
@@ -13,13 +13,16 @@
* along 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: Find a way to start the intro, best code for the intro
EndScriptData */
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
enum Quotes
{
YELL_INTRO = -1580017,
@@ -27,11 +30,13 @@ enum Quotes
YELL_INTRO_CHARGE = -1580019,
YELL_INTRO_KILL_MADRIGOSA = -1580020,
YELL_INTRO_TAUNT = -1580021,
+
YELL_MADR_ICE_BARRIER = -1580031,
YELL_MADR_INTRO = -1580032,
YELL_MADR_ICE_BLOCK = -1580033,
YELL_MADR_TRAP = -1580034,
YELL_MADR_DEATH = -1580035,
+
YELL_AGGRO = -1580022,
YELL_KILL1 = -1580023,
YELL_KILL2 = -1580024,
@@ -42,6 +47,7 @@ enum Quotes
YELL_BERSERK = -1580029,
YELL_DEATH = -1580030
};
+
enum Spells
{
SPELL_METEOR_SLASH = 45150,
@@ -49,47 +55,59 @@ enum Spells
SPELL_STOMP = 45185,
SPELL_BERSERK = 26662,
SPELL_DUAL_WIELD = 42459,
+
SPELL_INTRO_FROST_BLAST = 45203,
SPELL_INTRO_FROSTBOLT = 44843,
SPELL_INTRO_ENCAPSULATE = 45665,
SPELL_INTRO_ENCAPSULATE_CHANELLING = 45661
};
+
#define FELMYST 25038
+
struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
{
boss_brutallusAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
Unit* Madrigosa;
+
uint32 SlashTimer;
uint32 BurnTimer;
uint32 StompTimer;
uint32 BerserkTimer;
+
uint32 IntroPhase;
uint32 IntroPhaseTimer;
uint32 IntroFrostBoltTimer;
+
bool Intro;
bool IsIntro;
bool Enraged;
+
void Reset()
{
SlashTimer = 11000;
StompTimer = 30000;
BurnTimer = 60000;
BerserkTimer = 360000;
+
IntroPhase = 0;
IntroPhaseTimer = 0;
IntroFrostBoltTimer = 0;
+
IsIntro = false;
Enraged = false;
Intro = true; //for debug
+
m_creature->CastSpell(m_creature, SPELL_DUAL_WIELD, true);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
Madrigosa = Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_MADRIGOSA));
//Creature* boss = Unit::GetCreature((*m_creature),AzgalorGUID);
if (!Madrigosa) error_log("Madrigosa was not found");
+
if (Intro && Madrigosa){
if (!Madrigosa->isAlive())
EndIntro();
@@ -97,22 +115,28 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
}
else
EndIntro();
+
if (pInstance)
pInstance->SetData(DATA_BRUTALLUS_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(YELL_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_BRUTALLUS_EVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(YELL_KILL1,YELL_KILL2,YELL_KILL3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(YELL_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(DATA_BRUTALLUS_EVENT, DONE);
@@ -121,6 +145,7 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
m_creature->SummonCreature(FELMYST, x,y, z+30, m_creature->GetOrientation(), TEMPSUMMON_MANUAL_DESPAWN, 0);
}
}
+
void StartIntro()
{
if (!Intro)
@@ -133,6 +158,7 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
}
error_log("Start Intro");
}
+
void EndIntro()
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -140,10 +166,12 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
IsIntro = false;
error_log("End Intro");
}
+
void DoIntro()
{
if (!Madrigosa)
return;
+
switch (IntroPhase)
{
case 0:
@@ -214,13 +242,16 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
break;
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (pInstance && Intro)
pInstance->SetData(DATA_BRUTALLUS_EVENT, SPECIAL);
+
if (Intro && !IsIntro)
StartIntro();
}
+
void UpdateAI(const uint32 diff)
{
if (IsIntro)
@@ -228,6 +259,7 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
if (IntroPhaseTimer < diff)
DoIntro();
else IntroPhaseTimer -= diff;
+
if (IntroPhase == 3 + 1)
{
if (IntroFrostBoltTimer < diff)
@@ -240,41 +272,50 @@ struct TRINITY_DLL_DECL boss_brutallusAI : public ScriptedAI
} else IntroFrostBoltTimer -= diff;
}
}
+
if (!UpdateVictim() || IsIntro)
return;
+
if (SlashTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_METEOR_SLASH);
SlashTimer = 11000;
} else SlashTimer -= diff;
+
if (StompTimer < diff)
{
DoScriptText(RAND(YELL_LOVE1,YELL_LOVE2,YELL_LOVE3), m_creature);
DoCast(m_creature->getVictim(), SPELL_STOMP);
StompTimer = 30000;
} else StompTimer -= diff;
+
if (BurnTimer < diff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
target->CastSpell(target, SPELL_BURN, true);
BurnTimer = 60000;
} else BurnTimer -= diff;
+
if (BerserkTimer < diff && !Enraged)
{
DoScriptText(YELL_BERSERK, m_creature);
DoCast(m_creature, SPELL_BERSERK);
Enraged = true;
} else BerserkTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_brutallus(Creature* pCreature)
{
return new boss_brutallusAI (pCreature);
}
+
void AddSC_boss_brutallus()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_brutallus";
newscript->GetAI = &GetAI_boss_brutallus;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp
index 332ef12a60d..1d46bba348b 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_eredar_twins.cpp
@@ -13,13 +13,16 @@
* along 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_Eredar_Twins
SD%Complete: 100
SDComment:
EndScriptData */
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
enum Quotes
{
//Alytesh
@@ -29,6 +32,7 @@ enum Quotes
YELL_ALY_KILL_2 = -1580047,
YELL_ALY_DEAD = -1580048,
YELL_BERSERK = -1580049,
+
//Sacrolash
YELL_SHADOW_NOVA = -1580050,
YELL_SISTER_ALYTHESS_DEAD = -1580051,
@@ -36,6 +40,7 @@ enum Quotes
YELL_SAC_KILL_2 = -1580053,
SAY_SAC_DEAD = -1580054,
YELL_ENRAGE = -1580055,
+
//Intro
YELL_INTRO_SAC_1 = -1580056,
YELL_INTRO_ALY_2 = -1580057,
@@ -45,10 +50,12 @@ enum Quotes
YELL_INTRO_ALY_6 = -1580061,
YELL_INTRO_SAC_7 = -1580062,
YELL_INTRO_ALY_8 = -1580063,
+
//Emote
EMOTE_SHADOW_NOVA = -1580064,
EMOTE_CONFLAGRATION = -1580065
};
+
enum Spells
{
//Lady Sacrolash spells
@@ -57,13 +64,16 @@ enum Spells
SPELL_DARK_STRIKE = 45271,
SPELL_SHADOW_NOVA = 45329, //30-35 secs
SPELL_CONFOUNDING_BLOW = 45256, //25 secs
+
//Shadow Image spells
SPELL_SHADOW_FURY = 45270,
SPELL_IMAGE_VISUAL = 45263,
+
//Misc spells
SPELL_ENRAGE = 46587,
SPELL_EMPOWER = 45366,
SPELL_DARK_FLAME = 45345,
+
//Grand Warlock Alythess spells
SPELL_PYROGENICS = 45230, //15secs
SPELL_FLAME_TOUCHED = 45348,
@@ -73,30 +83,37 @@ enum Spells
SPELL_BLAZE_SUMMON = 45236, //187366 GO
SPELL_BLAZE_BURN = 45246
};
+
enum Creatures
{
GRAND_WARLOCK_ALYTHESS = 25166,
MOB_SHADOW_IMAGE = 25214,
LADY_SACROLASH = 25165
};
+
struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
{
boss_sacrolashAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
bool SisterDeath;
bool Enraged;
+
uint32 ShadowbladesTimer;
uint32 ShadownovaTimer;
uint32 ConfoundingblowTimer;
uint32 ShadowimageTimer;
uint32 ConflagrationTimer;
uint32 EnrageTimer;
+
void Reset()
{
Enraged = false;
+
if (pInstance)
{
Unit* Temp = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_ALYTHESS));
@@ -112,6 +129,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
}
}
}
+
if (!m_creature->isInCombat())
{
ShadowbladesTimer = 10000;
@@ -120,40 +138,49 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
ShadowimageTimer = 20000;
ConflagrationTimer = 30000;
EnrageTimer = 360000;
+
SisterDeath = false;
}
+
if (pInstance)
pInstance->SetData(DATA_EREDAR_TWINS_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
+
if (pInstance)
{
Unit* Temp = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_ALYTHESS));
if (Temp && Temp->isAlive() && !(Temp->getVictim()))
CAST_CRE(Temp)->AI()->AttackStart(who);
}
+
if (pInstance)
pInstance->SetData(DATA_EREDAR_TWINS_EVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
if (rand()%4 == 0)
DoScriptText(RAND(YELL_SAC_KILL_1,YELL_SAC_KILL_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
// only if ALY death
if (SisterDeath)
{
DoScriptText(SAY_SAC_DEAD, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_EREDAR_TWINS_EVENT, DONE);
}
else
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
+
void SpellHitTarget(Unit* target,const SpellEntry* spell)
{
switch(spell->Id)
@@ -169,6 +196,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
break;
}
}
+
void HandleTouchedSpells(Unit* target, uint32 TouchedType)
{
switch(TouchedType)
@@ -195,6 +223,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!SisterDeath)
@@ -212,8 +241,10 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
}
}
}
+
if (!UpdateVictim())
return;
+
if (SisterDeath)
{
if (ConflagrationTimer < diff)
@@ -239,6 +270,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (target)
DoCast(target, SPELL_SHADOW_NOVA);
+
if (!SisterDeath)
{
if (target)
@@ -249,6 +281,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
}
}else ShadownovaTimer -=diff;
}
+
if (ConfoundingblowTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -260,11 +293,12 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
ConfoundingblowTimer = 20000 + (rand()%5000);
}
}else ConfoundingblowTimer -=diff;
+
if (ShadowimageTimer < diff)
{
Unit* target = NULL;
Creature* temp = NULL;
- for (uint8 i = 0; i<3; ++i)
+ for(uint8 i = 0;i<3; ++i)
{
target = SelectUnit(SELECT_TARGET_RANDOM, 0);
temp = DoSpawnCreature(MOB_SHADOW_IMAGE,0,0,0,0,TEMPSUMMON_CORPSE_DESPAWN,10000);
@@ -273,6 +307,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
}
ShadowimageTimer = 20000;
}else ShadowimageTimer -=diff;
+
if (ShadowbladesTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -281,6 +316,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
ShadowbladesTimer = 10000;
}
}else ShadowbladesTimer -=diff;
+
if (EnrageTimer < diff && !Enraged)
{
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
@@ -288,6 +324,7 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
DoCast(m_creature,SPELL_ENRAGE);
Enraged = true;
}else EnrageTimer -= diff;
+
if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
{
//If we are within range melee the target
@@ -300,10 +337,12 @@ struct TRINITY_DLL_DECL boss_sacrolashAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_boss_sacrolash(Creature* pCreature)
{
return new boss_sacrolashAI (pCreature);
};
+
struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
{
boss_alythessAI(Creature *c) : Scripted_NoMovementAI(c)
@@ -311,20 +350,26 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
pInstance = c->GetInstanceData();
IntroStepCounter = 10;
}
+
ScriptedInstance *pInstance;
+
bool SisterDeath;
bool Enraged;
+
uint32 IntroStepCounter;
uint32 IntroYellTimer;
+
uint32 ConflagrationTimer;
uint32 BlazeTimer;
uint32 PyrogenicsTimer;
uint32 ShadownovaTimer;
uint32 FlamesearTimer;
uint32 EnrageTimer;
+
void Reset()
{
Enraged = false;
+
if (pInstance)
{
Unit* Temp = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_SACROLASH));
@@ -340,6 +385,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
}
}
}
+
if (!m_creature->isInCombat())
{
ConflagrationTimer = 45000;
@@ -349,23 +395,29 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
EnrageTimer = 360000;
FlamesearTimer = 15000;
IntroYellTimer = 10000;
+
SisterDeath = false;
}
+
if (pInstance)
pInstance->SetData(DATA_EREDAR_TWINS_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
+
if (pInstance)
{
Unit* Temp = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_SACROLASH));
if (Temp && Temp->isAlive() && !(Temp->getVictim()))
CAST_CRE(Temp)->AI()->AttackStart(who);
}
+
if (pInstance)
pInstance->SetData(DATA_EREDAR_TWINS_EVENT, IN_PROGRESS);
}
+
void AttackStart(Unit *who)
{
if (!m_creature->isInCombat())
@@ -373,12 +425,15 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
Scripted_NoMovementAI::AttackStart(who);
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim())
return;
+
if (who->isTargetableForAttack() && who->isInAccessiblePlaceFor(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))
{
@@ -393,6 +448,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
IntroStepCounter = 0;
}
}
+
void KilledUnit(Unit *victim)
{
if (rand()%4 == 0)
@@ -400,21 +456,25 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
DoScriptText(RAND(YELL_ALY_KILL_1,YELL_ALY_KILL_2), m_creature);
}
}
+
void JustDied(Unit* Killer)
{
if (SisterDeath)
{
DoScriptText(YELL_ALY_DEAD, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_EREDAR_TWINS_EVENT, DONE);
}
else
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
+
void SpellHitTarget(Unit* target,const SpellEntry* spell)
{
switch(spell->Id)
{
+
case SPELL_BLAZE:
target->CastSpell(target, SPELL_BLAZE_SUMMON, true);
case SPELL_CONFLAGRATION:
@@ -426,6 +486,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
break;
}
}
+
void HandleTouchedSpells(Unit* target, uint32 TouchedType)
{
switch(TouchedType)
@@ -455,6 +516,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
break;
}
}
+
uint32 IntroStep(uint32 step)
{
Creature* Sacrolash = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_SACROLASH) : 0);
@@ -484,6 +546,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
}
return 10000;
}
+
void UpdateAI(const uint32 diff)
{
if (IntroStepCounter < 9)
@@ -493,6 +556,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
IntroYellTimer = IntroStep(++IntroStepCounter);
}else IntroYellTimer -= diff;
}
+
if (!SisterDeath)
{
if (pInstance)
@@ -508,8 +572,10 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
}
}
}
+
if (!UpdateVictim())
return;
+
if (SisterDeath)
{
if (ShadownovaTimer < diff)
@@ -536,16 +602,19 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
if (target)
DoCast(target, SPELL_CONFLAGRATION);
ConflagrationTimer = 30000+(rand()%5000);
+
if (!SisterDeath)
{
if (target)
DoScriptText(EMOTE_CONFLAGRATION, m_creature, target);
DoScriptText(YELL_CANFLAGRATION, m_creature);
}
+
BlazeTimer = 4000;
}
}else ConflagrationTimer -= diff;
}
+
if (FlamesearTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -554,6 +623,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
FlamesearTimer = 15000;
}
}else FlamesearTimer -=diff;
+
if (PyrogenicsTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -562,6 +632,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
PyrogenicsTimer = 15000;
}
}else PyrogenicsTimer -= diff;
+
if (BlazeTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -570,6 +641,7 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
BlazeTimer = 3800;
}
}else BlazeTimer -= diff;
+
if (EnrageTimer < diff && !Enraged)
{
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
@@ -579,27 +651,34 @@ struct TRINITY_DLL_DECL boss_alythessAI : public Scripted_NoMovementAI
}else EnrageTimer -= diff;
}
};
+
CreatureAI* GetAI_boss_alythess(Creature* pCreature)
{
return new boss_alythessAI (pCreature);
};
+
struct TRINITY_DLL_DECL mob_shadow_imageAI : public ScriptedAI
{
mob_shadow_imageAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowfuryTimer;
uint32 KillTimer;
uint32 DarkstrikeTimer;
+
void Reset()
{
ShadowfuryTimer = 5000 + (rand()%15000);
DarkstrikeTimer = 3000;
KillTimer = 15000;
}
+
void EnterCombat(Unit *who){}
+
void SpellHitTarget(Unit* target,const SpellEntry* spell)
{
switch(spell->Id)
{
+
case SPELL_SHADOW_FURY:
case SPELL_DARK_STRIKE:
if (!target->HasAura(SPELL_DARK_FLAME))
@@ -613,22 +692,27 @@ struct TRINITY_DLL_DECL mob_shadow_imageAI : public ScriptedAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->HasAura(SPELL_IMAGE_VISUAL))
DoCast(m_creature, SPELL_IMAGE_VISUAL);
+
if (KillTimer < diff)
{
m_creature->Kill(m_creature);
KillTimer = 9999999;
} else KillTimer -= diff;
+
if (!UpdateVictim())
return;
+
if (ShadowfuryTimer < diff)
{
DoCast(m_creature, SPELL_SHADOW_FURY);
ShadowfuryTimer = 10000;
} else ShadowfuryTimer -=diff;
+
if (DarkstrikeTimer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -641,21 +725,26 @@ struct TRINITY_DLL_DECL mob_shadow_imageAI : public ScriptedAI
} else DarkstrikeTimer -= diff;
}
};
+
CreatureAI* GetAI_mob_shadow_image(Creature* pCreature)
{
return new mob_shadow_imageAI (pCreature);
};
+
void AddSC_boss_eredar_twins()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_sacrolash";
newscript->GetAI = &GetAI_boss_sacrolash;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_alythess";
newscript->GetAI = &GetAI_boss_alythess;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shadow_image";
newscript->GetAI = &GetAI_mob_shadow_image;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp
index 3d4338493ed..b81cbca3516 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_felmyst.cpp
@@ -13,13 +13,16 @@
* along 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_Felmyst
SD%Complete: 0
SDComment:
EndScriptData */
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
enum Quotes
{
YELL_BIRTH = -1580036,
@@ -31,11 +34,13 @@ enum Quotes
YELL_DEATH = -1580042,
YELL_KALECGOS = -1580043 //after felmyst's death spawned and say this
};
+
enum Spells
{
//Aura
AURA_SUNWELL_RADIANCE = 45769,
AURA_NOXIOUS_FUMES = 47002,
+
//Land phase
SPELL_CLEAVE = 19983,
SPELL_CORROSION = 45866,
@@ -43,6 +48,7 @@ enum Spells
SPELL_ENCAPSULATE_CHANNEL = 45661,
// SPELL_ENCAPSULATE_EFFECT = 45665,
// SPELL_ENCAPSULATE_AOE = 45662,
+
//Flight phase
SPELL_VAPOR_SELECT = 45391, // fel to player, force cast 45392, 50000y selete target
SPELL_VAPOR_SUMMON = 45392, // player summon vapor, radius around caster, 5y,
@@ -61,15 +67,18 @@ enum Spells
SPELL_FOG_INFORM = 45714, // player let fel cast 45717, script effect
SPELL_FOG_CHARM = 45717, // fel to player
SPELL_FOG_CHARM2 = 45726, // link to 45717
+
SPELL_TRANSFORM_TRIGGER = 44885, // madrigosa to self, trigger 46350
SPELL_TRANSFORM_VISUAL = 46350, //46411stun?
SPELL_TRANSFORM_FELMYST = 45068, // become fel
SPELL_FELMYST_SUMMON = 45069,
+
//Other
SPELL_BERSERK = 45078,
SPELL_CLOUD_VISUAL = 45212,
SPELL_CLOUD_SUMMON = 45884
};
+
enum Creatures
{
MOB_FELMYST = 25038,
@@ -84,36 +93,43 @@ enum Creatures
MOB_VAPOR = 25265,
MOB_VAPOR_TRAIL = 25267
};
+
enum PhaseFelmyst
{
PHASE_NULL = 0,
PHASE_GROUND = 1,
PHASE_FLIGHT = 2,
};
+
enum EventFelmyst
{
EVENT_NULL = 0,
EVENT_BERSERK = 1,
+
EVENT_CLEAVE = 2,
EVENT_CORROSION = 3,
EVENT_GAS_NOVA = 4,
EVENT_ENCAPSULATE = 5,
EVENT_FLIGHT = 6,
+
EVENT_FLIGHT_SEQUENCE = 2,
EVENT_SUMMON_DEAD = 3,
EVENT_SUMMON_FOG = 4
};
+
static EventFelmyst MaxTimer[]=
{
EVENT_NULL,
EVENT_FLIGHT,
EVENT_SUMMON_FOG,
};
+
struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
{
boss_felmystAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
+
// wait for core patch be accepted
/*SpellEntry *TempSpell = GET_SPELL(SPELL_ENCAPSULATE_EFFECT);
if (TempSpell->SpellIconID == 2294)
@@ -125,27 +141,35 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
if ((TempSpell->Attributes & SPELL_ATTR_PASSIVE) == 0)
TempSpell->Attributes |= SPELL_ATTR_PASSIVE;*/
}
+
ScriptedInstance *pInstance;
PhaseFelmyst Phase;
EventFelmyst Event;
uint32 Timer[EVENT_FLIGHT + 1];
+
uint32 FlightCount;
uint32 BreathCount;
+
float BreathX, BreathY;
+
void Reset()
{
Phase = PHASE_NULL;
Event = EVENT_NULL;
Timer[EVENT_BERSERK] = 600000;
FlightCount = 0;
+
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
m_creature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 10);
m_creature->SetFloatValue(UNIT_FIELD_COMBATREACH, 10);
+
DespawnSummons(MOB_VAPOR_TRAIL);
m_creature->setActive(false);
+
if (pInstance)
pInstance->SetData(DATA_FELMYST_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
m_creature->setActive(true);
@@ -153,33 +177,41 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
m_creature->CastSpell(m_creature, AURA_SUNWELL_RADIANCE, true);
m_creature->CastSpell(m_creature, AURA_NOXIOUS_FUMES, true);
EnterPhase(PHASE_GROUND);
+
if (pInstance)
pInstance->SetData(DATA_FELMYST_EVENT, IN_PROGRESS);
}
+
void AttackStart(Unit *who)
{
if (Phase != PHASE_FLIGHT)
ScriptedAI::AttackStart(who);
}
+
void MoveInLineOfSight(Unit *who)
{
if (Phase != PHASE_FLIGHT)
ScriptedAI::MoveInLineOfSight(who);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(YELL_KILL1,YELL_KILL2), m_creature);
}
+
void JustRespawned()
{
DoScriptText(YELL_BIRTH, m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(YELL_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_FELMYST_EVENT, DONE);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
// workaround for linked aura
@@ -202,6 +234,7 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
m_creature->DealDamage(caster, caster->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
}
+
void JustSummoned(Creature *summon)
{
if (summon->GetEntry() == MOB_DEAD)
@@ -211,15 +244,18 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
summon->CastSpell(summon, SPELL_DEAD_PASSIVE, true);
}
}
+
void MovementInform(uint32, uint32)
{
Timer[EVENT_FLIGHT_SEQUENCE] = 1;
}
+
void DamageTaken(Unit*, uint32 &damage)
{
if (Phase != PHASE_GROUND && damage >= m_creature->GetHealth())
damage = 0;
}
+
void EnterPhase(PhaseFelmyst NextPhase)
{
switch(NextPhase)
@@ -243,6 +279,7 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
}
Phase = NextPhase;
}
+
void HandleFlightSequence()
{
switch(FlightCount)
@@ -384,6 +421,7 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
}
FlightCount++;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -392,8 +430,9 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
EnterEvadeMode();
return;
}
+
Event = EVENT_NULL;
- for (uint32 i = 1; i <= MaxTimer[Phase]; ++i)
+ for(uint32 i = 1; i <= MaxTimer[Phase]; ++i)
{
if (Timer[i])
if (Timer[i] <= diff)
@@ -402,8 +441,10 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
Event = (EventFelmyst)i;
}else Timer[i] -= diff;
}
+
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
if (Phase == PHASE_GROUND)
{
switch(Event)
@@ -439,6 +480,7 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
break;
}
}
+
if (Phase == PHASE_FLIGHT)
{
switch(Event)
@@ -469,23 +511,29 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
}
}
}
+
void DespawnSummons(uint32 entry)
{
std::list<Creature*> templist;
float x, y, z;
m_creature->GetPosition(x, y, z);
+
{
CellPair pair(Trinity::ComputeCellPair(x, y));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllCreaturesOfEntryInRange check(m_creature, entry, 100);
Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(m_creature, templist, check);
+
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> cSearcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, cSearcher, *(m_creature->GetMap()));
}
- for (std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
+
+ for(std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
{
if (entry == MOB_VAPOR_TRAIL && Phase == PHASE_FLIGHT)
{
@@ -499,6 +547,7 @@ struct TRINITY_DLL_DECL boss_felmystAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_felmyst_vaporAI : public ScriptedAI
{
mob_felmyst_vaporAI(Creature *c) : ScriptedAI(c)
@@ -518,6 +567,7 @@ struct TRINITY_DLL_DECL mob_felmyst_vaporAI : public ScriptedAI
AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0));
}
};
+
struct TRINITY_DLL_DECL mob_felmyst_trailAI : public ScriptedAI
{
mob_felmyst_trailAI(Creature *c) : ScriptedAI(c)
@@ -533,18 +583,22 @@ struct TRINITY_DLL_DECL mob_felmyst_trailAI : public ScriptedAI
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff) {}
};
+
CreatureAI* GetAI_boss_felmyst(Creature* pCreature)
{
return new boss_felmystAI(pCreature);
}
+
CreatureAI* GetAI_mob_felmyst_vapor(Creature* pCreature)
{
return new mob_felmyst_vaporAI(pCreature);
}
+
CreatureAI* GetAI_mob_felmyst_trail(Creature* pCreature)
{
return new mob_felmyst_trailAI(pCreature);
}
+
void AddSC_boss_felmyst()
{
Script *newscript;
@@ -552,10 +606,12 @@ void AddSC_boss_felmyst()
newscript->Name = "boss_felmyst";
newscript->GetAI = &GetAI_boss_felmyst;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_felmyst_vapor";
newscript->GetAI = &GetAI_mob_felmyst_vapor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_felmyst_trail";
newscript->GetAI = &GetAI_mob_felmyst_trail;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp
index 2fcfdb43a5d..22328334919 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kalecgos.cpp
@@ -13,14 +13,17 @@
* along 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: 95
SDComment:
SDCategory: Sunwell_Plateau
EndScriptData */
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
enum Quotes
{
//Kalecgos dragon form
@@ -30,11 +33,13 @@ enum Quotes
SAY_EVIL_SLAY1 = -1580003,
SAY_EVIL_SLAY2 = -1580004,
SAY_EVIL_ENRAGE = -1580005,
+
//Kalecgos humanoid form
SAY_GOOD_AGGRO = -1580006,
SAY_GOOD_NEAR_DEATH = -1580007,
SAY_GOOD_NEAR_DEATH2 = -1580008,
SAY_GOOD_PLRWIN = -1580009,
+
//Shattrowar
SAY_SATH_AGGRO = -1580010,
SAY_SATH_DEATH = -1580011,
@@ -44,6 +49,7 @@ enum Quotes
SAY_SATH_SLAY2 = -1580015,
SAY_SATH_ENRAGE = -1580016
};
+
enum SpellIds
{
AURA_SUNWELL_RADIANCE = 45769,
@@ -51,20 +57,25 @@ enum SpellIds
AURA_SPECTRAL_REALM = 46021,
AURA_SPECTRAL_INVISIBILITY = 44801,
AURA_DEMONIC_VISUAL = 44800,
+
SPELL_SPECTRAL_BLAST = 44869,
SPELL_TELEPORT_SPECTRAL = 46019,
SPELL_ARCANE_BUFFET = 45018,
SPELL_FROST_BREATH = 44799,
SPELL_TAIL_LASH = 45122,
+
SPELL_BANISH = 44836,
SPELL_TRANSFORM_KALEC = 44670,
SPELL_ENRAGE = 44807,
+
SPELL_CORRUPTION_STRIKE = 45029,
SPELL_AGONY_CURSE = 45032,
SPELL_SHADOW_BOLT = 45031,
+
SPELL_HEROIC_STRIKE = 45026,
SPELL_REVITALIZE = 45027
};
+
enum Creatures
{
MOB_KALECGOS = 24850,
@@ -77,18 +88,26 @@ enum SWPActions
DO_BANISH = 2
};
+
#define GO_FAILED "You are unable to use this currently."
+
#define EMOTE_UNABLE_TO_FIND "is unable to find Kalecgos"
+
#define FLY_X 1679
#define FLY_Y 900
#define FLY_Z 82
+
#define CENTER_X 1705
#define CENTER_Y 930
#define RADIUS 30
+
#define DRAGON_REALM_Z 53.079
#define DEMON_REALM_Z -74.558
+
#define MAX_PLAYERS_IN_SPECTRAL_REALM 0 //over this, teleport object won't work, 0 disables check
+
uint32 WildMagic[]= { 44978, 45001, 45002, 45004, 45006, 45010 };
+
struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
{
boss_kalecgosAI(Creature *c) : ScriptedAI(c)
@@ -102,7 +121,9 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
if (TempSpell)
TempSpell->EffectImplicitTargetB[0] = TARGET_UNIT_TARGET_ENEMY;
}
+
ScriptedInstance *pInstance;
+
uint32 ArcaneBuffetTimer;
uint32 FrostBreathTimer;
uint32 WildMagicTimer;
@@ -112,12 +133,15 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
uint32 TalkTimer;
uint32 TalkSequence;
uint32 ResetTimer;
+
bool isFriendly;
bool isEnraged;
bool isBanished;
bool JustReseted;
+
uint64 SathGUID;
uint64 DoorGUID;
+
void Reset()
{
if (pInstance)
@@ -125,10 +149,12 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
SathGUID = pInstance->GetData64(DATA_SATHROVARR);
pInstance->SetData(DATA_KALECGOS_EVENT, NOT_STARTED);
}
+
if(Creature *Sath = Unit::GetCreature(*me, SathGUID))
Sath->AI()->EnterEvadeMode();
+
me->setFaction(14);
- if(!JustReseted) //first reset at create
+ if(!JustReseted)//first reset at create
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE);
me->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
@@ -143,12 +169,14 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
SpectralBlastTimer = 20000+(rand()%5000);
CheckTimer = 1000;
ResetTimer = 30000;
+
TalkTimer = 0;
TalkSequence = 0;
isFriendly = false;
isEnraged = false;
isBanished = false;
}
+
void EnterEvadeMode()
{
JustReseted = true;
@@ -156,6 +184,7 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE);
ScriptedAI::EnterEvadeMode();
}
+
void DoAction(const int32 param)
{
switch (param)
@@ -170,6 +199,7 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (TalkTimer)
@@ -209,6 +239,7 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
}
if (!UpdateVictim())
return;
+
if (CheckTimer < diff)
{
if(me->GetDistance(CENTER_X, CENTER_Y, DRAGON_REALM_Z) >= 75)
@@ -243,31 +274,36 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
}
CheckTimer = 1000;
}else CheckTimer -= diff;
+
if (ArcaneBuffetTimer < diff)
{
DoCastAOE(SPELL_ARCANE_BUFFET);
ArcaneBuffetTimer = 8000;
}else ArcaneBuffetTimer -= diff;
+
if (FrostBreathTimer < diff)
{
DoCastAOE(SPELL_FROST_BREATH);
FrostBreathTimer = 15000;
}else FrostBreathTimer -= diff;
+
if (TailLashTimer < diff)
{
DoCastAOE(SPELL_TAIL_LASH);
TailLashTimer = 15000;
}else TailLashTimer -= diff;
+
if (WildMagicTimer < diff)
{
DoCastAOE(WildMagic[rand()%6]);
WildMagicTimer = 20000;
}else WildMagicTimer -= diff;
+
if (SpectralBlastTimer < diff)
{
std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList();
std::list<Unit*> targetList;
- for (std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr)
if((*itr)->getTarget() && (*itr)->getTarget()->GetTypeId() == TYPEID_PLAYER && (*itr)->getTarget()->GetGUID() != me->getVictim()->GetGUID() && !(*itr)->getTarget()->HasAura(AURA_SPECTRAL_EXHAUSTION) && (*itr)->getTarget()->GetPositionZ() > me->GetPositionZ()-5)
targetList.push_back((*itr)->getTarget());
if(targetList.empty())
@@ -283,12 +319,14 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
SpectralBlastTimer = 20000+rand()%5000;
}else SpectralBlastTimer = 1000;
}else SpectralBlastTimer -= diff;
+
DoMeleeAttackIfReady();
}
}
+
void MoveInLineOfSight(Unit *who)
{
- if(JustReseted) //boss is invisible, don't attack
+ if(JustReseted)//boss is invisible, don't attack
return;
if (!m_creature->getVictim() && who->isTargetableForAttack() && (m_creature->IsHostileTo(who)))
{
@@ -299,23 +337,28 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
}
}
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= me->GetHealth() && done_by != me)
damage = 0;
}
+
void EnterCombat(Unit* who)
{
me->SetStandState(UNIT_STAND_STATE_STAND);
DoScriptText(SAY_EVIL_AGGRO, me);
DoZoneInCombat();
+
if (pInstance)
pInstance->SetData(DATA_KALECGOS_EVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_EVIL_SLAY1,SAY_EVIL_SLAY2), me);
}
+
void MovementInform(uint32 type,uint32 id)
{
me->SetVisibility(VISIBILITY_OFF);
@@ -327,6 +370,7 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
TalkTimer = 1000;
}
}
+
void GoodEnding()
{
switch(TalkSequence)
@@ -348,6 +392,7 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
break;
}
}
+
void BadEnding()
{
switch(TalkSequence)
@@ -369,6 +414,7 @@ struct TRINITY_DLL_DECL boss_kalecgosAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
{
boss_sathrovarrAI(Creature *c) : ScriptedAI(c)
@@ -377,16 +423,21 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
KalecGUID = 0;
KalecgosGUID = 0;
}
+
ScriptedInstance *pInstance;
+
uint32 CorruptionStrikeTimer;
uint32 AgonyCurseTimer;
uint32 ShadowBoltTimer;
uint32 CheckTimer;
uint32 ResetThreat;
+
uint64 KalecGUID;
uint64 KalecgosGUID;
+
bool isEnraged;
bool isBanished;
+
void Reset()
{
me->SetHealth(me->GetMaxHealth());//dunno why it does not resets health at evade..
@@ -402,6 +453,7 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
Kalec->setDeathState(JUST_DIED);
KalecGUID = 0;
}
+
ShadowBoltTimer = 7000 + rand()%3 * 1000;
AgonyCurseTimer = 20000;
CorruptionStrikeTimer = 13000;
@@ -409,9 +461,11 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
ResetThreat = 1000;
isEnraged = false;
isBanished = false;
+
me->CastSpell(me, AURA_DEMONIC_VISUAL, true);
TeleportAllPlayersBack();
}
+
void EnterCombat(Unit* who)
{
if(Creature *Kalec = me->SummonCreature(MOB_KALEC, me->GetPositionX() + 10, me->GetPositionY() + 5, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0))
@@ -423,11 +477,13 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
}
DoScriptText(SAY_SATH_AGGRO, me);
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= me->GetHealth() && done_by != me)
damage = 0;
}
+
void KilledUnit(Unit *target)
{
if (target->GetGUID() == KalecGUID)
@@ -443,6 +499,7 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
}
DoScriptText(RAND(SAY_SATH_SLAY1,SAY_SATH_SLAY2), me);
}
+
void JustDied(Unit *killer)
{
DoScriptText(SAY_SATH_DEATH, me);
@@ -453,15 +510,17 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
CAST_AI(boss_kalecgosAI, Kalecgos->AI())->TalkTimer = 1;
CAST_AI(boss_kalecgosAI, Kalecgos->AI())->isFriendly = true;
}
+
if (pInstance)
pInstance->SetData(DATA_KALECGOS_EVENT, DONE);
}
+
void TeleportAllPlayersBack()
{
Map* pMap = me->GetMap();
if (!pMap->IsDungeon()) return;
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if(i->getSource()->GetPositionZ() <= DRAGON_REALM_Z-5)
{
@@ -470,6 +529,7 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
}
}
}
+
void DoAction(const int32 param)
{
switch (param)
@@ -484,12 +544,14 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if(!me->HasAura(AURA_SPECTRAL_INVISIBILITY))
me->CastSpell(me, AURA_SPECTRAL_INVISIBILITY, true);
if (!UpdateVictim())
return;
+
if (CheckTimer < diff)
{
Creature *Kalec = Unit::GetCreature(*me, KalecGUID);
@@ -535,9 +597,10 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
}
CheckTimer = 1000;
}else CheckTimer -= diff;
+
if (ResetThreat < diff)
{
- for (std::list<HostilReference*>::iterator itr = me->getThreatManager().getThreatList().begin(); itr != me->getThreatManager().getThreatList().end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = me->getThreatManager().getThreatList().begin(); itr != me->getThreatManager().getThreatList().end(); ++itr)
{
if(Unit* pUnit = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid()))
{
@@ -549,12 +612,14 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
}
ResetThreat = 1000;
}else ResetThreat -= diff;
+
if (ShadowBoltTimer < diff)
{
if(!(rand()%5))DoScriptText(SAY_SATH_SPELL1, me);
DoCast(me, SPELL_SHADOW_BOLT);
ShadowBoltTimer = 7000+(rand()%3000);
}else ShadowBoltTimer -= diff;
+
if (AgonyCurseTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
@@ -562,38 +627,49 @@ struct TRINITY_DLL_DECL boss_sathrovarrAI : public ScriptedAI
DoCast(target, SPELL_AGONY_CURSE);
AgonyCurseTimer = 20000;
}else AgonyCurseTimer -= diff;
+
if (CorruptionStrikeTimer < diff)
{
if(!(rand()%5))DoScriptText(SAY_SATH_SPELL2, me);
DoCast(me->getVictim(), SPELL_CORRUPTION_STRIKE);
CorruptionStrikeTimer = 13000;
}else CorruptionStrikeTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_kalecAI : public ScriptedAI
{
ScriptedInstance *pInstance;
+
uint32 RevitalizeTimer;
uint32 HeroicStrikeTimer;
uint32 YellTimer;
uint32 YellSequence;
+
uint64 SathGUID;
+
bool isEnraged; // if demon is enraged
+
boss_kalecAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
void Reset()
{
if (pInstance)
SathGUID = pInstance->GetData64(DATA_SATHROVARR);
+
RevitalizeTimer = 5000;
HeroicStrikeTimer = 3000;
YellTimer = 5000;
YellSequence = 0;
+
isEnraged = false;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetGUID() != SathGUID)
@@ -601,12 +677,14 @@ struct TRINITY_DLL_DECL boss_kalecAI : public ScriptedAI
else if (isEnraged)
damage *= 3;
}
+
void UpdateAI(const uint32 diff)
{
if(!me->HasAura(AURA_SPECTRAL_INVISIBILITY))
me->CastSpell(me, AURA_SPECTRAL_INVISIBILITY, true);
if (!UpdateVictim())
return;
+
if (YellTimer < diff)
{
switch(YellSequence)
@@ -634,26 +712,30 @@ struct TRINITY_DLL_DECL boss_kalecAI : public ScriptedAI
}
YellTimer = 5000;
}
+
if (RevitalizeTimer < diff)
{
DoCast(me, SPELL_REVITALIZE);
RevitalizeTimer = 5000;
}else RevitalizeTimer -= diff;
+
if (HeroicStrikeTimer < diff)
{
DoCast(me->getVictim(), SPELL_HEROIC_STRIKE);
HeroicStrikeTimer = 2000;
}else HeroicStrikeTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
bool GOkalecgos_teleporter(Player* pPlayer, GameObject* pGo)
{
uint32 SpectralPlayers = 0;
Map* pMap = pGo->GetMap();
if (!pMap->IsDungeon()) return true;
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if(i->getSource() && i->getSource()->GetPositionZ() < DEMON_REALM_Z + 5)
SpectralPlayers++;
@@ -664,18 +746,22 @@ bool GOkalecgos_teleporter(Player* pPlayer, GameObject* pGo)
pPlayer->CastSpell(pPlayer, SPELL_TELEPORT_SPECTRAL, true);
return true;
}
+
CreatureAI* GetAI_boss_kalecgos(Creature* pCreature)
{
return new boss_kalecgosAI (pCreature);
}
+
CreatureAI* GetAI_boss_Sathrovarr(Creature* pCreature)
{
return new boss_sathrovarrAI (pCreature);
}
+
CreatureAI* GetAI_boss_kalec(Creature* pCreature)
{
return new boss_kalecAI (pCreature);
}
+
void AddSC_boss_kalecgos()
{
Script *newscript;
@@ -683,14 +769,17 @@ void AddSC_boss_kalecgos()
newscript->Name = "boss_kalecgos";
newscript->GetAI = &GetAI_boss_kalecgos;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_sathrovarr";
newscript->GetAI = &GetAI_boss_Sathrovarr;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_kalec";
newscript->GetAI = &GetAI_boss_kalec;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "kalecgos_teleporter";
newscript->pGOHello = &GOkalecgos_teleporter;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp
index 84627aca7c7..3bb91d52110 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_kiljaeden.cpp
@@ -13,17 +13,21 @@
* along 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_Kiljaeden
SD%Complete: 70
SDComment: Phase4, Phase5, Shadow Spike, Armageddon
SDCategory: Sunwell_Plateau
EndScriptData */
+
//TODO rewrite Amagedon
//TODO Remove blue visual from Orbs on reset and if it is used
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
#include <math.h>
+
/*** Speech and sounds***/
enum Speeches
{
@@ -33,6 +37,7 @@ enum Speeches
SAY_KJ_OFFCOMBAT3 = -1580068,
SAY_KJ_OFFCOMBAT4 = -1580069,
SAY_KJ_OFFCOMBAT5 = -1580070,
+
// Encounter speech and sounds
SAY_KJ_EMERGE = -1580071,
SAY_KJ_SLAY1 = -1580072,
@@ -47,6 +52,7 @@ enum Speeches
SAY_KJ_PHASE5 = -1580081,
SAY_KJ_DEATH = -1580093,
EMOTE_KJ_DARKNESS = -1580094,
+
/*** Kalecgos - Anveena speech at the beginning of Phase 5; Anveena's sacrifice ***/
SAY_KALECGOS_AWAKEN = -1580082,
SAY_ANVEENA_IMPRISONED = -1580083,
@@ -58,6 +64,7 @@ enum Speeches
SAY_ANVEENA_GOODBYE = -1580089,
SAY_KALECGOS_GOODBYE = -1580090,
SAY_KALECGOS_ENCOURAGE = -1580091,
+
/*** Kalecgos says throughout the fight ***/
SAY_KALECGOS_JOIN = -1580092,
SAY_KALEC_ORB_READY1 = -1580095,
@@ -65,6 +72,7 @@ enum Speeches
SAY_KALEC_ORB_READY3 = -1580097,
SAY_KALEC_ORB_READY4 = -1580098
};
+
/*** Spells used during the encounter ***/
enum SpellIds
{
@@ -73,8 +81,10 @@ enum SpellIds
SPELL_SHADOW_INFUSION = 45772, // They gain this at 20% - Immunity to Stun/Silence and makes them look angry!
SPELL_FELFIRE_PORTAL = 46875, // Creates a portal that spawns Felfire Fiends (LIVE FOR THE SWARM!1 FOR THE OVERMIND!)
SPELL_SHADOW_CHANNELING = 46757, // Channeling animation out of combat
+
/* Volatile Felfire Fiend's spells */
SPELL_FELFIRE_FISSION = 45779, // Felfire Fiends explode when they die or get close to target.
+
/* Kil'Jaeden's spells and cosmetics */
SPELL_TRANS = 23188, // Surprisingly, this seems to be the right spell.. (Where is it used?)
SPELL_REBIRTH = 44200, // Emerge from the Sunwell
@@ -82,15 +92,18 @@ enum SpellIds
SPELL_SOUL_FLAY_SLOW = 47106,
SPELL_LEGION_LIGHTNING = 45664, // Chain Lightning, 4 targets, ~3k Shadow damage, 1.5k mana burn
SPELL_FIRE_BLOOM = 45641, // Places a debuff on 5 raid members, which causes them to deal 2k Fire damage to nearby allies and selves. MIGHT NOT WORK
+
SPELL_SINISTER_REFLECTION = 45785, // Summon shadow copies of 5 raid members that fight against KJ's enemies
SPELL_COPY_WEAPON = 41055, // }
SPELL_COPY_WEAPON2 = 41054, // }
SPELL_COPY_OFFHAND = 45206, // }- Spells used in Sinister Reflection creation
SPELL_COPY_OFFHAND_WEAPON = 45205, // }
+
SPELL_SHADOW_SPIKE = 46680, // Bombard random raid members with Shadow Spikes (Very similar to Void Reaver orbs)
SPELL_FLAME_DART = 45737, // Bombards the raid with flames every 3(?) seconds
SPELL_DARKNESS_OF_A_THOUSAND_SOULS = 46605, // Begins a 8-second channeling, after which he will deal 50'000 damage to the raid
SPELL_DARKNESS_OF_A_THOUSAND_SOULS_DAMAGE = 45657,
+
/* Armageddon spells wrong visual */
SPELL_ARMAGEDDON_TRIGGER = 45909, // Meteor spell trigger missile should cast Creature on himself
SPELL_ARMAGEDDON_VISUAL = 45911, // Does the hellfire visual to indicate where the meteor missle lands
@@ -98,34 +111,47 @@ enum SpellIds
SPELL_ARMAGEDDON_VISUAL3 = 24207, // This shouldn't correct but same as seen on the movie
SPELL_ARMAGEDDON_SUMMON_TRIGGER = 45921, // Summons the triggers that cast the spells on himself need random target select
SPELL_ARMAGEDDON_DAMAGE = 45915, // This does the area damage
+
/* Shield Orb Spells*/
SPELL_SHADOW_BOLT = 45680, //45679 would be correct but triggers to often //TODO fix console error
+
/* Anveena's spells and cosmetics (Or, generally, everything that has "Anveena" in name) */
SPELL_ANVEENA_PRISON = 46367, // She hovers locked within a bubble
SPELL_ANVEENA_ENERGY_DRAIN = 46410, // Sunwell energy glow animation (Control mob uses this)
SPELL_SACRIFICE_OF_ANVEENA = 46474, // This is cast on Kil'Jaeden when Anveena sacrifices herself into the Sunwell
+
/* Sinister Reflection Spells */
SPELL_SR_CURSE_OF_AGONY = 46190,
SPELL_SR_SHADOW_BOLT = 47076,
+
SPELL_SR_EARTH_SHOCK = 47071,
+
SPELL_SR_FIREBALL = 47074,
+
SPELL_SR_HEMORRHAGE = 45897,
+
SPELL_SR_HOLY_SHOCK = 38921,
SPELL_SR_HAMMER_OF_JUSTICE = 37369,
+
SPELL_SR_HOLY_SMITE = 47077,
SPELL_SR_RENEW = 47079,
+
SPELL_SR_SHOOT = 16496,
SPELL_SR_MULTI_SHOT = 48098,
SPELL_SR_WING_CLIP = 40652,
+
SPELL_SR_WHIRLWIND = 17207,
+
SPELL_SR_MOONFIRE = 47072,
//SPELL_SR_PLAGU STRIKE = 58843, Dk Spell!
+
/*** Other Spells (used by players, etc) ***/
SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT = 45839, // Possess the blue dragon from the orb to help the raid.
SPELL_ENTROPIUS_BODY = 46819, // Visual for Entropius at the Epilogue
SPELL_RING_OF_BLUE_FLAMES = 45825 //Cast this spell when the go is activated
};
+
enum CreatureIds
{
CREATURE_ANVEENA = 26046, // Embodiment of the Sunwell
@@ -143,13 +169,17 @@ enum CreatureIds
CREATURE_SPIKE_TARGET2 = 30614,
CREATURE_SINISTER_REFLECTION = 25708 //Sinister Relection spawnd on Phase swichtes
};
+
/*** GameObjects ***/
#define GAMEOBJECT_ORB_OF_THE_BLUE_DRAGONFLIGHT 188415
+
/*** Error messages ***/
#define ERROR_KJ_NOT_SUMMONED "TSCR ERROR: Unable to summon Kil'Jaeden for some reason"
+
/*** Others ***/
#define FLOOR_Z 28.050388
#define SHIELD_ORB_Z 45.000
+
enum Phase
{
PHASE_DECEIVERS = 1, // Fight 3 adds
@@ -158,22 +188,27 @@ enum Phase
PHASE_ARMAGEDDON = 4, // At 55%, he gains even more abilities
PHASE_SACRIFICE = 5, // At 25%, Anveena sacrifices herself into the Sunwell; at this point he becomes enraged and has *significally* shorter cooldowns.
};
+
//Timers
enum KilJaedenTimers {
TIMER_KALEC_JOIN = 0,
+
//Phase 2 Timer
TIMER_SOUL_FLAY = 1,
TIMER_LEGION_LIGHTNING = 2,
TIMER_FIRE_BLOOM = 3,
TIMER_SUMMON_SHILEDORB = 4,
+
//Phase 3 Timer
TIMER_SHADOW_SPIKE = 5,
TIMER_FLAME_DART = 6,
TIMER_DARKNESS = 7,
TIMER_ORBS_EMPOWER = 8,
+
//Phase 4 Timer
TIMER_ARMAGEDDON = 9
};
+
// Locations of the Hand of Deceiver adds
float DeceiverLocations[3][3]=
{
@@ -181,6 +216,7 @@ float DeceiverLocations[3][3]=
{1684.099, 618.848, 0.589},
{1694.170, 612.272, 1.416},
};
+
// Locations, where Shield Orbs will spawn
float ShieldOrbLocations[4][2]=
{
@@ -189,12 +225,14 @@ float ShieldOrbLocations[4][2]=
{12, 3.14/0.7}, // Second one spawns southeast
{12, 3.14*3.8} // Third one spawns (?)
};
+
float OrbLocations[4][5] = {
(1694.48, 674.29, 28.0502, 4.86985),
(1745.68, 621.823, 28.0505, 2.93777),
(1704.14, 583.591, 28.1696, 1.59003),
(1653.12, 635.41, 28.0932, 0.0977725),
};
+
struct Speech
{
int32 textid;
@@ -215,6 +253,7 @@ static Speech Sacrifice[]=
{SAY_KJ_PHASE5, CREATURE_KILJAEDEN, 8000},
{SAY_KALECGOS_ENCOURAGE, CREATURE_KALECGOS, 5000}
};
+
class AllOrbsInGrid
{
public:
@@ -226,6 +265,7 @@ public:
return false;
}
};
+
bool GOHello_go_orb_of_the_blue_flight(Player* pPlayer, GameObject* pGo)
{
if (pGo->GetUInt32Value(GAMEOBJECT_FACTION) == 35)
@@ -242,7 +282,7 @@ bool GOHello_go_orb_of_the_blue_flight(Player* pPlayer, GameObject* pGo)
pGo->GetPosition(x,y,z);
// this won't work. need rewritten
/*
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (DynamicObject* Dyn = Kalec->GetDynObject(SPELL_RING_OF_BLUE_FLAMES))
{
@@ -259,6 +299,7 @@ bool GOHello_go_orb_of_the_blue_flight(Player* pPlayer, GameObject* pGo)
}
return true;
}
+
//AI for Kalecgos
struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
{
@@ -266,13 +307,16 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
GameObject* Orb[4];
ScriptedInstance* pInstance;
uint8 OrbsEmpowered;
uint8 EmpowerCount;
+
bool Searched;
+
void InitializeAI(){
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
Orb[i] = NULL;
FindOrbs();
OrbsEmpowered = 0;
@@ -282,9 +326,12 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
m_creature->setActive(true);
Searched = false;
FindOrbs();
+
ScriptedAI::InitializeAI();
}
+
void Reset(){}
+
void FindOrbs()
{
CellPair pair(Trinity::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
@@ -300,16 +347,18 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
if (orbList.empty())
return;
uint8 i = 0;
- for (std::list<GameObject*>::iterator itr = orbList.begin(); itr != orbList.end(); ++itr, ++i){
+ for(std::list<GameObject*>::iterator itr = orbList.begin(); itr != orbList.end(); ++itr, ++i){
Orb[i] = pInstance ? pInstance->instance->GetGameObject(pInstance->GetData64((*itr)->GetGUID())) : NULL;
}
}
+
void ResetOrbs()
{
m_creature->RemoveDynObject(SPELL_RING_OF_BLUE_FLAMES);
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
if (Orb[i]) Orb[i]->SetUInt32Value(GAMEOBJECT_FACTION, 0);
}
+
void EmpowerOrb(bool all)
{
if (!Orb[OrbsEmpowered])
@@ -318,7 +367,7 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
if (all)
{
m_creature->RemoveDynObject(SPELL_RING_OF_BLUE_FLAMES);
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (!Orb[i]) return;
Orb[i]->CastSpell(m_creature, SPELL_RING_OF_BLUE_FLAMES);
@@ -333,7 +382,7 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
/*
float x,y,z, dx,dy,dz;
Orb[random]->GetPosition(x,y,z);
- for (uint8 i = 0; i < 4; ++i){
+ for(uint8 i = 0; i < 4; ++i){
DynamicObject* Dyn = m_creature->GetDynObject(SPELL_RING_OF_BLUE_FLAMES);
if (Dyn){
Dyn->GetPosition(dx,dy,dz);
@@ -353,6 +402,7 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
++OrbsEmpowered;
}
++EmpowerCount;
+
switch(EmpowerCount)
{
case 1: DoScriptText(SAY_KALEC_ORB_READY1, m_creature); break;
@@ -361,6 +411,7 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
case 4: DoScriptText(SAY_KALEC_ORB_READY4, m_creature); break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!Searched)
@@ -368,14 +419,17 @@ struct TRINITY_DLL_DECL boss_kalecgos_kjAI : public ScriptedAI
FindOrbs();
Searched = true;
}
+
if (OrbsEmpowered == 4)
OrbsEmpowered = 0;
}
};
+
CreatureAI* GetAI_boss_kalecgos_kj(Creature* pCreature)
{
return new boss_kalecgos_kjAI (pCreature);
}
+
//AI for Kil'jaeden
struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
{
@@ -383,57 +437,72 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
SummonList Summons;
Creature* Kalec;
Unit* randomPlayer;
+
uint8 Phase;
uint8 ActiveTimers;
+
uint32 Timer[10];
uint32 WaitTimer;
+
/* Boolean */
bool IsKalecJoined;
bool IsInDarkness;
bool TimerIsDeactiveted[10];
bool IsWaiting;
bool OrbActivated;
+
void Reset()
{
// TODO: Fix timers
Timer[TIMER_KALEC_JOIN] = 26000;
+
//Phase 2 Timer
Timer[TIMER_SOUL_FLAY] = 20000;
Timer[TIMER_LEGION_LIGHTNING] = 40000;
Timer[TIMER_FIRE_BLOOM] = 30000;
Timer[TIMER_SUMMON_SHILEDORB] = 45000;
+
//Phase 3 Timer
Timer[TIMER_SHADOW_SPIKE] = 4000;
Timer[TIMER_FLAME_DART] = 3000;
Timer[TIMER_DARKNESS] = 45000;
Timer[TIMER_ORBS_EMPOWER] = 35000;
+
//Phase 4 Timer
Timer[TIMER_ARMAGEDDON] = 2000;
+
ActiveTimers = 5;
WaitTimer = 0;
+
Phase = PHASE_DECEIVERS;
+
IsKalecJoined = false;
IsInDarkness = false;
IsWaiting = false;
OrbActivated = false;
+
if(pInstance)
Kalec = CAST_CRE(Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KALECGOS_KJ)));
ChangeTimers(false, 0);
}
+
void ChangeTimers(bool status, uint32 WTimer)
{
- for (uint8 i = 0; i < 10; ++i)
+ for(uint8 i = 0; i < 10; ++i)
TimerIsDeactiveted[i] = status;
TimerIsDeactiveted[TIMER_KALEC_JOIN] = IsKalecJoined;
+
if (WTimer > 0)
{
IsWaiting = true;
WaitTimer = WTimer;
}
+
if (OrbActivated)
TimerIsDeactiveted[TIMER_ORBS_EMPOWER] = true;
if (Timer[TIMER_SHADOW_SPIKE] == 0)
@@ -441,53 +510,59 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
if (Phase == PHASE_SACRIFICE)
TimerIsDeactiveted[TIMER_SUMMON_SHILEDORB] = true;
}
+
void JustSummoned(Creature* summoned)
{
if (summoned->GetEntry() == CREATURE_ARMAGEDDON_TARGET)
{
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
- }
- else
- {
+ }else{
summoned->SetLevel(m_creature->getLevel());
}
summoned->setFaction(m_creature->getFaction());
Summons.Summon(summoned);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_KJ_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_KILJAEDEN_EVENT, DONE);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KJ_SLAY1,SAY_KJ_SLAY2), m_creature);
}
+
void EnterEvadeMode()
{
Scripted_NoMovementAI::EnterEvadeMode();
Summons.DespawnAll();
+
// Reset the controller
if (pInstance)
if(Creature* Control = CAST_CRE(Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KILJAEDEN_CONTROLLER))))
CAST_AI(Scripted_NoMovementAI, Control->AI())->Reset();
}
+
void EnterCombat(Unit* who)
{
DoZoneInCombat();
DoScriptText(SAY_KJ_EMERGE, m_creature);
}
+
void CastSinisterReflection()
{
DoScriptText(RAND(SAY_KJ_REFLECTION1,SAY_KJ_REFLECTION2), m_creature);
DoCast(m_creature, SPELL_SINISTER_REFLECTION, true);
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
float x,y,z;
Unit* target;
- for (uint8 z = 0; z < 6; ++z)
+ for(uint8 z = 0; z < 6; ++z)
{
target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
if (!target->HasAura(SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT,0))break;
@@ -498,10 +573,12 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
SinisterReflection->AI()->AttackStart(target);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim() || Phase < PHASE_NORMAL)
return;
+
if (IsWaiting)
{
if (WaitTimer < diff)
@@ -510,7 +587,8 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
ChangeTimers(false, 0);
} else WaitTimer -= diff;
}
- for (uint8 t = 0; t < ActiveTimers; ++t)
+
+ for(uint8 t = 0; t < ActiveTimers; ++t)
{
if (Timer[t] < diff && !TimerIsDeactiveted[t])
{
@@ -536,7 +614,7 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
if (!m_creature->IsNonMeleeSpellCasted(false))
{
m_creature->RemoveAurasDueToSpell(SPELL_SOUL_FLAY);
- for (uint8 z = 0; z < 6; ++z)
+ for(uint8 z = 0; z < 6; ++z)
{
randomPlayer = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
if (!randomPlayer->HasAura(SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT,0)) break;
@@ -559,7 +637,7 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
}
break;
case TIMER_SUMMON_SHILEDORB:
- for (uint8 i = 1; i < Phase; ++i)
+ for(uint8 i = 1; i < Phase; ++i)
{
float sx, sy;
sx = ShieldOrbLocations[0][0] + sin(ShieldOrbLocations[i][0]);
@@ -626,7 +704,7 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
break;
case TIMER_ARMAGEDDON: //Phase 4
Unit* target;
- for (uint8 z = 0; z < 6; ++z)
+ for(uint8 z = 0; z < 6; ++z)
{
target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
if (!target->HasAura(SPELL_VENGEANCE_OF_THE_BLUE_FLIGHT,0)) break;
@@ -644,12 +722,13 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
}
}
//Time runs over!
- for (uint8 i = 0; i < ActiveTimers; ++i)
+ for(uint8 i = 0; i < ActiveTimers; ++i)
if (!TimerIsDeactiveted[i])
{
Timer[i] -= diff;
if (((int32)Timer[i]) < 0) Timer[i] = 0;
}
+
//Phase 3
if (Phase <= PHASE_NORMAL)
{
@@ -663,6 +742,7 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
}
else return;
}
+
//Phase 4
if (Phase <= PHASE_DARKNESS)
{
@@ -675,6 +755,7 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
}
else return;
}
+
//Phase 5 specific spells all we can
if (Phase <= PHASE_ARMAGEDDON)
{
@@ -689,12 +770,15 @@ struct TRINITY_DLL_DECL boss_kiljaedenAI : public Scripted_NoMovementAI
}
else return;
}
+
}
};
+
CreatureAI* GetAI_boss_kiljaeden(Creature* pCreature)
{
return new boss_kiljaedenAI (pCreature);
}
+
//AI for Kil'jaeden Event Controller
struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementAI
{
@@ -702,14 +786,18 @@ struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementA
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
Creature* KalecKJ;
SummonList Summons;
+
bool SummonedDeceivers;
bool KiljaedenDeath;
+
uint32 RandomSayTimer;
uint32 Phase;
uint8 DeceiverDeathCount;
+
void InitializeAI()
{
if(pInstance)
@@ -717,8 +805,10 @@ struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementA
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->addUnitState(UNIT_STAT_STUNNED);
+
ScriptedAI::InitializeAI();
}
+
void Reset()
{
Phase = PHASE_DECEIVERS;
@@ -730,6 +820,7 @@ struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementA
RandomSayTimer = 30000;
Summons.DespawnAll();
}
+
void JustSummoned(Creature* summoned)
{
switch(summoned->GetEntry())
@@ -750,6 +841,7 @@ struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementA
}
Summons.Summon(summoned);
}
+
void UpdateAI(const uint32 diff)
{
if (RandomSayTimer < diff && pInstance && pInstance->GetData(DATA_MURU_EVENT) != DONE && pInstance->GetData(DATA_KILJAEDEN_EVENT) == NOT_STARTED)
@@ -757,14 +849,17 @@ struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementA
DoScriptText(RAND(SAY_KJ_OFFCOMBAT1,SAY_KJ_OFFCOMBAT2,SAY_KJ_OFFCOMBAT3,SAY_KJ_OFFCOMBAT4,SAY_KJ_OFFCOMBAT5), m_creature);
RandomSayTimer = 30000;
} else RandomSayTimer -= diff;
+
if (!SummonedDeceivers)
{
for (uint8 i = 0; i < 3; ++i)
m_creature->SummonCreature(CREATURE_HAND_OF_THE_DECEIVER, DeceiverLocations[i][0], DeceiverLocations[i][1], FLOOR_Z, DeceiverLocations[i][2], TEMPSUMMON_DEAD_DESPAWN, 0);
+
DoSpawnCreature(CREATURE_ANVEENA, 0, 0, 40, 0, TEMPSUMMON_DEAD_DESPAWN, 0);
DoCast(m_creature, SPELL_ANVEENA_ENERGY_DRAIN);
SummonedDeceivers = true;
}
+
if (DeceiverDeathCount > 2 && Phase == PHASE_DECEIVERS)
{
m_creature->RemoveAurasDueToSpell(SPELL_ANVEENA_ENERGY_DRAIN);
@@ -773,10 +868,12 @@ struct TRINITY_DLL_DECL mob_kiljaeden_controllerAI : public Scripted_NoMovementA
}
}
};
+
CreatureAI* GetAI_mob_kiljaeden_controller(Creature* pCreature)
{
return new mob_kiljaeden_controllerAI (pCreature);
}
+
//AI for Hand of the Deceiver
struct TRINITY_DLL_DECL mob_hand_of_the_deceiverAI : public ScriptedAI
{
@@ -785,8 +882,10 @@ struct TRINITY_DLL_DECL mob_hand_of_the_deceiverAI : public ScriptedAI
pInstance = c->GetInstanceData();
}
ScriptedInstance* pInstance;
+
uint32 ShadowBoltVolleyTimer;
uint32 FelfirePortalTimer;
+
void Reset()
{
// TODO: Timers!
@@ -795,11 +894,13 @@ struct TRINITY_DLL_DECL mob_hand_of_the_deceiverAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_KILJAEDEN_EVENT, NOT_STARTED);
}
+
void JustSummoned(Creature* summoned)
{
summoned->setFaction(m_creature->getFaction());
summoned->SetLevel(m_creature->getLevel());
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
@@ -810,34 +911,41 @@ struct TRINITY_DLL_DECL mob_hand_of_the_deceiverAI : public ScriptedAI
}
m_creature->InterruptNonMeleeSpells(true);
}
+
void JustDied(Unit* killer)
{
if (!pInstance)
return;
+
if (Creature* Control = CAST_CRE(Unit::GetUnit(*m_creature, pInstance->GetData64(DATA_KILJAEDEN_CONTROLLER))))
++(CAST_AI(mob_kiljaeden_controllerAI, Control->AI())->DeceiverDeathCount);
}
+
void UpdateAI(const uint32 diff)
{
if (!me->isInCombat())
DoCast(m_creature, SPELL_SHADOW_CHANNELING);
+
if (!UpdateVictim())
return;
+
// Gain Shadow Infusion at 20% health
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20) && !m_creature->HasAura(SPELL_SHADOW_INFUSION, 0))
DoCast(m_creature, SPELL_SHADOW_INFUSION, true);
+
// Shadow Bolt Volley - Shoots Shadow Bolts at all enemies within 30 yards, for ~2k Shadow damage.
if (ShadowBoltVolleyTimer < diff){
DoCast(m_creature->getVictim(), SPELL_SHADOW_BOLT_VOLLEY);
ShadowBoltVolleyTimer = 12000;
}else ShadowBoltVolleyTimer -= diff;
+
// Felfire Portal - Creatres a portal, that spawns Volatile Felfire Fiends, which do suicide bombing.
if (FelfirePortalTimer < diff){
Creature* Portal = DoSpawnCreature(CREATURE_FELFIRE_PORTAL, 0, 0,0, 0, TEMPSUMMON_TIMED_DESPAWN, 20000);
if (Portal)
{
std::list<HostilReference*>::iterator itr;
- for (itr = m_creature->getThreatManager().getThreatList().begin(); itr != m_creature->getThreatManager().getThreatList().end(); ++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)
@@ -846,36 +954,46 @@ struct TRINITY_DLL_DECL mob_hand_of_the_deceiverAI : public ScriptedAI
}
FelfirePortalTimer = 20000;
}else FelfirePortalTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_hand_of_the_deceiver(Creature* pCreature)
{
return new mob_hand_of_the_deceiverAI (pCreature);
}
+
//AI for Felfire Portal
struct TRINITY_DLL_DECL mob_felfire_portalAI : public Scripted_NoMovementAI
{
mob_felfire_portalAI(Creature* c) : Scripted_NoMovementAI(c) {}
+
uint32 SpawnFiendTimer;
+
void InitializeAI()
{
SpawnFiendTimer = 5000;
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
ScriptedAI::InitializeAI();
}
+
// TODO: Timers
void Reset() {}
+
void JustSummoned(Creature* summoned)
{
summoned->setFaction(m_creature->getFaction());
summoned->SetLevel(m_creature->getLevel());
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (SpawnFiendTimer < diff)
{
Creature* Fiend = DoSpawnCreature(CREATURE_VOLATILE_FELFIRE_FIEND, 0, 0, 0, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 20000);
@@ -885,35 +1003,44 @@ struct TRINITY_DLL_DECL mob_felfire_portalAI : public Scripted_NoMovementAI
}else SpawnFiendTimer -= diff;
}
};
+
CreatureAI* GetAI_mob_felfire_portal(Creature* pCreature)
{
return new mob_felfire_portalAI (pCreature);
}
+
//AI for Felfire Fiend
struct TRINITY_DLL_DECL mob_volatile_felfire_fiendAI : public ScriptedAI
{
mob_volatile_felfire_fiendAI(Creature* c) : ScriptedAI(c) {}
+
uint32 ExplodeTimer;
+
bool LockedTarget;
+
void Reset()
{
ExplodeTimer = 2000;
LockedTarget = false;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage > m_creature->GetHealth())
DoCast(m_creature, SPELL_FELFIRE_FISSION, true);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (!LockedTarget)
{
m_creature->AddThreat(m_creature->getVictim(), 10000000.0f);
LockedTarget = true;
}
+
if (ExplodeTimer)
{
if (ExplodeTimer < diff)
@@ -927,21 +1054,26 @@ struct TRINITY_DLL_DECL mob_volatile_felfire_fiendAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mob_volatile_felfire_fiend(Creature* pCreature)
{
return new mob_volatile_felfire_fiendAI (pCreature);
}
+
//AI for Armageddon target
struct TRINITY_DLL_DECL mob_armageddonAI : public Scripted_NoMovementAI
{
mob_armageddonAI(Creature* c) : Scripted_NoMovementAI(c) {}
+
uint8 Spell;
uint32 Timer;
+
void Reset()
{
Spell = 0;
Timer = 0;
}
+
void UpdateAI(const uint32 diff)
{
if (Timer < diff)
@@ -970,10 +1102,12 @@ struct TRINITY_DLL_DECL mob_armageddonAI : public Scripted_NoMovementAI
} else Timer -=diff;
}
};
+
CreatureAI* GetAI_mob_armageddon(Creature* pCreature)
{
return new mob_armageddonAI (pCreature);
}
+
//AI for Shield Orbs
struct TRINITY_DLL_DECL mob_shield_orbAI : public ScriptedAI
{
@@ -981,12 +1115,14 @@ struct TRINITY_DLL_DECL mob_shield_orbAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
bool PointReached;
bool Clockwise;
uint32 Timer;
uint32 CheckTimer;
ScriptedInstance* pInstance;
float x, y, r, c, mx, my;
+
void InitializeAI()
{
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
@@ -998,9 +1134,12 @@ struct TRINITY_DLL_DECL mob_shield_orbAI : public ScriptedAI
mx = ShieldOrbLocations[0][0];
my = ShieldOrbLocations[0][1];
Clockwise = urand(0,1);
+
ScriptedAI::InitializeAI();
}
+
void Reset(){}
+
void UpdateAI(const uint32 diff)
{
if (PointReached)
@@ -1028,7 +1167,9 @@ struct TRINITY_DLL_DECL mob_shield_orbAI : public ScriptedAI
DoTeleportTo(x,y,SHIELD_ORB_Z);
PointReached = true;
} else CheckTimer -= diff;
+
}
+
if (Timer < diff)
{
if (Unit* random = Unit::GetUnit(*m_creature, pInstance ? pInstance->GetData64(DATA_PLAYER_GUID) : 0))
@@ -1036,23 +1177,29 @@ struct TRINITY_DLL_DECL mob_shield_orbAI : public ScriptedAI
Timer = urand(500,1000);
} else Timer -= diff;
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
PointReached = true;
}
};
+
CreatureAI* GetAI_mob_shield_orb(Creature* pCreature)
{
return new mob_shield_orbAI (pCreature);
}
+
//AI for Sinister Reflection
struct TRINITY_DLL_DECL mob_sinster_reflectionAI : public ScriptedAI
{
mob_sinster_reflectionAI(Creature* c) : ScriptedAI(c) {}
+
uint8 Class;
uint32 Timer[3];
+
void Reset()
{
Timer[0] = 0;
@@ -1060,8 +1207,10 @@ struct TRINITY_DLL_DECL mob_sinster_reflectionAI : public ScriptedAI
Timer[2] = 0;
Class = 0;
}
+
void UpdateAI(const uint32 diff)
{
+
if (Class == 0)
{
Class = m_creature->getVictim()->getClass();
@@ -1090,6 +1239,7 @@ struct TRINITY_DLL_DECL mob_sinster_reflectionAI : public ScriptedAI
break;
}
}
+
switch(Class){
case CLASS_DRUID:
if (Timer[1] < diff)
@@ -1193,53 +1343,66 @@ struct TRINITY_DLL_DECL mob_sinster_reflectionAI : public ScriptedAI
break;
}
debug_log("Sinister-Timer");
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
Timer[i] -= diff;
}
+
};
+
CreatureAI* GetAI_mob_sinster_reflection(Creature* pCreature)
{
return new mob_sinster_reflectionAI (pCreature);
}
+
void AddSC_boss_kiljaeden()
{
Script* newscript;
+
newscript = new Script;
newscript->pGOHello = &GOHello_go_orb_of_the_blue_flight;
newscript->Name = "go_orb_of_the_blue_flight";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_kalecgos_kj;
newscript->Name = "boss_kalecgos_kj";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_boss_kiljaeden;
newscript->Name = "boss_kiljaeden";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_kiljaeden_controller;
newscript->Name = "mob_kiljaeden_controller";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_hand_of_the_deceiver;
newscript->Name = "mob_hand_of_the_deceiver";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_felfire_portal;
newscript->Name = "mob_felfire_portal";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_volatile_felfire_fiend;
newscript->Name = "mob_volatile_felfire_fiend";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_armageddon;
newscript->Name = "mob_armageddon";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_shield_orb;
newscript->Name = "mob_shield_orb";
newscript->RegisterSelf();
+
newscript = new Script;
newscript->GetAI = &GetAI_mob_sinster_reflection;
newscript->Name = "mob_sinster_reflection";
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp
index 26f3329e6de..ba95eb51e0f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/boss_muru.cpp
@@ -13,17 +13,21 @@
* along 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_Muru
SD%Complete: 80
SDComment: all sounds, black hole effect triggers to often (46228)
*/
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
// Muru & Entropius's spells
enum Spells
{
SPELL_ENRAGE = 26662,
+
// Muru's spells
SPELL_NEGATIVE_ENERGY = 46009, //(this trigger 46008)
SPELL_DARKNESS = 45999,
@@ -34,30 +38,38 @@ enum Spells
SPELL_SUMNON_FURY_MAGE = 46038,
SPELL_SUMMON_VOID_SENTINEL = 45988,
SPELL_SUMMON_ENTROPIUS = 46217,
+
// Entropius's spells
SPELL_DARKNESS_E = 46269,
SPELL_BLACKHOLE = 46282,
SPELL_NEGATIVE_ENERGY_E = 46284,
SPELL_ENTROPIUS_SPAWN = 46223,
+
// Shadowsword Berserker's spells
SPELL_FLURRY = 46160,
SPELL_DUAL_WIELD = 29651,
+
// Shadowsword Fury Mage's spells
SPELL_FEL_FIREBALL = 46101,
SPELL_SPELL_FURY = 46102,
+
// Void Sentinel's spells
SPELL_SHADOW_PULSE = 46087,
SPELL_VOID_BLAST = 46161,
+
// Void Spawn's spells
SPELL_SHADOW_BOLT_VOLLEY = 46082,
+
//Dark Fiend Spells
SPELL_DARKFIEND_AOE = 45944,
SPELL_DARKFIEND_VISUAL = 45936,
SPELL_DARKFIEND_SKIN = 45934,
+
//Black Hole Spells
SPELL_BLACKHOLE_SPAWN = 46242,
SPELL_BLACKHOLE_GROW = 46228
};
+
enum Creatures
{
CREATURE_DARKNESS = 25879,
@@ -70,12 +82,14 @@ enum Creatures
BOSS_MURU = 25741,
BOSS_ENTROPIUS = 25840
};
+
enum BossTimers{
TIMER_DARKNESS = 0,
TIMER_HUMANOIDES = 1,
TIMER_PHASE = 2,
TIMER_SENTINEL = 3
};
+
float DarkFiends[8][4] =
{
{1819.9, 609.80, 69.74, 1.94},
@@ -87,6 +101,7 @@ float DarkFiends[8][4] =
{1823.9 , 639.69, 69.74, 4.12},
{1811.85, 640.46, 69.73, 4.97}
};
+
float Humanoides[6][5] =
{
{CREATURE_FURY_MAGE, 1780.16, 666.83, 71.19, 5.21},
@@ -96,6 +111,7 @@ float Humanoides[6][5] =
{CREATURE_BERSERKER, 1845.17, 602.63, 71.28, 2.43},
{CREATURE_BERSERKER, 1842.91, 599.93, 71.23, 2.44}
};
+
uint32 EnrageTimer = 600000;
struct TRINITY_DLL_DECL boss_entropiusAI : public ScriptedAI
{
@@ -103,24 +119,32 @@ struct TRINITY_DLL_DECL boss_entropiusAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
SummonList Summons;
+
uint32 BlackHoleSummonTimer;
+
void Reset()
{
BlackHoleSummonTimer = 15000;
DoCastAOE(SPELL_NEGATIVE_ENERGY_E, false);
+
Summons.DespawnAll();
+
if (pInstance)
pInstance->SetData(DATA_MURU_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoCastAOE(SPELL_NEGATIVE_ENERGY_E, true);
DoCast(m_creature, SPELL_ENTROPIUS_SPAWN, false);
+
if (pInstance)
pInstance->SetData(DATA_MURU_EVENT, IN_PROGRESS);
}
+
void JustSummoned(Creature* summoned)
{
switch(summoned->GetEntry())
@@ -138,71 +162,93 @@ struct TRINITY_DLL_DECL boss_entropiusAI : public ScriptedAI
summoned->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM,0, 50, true));
Summons.Summon(summoned);
}
+
void JustDied(Unit* killer)
{
Summons.DespawnAll();
+
if (pInstance)
pInstance->SetData(DATA_MURU_EVENT, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (EnrageTimer < diff && !m_creature->HasAura(SPELL_ENRAGE, 0))
{
DoCast(m_creature,SPELL_ENRAGE, false);
}else EnrageTimer -= diff;
+
if (BlackHoleSummonTimer < diff)
{
Unit* random = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
if (!random)
return;
+
DoCast(random, SPELL_DARKNESS_E, false);
+
random = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
if (!random)
return;
+
random->CastSpell(random, SPELL_BLACKHOLE, false);
BlackHoleSummonTimer = 15000;
}else BlackHoleSummonTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_entropius(Creature* pCreature)
{
return new boss_entropiusAI (pCreature);
}
+
struct TRINITY_DLL_DECL boss_muruAI : public Scripted_NoMovementAI
{
boss_muruAI(Creature *c) : Scripted_NoMovementAI(c), Summons(m_creature)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
SummonList Summons;
+
uint8 Phase;
uint32 Timer[4];
+
bool DarkFiend;
+
void Reset()
{
DarkFiend = false;
Phase = 1;
+
EnrageTimer = 600000;
Timer[TIMER_DARKNESS] = 45000;
Timer[TIMER_HUMANOIDES] = 10000;
Timer[TIMER_PHASE] = 2000;
Timer[TIMER_SENTINEL] = 31500;
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetVisibility(VISIBILITY_ON);
+
Summons.DespawnAll();
+
if (pInstance)
pInstance->SetData(DATA_MURU_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoCastAOE(SPELL_NEGATIVE_ENERGY,false);
+
if (pInstance)
pInstance->SetData(DATA_MURU_EVENT, IN_PROGRESS);
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage > m_creature->GetHealth() && Phase == 1)
@@ -216,6 +262,7 @@ struct TRINITY_DLL_DECL boss_muruAI : public Scripted_NoMovementAI
if (Phase > 1 && Phase < 4)
damage = 0;
}
+
void JustSummoned(Creature* summoned)
{
switch(summoned->GetEntry())
@@ -230,10 +277,12 @@ struct TRINITY_DLL_DECL boss_muruAI : public Scripted_NoMovementAI
summoned->AI()->AttackStart(SelectTarget(SELECT_TARGET_RANDOM,0, 50, true));
Summons.Summon(summoned);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Phase == 3)
{
if (Timer[TIMER_PHASE] <diff)
@@ -254,10 +303,12 @@ struct TRINITY_DLL_DECL boss_muruAI : public Scripted_NoMovementAI
}else Timer[TIMER_PHASE] -= diff;
return;
}
+
if (EnrageTimer < diff && !m_creature->HasAura(SPELL_ENRAGE, 0))
{
DoCast(m_creature, SPELL_ENRAGE, false);
}else EnrageTimer -= diff;
+
for (uint8 i = 0; i < 4; ++i)
{
if (Timer[i] < diff)
@@ -274,13 +325,13 @@ struct TRINITY_DLL_DECL boss_muruAI : public Scripted_NoMovementAI
else
{
DarkFiend = false;
- for (uint8 i = 0; i < 8; ++i)
+ for(uint8 i = 0; i < 8; ++i)
m_creature->SummonCreature(CREATURE_DARK_FIENDS,DarkFiends[i][0],DarkFiends[i][1],DarkFiends[i][2], DarkFiends[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0);
Timer[TIMER_DARKNESS] = 42000;
}
break;
case TIMER_HUMANOIDES:
- for (uint8 i = 0; i < 6; ++i)
+ for(uint8 i = 0; i < 6; ++i)
m_creature->SummonCreature(Humanoides[i][0],Humanoides[i][1],Humanoides[i][2],Humanoides[i][3], Humanoides[i][4], TEMPSUMMON_CORPSE_DESPAWN, 0);
Timer[TIMER_HUMANOIDES] = 60000;
break;
@@ -298,45 +349,59 @@ struct TRINITY_DLL_DECL boss_muruAI : public Scripted_NoMovementAI
break;
}
}
+
//Timer
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (i != TIMER_PHASE)Timer[i] -= diff;
else if (Phase == 2) Timer[i] -= diff;
}
}
};
+
CreatureAI* GetAI_boss_muru(Creature* pCreature)
{
return new boss_muruAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_muru_portalAI : public Scripted_NoMovementAI
{
npc_muru_portalAI(Creature *c) : Scripted_NoMovementAI(c), Summons(m_creature)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
SummonList Summons;
Creature* Muru;
+
bool SummonSentinel;
bool InAction;
+
uint32 SummonTimer;
+
void Reset()
{
SummonTimer = 5000;
+
InAction = false;
SummonSentinel = false;
+
m_creature->addUnitState(UNIT_STAT_STUNNED);
+
Summons.DespawnAll();
}
+
void JustSummoned(Creature* summoned)
{
if (pInstance)
if (Player* Target = Unit::GetPlayer(pInstance->GetData64(DATA_PLAYER_GUID)))
summoned->AI()->AttackStart(Target);
+
Summons.Summon(summoned);
}
+
void SpellHit(Unit* caster, const SpellEntry* Spell)
{
float x,y,z,o;
@@ -354,6 +419,7 @@ struct TRINITY_DLL_DECL npc_muru_portalAI : public Scripted_NoMovementAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!SummonSentinel)
@@ -370,31 +436,39 @@ struct TRINITY_DLL_DECL npc_muru_portalAI : public Scripted_NoMovementAI
}else SummonTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_muru_portal(Creature* pCreature)
{
return new npc_muru_portalAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_dark_fiendAI : public ScriptedAI
{
npc_dark_fiendAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WaitTimer;
bool InAction;
+
void Reset()
{
WaitTimer = 2000;
InAction = false;
+
m_creature->addUnitState(UNIT_STAT_STUNNED);
}
+
void SpellHit(Unit* caster, const SpellEntry* Spell)
{
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
if (Spell->Effect[i] == 38)
m_creature->DisappearAndDie();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (WaitTimer < diff)
{
if (!InAction)
@@ -407,6 +481,7 @@ struct TRINITY_DLL_DECL npc_dark_fiendAI : public ScriptedAI
}
else
{
+
if (m_creature->IsWithinDist(m_creature->getVictim(), 5))
{
DoCastAOE(SPELL_DARKFIEND_AOE, false);
@@ -417,68 +492,85 @@ struct TRINITY_DLL_DECL npc_dark_fiendAI : public ScriptedAI
} else WaitTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_dark_fiend(Creature* pCreature)
{
return new npc_dark_fiendAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_void_sentinelAI : public ScriptedAI
{
npc_void_sentinelAI(Creature *c) : ScriptedAI(c){}
+
uint32 PulseTimer;
uint32 VoidBlastTimer;
+
void Reset()
{
PulseTimer = 3000;
VoidBlastTimer = 45000; //is this a correct timer?
+
float x,y,z,o;
m_creature->GetHomePosition(x,y,z,o);
DoTeleportTo(x,y,71);
}
+
void JustDied(Unit* killer)
{
for (uint8 i = 0; i < 8; ++i)
m_creature->SummonCreature(CREATURE_VOID_SPAWN, m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ(), rand()%6, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 180000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (PulseTimer < diff)
{
DoCastAOE(SPELL_SHADOW_PULSE, true);
PulseTimer = 3000;
} else PulseTimer -= diff;
+
if (VoidBlastTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_VOID_BLAST, false);
VoidBlastTimer = 45000;
} else VoidBlastTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_void_sentinel(Creature* pCreature)
{
return new npc_void_sentinelAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_blackholeAI : public ScriptedAI
{
npc_blackholeAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 DespawnTimer;
uint32 SpellTimer;
uint8 Phase;
uint8 NeedForAHack;
+
void Reset()
{
DespawnTimer = 15000;
SpellTimer = 5000;
Phase = 0;
+
m_creature->addUnitState(UNIT_STAT_STUNNED);
DoCastAOE(SPELL_BLACKHOLE_SPAWN, true);
}
+
void UpdateAI(const uint32 diff)
{
if (SpellTimer < diff)
@@ -515,15 +607,18 @@ struct TRINITY_DLL_DECL npc_blackholeAI : public ScriptedAI
return;
}
} else SpellTimer -= diff;
+
if (DespawnTimer < diff)
m_creature->DisappearAndDie();
else DespawnTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_blackhole(Creature* pCreature)
{
return new npc_blackholeAI (pCreature);
}
+
void AddSC_boss_muru()
{
Script *newscript;
@@ -531,22 +626,27 @@ void AddSC_boss_muru()
newscript->Name = "boss_muru";
newscript->GetAI = &GetAI_boss_muru;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_entropius";
newscript->GetAI = &GetAI_boss_entropius;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_muru_portal";
newscript->GetAI = &GetAI_npc_muru_portal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_dark_fiend";
newscript->GetAI = &GetAI_npc_dark_fiend;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_void_sentinel";
newscript->GetAI = &GetAI_npc_void_sentinel;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_blackhole";
newscript->GetAI = &GetAI_npc_blackhole;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/def_sunwell_plateau.h b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/def_sunwell_plateau.h
index bfcb042fc7e..f955e8c7e17 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/def_sunwell_plateau.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/def_sunwell_plateau.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SUNWELLPLATEAU_H
#define DEF_SUNWELLPLATEAU_H
+
/*** Encounters ***/
#define DATA_KALECGOS_EVENT 1
#define DATA_BRUTALLUS_EVENT 2
@@ -10,6 +12,7 @@
#define DATA_EREDAR_TWINS_EVENT 4
#define DATA_MURU_EVENT 5
#define DATA_KILJAEDEN_EVENT 6
+
/*** Creatures ***/
#define DATA_KALECGOS_DRAGON 7
#define DATA_KALECGOS_HUMAN 8
@@ -24,8 +27,10 @@
#define DATA_KILJAEDEN_CONTROLLER 17
#define DATA_ANVEENA 18
#define DATA_KALECGOS_KJ 19
+
/*** GameObjects ***/
#define DATA_GO_FORCEFIELD 20
+
/*** Misc ***/
#define DATA_PLAYER_GUID 21
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp
index ce51643f6de..4195ebd91a5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/instance_sunwell_plateau.cpp
@@ -1,15 +1,19 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
/* ScriptData
SDName: Instance_Sunwell_Plateau
SD%Complete: 25
SDComment: VERIFY SCRIPT
SDCategory: Sunwell_Plateau
EndScriptData */
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
#define MAX_ENCOUNTER 6
+
/* Sunwell Plateau:
0 - Kalecgos and Sathrovarr
1 - Brutallus
@@ -18,10 +22,13 @@ EndScriptData */
4 - M'uru
5 - Kil'Jaeden
*/
+
struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
{
instance_sunwell_plateau(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
/** Creatures **/
uint64 Kalecgos_Dragon;
uint64 Kalecgos_Human;
@@ -37,17 +44,21 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
uint64 Anveena;
uint64 KalecgosKJ;
uint32 SpectralPlayers;
+
/** GameObjects **/
uint64 ForceField; // Kalecgos Encounter
uint64 KalecgosWall[2];
uint64 FireBarrier; // Felmysts Encounter
uint64 MurusGate[2]; // Murus Encounter
+
/*** Misc ***/
uint32 SpectralRealmTimer;
std::vector<uint64> SpectralRealmList;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
/*** Creatures ***/
Kalecgos_Dragon = 0;
Kalecgos_Human = 0;
@@ -63,6 +74,7 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
Anveena = 0;
KalecgosKJ = 0;
SpectralPlayers = 0;
+
/*** GameObjects ***/
ForceField = 0;
FireBarrier = 0;
@@ -70,31 +82,38 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
MurusGate[1] = 0;
KalecgosWall[0] = 0;
KalecgosWall[1] = 0;
+
/*** Misc ***/
SpectralRealmTimer = 5000;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
+
return false;
}
+
Player* GetPlayerInMap()
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
Player* plr = itr->getSource();
if (plr && !plr->HasAura(45839,0))
return plr;
}
}
+
debug_log("TSCR: Instance Sunwell Plateau: GetPlayerInMap, but PlayerList is empty!");
return NULL;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -114,6 +133,7 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
case 25319: KalecgosKJ = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -134,6 +154,7 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 id)
{
switch(id)
@@ -147,6 +168,7 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 id)
{
switch(id)
@@ -171,6 +193,7 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 id, uint32 data)
{
switch(id)
@@ -216,9 +239,11 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
m_auiEncounter[4] = data; break;
case DATA_KILJAEDEN_EVENT: m_auiEncounter[5] = data; break;
}
+
if (data == DONE)
SaveToDB();
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -234,6 +259,7 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
}
return NULL;
}
+
void Load(const char* in)
{
if (!in)
@@ -241,23 +267,27 @@ struct TRINITY_DLL_DECL instance_sunwell_plateau : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
std::istringstream stream(in);
stream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]
>> m_auiEncounter[4] >> m_auiEncounter[5];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead.
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_sunwell_plateau(Map* pMap)
{
return new instance_sunwell_plateau(pMap);
}
+
void AddSC_instance_sunwell_plateau()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "instance_sunwell_plateau";
newscript->GetInstanceData = &GetInstanceData_instance_sunwell_plateau;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp
index 88f0a8d9db9..e68717857cb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/sunwell_plateau/sunwell_plateau.cpp
@@ -13,20 +13,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Sunwell_Plateau
SD%Complete: 0
SDComment: Placeholder, Epilogue after Kil'jaeden, Captain Selana Gossips
EndScriptData */
+
/* ContentData
npc_prophet_velen
npc_captain_selana
EndContentData */
+
#include "precompiled.h"
#include "def_sunwell_plateau.h"
+
/*######
## npc_prophet_velen
######*/
+
enum ProphetSpeeches
{
PROPHET_SAY1 = -1580099,
@@ -38,6 +43,7 @@ enum ProphetSpeeches
PROPHET_SAY7 = -1580105,
PROPHET_SAY8 = -1580106
};
+
enum LiadrinnSpeeches
{
LIADRIN_SAY1 = -1580107,
@@ -45,13 +51,16 @@ enum LiadrinnSpeeches
LIADRIN_SAY3 = -1580109
};
+
/*######
## npc_captain_selana
######*/
+
#define CS_GOSSIP1 "Give me a situation report, Captain."
#define CS_GOSSIP2 "What went wrong?"
#define CS_GOSSIP3 "Why did they stop?"
#define CS_GOSSIP4 "Your insight is appreciated."
+
void AddSC_sunwell_plateau()
{
}
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/tirisfal_glades.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/tirisfal_glades.cpp
index 29c10e34da4..ba2a9cd762a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/tirisfal_glades.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/tirisfal_glades.cpp
@@ -13,21 +13,26 @@
* along 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, 1819
SDCategory: Tirisfal Glades
EndScriptData */
+
/* ContentData
npc_calvin_montague
go_mausoleum_door
go_mausoleum_trigger
EndContentData */
+
#include "precompiled.h"
+
/*######
## npc_calvin_montague
######*/
+
enum eCalvin
{
SAY_COMPLETE = -1000431,
@@ -35,41 +40,54 @@ enum eCalvin
QUEST_590 = 590,
FACTION_HOSTILE = 168
};
+
struct TRINITY_DLL_DECL npc_calvin_montagueAI : public ScriptedAI
{
npc_calvin_montagueAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
uint32 m_uiPhase;
uint32 m_uiPhaseTimer;
uint64 m_uiPlayerGUID;
+
void Reset()
{
m_uiPhase = 0;
m_uiPhaseTimer = 5000;
m_uiPlayerGUID = 0;
+
me->RestoreFaction();
+
if (!m_creature->HasFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_OOC_NOT_ATTACKABLE))
m_creature->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
+
void EnterCombat(Unit* who) { }
+
void AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim() || m_creature->IsFriendlyTo(pAttacker))
return;
+
AttackStart(pAttacker);
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage)*100 / m_creature->GetMaxHealth() < 15))
{
uiDamage = 0;
+
me->RestoreFaction();
m_creature->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_OOC_NOT_ATTACKABLE);
m_creature->CombatStop(true);
+
m_uiPhase = 1;
+
if (pDoneBy->GetTypeId() == TYPEID_PLAYER)
m_uiPlayerGUID = pDoneBy->GetGUID();
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (m_uiPhase)
@@ -81,6 +99,7 @@ struct TRINITY_DLL_DECL npc_calvin_montagueAI : public ScriptedAI
m_uiPhaseTimer -= uiDiff;
return;
}
+
switch(m_uiPhase)
{
case 1:
@@ -90,6 +109,7 @@ struct TRINITY_DLL_DECL npc_calvin_montagueAI : public ScriptedAI
case 2:
if (Unit* pUnit = Unit::GetUnit(*m_creature, m_uiPlayerGUID))
CAST_PLR(pUnit)->AreaExploredOrEventHappens(QUEST_590);
+
m_creature->CastSpell(m_creature,SPELL_DRINK,true);
++m_uiPhase;
break;
@@ -97,10 +117,13 @@ struct TRINITY_DLL_DECL npc_calvin_montagueAI : public ScriptedAI
EnterEvadeMode();
break;
}
+
return;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
@@ -108,6 +131,7 @@ CreatureAI* GetAI_npc_calvin_montague(Creature* pCreature)
{
return new npc_calvin_montagueAI (pCreature);
}
+
bool QuestAccept_npc_calvin_montague(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_590)
@@ -118,10 +142,12 @@ bool QuestAccept_npc_calvin_montague(Player* pPlayer, Creature* pCreature, Quest
}
return true;
}
+
/*######
## go_mausoleum_door
## go_mausoleum_trigger
######*/
+
enum eMausoleum
{
QUEST_ULAG = 1819,
@@ -129,42 +155,52 @@ enum eMausoleum
GO_TRIGGER = 104593,
GO_DOOR = 176594
};
+
bool GOHello_go_mausoleum_door(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestStatus(QUEST_ULAG) != QUEST_STATUS_INCOMPLETE)
return false;
+
if (GameObject* pTrigger = pPlayer->FindNearestGameObject(GO_TRIGGER, 30.0f))
{
pTrigger->SetGoState(GO_STATE_READY);
pPlayer->SummonCreature(NPC_ULAG, 2390.26, 336.47, 40.01, 2.26, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 300000);
return false;
}
+
return false;
}
+
bool GOHello_go_mausoleum_trigger(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestStatus(QUEST_ULAG) != QUEST_STATUS_INCOMPLETE)
return false;
+
if (GameObject* pDoor = pPlayer->FindNearestGameObject(GO_DOOR, 30.0f))
{
pGo->SetGoState(GO_STATE_ACTIVE);
pDoor->RemoveFlag(GAMEOBJECT_FLAGS,GO_FLAG_INTERACT_COND);
return true;
}
+
return false;
}
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_mausoleum_door";
newscript->pGOHello = &GOHello_go_mausoleum_door;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_mausoleum_trigger";
newscript->pGOHello = &GOHello_go_mausoleum_trigger;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp
index c22340031bf..203fc8268d5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_archaedas.cpp
@@ -13,6 +13,7 @@
* along 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_archaedas
SD%Complete: 100
@@ -22,16 +23,22 @@ At 66%, he will awaken the 6 Guardians.
At 33%, he will awaken the Vault Walkers
On his death the vault door opens.
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO "Who dares awaken Archaedas? Who dares the wrath of the makers!"
#define SOUND_AGGRO 5855
+
#define SAY_SUMMON "Awake ye servants, defend the discs!"
#define SOUND_SUMMON 5856
+
#define SAY_SUMMON2 "To my side, brothers. For the makers!"
#define SOUND_SUMMON2 5857
+
#define SAY_KILL "Reckless mortal."
#define SOUND_KILL 5858
+
#define SPELL_GROUND_TREMOR 6524
#define SPELL_ARCHAEDAS_AWAKEN 10347
#define SPELL_BOSS_OBJECT_VISUAL 11206
@@ -39,36 +46,45 @@ EndScriptData */
#define SPELL_SUB_BOSS_AGGRO 11568
#define SPELL_AWAKEN_VAULT_WALKER 10258
#define SPELL_AWAKEN_EARTHEN_GUARDIAN 10252
+
struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
{
boss_archaedasAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
uint32 Tremor_Timer;
int32 Awaken_Timer;
uint32 WallMinionTimer;
bool wakingUp;
+
bool guardiansAwake;
bool vaultWalkersAwake;
ScriptedInstance* pInstance;
+
void Reset()
{
Tremor_Timer = 60000;
Awaken_Timer = 0;
WallMinionTimer = 10000;
+
wakingUp = false;
guardiansAwake = false;
vaultWalkersAwake = false;
+
if (pInstance)
pInstance->SetData (NULL, 5); // respawn any dead minions
m_creature->setFaction(35);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
+
}
+
void ActivateMinion (uint64 guid, bool flag)
{
Unit *minion = Unit::GetUnit(*m_creature, guid);
+
if (minion && minion->isAlive())
{
DoCast (minion, SPELL_AWAKEN_VAULT_WALKER, flag);
@@ -76,12 +92,14 @@ struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
}
}
+
void EnterCombat(Unit *who)
{
m_creature->setFaction (14);
m_creature->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->RemoveFlag (UNIT_FIELD_FLAGS,UNIT_FLAG_DISABLE_MOVE);
}
+
void SpellHit (Unit* caster, const SpellEntry *spell)
{
// Being woken up from the altar, start the awaken sequence
@@ -92,11 +110,13 @@ struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
wakingUp = true;
}
}
+
void KilledUnit(Unit *victim)
{
m_creature->MonsterYell(SAY_KILL,LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_KILL);
}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance)
@@ -110,15 +130,19 @@ struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
AttackStart(Unit::GetUnit(*m_creature, pInstance->GetData64(0)));
return; // dont want to continue until we finish the AttackStart method
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
// wake a wall minion
if (WallMinionTimer < diff) {
pInstance->SetData (NULL, 2);
+
WallMinionTimer = 10000;
} else WallMinionTimer -= diff;
+
//If we are <66 summon the guardians
if (!guardiansAwake && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 66) {
ActivateMinion(pInstance->GetData64(5),true); // EarthenGuardian1
@@ -131,6 +155,7 @@ struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
DoPlaySoundToSet(m_creature, SOUND_SUMMON);
guardiansAwake = true;
}
+
//If we are <33 summon the vault walkers
if (!vaultWalkersAwake && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 33) {
ActivateMinion(pInstance->GetData64(1),true); // VaultWalker1
@@ -142,15 +167,19 @@ struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
vaultWalkersAwake = true;
}
+
if (Tremor_Timer < diff)
{
//Cast
DoCast(m_creature->getVictim(),SPELL_GROUND_TREMOR);
+
//45 seconds until we should cast this agian
Tremor_Timer = 45000;
}else Tremor_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
void JustDied (Unit *killer) {
if (pInstance)
{
@@ -158,11 +187,14 @@ struct TRINITY_DLL_DECL boss_archaedasAI : public ScriptedAI
pInstance->SetData(NULL,4); // deactivate his minions
}
}
+
};
+
CreatureAI* GetAI_boss_archaedas(Creature* pCreature)
{
return new boss_archaedasAI (pCreature);
}
+
/* ScriptData
SDName: mob_archaedas_minions
SD%Complete: 100
@@ -170,29 +202,37 @@ SDComment: These mobs are initially frozen until Archaedas awakens them
one at a time.
EndScriptData */
+
#define SPELL_ARCHAEDAS_AWAKEN 10347
+
struct TRINITY_DLL_DECL mob_archaedas_minionsAI : public ScriptedAI
{
mob_archaedas_minionsAI(Creature *c) : ScriptedAI(c)
{
pInstance = m_creature->GetInstanceData();
}
+
uint32 Arcing_Timer;
int32 Awaken_Timer;
bool wakingUp;
+
bool amIAwake;
ScriptedInstance* pInstance;
+
void Reset()
{
Arcing_Timer = 3000;
Awaken_Timer = 0;
+
wakingUp = false;
amIAwake = false;
+
m_creature->setFaction(35);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
m_creature->RemoveAllAuras();
}
+
void EnterCombat(Unit *who)
{
m_creature->setFaction (14);
@@ -201,6 +241,7 @@ struct TRINITY_DLL_DECL mob_archaedas_minionsAI : public ScriptedAI
m_creature->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
amIAwake = true;
}
+
void SpellHit (Unit* caster, const SpellEntry *spell) {
// time to wake up, start animation
if (spell == GetSpellStore()->LookupEntry(SPELL_ARCHAEDAS_AWAKEN)){
@@ -208,11 +249,13 @@ struct TRINITY_DLL_DECL mob_archaedas_minionsAI : public ScriptedAI
wakingUp = true;
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (amIAwake)
ScriptedAI::MoveInLineOfSight(who);
}
+
void UpdateAI(const uint32 diff)
{
// we're still in the awaken animation
@@ -225,17 +268,21 @@ struct TRINITY_DLL_DECL mob_archaedas_minionsAI : public ScriptedAI
// AttackStart(Unit::GetUnit(*m_creature, pInstance->GetData64(0))); // whoWokeArchaedasGUID
return; // dont want to continue until we finish the AttackStart method
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_archaedas_minions(Creature* pCreature)
{
return new mob_archaedas_minionsAI (pCreature);
}
+
/* ScriptData
SDName: go_altar_archaedas
SD%Complete: 100
@@ -243,16 +290,22 @@ SDComment: Needs 1 person to activate the Archaedas script
SDCategory: Uldaman
EndScriptData */
+
#define OBJECT_ALTAR_OF_ARCHAEDAS 133234
+
#define NUMBER_NEEDED_TO_ACTIVATE 1 // as of patch 3.0.8 the altars can be opened by a single player (previously 3)
+
#define SPELL_BOSS_OBJECT_VISUAL 11206
+
//uint64 altarOfArchaedasCount[5];
//int32 altarOfArchaedasCounter=0;
+
bool GOHello_go_altar_of_archaedas(Player* pPlayer, GameObject* pGo)
{
//bool alreadyUsed;
//pGo->AddUse ();
+
/*
alreadyUsed = false;
for (uint32 loop=0; loop<5; loop++) {
@@ -261,11 +314,14 @@ bool GOHello_go_altar_of_archaedas(Player* pPlayer, GameObject* pGo)
if (!alreadyUsed)
altarOfArchaedasCount[altarOfArchaedasCounter++] = pPlayer->GetGUID();
*/
+
pPlayer->CastSpell (pPlayer, SPELL_BOSS_OBJECT_VISUAL, false);
+
/*
if (altarOfArchaedasCounter < NUMBER_NEEDED_TO_ACTIVATE) {
return false; // not enough people yet
}
+
// Check to make sure at least three people are still casting
uint32 count=0;
Unit *pTarget;
@@ -275,32 +331,42 @@ bool GOHello_go_altar_of_archaedas(Player* pPlayer, GameObject* pGo)
if (pTarget->IsNonMeleeSpellCasted(true)) count++;
if (count >= NUMBER_NEEDED_TO_ACTIVATE) break;
}
+
if (count < NUMBER_NEEDED_TO_ACTIVATE) {
return false; // not enough people
}
*/
+
ScriptedInstance* pInstance = pPlayer->GetInstanceData();
if (!pInstance)
return false;
+
pInstance->SetData(NULL,0);
pInstance->SetData64(0,pPlayer->GetGUID()); // activate archaedas
+
return false;
}
+
/* ScriptData
SDName: mob_stonekeepers
SD%Complete: 100
SDComment: After activating the altar of the keepers, the stone keepers will
wake up one by one.
EndScriptData */
+
#include "precompiled.h"
+
#define SPELL_SELF_DESTRUCT 9874
+
struct TRINITY_DLL_DECL mob_stonekeepersAI : public ScriptedAI
{
mob_stonekeepersAI(Creature *c) : ScriptedAI(c)
{
pInstance = (m_creature->GetInstanceData());
}
+
ScriptedInstance* pInstance;
+
void Reset()
{
m_creature->setFaction(35);
@@ -308,30 +374,38 @@ struct TRINITY_DLL_DECL mob_stonekeepersAI : public ScriptedAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
m_creature->RemoveAllAuras();
}
+
void EnterCombat(Unit *who)
{
m_creature->setFaction (14);
m_creature->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
}
+
void UpdateAI(const uint32 diff)
{
+
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit *attacker)
{
DoCast (m_creature, SPELL_SELF_DESTRUCT,true);
if (pInstance)
pInstance->SetData(NULL, 1); // activate next stonekeeper
}
+
};
+
CreatureAI* GetAI_mob_stonekeepers(Creature* pCreature)
{
return new mob_stonekeepersAI (pCreature);
}
+
/* ScriptData
SDName: go_altar_of_the_keepers
SD%Complete: 100
@@ -339,17 +413,24 @@ SDComment: Need 1 person to activate to open the altar. One by one the StoneKee
SDCategory: Uldaman
EndScriptData */
+
#define SPELL_BOSS_OBJECT_VISUAL 11206
+
#define NUMBER_NEEDED_TO_ACTIVATE 1 // as of patch 3.0.8 the altars can be opened by a single player (previously 3)
+
//static uint64 altarOfTheKeeperCount[5];
//static uint32 altarOfTheKeeperCounter=0;
+
bool GOHello_go_altar_of_the_keepers(Player* pPlayer, GameObject* pGo)
{
ScriptedInstance* pInstance = pPlayer->GetInstanceData();
if (!pInstance)
return true;
+
//bool alreadyUsed;
+
//pGo->AddUse ();
+
//alreadyUsed = false;
//for (uint32 loop=0; loop<5; ++loop)
//{
@@ -359,6 +440,7 @@ bool GOHello_go_altar_of_the_keepers(Player* pPlayer, GameObject* pGo)
//if (!alreadyUsed && altarOfTheKeeperCounter < 5)
// altarOfTheKeeperCount[altarOfTheKeeperCounter++] = pPlayer->GetGUID();
pPlayer->CastSpell (pPlayer, SPELL_BOSS_OBJECT_VISUAL, false);
+
//if (altarOfTheKeeperCounter < NUMBER_NEEDED_TO_ACTIVATE)
//{
//error_log ("not enough people yet, altarOfTheKeeperCounter = %d", altarOfTheKeeperCounter);
@@ -376,6 +458,7 @@ bool GOHello_go_altar_of_the_keepers(Player* pPlayer, GameObject* pGo)
if (pTarget->IsNonMeleeSpellCasted(true)) count++;
if (count >= NUMBER_NEEDED_TO_ACTIVATE) break;
}
+
if (count < NUMBER_NEEDED_TO_ACTIVATE)
{
// error_log ("still not enough people");
@@ -386,6 +469,7 @@ bool GOHello_go_altar_of_the_keepers(Player* pPlayer, GameObject* pGo)
pInstance->SetData(NULL,1); // activate the Stone Keepers
return true;
}
+
void AddSC_boss_archaedas()
{
Script *newscript;
@@ -393,18 +477,22 @@ void AddSC_boss_archaedas()
newscript->Name = "boss_archaedas";
newscript->GetAI = &GetAI_boss_archaedas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_altar_of_archaedas";
newscript->pGOHello = &GOHello_go_altar_of_archaedas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_archaedas_minions";
newscript->GetAI = &GetAI_mob_archaedas_minions;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_altar_of_the_keepers";
newscript->pGOHello = &GOHello_go_altar_of_the_keepers;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_stonekeepers";
newscript->GetAI = &GetAI_mob_stonekeepers;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp
index 11ece7039c4..06c0289812b 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/boss_ironaya.cpp
@@ -13,69 +13,88 @@
* along 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 SAY_AGGRO -1070000
+
#define SPELL_ARCINGSMASH 8374
#define SPELL_KNOCKAWAY 10101
#define SPELL_WSTOMP 11876
+
struct TRINITY_DLL_DECL boss_ironayaAI : public ScriptedAI
{
boss_ironayaAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Arcing_Timer;
bool hasCastedWstomp;
bool hasCastedKnockaway;
+
void Reset()
{
Arcing_Timer = 3000;
hasCastedKnockaway = false;
hasCastedWstomp = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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* pCreature)
{
return new boss_ironayaAI (pCreature);
}
+
void AddSC_boss_ironaya()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp
index 178ea0985b3..21b639e2449 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/instance_uldaman.cpp
@@ -14,21 +14,28 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "precompiled.h"
+
#define SPELL_ARCHAEDAS_AWAKEN 10347
#define SPELL_AWAKEN_VAULT_WALKER 10258
+
#define ARCHAEDAS_TEMPLE_DOOR 141869
#define ALTAR_OF_ARCHAEDAS 133234
+
#define ALTAR_OF_THE_KEEPER_TEMPLE_DOOR 124367
#define ALTAR_OF_THE_KEEPER_TEMPLE 130511
+
#define ANCIENT_VAULT_DOOR 124369
+
struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
{
instance_uldaman(Map* pMap) : ScriptedInstance(pMap)
{
Initialize();
};
+
void Initialize()
{
archaedasGUID = 0;
@@ -37,16 +44,19 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
ancientVaultDoor = 0;
whoWokeArchaedasGUID = 0;
}
+
uint64 archaedasGUID;
uint64 altarOfTheKeeperTempleDoor;
uint64 archaedasTempleDoor;
uint64 ancientVaultDoor;
uint64 whoWokeArchaedasGUID;
+
std::vector<uint64> stoneKeeper;
std::vector<uint64> altarOfTheKeeperCount;
std::vector<uint64> vaultWalker;
std::vector<uint64> earthenGuardian;
std::vector<uint64> archaedasWallMinions; // minions lined up around the wall
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch (pGo->GetEntry())
@@ -54,9 +64,11 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
case ALTAR_OF_THE_KEEPER_TEMPLE_DOOR: // lock the door
altarOfTheKeeperTempleDoor = pGo->GetGUID();
break;
+
case ARCHAEDAS_TEMPLE_DOOR:
archaedasTempleDoor = pGo->GetGUID();
break;
+
case ANCIENT_VAULT_DOOR:
pGo->SetGoState(GO_STATE_READY);
pGo->SetUInt32Value(GAMEOBJECT_FLAGS, 33);
@@ -64,6 +76,7 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
break;
}
}
+
void SetFrozenState(Creature* pCreature)
{
pCreature->setFaction(35);
@@ -72,17 +85,20 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
pCreature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE);
}
+
void OpenDoor(uint64 guid)
{
GameObject* pGo = instance->GetGameObject(guid);
if (!pGo)
return;
+
pGo->SetUInt32Value(GAMEOBJECT_FLAGS, 33);
pGo->SetGoState(GO_STATE_ACTIVE);
}
+
void ActivateStoneKeepers()
{
- for (std::vector<uint64>::iterator i = stoneKeeper.begin(); i != stoneKeeper.end(); ++i)
+ for(std::vector<uint64>::iterator i = stoneKeeper.begin(); i != stoneKeeper.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (!target || !target->isAlive() || target->getFaction()==14)
@@ -95,12 +111,14 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
// if we get this far than all four are dead so open the door
SetData (NULL, 0);
}
+
void ActivateWallMinions()
{
Creature *archaedas = instance->GetCreature(archaedasGUID);
if (!archaedas)
return;
- for (std::vector<uint64>::iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
+
+ for(std::vector<uint64>::iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (!target || !target->isAlive() || target->getFaction()==14)
@@ -110,11 +128,12 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
return; // only want the first one we find
}
}
+
// used when Archaedas dies. All active minions must be despawned.
void DeActivateMinions()
{
// first despawn any aggroed wall minions
- for (std::vector<uint64>::iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
+ for(std::vector<uint64>::iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (!target || target->isDead() || target->getFaction()!=14)
@@ -122,8 +141,9 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
target->setDeathState(JUST_DIED);
target->RemoveCorpse();
}
+
// Vault Walkers
- for (std::vector<uint64>::iterator i = vaultWalker.begin(); i != vaultWalker.end(); ++i)
+ for(std::vector<uint64>::iterator i = vaultWalker.begin(); i != vaultWalker.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (!target || target->isDead() || target->getFaction()!=14)
@@ -131,8 +151,9 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
target->setDeathState(JUST_DIED);
target->RemoveCorpse();
}
+
// Earthen Guardians
- for (std::vector<uint64>::iterator i = earthenGuardian.begin(); i != earthenGuardian.end(); ++i)
+ for(std::vector<uint64>::iterator i = earthenGuardian.begin(); i != earthenGuardian.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (!target || target->isDead() || target->getFaction()!=14)
@@ -141,21 +162,24 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
target->RemoveCorpse();
}
}
+
void ActivateArchaedas(uint64 target)
{
Creature *archaedas = instance->GetCreature(archaedasGUID);
if (!archaedas)
return;
+
if (Unit *victim = Unit::GetUnit(*archaedas, target))
{
archaedas->CastSpell(archaedas, SPELL_ARCHAEDAS_AWAKEN,false);
whoWokeArchaedasGUID = target;
}
}
+
void RespawnMinions()
{
// first respawn any aggroed wall minions
- for (std::vector<uint64>::iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
+ for(std::vector<uint64>::iterator i = archaedasWallMinions.begin(); i != archaedasWallMinions.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (target && target->isDead())
@@ -165,8 +189,9 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
SetFrozenState(target);
}
}
+
// Vault Walkers
- for (std::vector<uint64>::iterator i = vaultWalker.begin(); i != vaultWalker.end(); ++i)
+ for(std::vector<uint64>::iterator i = vaultWalker.begin(); i != vaultWalker.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (target && target->isDead())
@@ -176,8 +201,9 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
SetFrozenState(target);
}
}
+
// Earthen Guardians
- for (std::vector<uint64>::iterator i = earthenGuardian.begin(); i != earthenGuardian.end(); ++i)
+ for(std::vector<uint64>::iterator i = earthenGuardian.begin(); i != earthenGuardian.end(); ++i)
{
Creature *target = instance->GetCreature(*i);
if (target && target->isDead())
@@ -188,6 +214,7 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
}
}
}
+
void SetData (uint32 type, uint32 data)
{
//error_log ("SetData: data = %d", data);
@@ -200,6 +227,7 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
if (data==5) RespawnMinions();
}
+
void SetData64 (uint32 type, uint64 data)
{
// Archaedas
@@ -209,6 +237,7 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch (pCreature->GetEntry()) {
@@ -216,23 +245,30 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
SetFrozenState (pCreature);
stoneKeeper.push_back(pCreature->GetGUID());
break;
+
case 7309: // Earthen Custodian
archaedasWallMinions.push_back(pCreature->GetGUID());
break;
+
case 7077: // Earthen Hallshaper
archaedasWallMinions.push_back(pCreature->GetGUID());
break;
+
case 7076: // Earthen Guardian
earthenGuardian.push_back(pCreature->GetGUID());
break;
+
case 10120: // Vault Walker
vaultWalker.push_back(pCreature->GetGUID());
break;
+
case 2748: // Archaedas
archaedasGUID = pCreature->GetGUID();
break;
+
} // end switch
} // end OnCreatureCreate
+
uint64 GetData64 (uint32 identifier)
{
if (identifier == 0) return whoWokeArchaedasGUID;
@@ -240,20 +276,25 @@ struct TRINITY_DLL_DECL instance_uldaman : public ScriptedInstance
if (identifier == 2) return vaultWalker[1]; // VaultWalker2
if (identifier == 3) return vaultWalker[2]; // VaultWalker3
if (identifier == 4) return vaultWalker[3]; // VaultWalker4
+
if (identifier == 5) return earthenGuardian[0];
if (identifier == 6) return earthenGuardian[1];
if (identifier == 7) return earthenGuardian[2];
if (identifier == 8) return earthenGuardian[3];
if (identifier == 9) return earthenGuardian[4];
if (identifier == 10) return earthenGuardian[5];
+
return 0;
} // end GetData64
};
+
+
InstanceData* GetInstanceData_instance_uldaman(Map* pMap)
{
return new instance_uldaman(pMap);
}
+
void AddSC_instance_uldaman()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/uldaman.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/uldaman.cpp
index 3872d9bfa13..4ba8ed0c65b 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/uldaman.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/uldaman/uldaman.cpp
@@ -13,61 +13,81 @@
* along 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 TRINITY_DLL_DECL mob_jadespine_basiliskAI : public ScriptedAI
{
mob_jadespine_basiliskAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Cslumber_Timer;
+
void Reset()
{
Cslumber_Timer = 2000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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* pCreature)
{
return new mob_jadespine_basiliskAI (pCreature);
}
+
/*######
## npc_lore_keeper_of_norgannon
######*/
+
#define GOSSIP_HELLO_KEEPER "Who are the Earthen?"
#define GOSSIP_SELECT_KEEPER1 "What is a \"subterranean being matrix\"?"
#define GOSSIP_SELECT_KEEPER2 "What are the anomalies you speak of?"
@@ -84,13 +104,17 @@ CreatureAI* GetAI_mob_jadespine_basilisk(Creature* pCreature)
#define GOSSIP_SELECT_KEEPER13 "Who are the Creators?"
#define GOSSIP_SELECT_KEEPER14 "This is a lot to think about."
#define GOSSIP_SELECT_KEEPER15 "I will access the discs now."
+
bool GossipHello_npc_lore_keeper_of_norgannon(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(2278) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_KEEPER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(1079, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lore_keeper_of_norgannon(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -162,13 +186,16 @@ bool GossipSelect_npc_lore_keeper_of_norgannon(Player* pPlayer, Creature* pCreat
}
return true;
}
+
void AddSC_uldaman()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_jadespine_basilisk";
newscript->GetAI = &GetAI_mob_jadespine_basilisk;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lore_keeper_of_norgannon";
newscript->pGossipHello = &GossipHello_npc_lore_keeper_of_norgannon;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/undercity.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/undercity.cpp
index c1c72fd9f9b..ecfd0894ddf 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/undercity.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/undercity.cpp
@@ -13,29 +13,37 @@
* along 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, 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 -1000357
#define EMOTE_LAMENT_END -1000358
+
#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},
@@ -43,21 +51,27 @@ float HighborneLoc[4][3]=
{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 TRINITY_DLL_DECL npc_lady_sylvanas_windrunnerAI : public ScriptedAI
{
npc_lady_sylvanas_windrunnerAI(Creature *c) : ScriptedAI(c) {}
+
uint32 LamentEvent_Timer;
bool LamentEvent;
uint64 targetGUID;
+
void Reset()
{
LamentEvent_Timer = 5000;
LamentEvent = false;
targetGUID = 0;
}
+
void EnterCombat(Unit *who) {}
+
void JustSummoned(Creature *summoned)
{
if (summoned->GetEntry() == ENTRY_HIGHBORNE_BUNNY)
@@ -68,10 +82,12 @@ struct TRINITY_DLL_DECL npc_lady_sylvanas_windrunnerAI : public ScriptedAI
target->GetMap()->CreatureRelocation(m_creature, target->GetPositionX(), target->GetPositionY(), me->GetPositionZ()+15.0, 0.0f);
summoned->CastSpell(target, SPELL_RIBBON_OF_SOULS, false);
}
+
summoned->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
targetGUID = summoned->GetGUID();
}
}
+
void UpdateAI(const uint32 diff)
{
if (LamentEvent)
@@ -79,6 +95,7 @@ struct TRINITY_DLL_DECL npc_lady_sylvanas_windrunnerAI : public ScriptedAI
if (LamentEvent_Timer < diff)
{
DoSummon(ENTRY_HIGHBORNE_BUNNY, me, 10.0f, 3000, TEMPSUMMON_TIMED_DESPAWN);
+
LamentEvent_Timer = 2000;
if (!m_creature->HasAura(SPELL_SYLVANAS_CAST))
{
@@ -88,8 +105,10 @@ struct TRINITY_DLL_DECL npc_lady_sylvanas_windrunnerAI : public ScriptedAI
}
}else LamentEvent_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
@@ -97,6 +116,7 @@ CreatureAI* GetAI_npc_lady_sylvanas_windrunner(Creature* pCreature)
{
return new npc_lady_sylvanas_windrunnerAI (pCreature);
}
+
bool ChooseReward_npc_lady_sylvanas_windrunner(Player* pPlayer, Creature* pCreature, const Quest *_Quest, uint32 slot)
{
if (_Quest->GetQuestId() == 9180)
@@ -104,21 +124,27 @@ bool ChooseReward_npc_lady_sylvanas_windrunner(Player* pPlayer, Creature* pCreat
CAST_AI(npc_lady_sylvanas_windrunnerAI, pCreature->AI())->LamentEvent = true;
CAST_AI(npc_lady_sylvanas_windrunnerAI, pCreature->AI())->DoPlaySoundToSet(pCreature,SOUND_CREDIT);
pCreature->CastSpell(pCreature,SPELL_SYLVANAS_CAST,false);
- for (uint8 i = 0; i < 4; ++i)
+
+ for(uint8 i = 0; i < 4; ++i)
pCreature->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 TRINITY_DLL_DECL npc_highborne_lamenterAI : public ScriptedAI
{
npc_highborne_lamenterAI(Creature *c) : ScriptedAI(c) {}
+
uint32 EventMove_Timer;
uint32 EventCast_Timer;
bool EventMove;
bool EventCast;
+
void Reset()
{
EventMove_Timer = 10000;
@@ -126,7 +152,9 @@ struct TRINITY_DLL_DECL npc_highborne_lamenterAI : public ScriptedAI
EventMove = true;
EventCast = true;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (EventMove)
@@ -153,17 +181,22 @@ CreatureAI* GetAI_npc_highborne_lamenter(Creature* pCreature)
{
return new npc_highborne_lamenterAI (pCreature);
}
+
/*######
## npc_parqual_fintallas
######*/
+
#define SPELL_MARK_OF_SHAME 6767
+
#define GOSSIP_HPF1 "Gul'dan"
#define GOSSIP_HPF2 "Kel'Thuzad"
#define GOSSIP_HPF3 "Ner'zhul"
+
bool GossipHello_npc_parqual_fintallas(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(6628) == QUEST_STATUS_INCOMPLETE && !pPlayer->HasAura(SPELL_MARK_OF_SHAME))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HPF1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
@@ -173,8 +206,10 @@ bool GossipHello_npc_parqual_fintallas(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(5821, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_parqual_fintallas(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -189,21 +224,26 @@ bool GossipSelect_npc_parqual_fintallas(Player* pPlayer, Creature* pCreature, ui
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_highborne_lamenter";
newscript->GetAI = &GetAI_npc_highborne_lamenter;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_parqual_fintallas";
newscript->pGossipHello = &GossipHello_npc_parqual_fintallas;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/western_plaguelands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/western_plaguelands.cpp
index bf1bed2152b..4181bc89867 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/western_plaguelands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/western_plaguelands.cpp
@@ -13,32 +13,40 @@
* along 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_myranda_the_hag
npc_the_scourge_cauldron
EndContentData */
+
#include "precompiled.h"
+
/*######
## npcs_dithers_and_arbington
######*/
+
#define GOSSIP_HDA1 "What does the Felstone Field Cauldron need?"
#define GOSSIP_HDA2 "What does the Dalson's Tears Cauldron need?"
#define GOSSIP_HDA3 "What does the Writhing Haunt Cauldron need?"
#define GOSSIP_HDA4 "What does the Gahrron's Withering Cauldron need?"
+
#define GOSSIP_SDA1 "Thanks, i need a Vitreous Focuser"
+
bool GossipHello_npcs_dithers_and_arbington(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
if (pCreature->isVendor())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
if (pPlayer->GetQuestRewardStatus(5237) || pPlayer->GetQuestRewardStatus(5238))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HDA1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
@@ -48,8 +56,10 @@ bool GossipHello_npcs_dithers_and_arbington(Player* pPlayer, Creature* pCreature
pPlayer->SEND_GOSSIP_MENU(3985, pCreature->GetGUID());
}else
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npcs_dithers_and_arbington(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -80,20 +90,25 @@ bool GossipSelect_npcs_dithers_and_arbington(Player* pPlayer, Creature* pCreatur
}
return true;
}
+
/*######
## npc_myranda_the_hag
######*/
+
enum eMyranda
{
QUEST_SUBTERFUGE = 5862,
QUEST_IN_DREAMS = 5944,
SPELL_SCARLET_ILLUSION = 17961
};
+
#define GOSSIP_ITEM_ILLUSION "I am ready for the illusion, Myranda."
+
bool GossipHello_npc_myranda_the_hag(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_SUBTERFUGE) == QUEST_STATUS_COMPLETE &&
!pPlayer->GetQuestRewardStatus(QUEST_IN_DREAMS) && !pPlayer->HasAura(SPELL_SCARLET_ILLUSION))
{
@@ -103,8 +118,10 @@ bool GossipHello_npc_myranda_the_hag(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_myranda_the_hag(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -114,14 +131,19 @@ bool GossipSelect_npc_myranda_the_hag(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
/*######
## npc_the_scourge_cauldron
######*/
+
struct TRINITY_DLL_DECL npc_the_scourge_cauldronAI : public ScriptedAI
{
npc_the_scourge_cauldronAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
+
void EnterCombat(Unit* who) {}
+
void DoDie()
{
//summoner dies here
@@ -131,10 +153,12 @@ struct TRINITY_DLL_DECL npc_the_scourge_cauldronAI : public ScriptedAI
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())
@@ -179,22 +203,27 @@ CreatureAI* GetAI_npc_the_scourge_cauldron(Creature* pCreature)
{
return new npc_the_scourge_cauldronAI (pCreature);
}
+
/*######
##
######*/
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_myranda_the_hag";
newscript->pGossipHello = &GossipHello_npc_myranda_the_hag;
newscript->pGossipSelect = &GossipSelect_npc_myranda_the_hag;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_the_scourge_cauldron";
newscript->GetAI = &GetAI_npc_the_scourge_cauldron;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/westfall.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/westfall.cpp
index c25dd521c68..6a1df820efa 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/westfall.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/westfall.cpp
@@ -13,21 +13,26 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Westfall
SD%Complete: 90
SDComment: Quest support: 155, 1651
SDCategory: Westfall
EndScriptData */
+
/* ContentData
npc_daphne_stilwell
npc_defias_traitor
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_daphne_stilwell
######*/
+
enum eEnums
{
SAY_DS_START = -1000402,
@@ -35,16 +40,20 @@ enum eEnums
SAY_DS_DOWN_2 = -1000404,
SAY_DS_DOWN_3 = -1000405,
SAY_DS_PROLOGUE = -1000406,
+
SPELL_SHOOT = 6660,
QUEST_TOME_VALOR = 1651,
NPC_DEFIAS_RAIDER = 6180,
EQUIP_ID_RIFLE = 2511
};
+
struct TRINITY_DLL_DECL npc_daphne_stilwellAI : public npc_escortAI
{
npc_daphne_stilwellAI(Creature* pCreature) : npc_escortAI(pCreature) {}
+
uint32 uiWPHolder;
uint32 uiShootTimer;
+
void Reset()
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
@@ -58,14 +67,19 @@ struct TRINITY_DLL_DECL npc_daphne_stilwellAI : public npc_escortAI
}
else
uiWPHolder = 0;
+
uiShootTimer = 0;
}
+
void WaypointReached(uint32 uiPoint)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
uiWPHolder = uiPoint;
+
switch(uiPoint)
{
case 4:
@@ -109,66 +123,85 @@ struct TRINITY_DLL_DECL npc_daphne_stilwellAI : public npc_escortAI
break;
}
}
+
void AttackStart(Unit* pWho)
{
if (!pWho)
return;
+
if (m_creature->Attack(pWho, false))
{
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
m_creature->GetMotionMaster()->MoveChase(pWho, 30.0f);
}
}
+
void JustSummoned(Creature* pSummoned)
{
pSummoned->AI()->AttackStart(m_creature);
}
+
void Update(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
+
if (!UpdateVictim())
return;
+
if (uiShootTimer < diff)
{
uiShootTimer = 1500;
+
if (!m_creature->IsWithinDist(m_creature->getVictim(), ATTACK_DISTANCE))
DoCast(m_creature->getVictim(), SPELL_SHOOT);
}else uiShootTimer -= diff;
}
};
+
bool QuestAccept_npc_daphne_stilwell(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_TOME_VALOR)
{
DoScriptText(SAY_DS_START, pCreature);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_daphne_stilwellAI, pCreature->AI()))
pEscortAI->Start(true, true, pPlayer->GetGUID());
}
+
return true;
}
+
CreatureAI* GetAI_npc_daphne_stilwell(Creature* pCreature)
{
return new npc_daphne_stilwellAI(pCreature);
}
+
/*######
## npc_defias_traitor
######*/
+
#define SAY_START -1000101
#define SAY_PROGRESS -1000102
#define SAY_END -1000103
#define SAY_AGGRO_1 -1000104
#define SAY_AGGRO_2 -1000105
+
#define QUEST_DEFIAS_BROTHERHOOD 155
+
struct TRINITY_DLL_DECL npc_defias_traitorAI : public npc_escortAI
{
npc_defias_traitorAI(Creature *c) : npc_escortAI(c) { Reset(); }
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch (i)
{
case 35:
@@ -190,30 +223,38 @@ struct TRINITY_DLL_DECL npc_defias_traitorAI : public npc_escortAI
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2), m_creature, who);
}
+
void Reset() {}
};
+
bool QuestAccept_npc_defias_traitor(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_DEFIAS_BROTHERHOOD)
{
if (npc_escortAI* pEscortAI = CAST_AI(npc_defias_traitorAI, pCreature->AI()))
pEscortAI->Start(true, true, pPlayer->GetGUID());
+
DoScriptText(SAY_START, pCreature, pPlayer);
}
+
return true;
}
+
CreatureAI* GetAI_npc_defias_traitor(Creature* pCreature)
{
return new npc_defias_traitorAI(pCreature);
}
+
void AddSC_westfall()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_daphne_stilwell";
newscript->GetAI = &GetAI_npc_daphne_stilwell;
newscript->pQuestAccept = &QuestAccept_npc_daphne_stilwell;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_defias_traitor";
newscript->GetAI = &GetAI_npc_defias_traitor;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/wetlands.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/wetlands.cpp
index 76bf0e7ee60..d50d0fa2d2d 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/wetlands.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/wetlands.cpp
@@ -13,21 +13,26 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Wetlands
SD%Complete: 80
SDComment: Quest support: 1249
SDCategory: Wetlands
EndScriptData */
+
/* ContentData
npc_mikhail
npc_tapoke_slim_jahn
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_tapoke_slim_jahn
######*/
+
enum eTapokeSlim
{
QUEST_MISSING_DIPLO_PT11 = 1249,
@@ -37,15 +42,19 @@ enum eTapokeSlim
NPC_SLIMS_FRIEND = 4971,
NPC_TAPOKE_SLIM_JAHN = 4962
};
+
struct TRINITY_DLL_DECL npc_tapoke_slim_jahnAI : public npc_escortAI
{
npc_tapoke_slim_jahnAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
bool m_bFriendSummoned;
+
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
m_bFriendSummoned = false;
}
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -53,34 +62,43 @@ struct TRINITY_DLL_DECL npc_tapoke_slim_jahnAI : public npc_escortAI
case 2:
if (m_creature->HasStealthAura())
m_creature->RemoveAurasDueToSpell(SPELL_AURA_MOD_STEALTH);
+
SetRun();
m_creature->setFaction(FACTION_ENEMY);
break;
}
}
+
void Aggro(Unit* pWho)
{
Player* pPlayer = GetPlayerForEscort();
+
if (HasEscortState(STATE_ESCORT_ESCORTING) && !m_bFriendSummoned && pPlayer)
{
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
m_creature->CastSpell(m_creature, SPELL_CALL_FRIENDS, true);
+
m_bFriendSummoned = true;
}
}
+
void JustSummoned(Creature* pSummoned)
{
if (Player* pPlayer = GetPlayerForEscort())
pSummoned->AI()->AttackStart(pPlayer);
}
+
void AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim())
return;
+
if (m_creature->IsFriendlyTo(pAttacker))
return;
+
AttackStart(pAttacker);
}
+
void DamageTaken(Unit* pDoneBy, uint32& uiDamage)
{
if (m_creature->GetHealth()*100 < m_creature->GetMaxHealth()*20)
@@ -89,47 +107,60 @@ struct TRINITY_DLL_DECL npc_tapoke_slim_jahnAI : public npc_escortAI
{
if (pPlayer->GetTypeId() == TYPEID_PLAYER)
CAST_PLR(pPlayer)->GroupEventHappens(QUEST_MISSING_DIPLO_PT11, m_creature);
+
uiDamage = 0;
+
me->RestoreFaction();
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
+
SetRun(false);
}
}
}
};
+
CreatureAI* GetAI_npc_tapoke_slim_jahn(Creature* pCreature)
{
return new npc_tapoke_slim_jahnAI(pCreature);
}
+
/*######
## npc_mikhail
######*/
+
bool QuestAccept_npc_mikhail(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_MISSING_DIPLO_PT11)
{
Creature* pSlim = pCreature->FindNearestCreature(NPC_TAPOKE_SLIM_JAHN, 25.0f);
+
if (!pSlim)
return false;
+
if (!pSlim->HasStealthAura())
pSlim->CastSpell(pSlim, SPELL_STEALTH, true);
+
if (npc_tapoke_slim_jahnAI* pEscortAI = CAST_AI(npc_tapoke_slim_jahnAI, pSlim->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return false;
}
+
/*######
## AddSC
######*/
+
void AddSC_wetlands()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_tapoke_slim_jahn";
newscript->GetAI = &GetAI_npc_tapoke_slim_jahn;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_mikhail";
newscript->pQuestAccept = &QuestAccept_npc_mikhail;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp
index 2d2222fcbbd..8ac1d4166e6 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_akilzon.cpp
@@ -13,16 +13,20 @@
* along 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_Akilzon
SD%Complete: 75%
SDComment: Missing timer for Call Lightning and Sound ID's
SQLUpdate:
#Temporary fix for Soaring Eagles
+
EndScriptData */
+
#include "precompiled.h"
#include "def_zulaman.h"
#include "Weather.h"
+
#define SPELL_STATIC_DISRUPTION 43622
#define SPELL_STATIC_VISUAL 45265
#define SPELL_CALL_LIGHTNING 43661 //Missing timer
@@ -32,6 +36,7 @@ EndScriptData */
#define SPELL_ELECTRICAL_DAMAGE 43657
#define SPELL_ELECTRICAL_OVERLOAD 43658
#define SPELL_EAGLE_SWOOP 44732
+
//"Your death gonna be quick, strangers. You shoulda never have come to this place..."
#define SAY_ONAGGRO "I be da predator! You da prey..."
#define SAY_ONDEATH "You can't... kill... me spirit!"
@@ -45,11 +50,13 @@ EndScriptData */
#define SOUND_ONSLAY2 12018
#define SOUND_ONSUMMON 12014
#define SOUND_ONENRAGE 12016
+
#define MOB_SOARING_EAGLE 24858
#define SE_LOC_X_MAX 400
#define SE_LOC_X_MIN 335
#define SE_LOC_Y_MAX 1435
#define SE_LOC_Y_MIN 1370
+
struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
{
boss_akilzonAI(Creature *c) : ScriptedAI(c)
@@ -60,40 +67,51 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
pInstance = c->GetInstanceData();
}
ScriptedInstance *pInstance;
+
uint64 BirdGUIDs[8];
uint64 TargetGUID;
uint64 CycloneGUID;
uint64 CloudGUID;
+
uint32 StaticDisruption_Timer;
uint32 GustOfWind_Timer;
uint32 CallLighting_Timer;
uint32 ElectricalStorm_Timer;
uint32 SummonEagles_Timer;
uint32 Enrage_Timer;
+
uint32 StormCount;
uint32 StormSequenceTimer;
+
bool isRaining;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_AKILZONEVENT, NOT_STARTED);
+
StaticDisruption_Timer = (10+rand()%10)*1000; //10 to 20 seconds (bosskillers)
GustOfWind_Timer = (20+rand()%10)*1000; //20 to 30 seconds(bosskillers)
CallLighting_Timer = (10+rand()%10)*1000; //totaly random timer. can't find any info on this
ElectricalStorm_Timer = 60*1000; //60 seconds(bosskillers)
Enrage_Timer = 10*60*1000; //10 minutes till enrage(bosskillers)
SummonEagles_Timer = 99999;
+
TargetGUID = 0;
CloudGUID = 0;
CycloneGUID = 0;
DespawnSummons();
- for (uint8 i = 0; i < 8; ++i)
+ for(uint8 i = 0; i < 8; ++i)
BirdGUIDs[i] = 0;
+
StormCount = 0;
StormSequenceTimer = 0;
+
isRaining = false;
+
SetWeather(WEATHER_STATE_FINE, 0.0f);
}
+
void EnterCombat(Unit *who)
{
m_creature->MonsterYell(SAY_ONAGGRO, LANG_UNIVERSAL, NULL);
@@ -102,6 +120,7 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_AKILZONEVENT, IN_PROGRESS);
}
+
void JustDied(Unit* Killer)
{
m_creature->MonsterYell(SAY_ONDEATH,LANG_UNIVERSAL,NULL);
@@ -110,6 +129,7 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
pInstance->SetData(DATA_AKILZONEVENT, DONE);
DespawnSummons();
}
+
void KilledUnit(Unit* victim)
{
switch(rand()%2)
@@ -124,6 +144,7 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
break;
}
}
+
void DespawnSummons()
{
for (uint8 i = 0; i < 8; ++i)
@@ -136,38 +157,47 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
}
}
}
+
void SetWeather(uint32 weather, float grade)
{
Map* pMap = m_creature->GetMap();
if (!pMap->IsDungeon()) return;
+
WorldPacket data(SMSG_WEATHER, (4+4+4));
data << uint32(weather) << (float)grade << uint8(0);
+
pMap->SendToPlayers(&data);
}
+
void HandleStormSequence(Unit *Cloud) // 1: begin, 2-9: tick, 10: end
{
if (StormCount < 10 && StormCount > 1)
{
// deal damage
int32 bp0 = 800;
- for (uint8 i = 2; i < StormCount; ++i)
+ for(uint8 i = 2; i < StormCount; ++i)
bp0 *= 2;
+
CellPair p(Trinity::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
std::list<Unit *> tempUnitMap;
+
{
Trinity::AnyAoETargetUnitInObjectRangeCheck u_check(m_creature, m_creature, 999);
Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck> searcher(m_creature, tempUnitMap, u_check);
+
TypeContainerVisitor<Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
TypeContainerVisitor<Trinity::UnitListSearcher<Trinity::AnyAoETargetUnitInObjectRangeCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, world_unit_searcher, *(m_creature->GetMap()));
cell_lock->Visit(cell_lock, grid_unit_searcher, *(m_creature->GetMap()));
}
//dealdamege
- for (std::list<Unit*>::iterator i = tempUnitMap.begin(); i != tempUnitMap.end(); ++i)
+ for(std::list<Unit*>::iterator i = tempUnitMap.begin(); i != tempUnitMap.end(); ++i)
{
if (!Cloud->IsWithinDist(*i, 6, false))
{
@@ -177,7 +207,7 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
// visual
float x,y,z;
z = m_creature->GetPositionZ();
- for (uint8 i = 0; i < 5+rand()%5; ++i)
+ for(uint8 i = 0; i < 5+rand()%5; ++i)
{
x = 343+rand()%60;
y = 1380+rand()%60;
@@ -206,10 +236,12 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
}
StormSequenceTimer = 1000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (StormCount)
{
Unit* target = Unit::GetUnit(*m_creature, CloudGUID);
@@ -220,17 +252,20 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
}
else if (Unit* Cyclone = Unit::GetUnit(*m_creature, CycloneGUID))
Cyclone->CastSpell(target, 25160, true); // keep casting or...
+
if (StormSequenceTimer < diff) {
HandleStormSequence(target);
}else StormSequenceTimer -= diff;
return;
}
+
if (Enrage_Timer < diff) {
m_creature->MonsterYell(SAY_ONENRAGE, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_ONENRAGE);
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
Enrage_Timer = 600000;
}else Enrage_Timer -= diff;
+
if (StaticDisruption_Timer < diff) {
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
if (!target) target = m_creature->getVictim();
@@ -238,23 +273,28 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
m_creature->CastSpell(target, SPELL_STATIC_DISRUPTION, false);
m_creature->SetInFront(m_creature->getVictim());
StaticDisruption_Timer = (10+rand()%8)*1000; // < 20s
+
/*if (float dist = m_creature->IsWithinDist3d(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 5.0f) dist = 5.0f;
SDisruptAOEVisual_Timer = 1000 + floor(dist / 30 * 1000.0f);*/
}else StaticDisruption_Timer -= diff;
+
if (GustOfWind_Timer < diff) {
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
if (!target) target = m_creature->getVictim();
DoCast(target, SPELL_GUST_OF_WIND);
GustOfWind_Timer = (20+rand()%10)*1000; //20 to 30 seconds(bosskillers)
} else GustOfWind_Timer -= diff;
+
if (CallLighting_Timer < diff) {
DoCast(m_creature->getVictim(), SPELL_CALL_LIGHTNING);
CallLighting_Timer = (12 + rand()%5)*1000; //totaly random timer. can't find any info on this
} else CallLighting_Timer -= diff;
+
if (!isRaining && ElectricalStorm_Timer < 8000 + rand()%5000) {
SetWeather(WEATHER_STATE_HEAVY_RAIN, 0.9999f);
isRaining = true;
}
+
if (ElectricalStorm_Timer < diff) {
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 50, true);
if (!target)
@@ -287,16 +327,19 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
StormCount = 1;
StormSequenceTimer = 0;
} else ElectricalStorm_Timer -= diff;
+
if (SummonEagles_Timer < diff)
{
m_creature->MonsterYell(SAY_ONSUMMON, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_ONSUMMON);
+
float x, y, z;
m_creature->GetPosition(x, y, z);
+
for (uint8 i = 0; i < 8; ++i)
{
Unit* bird = Unit::GetUnit(*m_creature,BirdGUIDs[i]);
- if (!bird) //they despawned on die
+ if (!bird)//they despawned on die
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
{
@@ -316,15 +359,19 @@ struct TRINITY_DLL_DECL boss_akilzonAI : public ScriptedAI
}
SummonEagles_Timer = 999999;
} else SummonEagles_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_soaring_eagleAI : public ScriptedAI
{
mob_soaring_eagleAI(Creature *c) : ScriptedAI(c) {}
+
uint32 EagleSwoop_Timer;
bool arrived;
uint32 TargetGUID;
+
void Reset()
{
EagleSwoop_Timer = 5000 + rand()%5000;
@@ -332,8 +379,11 @@ struct TRINITY_DLL_DECL mob_soaring_eagleAI : public ScriptedAI
TargetGUID = 0;
m_creature->SetUnitMovementFlags(MOVEMENTFLAG_LEVITATING);
}
+
void EnterCombat(Unit *who) {DoZoneInCombat();}
+
void MoveInLineOfSight(Unit* who) {}
+
void MovementInform(uint32, uint32)
{
arrived = true;
@@ -346,10 +396,12 @@ struct TRINITY_DLL_DECL mob_soaring_eagleAI : public ScriptedAI
EagleSwoop_Timer = 5000 + rand()%5000;
}
}
+
void UpdateAI(const uint32 diff)
{
if (EagleSwoop_Timer < diff) EagleSwoop_Timer = 0;
else EagleSwoop_Timer -= diff;
+
if (arrived)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -375,22 +427,27 @@ struct TRINITY_DLL_DECL mob_soaring_eagleAI : public ScriptedAI
}
}
};
+
//Soaring Eagle
CreatureAI* GetAI_mob_soaring_eagle(Creature* pCreature)
{
return new mob_soaring_eagleAI(pCreature);
}
+
CreatureAI* GetAI_boss_akilzon(Creature* pCreature)
{
return new boss_akilzonAI(pCreature);
}
+
void AddSC_boss_akilzon()
{
Script *newscript = NULL;
+
newscript = new Script;
newscript->Name = "boss_akilzon";
newscript->GetAI = &GetAI_boss_akilzon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_akilzon_eagle";
newscript->GetAI = &GetAI_mob_soaring_eagle;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp
index ff1a26e548c..2cced564afb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_halazzi.cpp
@@ -13,15 +13,18 @@
* along 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_Halazzi
SD%Complete: 80
SDComment:
SDCategory: Zul'Aman
EndScriptData */
+
#include "precompiled.h"
#include "def_zulaman.h"
//#include "spell.h"
+
#define YELL_AGGRO "Get on your knees and bow to da fang and claw!"
#define SOUND_AGGRO 12020
#define YELL_SABER_ONE "You gonna leave in pieces!"
@@ -38,6 +41,7 @@ EndScriptData */
#define SOUND_DEATH 12028
#define YELL_BERSERK "Whatch you be doing? Pissin' yourselves..."
#define SOUND_BERSERK 12025
+
#define SPELL_DUAL_WIELD 29651
#define SPELL_SABER_LASH 43267
#define SPELL_FRENZY 43139
@@ -49,11 +53,14 @@ EndScriptData */
#define SPELL_SUMMON_LYNX 43143
#define SPELL_SUMMON_TOTEM 43302
#define SPELL_BERSERK 45078
+
#define MOB_SPIRIT_LYNX 24143
#define SPELL_LYNX_FRENZY 43290
#define SPELL_SHRED_ARMOR 43243
+
#define MOB_TOTEM 24224
#define SPELL_LIGHTNING 43301
+
enum PhaseHalazzi
{
PHASE_NONE = 0,
@@ -63,6 +70,7 @@ enum PhaseHalazzi
PHASE_MERGE = 4,
PHASE_ENRAGE = 5
};
+
struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
{
boss_halazziAI(Creature *c) : ScriptedAI(c)
@@ -73,55 +81,72 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
if (TempSpell && TempSpell->CastingTimeIndex != 5)
TempSpell->CastingTimeIndex = 5; // 2000 ms casting time
}
+
ScriptedInstance *pInstance;
+
uint32 FrenzyTimer;
uint32 SaberlashTimer;
uint32 ShockTimer;
uint32 TotemTimer;
uint32 CheckTimer;
uint32 BerserkTimer;
+
uint32 TransformCount;
+
PhaseHalazzi Phase;
+
uint64 LynxGUID;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_HALAZZIEVENT, NOT_STARTED);
+
TransformCount = 0;
BerserkTimer = 600000;
CheckTimer = 1000;
+
DoCast(m_creature, SPELL_DUAL_WIELD, true);
+
Phase = PHASE_NONE;
EnterPhase(PHASE_LYNX);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_HALAZZIEVENT, IN_PROGRESS);
+
m_creature->MonsterYell(YELL_AGGRO, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_AGGRO);
+
EnterPhase(PHASE_LYNX);
}
+
void JustSummoned(Creature* summon)
{
summon->AI()->AttackStart(m_creature->getVictim());
if (summon->GetEntry() == MOB_SPIRIT_LYNX)
LynxGUID = summon->GetGUID();
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= m_creature->GetHealth() && Phase != PHASE_ENRAGE)
damage = 0;
}
+
void SpellHit(Unit*, const SpellEntry *spell)
{
if (spell->Id == SPELL_TRANSFORM_SPLIT2)
EnterPhase(PHASE_HUMAN);
}
+
void AttackStart(Unit *who)
{
if (Phase != PHASE_MERGE) ScriptedAI::AttackStart(who);
}
+
void EnterPhase(PhaseHalazzi NextPhase)
{
switch(NextPhase)
@@ -176,10 +201,12 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
}
Phase = NextPhase;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (BerserkTimer < diff)
{
m_creature->MonsterYell(YELL_BERSERK, LANG_UNIVERSAL, NULL);
@@ -187,6 +214,7 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
DoCast(m_creature, SPELL_BERSERK, true);
BerserkTimer = 60000;
}else BerserkTimer -= diff;
+
if (Phase == PHASE_LYNX || Phase == PHASE_ENRAGE)
{
if (SaberlashTimer < diff)
@@ -197,11 +225,13 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
//m_creature->RemoveAurasDueToSpell(41296);
SaberlashTimer = 30000;
}else SaberlashTimer -= diff;
+
if (FrenzyTimer < diff)
{
DoCast(m_creature, SPELL_FRENZY);
FrenzyTimer = (10+rand()%5)*1000;
}else FrenzyTimer -= diff;
+
if (Phase == PHASE_LYNX)
if (CheckTimer < diff)
{
@@ -210,6 +240,7 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
CheckTimer = 1000;
}else CheckTimer -= diff;
}
+
if (Phase == PHASE_HUMAN || Phase == PHASE_ENRAGE)
{
if (TotemTimer < diff)
@@ -217,6 +248,7 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
DoCast(m_creature, SPELL_SUMMON_TOTEM);
TotemTimer = 20000;
}else TotemTimer -= diff;
+
if (ShockTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
@@ -228,6 +260,7 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
ShockTimer = 10000 + rand()%5000;
}
}else ShockTimer -= diff;
+
if (Phase == PHASE_HUMAN)
if (CheckTimer < diff)
{
@@ -242,6 +275,7 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
CheckTimer = 1000;
}else CheckTimer -= diff;
}
+
if (Phase == PHASE_MERGE)
{
if (CheckTimer < diff)
@@ -262,8 +296,10 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
CheckTimer = 1000;
}else CheckTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
void KilledUnit(Unit* victim)
{
switch(rand()%2)
@@ -272,67 +308,85 @@ struct TRINITY_DLL_DECL boss_halazziAI : public ScriptedAI
m_creature->MonsterYell(YELL_KILL_ONE, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_KILL_ONE);
break;
+
case 1:
m_creature->MonsterYell(YELL_KILL_TWO, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_KILL_TWO);
break;
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(DATA_HALAZZIEVENT, DONE);
+
m_creature->MonsterYell(YELL_DEATH, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_DEATH);
}
};
+
// Spirits Lynx AI
+
struct TRINITY_DLL_DECL boss_spiritlynxAI : public ScriptedAI
{
boss_spiritlynxAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FrenzyTimer;
uint32 shredder_timer;
+
void Reset()
{
FrenzyTimer = (30+rand()%20)*1000; //frenzy every 30-50 seconds
shredder_timer = 4000;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= m_creature->GetHealth())
damage = 0;
}
+
void AttackStart(Unit *who)
{
if (!m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
ScriptedAI::AttackStart(who);
}
+
void EnterCombat(Unit *who) {/*DoZoneInCombat();*/}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (FrenzyTimer < diff)
{
DoCast(m_creature, SPELL_LYNX_FRENZY);
FrenzyTimer = (30+rand()%20)*1000;
}else FrenzyTimer -= diff;
+
if (shredder_timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHRED_ARMOR);
shredder_timer = 4000;
}else shredder_timer -= diff;
+
DoMeleeAttackIfReady();
}
+
};
+
CreatureAI* GetAI_boss_halazziAI(Creature* pCreature)
{
return new boss_halazziAI (pCreature);
}
+
CreatureAI* GetAI_boss_spiritlynxAI(Creature* pCreature)
{
return new boss_spiritlynxAI (pCreature);
}
+
void AddSC_boss_halazzi()
{
Script *newscript;
@@ -340,6 +394,7 @@ void AddSC_boss_halazzi()
newscript->Name = "boss_halazzi";
newscript->GetAI = &GetAI_boss_halazziAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_halazzi_lynx";
newscript->GetAI = &GetAI_boss_spiritlynxAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp
index 202634abbd1..d2933de614f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_hexlord.cpp
@@ -13,14 +13,17 @@
* along 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_Hex_Lord_Malacrass
SD%Complete:
SDComment:
SDCategory: Zul'Aman
EndScriptData */
+
#include "precompiled.h"
#include "def_zulaman.h"
+
#define YELL_AGGRO "Da shadow gonna fall on you... "
#define SOUND_YELL_AGGRO 12041
#define YELL_SPIRIT_BOLTS "Your soul gonna bleed!"
@@ -33,28 +36,36 @@ EndScriptData */
#define SOUND_YELL_KILL_TWO 12044
#define YELL_DEATH "Dis not... da end of me..."
#define SOUND_YELL_DEATH 12051
+
#define SPELL_SPIRIT_BOLTS 43383
#define SPELL_DRAIN_POWER 44131
#define SPELL_SIPHON_SOUL 43501
+
#define MOB_TEMP_TRIGGER 23920
+
//Defines for various powers he uses after using soul drain
+
//Druid
#define SPELL_DR_LIFEBLOOM 43421
#define SPELL_DR_THORNS 43420
#define SPELL_DR_MOONFIRE 43545
+
//Hunter
#define SPELL_HU_EXPLOSIVE_TRAP 43444
#define SPELL_HU_FREEZING_TRAP 43447
#define SPELL_HU_SNAKE_TRAP 43449
+
//Mage
#define SPELL_MG_FIREBALL 41383
#define SPELL_MG_FROSTBOLT 43428
#define SPELL_MG_FROST_NOVA 43426
#define SPELL_MG_ICE_LANCE 43427
+
//Paladin
#define SPELL_PA_CONSECRATION 43429
#define SPELL_PA_HOLY_LIGHT 43451
#define SPELL_PA_AVENGING_WRATH 43430
+
//Priest
#define SPELL_PR_HEAL 41372
#define SPELL_PR_MIND_CONTROL 43550
@@ -62,26 +73,33 @@ EndScriptData */
#define SPELL_PR_SW_DEATH 41375
#define SPELL_PR_PSYCHIC_SCREAM 43432
#define SPELL_PR_PAIN_SUPP 44416
+
//Rogue
#define SPELL_RO_BLIND 43433
#define SPELL_RO_SLICE_DICE 43457
#define SPELL_RO_WOUND_POISON 39665
+
//Shaman
#define SPELL_SH_FIRE_NOVA 43436
#define SPELL_SH_HEALING_WAVE 43548
#define SPELL_SH_CHAIN_LIGHT 43435
+
//Warlock
#define SPELL_WL_CURSE_OF_DOOM 43439
#define SPELL_WL_RAIN_OF_FIRE 43440
#define SPELL_WL_UNSTABLE_AFFL 35183
+
//Warrior
#define SPELL_WR_SPELL_REFLECT 43443
#define SPELL_WR_WHIRLWIND 43442
#define SPELL_WR_MORTAL_STRIKE 43441
+
#define ORIENT 1.5696
#define POS_Y 921.2795
#define POS_Z 33.8883
+
static float Pos_X[4] = {112.8827, 107.8827, 122.8827, 127.8827};
+
static uint32 AddEntryList[8]=
{
24240, //Alyson Antille
@@ -93,6 +111,7 @@ static uint32 AddEntryList[8]=
24246, //Darkheart
24247 //Koragg
};
+
enum AbilityTarget
{
ABILITY_TARGET_SELF = 0,
@@ -102,12 +121,14 @@ enum AbilityTarget
ABILITY_TARGET_BUFF = 4,
ABILITY_TARGET_SPECIAL = 5
};
+
struct PlayerAbilityStruct
{
uint32 spell;
AbilityTarget target;
uint32 cooldown;
};
+
static PlayerAbilityStruct PlayerAbility[][3] =
{
// 1 warrior
@@ -151,15 +172,20 @@ static PlayerAbilityStruct PlayerAbility[][3] =
{SPELL_DR_THORNS, ABILITY_TARGET_SELF, 10000},
{SPELL_DR_MOONFIRE, ABILITY_TARGET_ENEMY, 8000}}
};
+
struct TRINITY_DLL_DECL boss_hexlord_addAI : public ScriptedAI
{
ScriptedInstance* pInstance;
+
boss_hexlord_addAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
void Reset() {}
+
void EnterCombat(Unit* who) {DoZoneInCombat();}
+
void UpdateAI(const uint32 diff)
{
if (pInstance && pInstance->GetData(DATA_HEXLORDEVENT) != IN_PROGRESS)
@@ -167,52 +193,67 @@ struct TRINITY_DLL_DECL boss_hexlord_addAI : public ScriptedAI
EnterEvadeMode();
return;
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
{
boss_hex_lord_malacrassAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
SelectAddEntry();
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
AddGUID[i] = 0;
}
+
ScriptedInstance *pInstance;
+
uint64 AddGUID[4];
uint32 AddEntry[4];
+
uint64 PlayerGUID;
+
uint32 SpiritBolts_Timer;
uint32 DrainPower_Timer;
uint32 SiphonSoul_Timer;
uint32 PlayerAbility_Timer;
uint32 CheckAddState_Timer;
uint32 ResetTimer;
+
uint32 PlayerClass;
+
Unit* SoulDrainTarget;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_HEXLORDEVENT, NOT_STARTED);
+
SpiritBolts_Timer = 20000;
DrainPower_Timer = 60000;
SiphonSoul_Timer = 100000;
PlayerAbility_Timer = 99999;
CheckAddState_Timer = 5000;
ResetTimer = 5000;
+
SpawnAdds();
+
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, 46916);
m_creature->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
pInstance->SetData(DATA_HEXLORDEVENT, IN_PROGRESS);
+
DoZoneInCombat();
m_creature->MonsterYell(YELL_AGGRO, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_YELL_AGGRO);
- for (uint8 i = 0; i < 4; ++i)
+
+ for(uint8 i = 0; i < 4; ++i)
{
Unit* Temp = Unit::GetUnit((*m_creature),AddGUID[i]);
if (Temp && Temp->isAlive())
@@ -224,6 +265,7 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit* victim)
{
switch(rand()%2)
@@ -238,33 +280,41 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
break;
}
}
+
void JustDied(Unit* victim)
{
if (pInstance)
pInstance->SetData(DATA_HEXLORDEVENT, DONE);
+
m_creature->MonsterYell(YELL_DEATH, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_YELL_DEATH);
- for (uint8 i = 0; i < 4 ; ++i)
+
+ for(uint8 i = 0; i < 4 ; ++i)
{
Unit* Temp = Unit::GetUnit((*m_creature),AddGUID[i]);
if (Temp && Temp->isAlive())
Temp->DealDamage(Temp, Temp->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
}
+
void SelectAddEntry()
{
std::vector<uint32> AddList;
- for (uint8 i = 0; i < 8; ++i)
+
+ for(uint8 i = 0; i < 8; ++i)
AddList.push_back(AddEntryList[i]);
+
while(AddList.size() > 4)
AddList.erase(AddList.begin()+rand()%AddList.size());
+
uint8 i = 0;
- for (std::vector<uint32>::iterator itr = AddList.begin(); itr != AddList.end(); ++itr, ++i)
+ for(std::vector<uint32>::iterator itr = AddList.begin(); itr != AddList.end(); ++itr, ++i)
AddEntry[i] = *itr;
}
+
void SpawnAdds()
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Creature *pCreature = (Unit::GetCreature((*m_creature), AddGUID[i]));
if (!pCreature || !pCreature->isAlive())
@@ -281,10 +331,12 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (ResetTimer < diff)
{
if (m_creature->IsWithinDist3d(119.223,1035.45,29.4481, 10))
@@ -294,9 +346,10 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
}
ResetTimer = 5000;
}else ResetTimer -= diff;
+
if (CheckAddState_Timer < diff)
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Unit* Temp = Unit::GetUnit((*m_creature),AddGUID[i]);
if (Temp && Temp->isAlive() && !Temp->getVictim())
@@ -304,6 +357,7 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
}
CheckAddState_Timer = 5000;
}else CheckAddState_Timer -= diff;
+
if (DrainPower_Timer < diff)
{
m_creature->CastSpell(m_creature, SPELL_DRAIN_POWER, true);
@@ -311,6 +365,7 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
DoPlaySoundToSet(m_creature, SOUND_YELL_DRAIN_POWER);
DrainPower_Timer = 40000 + rand()%15000; // must cast in 60 sec, or buff/debuff will disappear
}else DrainPower_Timer -= diff;
+
if (SpiritBolts_Timer < diff)
{
if (DrainPower_Timer < 12000) // channel 10 sec
@@ -325,6 +380,7 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
PlayerAbility_Timer = 99999;
}
}else SpiritBolts_Timer -= diff;
+
if (SiphonSoul_Timer < diff)
{
Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 70, true);
@@ -340,9 +396,11 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
trigger->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
trigger->CastSpell(target, SPELL_SIPHON_SOUL, true);
trigger->GetMotionMaster()->MoveChase(m_creature);
+
//m_creature->CastSpell(target, SPELL_SIPHON_SOUL, true);
//m_creature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, target->GetGUID());
//m_creature->SetUInt32Value(UNIT_CHANNEL_SPELL, SPELL_SIPHON_SOUL);
+
PlayerGUID = target->GetGUID();
PlayerAbility_Timer = 8000 + rand()%2000;
PlayerClass = target->getClass() - 1;
@@ -351,6 +409,7 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
SiphonSoul_Timer = 99999; // buff lasts 30 sec
}
}else SiphonSoul_Timer -= diff;
+
if (PlayerAbility_Timer < diff)
{
//Unit* target = Unit::GetUnit(*m_creature, PlayerGUID);
@@ -360,8 +419,10 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
PlayerAbility_Timer = 8000 + rand()%2000;
}
}else PlayerAbility_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
void UseAbility()
{
uint32 random = rand()%3;
@@ -392,23 +453,31 @@ struct TRINITY_DLL_DECL boss_hex_lord_malacrassAI : public ScriptedAI
m_creature->CastSpell(target, PlayerAbility[PlayerClass][random].spell, false);
}
};
+
#define SPELL_BLOODLUST 43578
#define SPELL_CLEAVE 15496
+
struct TRINITY_DLL_DECL boss_thurgAI : public boss_hexlord_addAI
{
+
boss_thurgAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 bloodlust_timer;
uint32 cleave_timer;
+
void Reset()
{
bloodlust_timer = 15000;
cleave_timer = 10000;
+
boss_hexlord_addAI::Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (bloodlust_timer < diff)
{
std::list<Creature*> templist = DoFindFriendlyMissingBuff(50, SPELL_BLOODLUST);
@@ -419,33 +488,43 @@ struct TRINITY_DLL_DECL boss_thurgAI : public boss_hexlord_addAI
}
bloodlust_timer = 12000;
}else bloodlust_timer -= diff;
+
if (cleave_timer < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_CLEAVE, false);
cleave_timer = 12000; //3 sec cast
}else cleave_timer -= diff;
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
#define SPELL_FLASH_HEAL 43575
#define SPELL_DISPEL_MAGIC 43577
+
struct TRINITY_DLL_DECL boss_alyson_antilleAI : public boss_hexlord_addAI
{
//Holy Priest
boss_alyson_antilleAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 flashheal_timer;
uint32 dispelmagic_timer;
+
void Reset()
{
flashheal_timer = 2500;
dispelmagic_timer = 10000;
+
//AcquireGUID();
+
boss_hexlord_addAI::Reset();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (who->isTargetableForAttack())
{
if (m_creature->Attack(who, false))
@@ -455,10 +534,12 @@ struct TRINITY_DLL_DECL boss_alyson_antilleAI : public boss_hexlord_addAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (flashheal_timer < diff)
{
Unit* target = DoSelectLowestHpFriendly(99, 30000);
@@ -485,34 +566,44 @@ struct TRINITY_DLL_DECL boss_alyson_antilleAI : public boss_hexlord_addAI
}
flashheal_timer = 2500;
}else flashheal_timer -= diff;
+
/*if (dispelmagic_timer < diff)
{
if (rand()%2)
{
Unit* target = SelectTarget();
+
m_creature->CastSpell(target, SPELL_DISPEL_MAGIC, false);
}
else
m_creature->CastSpell(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DISPEL_MAGIC, false);
+
dispelmagic_timer = 12000;
}else dispelmagic_timer -= diff;*/
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
#define SPELL_FIREBOLT 43584
+
struct TRINITY_DLL_DECL boss_gazakrothAI : public boss_hexlord_addAI
{
boss_gazakrothAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 firebolt_timer;
+
void Reset()
{
firebolt_timer = 2000;
boss_hexlord_addAI::Reset();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (who->isTargetableForAttack())
{
if (m_creature->Attack(who, false))
@@ -522,85 +613,111 @@ struct TRINITY_DLL_DECL boss_gazakrothAI : public boss_hexlord_addAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (firebolt_timer < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_FIREBOLT, false);
firebolt_timer = 700;
}else firebolt_timer -= diff;
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
#define SPELL_FLAME_BREATH 43582
#define SPELL_THUNDERCLAP 43583
+
struct TRINITY_DLL_DECL boss_lord_raadanAI : public boss_hexlord_addAI
{
boss_lord_raadanAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 flamebreath_timer;
uint32 thunderclap_timer;
+
void Reset()
{
flamebreath_timer = 8000;
thunderclap_timer = 13000;
+
boss_hexlord_addAI::Reset();
+
}
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (thunderclap_timer < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_THUNDERCLAP, false);
thunderclap_timer = 12000;
}else thunderclap_timer -= diff;
+
if (flamebreath_timer < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_FLAME_BREATH, false);
flamebreath_timer = 12000;
}else flamebreath_timer -= diff;
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
#define SPELL_PSYCHIC_WAIL 43590
+
struct TRINITY_DLL_DECL boss_darkheartAI : public boss_hexlord_addAI
{
boss_darkheartAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 psychicwail_timer;
+
void Reset()
{
psychicwail_timer = 8000;
+
boss_hexlord_addAI::Reset();
+
}
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (psychicwail_timer < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_PSYCHIC_WAIL, false);
psychicwail_timer = 12000;
}else psychicwail_timer -= diff;
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
#define SPELL_VENOM_SPIT 43579
+
struct TRINITY_DLL_DECL boss_slitherAI : public boss_hexlord_addAI
{
boss_slitherAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 venomspit_timer;
+
void Reset()
{
venomspit_timer = 5000;
boss_hexlord_addAI::Reset();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (who->isTargetableForAttack())
{
if (m_creature->Attack(who, false))
@@ -610,64 +727,80 @@ struct TRINITY_DLL_DECL boss_slitherAI : public boss_hexlord_addAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (venomspit_timer < diff)
{
if (Unit* victim = SelectUnit(SELECT_TARGET_RANDOM, 0))
m_creature->CastSpell(victim,SPELL_VENOM_SPIT, false);
venomspit_timer = 2500;
}else venomspit_timer -= diff;
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
//Fenstalker
#define SPELL_VOLATILE_INFECTION 43586
+
struct TRINITY_DLL_DECL boss_fenstalkerAI : public boss_hexlord_addAI
{
boss_fenstalkerAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 volatileinf_timer;
+
void Reset()
{
volatileinf_timer = 15000;
boss_hexlord_addAI::Reset();
+
}
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (volatileinf_timer < diff)
{
// core bug
m_creature->getVictim()->CastSpell(m_creature->getVictim(),SPELL_VOLATILE_INFECTION, false);
volatileinf_timer = 12000;
}else volatileinf_timer -= diff;
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
//Koragg
#define SPELL_COLD_STARE 43593
#define SPELL_MIGHTY_BLOW 43592
+
struct TRINITY_DLL_DECL boss_koraggAI : public boss_hexlord_addAI
{
boss_koraggAI(Creature *c) : boss_hexlord_addAI(c) {}
+
uint32 coldstare_timer;
uint32 mightyblow_timer;
+
void Reset()
{
coldstare_timer = 15000;
mightyblow_timer = 10000;
boss_hexlord_addAI::Reset();
+
}
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (mightyblow_timer < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_MIGHTY_BLOW, false);
@@ -679,41 +812,51 @@ struct TRINITY_DLL_DECL boss_koraggAI : public boss_hexlord_addAI
m_creature->CastSpell(victim,SPELL_COLD_STARE, false);
coldstare_timer = 12000;
}
+
boss_hexlord_addAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_boss_hex_lord_malacrass(Creature* pCreature)
{
return new boss_hex_lord_malacrassAI (pCreature);
}
+
CreatureAI* GetAI_boss_thurg(Creature* pCreature)
{
return new boss_thurgAI (pCreature);
}
+
CreatureAI* GetAI_boss_alyson_antille(Creature* pCreature)
{
return new boss_alyson_antilleAI (pCreature);
}
+
CreatureAI* GetAI_boss_gazakroth(Creature* pCreature)
{
return new boss_gazakrothAI (pCreature);
}
+
CreatureAI* GetAI_boss_lord_raadan(Creature* pCreature)
{
return new boss_lord_raadanAI (pCreature);
}
+
CreatureAI* GetAI_boss_darkheart(Creature* pCreature)
{
return new boss_darkheartAI (pCreature);
}
+
CreatureAI* GetAI_boss_slither(Creature* pCreature)
{
return new boss_slitherAI (pCreature);
}
+
CreatureAI* GetAI_boss_fenstalker(Creature* pCreature)
{
return new boss_fenstalkerAI (pCreature);
}
+
CreatureAI* GetAI_boss_koragg(Creature* pCreature)
{
return new boss_koraggAI (pCreature);
@@ -725,34 +868,42 @@ void AddSC_boss_hex_lord_malacrass()
newscript->Name = "boss_hexlord_malacrass";
newscript->GetAI = &GetAI_boss_hex_lord_malacrass;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_thurg";
newscript->GetAI = &GetAI_boss_thurg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_gazakroth";
newscript->GetAI = &GetAI_boss_gazakroth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lord_raadan";
newscript->GetAI = &GetAI_boss_lord_raadan;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_darkheart";
newscript->GetAI = &GetAI_boss_darkheart;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_slither";
newscript->GetAI = &GetAI_boss_slither;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_fenstalker";
newscript->GetAI = &GetAI_boss_fenstalker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_koragg";
newscript->GetAI = &GetAI_boss_koragg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_alyson_antille";
newscript->GetAI = &GetAI_boss_alyson_antille;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp
index b5ae692bc8f..d946b26ffdf 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_janalai.cpp
@@ -13,15 +13,18 @@
* along 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"
#include "GridNotifiers.h"
+
enum eEnums
{
SAY_AGGRO = -1568000,
@@ -34,6 +37,7 @@ enum eEnums
SAY_DEATH = -1568007,
SAY_EVENT_STRANGERS = -1568008,
SAY_EVENT_FRIENDS = -1568009,
+
// Jan'alai
SPELL_FLAME_BREATH = 43140,
SPELL_FIRE_WALL = 43113,
@@ -42,27 +46,34 @@ enum eEnums
SPELL_TELE_TO_CENTER = 43098, // coord
SPELL_HATCH_ALL = 43144,
SPELL_BERSERK = 45078,
+
// -- Fire Bob Spells
SPELL_FIRE_BOMB_CHANNEL = 42621, // last forever
SPELL_FIRE_BOMB_THROW = 42628, // throw visual
SPELL_FIRE_BOMB_DUMMY = 42629, // bomb visual
SPELL_FIRE_BOMB_DAMAGE = 42630,
+
// --Summons
MOB_AMANI_HATCHER = 23818,
MOB_HATCHLING = 23598, // 42493
MOB_EGG = 23817,
MOB_FIRE_BOMB = 23920,
+
// -- Hatcher Spells
SPELL_HATCH_EGG = 43734, // 42471
+
// -- Hatchling Spells
SPELL_FLAMEBUFFET = 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},
@@ -70,6 +81,7 @@ float FireWallCoords[4][4] =
{-54.80, 1150.08, 19, 0},
{-33.93, 1175.68, 19, 1.5*3.1415}
};
+
float hatcherway[2][5][3] =
{
{
@@ -87,11 +99,13 @@ float hatcherway[2][5][3] =
{-34.29,1095.22,19}
}
};
+
struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
{
boss_janalaiAI(Creature *c) : ScriptedAI(c)
{
pInstance =c->GetInstanceData();
+
SpellEntry *TempSpell = GET_SPELL(SPELL_HATCH_EGG);
if (TempSpell && TempSpell->EffectImplicitTargetA[0] != 1)
{
@@ -99,53 +113,70 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
TempSpell->EffectImplicitTargetB[0] = 0;
}
}
+
ScriptedInstance *pInstance;
+
uint32 FireBreathTimer;
uint32 BombTimer;
uint32 BombSequenceTimer;
uint32 BombCount;
uint32 HatcherTimer;
uint32 EnrageTimer;
+
bool noeggs;
bool enraged;
bool isBombing;
+
bool isFlameBreathing;
+
uint64 FireBombGUIDs[40];
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_JANALAIEVENT, NOT_STARTED);
+
FireBreathTimer = 8000;
BombTimer = 30000;
BombSequenceTimer = 1000;
BombCount = 0;
HatcherTimer = 10000;
EnrageTimer = MINUTE*5*IN_MILISECONDS;
+
noeggs = false;
isBombing =false;
enraged = false;
+
isFlameBreathing = false;
- for (uint8 i = 0; i < 40; ++i)
+
+ for(uint8 i = 0; i < 40; ++i)
FireBombGUIDs[i] = 0;
+
HatchAllEggs(1);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_JANALAIEVENT, DONE);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_JANALAIEVENT, IN_PROGRESS);
+
DoScriptText(SAY_AGGRO, m_creature);
// DoZoneInCombat();
}
+
void DamageDeal(Unit* target, uint32 &damage)
{
if (isFlameBreathing)
@@ -154,17 +185,19 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
damage = 0;
}
}
+
void FireWall()
{
uint8 WallNum;
Creature* wall = NULL;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (i == 0 || i == 2)
WallNum = 3;
else
WallNum = 2;
- for (uint8 j = 0; j < WallNum; j++)
+
+ for(uint8 j = 0; j < WallNum; j++)
{
if (WallNum == 3)
wall = m_creature->SummonCreature(MOB_FIRE_BOMB, FireWallCoords[i][0],FireWallCoords[i][1]+5*(j-1),FireWallCoords[i][2],FireWallCoords[i][3],TEMPSUMMON_TIMED_DESPAWN,15000);
@@ -174,38 +207,47 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
}
}
}
+
void SpawnBombs()
{
float dx, dy;
- for (int i(0); i < 40; ++i)
+ 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, 15000);
if (bomb) FireBombGUIDs[i] = bomb->GetGUID();
}
BombCount = 0;
}
+
bool HatchAllEggs(uint32 uiAction) //1: reset, 2: isHatching all
{
std::list<Creature*> templist;
float x, y, z;
m_creature->GetPosition(x, y, z);
+
{
CellPair pair(Trinity::ComputeCellPair(x, y));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllCreaturesOfEntryInRange check(m_creature, MOB_EGG, 100);
Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(m_creature, templist, check);
+
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> cSearcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, cSearcher, *(m_creature->GetMap()));
}
+
//error_log("Eggs %d at middle", templist.size());
if (!templist.size())
return false;
- for (std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
+
+ for(std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
{
if (uiAction == 1)
(*i)->SetDisplayId(10056);
@@ -214,28 +256,34 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
}
return true;
}
+
void Boom()
{
std::list<Creature*> templist;
float x, y, z;
m_creature->GetPosition(x, y, z);
+
{
CellPair pair(Trinity::ComputeCellPair(x, y));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllCreaturesOfEntryInRange check(m_creature, MOB_FIRE_BOMB, 100);
Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(m_creature, templist, check);
+
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> cSearcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, cSearcher, *(m_creature->GetMap()));
}
- for (std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
+ for(std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
{
(*i)->CastSpell(*i, SPELL_FIRE_BOMB_DAMAGE, true);
(*i)->RemoveAllAuras();
}
}
+
void HandleBombSequence()
{
if (BombCount < 40)
@@ -264,6 +312,7 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
EnrageTimer -= 10000;
}
}
+
void UpdateAI(const uint32 diff)
{
if (isFlameBreathing)
@@ -273,6 +322,7 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
isFlameBreathing = false;
}else return;
}
+
if (isBombing)
{
if (BombSequenceTimer < diff)
@@ -281,11 +331,14 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
}else BombSequenceTimer -= diff;
return;
}
+
if (!UpdateVictim())
return;
+
//enrage if under 25% hp before 5 min.
if (!enraged && m_creature->GetHealth() * 4 < m_creature->GetMaxHealth())
EnrageTimer = 0;
+
if (EnrageTimer < diff)
{
if (!enraged)
@@ -301,9 +354,11 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
EnrageTimer = 300000;
}
}else EnrageTimer -= diff;
+
if (BombTimer < diff)
{
DoScriptText(SAY_FIRE_BOMBS, m_creature);
+
m_creature->AttackStop();
m_creature->GetMotionMaster()->Clear();
DoTeleportTo(JanalainPos[0][0],JanalainPos[0][1],JanalainPos[0][2]);
@@ -311,15 +366,17 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
m_creature->CastSpell(m_creature, SPELL_FIRE_BOMB_CHANNEL, false);
//DoTeleportPlayer(m_creature, JanalainPos[0][0], JanalainPos[0][1],JanalainPos[0][2], 0);
//m_creature->CastSpell(m_creature, SPELL_TELE_TO_CENTER, true);
+
FireWall();
SpawnBombs();
isBombing = true;
BombSequenceTimer = 100;
+
//Teleport every Player into the middle
Map* pMap = m_creature->GetMap();
if (!pMap->IsDungeon()) return;
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (Player* i_pl = i->getSource())
if (i_pl->isAlive())
@@ -328,11 +385,13 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
//m_creature->CastSpell(Temp, SPELL_SUMMON_PLAYERS, true); // core bug, spell does not work if too far
return;
}else BombTimer -= diff;
+
if (!noeggs)
{
if (100 * m_creature->GetHealth() < 35 * m_creature->GetMaxHealth())
{
DoScriptText(SAY_ALL_EGGS, m_creature);
+
m_creature->AttackStop();
m_creature->GetMotionMaster()->Clear();
DoTeleportTo(JanalainPos[0][0],JanalainPos[0][1],JanalainPos[0][2]);
@@ -354,8 +413,11 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
noeggs = true;
}else HatcherTimer -= diff;
}
+
EnterEvadeIfOutOfCombatArea(diff);
+
DoMeleeAttackIfReady();
+
if (FireBreathTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
@@ -370,41 +432,55 @@ struct TRINITY_DLL_DECL boss_janalaiAI : public ScriptedAI
}else FireBreathTimer -= diff;
}
};
+
CreatureAI* GetAI_boss_janalaiAI(Creature* pCreature)
{
return new boss_janalaiAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_janalai_firebombAI : public ScriptedAI
{
mob_janalai_firebombAI(Creature *c) : ScriptedAI(c){}
+
void Reset() {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_FIRE_BOMB_THROW)
m_creature->CastSpell(m_creature, SPELL_FIRE_BOMB_DUMMY, true);
}
+
void EnterCombat(Unit* who) {}
+
void AttackStart(Unit* who) {}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff) {}
};
+
CreatureAI* GetAI_mob_janalai_firebombAI(Creature* pCreature)
{
return new mob_janalai_firebombAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI
{
mob_amanishi_hatcherAI(Creature *c) : ScriptedAI(c)
{
pInstance =c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 waypoint;
uint32 HatchNum;
uint32 WaitTimer;
+
bool side;
bool hasChangedSide;
bool isHatching;
+
void Reset()
{
side =(m_creature->GetPositionY() < 1150);
@@ -414,24 +490,31 @@ struct TRINITY_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI
WaitTimer = 1;
HatchNum = 0;
}
+
bool HatchEggs(uint32 num)
{
std::list<Creature*> templist;
float x, y, z;
m_creature->GetPosition(x, y, z);
+
{
CellPair pair(Trinity::ComputeCellPair(x, y));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllCreaturesOfEntryInRange check(m_creature, 23817, 50);
Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(m_creature, templist, check);
+
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> cSearcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, cSearcher, *(m_creature->GetMap()));
}
+
//error_log("Eggs %d at %d", templist.size(), side);
- for (std::list<Creature*>::iterator i = templist.begin(); i != templist.end() && num > 0; ++i)
+
+ for(std::list<Creature*>::iterator i = templist.begin(); i != templist.end() && num > 0; ++i)
{
if ((*i)->GetDisplayId() != 11686)
{
@@ -439,11 +522,13 @@ struct TRINITY_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI
num--;
}
}
+
if (num)
return false; // no more templist
else
return true;
}
+
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
@@ -458,6 +543,7 @@ struct TRINITY_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI
else
WaitTimer = 1;
}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance || !(pInstance->GetData(DATA_JANALAIEVENT) == IN_PROGRESS))
@@ -466,6 +552,7 @@ struct TRINITY_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI
m_creature->setDeathState(JUST_DIED);
return;
}
+
if (!isHatching)
{
if (WaitTimer)
@@ -502,18 +589,22 @@ struct TRINITY_DLL_DECL mob_amanishi_hatcherAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mob_amanishi_hatcherAI(Creature* pCreature)
{
return new mob_amanishi_hatcherAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_hatchlingAI : public ScriptedAI
{
mob_hatchlingAI(Creature *c) : ScriptedAI(c)
{
pInstance =c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 BuffetTimer;
+
void Reset()
{
BuffetTimer = 7000;
@@ -521,9 +612,12 @@ struct TRINITY_DLL_DECL mob_hatchlingAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(0, hatcherway[0][3][0]+rand()%4-2,1150+rand()%4-2,hatcherway[0][3][2]);
else
m_creature->GetMotionMaster()->MovePoint(0,hatcherway[1][3][0]+rand()%4-2,1150+rand()%4-2,hatcherway[1][3][2]);
+
m_creature->SetUnitMovementFlags(MOVEMENTFLAG_LEVITATING);
}
+
void EnterCombat(Unit *who) {/*DoZoneInCombat();*/}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance || !(pInstance->GetData(DATA_JANALAIEVENT) == IN_PROGRESS))
@@ -532,20 +626,25 @@ struct TRINITY_DLL_DECL mob_hatchlingAI : public ScriptedAI
m_creature->setDeathState(JUST_DIED);
return;
}
+
if (!UpdateVictim())
return;
+
if (BuffetTimer < diff)
{
m_creature->CastSpell(m_creature->getVictim(), SPELL_FLAMEBUFFET, false);
BuffetTimer = 10000;
}else BuffetTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_hatchlingAI(Creature* pCreature)
{
return new mob_hatchlingAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_eggAI : public ScriptedAI
{
mob_eggAI(Creature *c) : ScriptedAI(c){}
@@ -554,6 +653,7 @@ struct TRINITY_DLL_DECL mob_eggAI : public ScriptedAI
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_HATCH_EGG)
@@ -563,10 +663,12 @@ struct TRINITY_DLL_DECL mob_eggAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mob_eggAI(Creature* pCreature)
{
return new mob_eggAI(pCreature);
}
+
void AddSC_boss_janalai()
{
Script *newscript;
@@ -574,18 +676,22 @@ void AddSC_boss_janalai()
newscript->Name = "boss_janalai";
newscript->GetAI = &GetAI_boss_janalaiAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_janalai_firebomb";
newscript->GetAI = &GetAI_mob_janalai_firebombAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_janalai_hatcher";
newscript->GetAI = &GetAI_mob_amanishi_hatcherAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_janalai_hatchling";
newscript->GetAI = &GetAI_mob_hatchlingAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_janalai_egg";
newscript->GetAI = &GetAI_mob_eggAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp
index 4c1566b6c51..c15df162430 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_nalorakk.cpp
@@ -13,15 +13,18 @@
* along 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: 100
SDComment:
SDCategory: Zul'Aman
EndScriptData */
+
#include "precompiled.h"
#include "def_zulaman.h"
#include "GridNotifiers.h"
+
//Trash Waves
float NalorakkWay[8][3] =
{
@@ -34,6 +37,7 @@ float NalorakkWay[8][3] =
{-80.072, 1314.398, 40.87},
{-80.072, 1295.775, 48.60} // waypoint 4
};
+
#define YELL_NALORAKK_WAVE1 "Get da move on, guards! It be killin' time!"
#define SOUND_NALORAKK_WAVE1 12066
#define YELL_NALORAKK_WAVE2 "Guards, go already! Who you more afraid of, dem... or me?"
@@ -42,11 +46,13 @@ float NalorakkWay[8][3] =
#define SOUND_NALORAKK_WAVE3 12068
#define YELL_NALORAKK_WAVE4 "I be losin' me patience! Go on: make dem wish dey was never born!"
#define SOUND_NALORAKK_WAVE4 12069
+
//Unimplemented SoundIDs
/*
#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
@@ -58,24 +64,31 @@ float NalorakkWay[8][3] =
#define SOUND_YELL_DEATH 12077
#define YELL_BERSERK "You had your chance, now it be too late!" //Never seen this being used, so just guessing from what I hear.
#define SOUND_YELL_BERSERK 12074
+
#define SPELL_BERSERK 45078
+
//Defines for Troll form
#define SPELL_BRUTALSWIPE 42384
#define SPELL_MANGLE 42389
#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 TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
{
boss_nalorakkAI(Creature *c) : ScriptedAI(c)
@@ -84,20 +97,26 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
MovePhase = 0;
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 BrutalSwipe_Timer;
uint32 Mangle_Timer;
uint32 Surge_Timer;
+
uint32 LaceratingSlash_Timer;
uint32 RendFlesh_Timer;
uint32 DeafeningRoar_Timer;
+
uint32 ShapeShift_Timer;
uint32 Berserk_Timer;
+
bool inBearForm;
bool MoveEvent;
bool inMove;
uint32 MovePhase;
uint32 waitTimer;
+
void Reset()
{
if (MoveEvent)
@@ -112,35 +131,45 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
{
(*m_creature).GetMotionMaster()->MovePoint(0,NalorakkWay[7][0],NalorakkWay[7][1],NalorakkWay[7][2]);
}
+
if (pInstance)
pInstance->SetData(DATA_NALORAKKEVENT, NOT_STARTED);
+
Surge_Timer = 15000 + rand()%5000;
BrutalSwipe_Timer = 7000 + rand()%5000;
Mangle_Timer = 10000 + rand()%5000;
ShapeShift_Timer = 45000 + rand()%5000;
Berserk_Timer = 600000;
+
inBearForm = false;
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 1, 5122);
}
+
void SendAttacker(Unit* target)
{
std::list<Creature*> templist;
float x, y, z;
m_creature->GetPosition(x, y, z);
+
{
CellPair pair(Trinity::ComputeCellPair(x, y));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllFriendlyCreaturesInGrid check(m_creature);
Trinity::CreatureListSearcher<Trinity::AllFriendlyCreaturesInGrid> searcher(m_creature, templist, check);
+
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllFriendlyCreaturesInGrid>, GridTypeMapContainer> cSearcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, cSearcher, *(m_creature->GetMap()));
}
+
if (!templist.size())
return;
- for (std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
+
+ for(std::list<Creature*>::iterator i = templist.begin(); i != templist.end(); ++i)
{
if ((*i) && m_creature->IsWithinDistInMap((*i),25))
{
@@ -149,11 +178,13 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
}
}
}
+
void AttackStart(Unit* who)
{
if (!MoveEvent)
ScriptedAI::AttackStart(who);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!MoveEvent)
@@ -173,9 +204,11 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
{
m_creature->MonsterYell(YELL_NALORAKK_WAVE1, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_NALORAKK_WAVE1);
+
(*m_creature).GetMotionMaster()->MovePoint(1,NalorakkWay[1][0],NalorakkWay[1][1],NalorakkWay[1][2]);
MovePhase ++;
inMove = true;
+
SendAttacker(who);
}
break;
@@ -184,9 +217,11 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
{
m_creature->MonsterYell(YELL_NALORAKK_WAVE2, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_NALORAKK_WAVE2);
+
(*m_creature).GetMotionMaster()->MovePoint(3,NalorakkWay[3][0],NalorakkWay[3][1],NalorakkWay[3][2]);
MovePhase ++;
inMove = true;
+
SendAttacker(who);
}
break;
@@ -195,9 +230,11 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
{
m_creature->MonsterYell(YELL_NALORAKK_WAVE3, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_NALORAKK_WAVE3);
+
(*m_creature).GetMotionMaster()->MovePoint(6,NalorakkWay[6][0],NalorakkWay[6][1],NalorakkWay[6][2]);
MovePhase ++;
inMove = true;
+
SendAttacker(who);
}
break;
@@ -205,10 +242,13 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
if (m_creature->IsWithinDistInMap(who, 50))
{
SendAttacker(who);
+
m_creature->MonsterYell(YELL_NALORAKK_WAVE4, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_NALORAKK_WAVE4);
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
MoveEvent = false;
}
break;
@@ -217,21 +257,26 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_NALORAKKEVENT, IN_PROGRESS);
+
m_creature->MonsterYell(YELL_AGGRO, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_YELL_AGGRO);
DoZoneInCombat();
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(DATA_NALORAKKEVENT, DONE);
+
m_creature->MonsterYell(YELL_DEATH,LANG_UNIVERSAL,NULL);
DoPlaySoundToSet(m_creature, SOUND_YELL_DEATH);
}
+
void KilledUnit(Unit* victim)
{
switch(rand()%2)
@@ -246,16 +291,20 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
break;
}
}
+
void MovementInform(uint32 type, uint32 id)
{
if (MoveEvent)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (!inMove)
return;
+
if (MovePhase != id)
return;
+
switch(MovePhase)
{
case 2:
@@ -279,8 +328,10 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
inMove = false;
return;
}
+
}
}
+
void UpdateAI(const uint32 diff)
{
if (waitTimer)
@@ -293,8 +344,10 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
waitTimer = 0;
}else waitTimer -= diff;
}
+
if (!UpdateVictim())
return;
+
if (Berserk_Timer < diff)
{
DoCast(m_creature, SPELL_BERSERK, true);
@@ -302,6 +355,7 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
DoPlaySoundToSet(m_creature, SOUND_YELL_BERSERK);
Berserk_Timer = 600000;
}else Berserk_Timer -= diff;
+
if (ShapeShift_Timer < diff)
{
if (inBearForm)
@@ -329,6 +383,7 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
inBearForm = true;
}
}else ShapeShift_Timer -= diff;
+
if (!inBearForm)
{
if (BrutalSwipe_Timer < diff)
@@ -336,6 +391,7 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_BRUTALSWIPE);
BrutalSwipe_Timer = 7000 + rand()%5000;
}else BrutalSwipe_Timer -= diff;
+
if (Mangle_Timer < diff)
{
if (m_creature->getVictim() && !m_creature->getVictim()->HasAura(SPELL_MANGLEEFFECT))
@@ -345,6 +401,7 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
}
else Mangle_Timer = 10000 + rand()%5000;
}else Mangle_Timer -= diff;
+
if (Surge_Timer < diff)
{
m_creature->MonsterYell(YELL_SURGE, LANG_UNIVERSAL, NULL);
@@ -362,24 +419,29 @@ struct TRINITY_DLL_DECL boss_nalorakkAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_LACERATINGSLASH);
LaceratingSlash_Timer = 18000 + rand()%5000;
}else LaceratingSlash_Timer -= diff;
+
if (RendFlesh_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_RENDFLESH);
RendFlesh_Timer = 5000 + rand()%5000;
}else RendFlesh_Timer -= diff;
+
if (DeafeningRoar_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_DEAFENINGROAR);
DeafeningRoar_Timer = 15000 + rand()%5000;
}else DeafeningRoar_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_nalorakk(Creature* pCreature)
{
return new boss_nalorakkAI (pCreature);
}
+
void AddSC_boss_nalorakk()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp
index 1ce3feacdb6..a8df5202e64 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/boss_zuljin.cpp
@@ -13,37 +13,51 @@
* along 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_ZulJin
SD%Complete: 85%
SDComment:
EndScriptData */
+
#include "precompiled.h"
#include "def_zulaman.h"
+
//Speech
#define YELL_TRANSFORM_TO_LYNX "Let me introduce to you my new bruddahs: fang and claw!"
#define SOUND_TRANSFORM_TO_LYNX 12094
+
#define YELL_TRANSFORM_TO_BEAR "Got me some new tricks...like me bruddah bear!"
#define SOUND_TRANSFORM_TO_BEAR 12092
+
#define YELL_TRANSFORM_TO_DRAGONHAWK "Ya don' have to look to da sky to see da dragonhawk!"
#define SOUND_TRANSFORM_TO_DRAGONHAWK 12095
+
#define YELL_TRANSFORM_TO_EAGLE "Dere be no hidin' from da eagle!"
#define SOUND_TRANSFORM_TO_EAGLE 12093
+
#define YELL_KILL_ONE "Da Amani de chuka!"
#define SOUND_KILL_ONE 12098
+
#define YELL_KILL_TWO "Lot more gonna fall like you!"
#define SOUND_KILL_TWO 12099
+
#define YELL_FIRE_BREATH "Fire kill you just as quick!"
#define SOUND_FIRE_BRETH 12096
+
#define YELL_AGGRO "Nobody badduh dan me!"
#define SOUND_AGGRO 12091
+
#define YELL_BERSERK "You too slow! Me too strong!"
#define SOUND_BERSERK 12097
+
#define YELL_DEATH "Mebbe me fall...but da Amani empire...never gonna die..."
#define SOUND_DEATH 12100
+
//Still not used, need more info
#define YELL_INTRO "Everybody always wanna take from us. Now we gonna start takin' back. Anybody who get in our way...gonna drown in their own blood! De Amani empire be back now...seekin' vengeance. And we gonna start...with you!"
#define SOUND_INTRO 12090
+
//Spells:
// ====== Troll Form
#define SPELL_WHIRLWIND 17207
@@ -71,30 +85,37 @@ EndScriptData */
#define SPELL_SUMMON_PILLAR 43216 // summon 24187
#define CREATURE_COLUMN_OF_FIRE 24187
#define SPELL_PILLAR_TRIGGER 43218 // trigger 43217
+
//cosmetic
#define SPELL_SPIRIT_AURA 42466
#define SPELL_SIPHON_SOUL 43501
+
//Transforms:
#define SPELL_SHAPE_OF_THE_BEAR 42594 // 15% dmg
#define SPELL_SHAPE_OF_THE_EAGLE 42606
#define SPELL_SHAPE_OF_THE_LYNX 42607 // haste melee 30%
#define SPELL_SHAPE_OF_THE_DRAGONHAWK 42608
+
#define SPELL_BERSERK 45078
+
#define PHASE_BEAR 0
#define PHASE_EAGLE 1
#define PHASE_LYNX 2
#define PHASE_DRAGONHAWK 3
#define PHASE_TROLL 4
+
//coords for going for changing form
#define CENTER_X 120.148811
#define CENTER_Y 703.713684
#define CENTER_Z 45.111477
+
struct SpiritInfoStruct
{
uint32 entry;
float x, y, z, orient;
};
+
static SpiritInfoStruct SpiritInfo[] =
{
{23878, 147.87, 706.51, 45.11, 3.04},
@@ -102,12 +123,14 @@ static SpiritInfoStruct SpiritInfo[] =
{23877, 137.23, 725.98, 45.11, 3.71},
{23879, 104.29, 726.43, 45.11, 5.43}
};
+
struct TransformStruct
{
uint32 sound;
char* text;
uint32 spell, unaura;
};
+
static TransformStruct Transform[] =
{
{SOUND_TRANSFORM_TO_BEAR, YELL_TRANSFORM_TO_BEAR, SPELL_SHAPE_OF_THE_BEAR, SPELL_WHIRLWIND},
@@ -115,6 +138,7 @@ static TransformStruct Transform[] =
{SOUND_TRANSFORM_TO_LYNX, YELL_TRANSFORM_TO_LYNX, SPELL_SHAPE_OF_THE_LYNX, SPELL_SHAPE_OF_THE_EAGLE},
{SOUND_TRANSFORM_TO_DRAGONHAWK, YELL_TRANSFORM_TO_DRAGONHAWK, SPELL_SHAPE_OF_THE_DRAGONHAWK, SPELL_SHAPE_OF_THE_LYNX}
};
+
struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
{
boss_zuljinAI(Creature *c) : ScriptedAI(c), Summons(m_creature)
@@ -122,65 +146,89 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
pInstance = c->GetInstanceData();
}
ScriptedInstance *pInstance;
+
uint64 SpiritGUID[4];
uint64 ClawTargetGUID;
uint64 TankGUID;
+
uint32 Phase;
uint32 health_20;
+
uint32 Intro_Timer;
uint32 Berserk_Timer;
+
uint32 Whirlwind_Timer;
uint32 Grievous_Throw_Timer;
+
uint32 Creeping_Paralysis_Timer;
uint32 Overpower_Timer;
+
uint32 Claw_Rage_Timer;
uint32 Lynx_Rush_Timer;
uint32 Claw_Counter;
uint32 Claw_Loop_Timer;
+
uint32 Flame_Whirl_Timer;
uint32 Flame_Breath_Timer;
uint32 Pillar_Of_Fire_Timer;
+
SummonList Summons;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_ZULJINEVENT, NOT_STARTED);
+
Phase = 0;
+
health_20 = m_creature->GetMaxHealth()*0.2;
+
Intro_Timer = 37000;
Berserk_Timer = 600000;
+
Whirlwind_Timer = 7000;
Grievous_Throw_Timer = 8000;
+
Creeping_Paralysis_Timer = 7000;
Overpower_Timer = 0;
+
Claw_Rage_Timer = 5000;
Lynx_Rush_Timer = 14000;
Claw_Loop_Timer = 0;
Claw_Counter = 0;
+
Flame_Whirl_Timer = 5000;
Flame_Breath_Timer = 6000;
Pillar_Of_Fire_Timer = 7000;
+
ClawTargetGUID = 0;
TankGUID = 0;
+
Summons.DespawnAll();
+
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, 47174);
//m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_INFO, 218172674);
//m_creature->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_ZULJINEVENT, IN_PROGRESS);
+
DoZoneInCombat();
+
m_creature->MonsterYell(YELL_INTRO,LANG_UNIVERSAL,NULL);
DoPlaySoundToSet(m_creature, SOUND_INTRO);
SpawnAdds();
EnterPhase(0);
}
+
void KilledUnit(Unit* victim)
{
if (Intro_Timer)
return;
+
switch(rand()%2)
{
case 0:
@@ -193,16 +241,20 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
break;
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(DATA_ZULJINEVENT, DONE);
+
m_creature->MonsterYell(YELL_DEATH, LANG_UNIVERSAL, NULL);
DoPlaySoundToSet(m_creature, SOUND_DEATH);
Summons.DespawnEntry(CREATURE_COLUMN_OF_FIRE);
+
if (Unit *Temp = Unit::GetUnit(*m_creature, SpiritGUID[3]))
Temp->SetUInt32Value(UNIT_FIELD_BYTES_1,UNIT_STAND_STATE_DEAD);
}
+
void AttackStart(Unit *who)
{
if (Phase == 2)
@@ -210,6 +262,7 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
else
ScriptedAI::AttackStart(who);
}
+
void DoMeleeAttackIfReady()
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -230,10 +283,11 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
}
}
}
+
void SpawnAdds()
{
Creature *pCreature = NULL;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
pCreature = m_creature->SummonCreature(SpiritInfo[i].entry, SpiritInfo[i].x, SpiritInfo[i].y, SpiritInfo[i].z, SpiritInfo[i].orient, TEMPSUMMON_DEAD_DESPAWN, 0);
if (pCreature)
@@ -245,9 +299,10 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
}
}
}
+
void DespawnAdds()
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Unit* Temp = NULL;
if (SpiritGUID[i])
@@ -261,14 +316,17 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
SpiritGUID[i] = 0;
}
}
+
void JustSummoned(Creature *summon)
{
Summons.Summon(summon);
}
+
void SummonedCreatureDespawn(Creature *summon)
{
Summons.Despawn(summon);
}
+
void EnterPhase(uint32 NextPhase)
{
switch(NextPhase)
@@ -297,7 +355,7 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
{
m_creature->GetMotionMaster()->Clear();
m_creature->CastSpell(m_creature, SPELL_ENERGY_STORM, true); // enemy aura
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Creature* Vortex = DoSpawnCreature(CREATURE_FEATHER_VORTEX, 0, 0, 0, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
if (Vortex)
@@ -325,15 +383,18 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
}
Phase = NextPhase;
}
+
void UpdateAI(const uint32 diff)
{
if (!TankGUID)
{
if (!UpdateVictim())
return;
+
if (m_creature->GetHealth() < health_20 * (4 - Phase))
EnterPhase(Phase + 1);
}
+
if (Berserk_Timer < diff)
{
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
@@ -341,6 +402,7 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
DoPlaySoundToSet(m_creature, SOUND_BERSERK);
Berserk_Timer = 60000;
}else Berserk_Timer -= diff;
+
switch (Phase)
{
case 0:
@@ -353,11 +415,13 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
Intro_Timer = 0;
}else Intro_Timer -= diff;
}
+
if (Whirlwind_Timer < diff)
{
DoCast(m_creature, SPELL_WHIRLWIND);
Whirlwind_Timer = 15000 + rand()%5000;
}else Whirlwind_Timer -= diff;
+
if (Grievous_Throw_Timer < diff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -365,20 +429,24 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
Grievous_Throw_Timer = 10000;
}else Grievous_Throw_Timer -= diff;
break;
+
case 1:
if (Creeping_Paralysis_Timer < diff)
{
DoCast(m_creature, SPELL_CREEPING_PARALYSIS);
Creeping_Paralysis_Timer = 20000;
}else Creeping_Paralysis_Timer -= diff;
+
if (Overpower_Timer < diff)
{
// implemented in DoMeleeAttackIfReady()
Overpower_Timer = 0;
}else Overpower_Timer -= diff;
break;
+
case 2:
return;
+
case 3:
if (Claw_Rage_Timer <= diff)
{
@@ -428,6 +496,7 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
}else Claw_Loop_Timer -= diff;
} //if (TankGUID)
}else Claw_Rage_Timer -= diff;
+
if (Lynx_Rush_Timer <= diff)
{
if (!TankGUID)
@@ -473,6 +542,7 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
}
} //if (TankGUID)
}else Lynx_Rush_Timer -= diff;
+
break;
case 4:
if (Flame_Whirl_Timer < diff)
@@ -480,12 +550,14 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
DoCast(m_creature, SPELL_FLAME_WHIRL);
Flame_Whirl_Timer = 12000;
}Flame_Whirl_Timer -= diff;
+
if (Pillar_Of_Fire_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_SUMMON_PILLAR);
Pillar_Of_Fire_Timer = 10000;
}else Pillar_Of_Fire_Timer -= diff;
+
if (Flame_Breath_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -494,27 +566,35 @@ struct TRINITY_DLL_DECL boss_zuljinAI : public ScriptedAI
Flame_Breath_Timer = 10000;
}else Flame_Breath_Timer -= diff;
break;
+
default:
break;
}
+
if (!TankGUID)
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_zuljin(Creature* pCreature)
{
return new boss_zuljinAI (pCreature);
}
+
struct TRINITY_DLL_DECL feather_vortexAI : public ScriptedAI
{
feather_vortexAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
+
void EnterCombat(Unit* target) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_ZAP_INFORM)
m_creature->CastSpell(caster, SPELL_ZAP_DAMAGE, true);
}
+
void UpdateAI(const uint32 diff)
{
//if the vortex reach the target, it change his target to another player
@@ -522,10 +602,12 @@ struct TRINITY_DLL_DECL feather_vortexAI : public ScriptedAI
AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0));
}
};
+
CreatureAI* GetAI_feather_vortexAI(Creature* pCreature)
{
return new feather_vortexAI (pCreature);
}
+
void AddSC_boss_zuljin()
{
Script *newscript;
@@ -533,6 +615,7 @@ void AddSC_boss_zuljin()
newscript->Name = "boss_zuljin";
newscript->GetAI = &GetAI_boss_zuljin;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_zuljin_vortex";
newscript->GetAI = &GetAI_feather_vortexAI;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/def_zulaman.h b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/def_zulaman.h
index 082616146c6..6fb0ef173bd 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/def_zulaman.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/def_zulaman.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_ZULAMAN_H
#define DEF_ZULAMAN_H
+
#define DATA_NALORAKKEVENT 1
#define DATA_AKILZONEVENT 2
#define DATA_JANALAIEVENT 3
@@ -12,5 +14,6 @@
#define DATA_CHESTLOOTED 7
#define TYPE_RAND_VENDOR_1 8
#define TYPE_RAND_VENDOR_2 9
+
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp
index 327bda17592..74186442d9e 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/instance_zulaman.cpp
@@ -13,16 +13,20 @@
* along 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 MAX_ENCOUNTER 6
#define RAND_VENDOR 2
+
//187021 //Harkor's Satchel
//186648 //Tanzar's Trunk
//186672 //Ashli's Bag
@@ -35,56 +39,71 @@ struct SHostageInfo
uint32 npc, pGo;
float x, y, z, o;
};
+
static SHostageInfo HostageInfo[] =
{
{23790, 186648, -57, 1343, 40.77, 3.2}, // bear
{23999, 187021, 400, 1414, 74.36, 3.3}, // eagle
{24001, 186672, -35, 1134, 18.71, 1.9}, // dragonhawk
{24024, 186667, 413, 1117, 6.32, 3.1} // lynx
+
};
+
struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
{
instance_zulaman(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 HarkorsSatchelGUID;
uint64 TanzarsTrunkGUID;
uint64 AshlisBagGUID;
uint64 KrazsPackageGUID;
+
uint64 HexLordGateGUID;
uint64 ZulJinGateGUID;
uint64 AkilzonDoorGUID;
uint64 ZulJinDoorGUID;
uint64 HalazziDoorGUID;
+
uint32 QuestTimer;
uint16 BossKilled;
uint16 QuestMinute;
uint16 ChestLooted;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint32 RandVendor[RAND_VENDOR];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
HarkorsSatchelGUID = 0;
TanzarsTrunkGUID = 0;
AshlisBagGUID = 0;
KrazsPackageGUID = 0;
+
uint64 HexLordGateGUID = 0;
uint64 ZulJinGateGUID = 0;
uint64 AkilzonDoorGUID = 0;
uint64 HalazziDoorGUID = 0;
uint64 ZulJinDoorGUID = 0;
+
QuestTimer = 0;
QuestMinute = 21;
BossKilled = 0;
ChestLooted = 0;
- for (uint8 i = 0; i < RAND_VENDOR; ++i)
+
+ for(uint8 i = 0; i < RAND_VENDOR; ++i)
RandVendor[i] = NOT_STARTED;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -97,6 +116,7 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
default: break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -106,21 +126,26 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
case 186305: HexLordGateGUID = pGo->GetGUID(); break;
case 186858: AkilzonDoorGUID = pGo->GetGUID(); break;
case 186859: ZulJinDoorGUID = pGo->GetGUID(); break;
+
case 187021: HarkorsSatchelGUID = pGo->GetGUID(); break;
case 186648: TanzarsTrunkGUID = pGo->GetGUID(); break;
case 186672: AshlisBagGUID = pGo->GetGUID(); break;
case 186667: KrazsPackageGUID = pGo->GetGUID(); break;
default: break;
+
}
CheckInstanceStatus();
}
+
void SummonHostage(uint8 num)
{
if (!QuestMinute)
return;
+
Map::PlayerList const &PlayerList = instance->GetPlayers();
if (PlayerList.isEmpty())
return;
+
Map::PlayerList::const_iterator i = PlayerList.begin();
if (Player* i_pl = i->getSource())
{
@@ -131,13 +156,16 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
}
}
}
+
void CheckInstanceStatus()
{
if (BossKilled >= 4)
HandleGameObject(HexLordGateGUID, true);
+
if (BossKilled >= 5)
HandleGameObject(ZulJinGateGUID, true);
}
+
std::string GetSaveData()
{
std::ostringstream ss;
@@ -147,6 +175,7 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
//error_log("TSCR: Zul'aman saved, %s.", data);
return data;
}
+
void Load(const char* load)
{
if (!load) return;
@@ -163,6 +192,7 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
QuestMinute = data3;
}else error_log("TSCR: Zul'aman: corrupted save data.");
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -223,6 +253,7 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
RandVendor[1] = data;
break;
}
+
if (data == DONE)
{
BossKilled++;
@@ -235,6 +266,7 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
SaveToDB();
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -251,6 +283,7 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
default: return 0;
}
}
+
void Update(uint32 diff)
{
if (QuestMinute)
@@ -270,10 +303,12 @@ struct TRINITY_DLL_DECL instance_zulaman : public ScriptedInstance
}
}
};
+
InstanceData* GetInstanceData_instance_zulaman(Map* pMap)
{
return new instance_zulaman(pMap);
}
+
void AddSC_instance_zulaman()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/zulaman.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/zulaman.cpp
index 57ab45ac944..e3142e50540 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/zulaman.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulaman/zulaman.cpp
@@ -13,32 +13,42 @@
* along 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 TRINITY_DLL_DECL npc_forest_frogAI : public ScriptedAI
{
npc_forest_frogAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
void Reset() { }
+
void EnterCombat(Unit *who) { }
+
void DoSpawnRandom()
{
if (pInstance)
@@ -58,15 +68,19 @@ struct TRINITY_DLL_DECL npc_forest_frogAI : public ScriptedAI
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)
@@ -81,12 +95,16 @@ CreatureAI* GetAI_npc_forest_frog(Creature* pCreature)
{
return new npc_forest_frogAI (pCreature);
}
+
/*######
## npc_zulaman_hostage
######*/
+
#define GOSSIP_HOSTAGE1 "I am glad to help you."
+
static uint32 HostageEntry[] = {23790, 23999, 24024, 24001};
static uint32 ChestEntry[] = {186648, 187021, 186672, 186667};
+
struct TRINITY_DLL_DECL npc_zulaman_hostageAI : public ScriptedAI
{
npc_zulaman_hostageAI(Creature *c) : ScriptedAI(c) {IsLoot = false;}
@@ -104,19 +122,23 @@ struct TRINITY_DLL_DECL npc_zulaman_hostageAI : public ScriptedAI
if (IsLoot) m_creature->CastSpell(m_creature, 7, false);
}
};
+
bool GossipHello_npc_zulaman_hostage(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HOSTAGE1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_zulaman_hostage(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
pPlayer->CLOSE_GOSSIP_MENU();
+
if (!pCreature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
return true;
pCreature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+
ScriptedInstance* pInstance = pCreature->GetInstanceData();
if (pInstance)
{
@@ -125,7 +147,7 @@ bool GossipSelect_npc_zulaman_hostage(Player* pPlayer, Creature* pCreature, uint
float x, y, z;
pCreature->GetPosition(x, y, z);
uint32 entry = pCreature->GetEntry();
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (HostageEntry[i] == entry)
{
@@ -145,17 +167,21 @@ bool GossipSelect_npc_zulaman_hostage(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
CreatureAI* GetAI_npc_zulaman_hostage(Creature* pCreature)
{
return new npc_zulaman_hostageAI(pCreature);
}
+
void AddSC_zulaman()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_forest_frog";
newscript->GetAI = &GetAI_npc_forest_frog;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_zulaman_hostage";
newscript->GetAI = &GetAI_npc_zulaman_hostage;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp
index 4d5af424a2d..5244e5d3538 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_arlokk.cpp
@@ -13,57 +13,73 @@
* along 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"
+
bool GOHello_go_gong_of_bethekk(Player* pPlayer, GameObject* pGo)
{
if (ScriptedInstance* m_pInstance = pGo->GetInstanceData())
{
if (m_pInstance->GetData(TYPE_ARLOKK) == DONE || m_pInstance->GetData(TYPE_ARLOKK) == IN_PROGRESS)
return true;
+
m_pInstance->SetData(TYPE_ARLOKK, IN_PROGRESS);
}
+
return false;
}
+
enum eEnums
{
SAY_AGGRO = -1309011,
SAY_FEAST_PANTHER = -1309012,
SAY_DEATH = -1309013,
+
SPELL_SHADOWWORDPAIN = 23952,
SPELL_GOUGE = 24698,
SPELL_MARK = 24210,
SPELL_CLEAVE = 26350, //Perhaps not right. Not a red aura...
SPELL_PANTHER_TRANSFORM = 24190,
+
MODEL_ID_NORMAL = 15218,
MODEL_ID_PANTHER = 15215,
MODEL_ID_BLANK = 11686,
+
NPC_ZULIAN_PROWLER = 15101
};
+
struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
{
boss_arlokkAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 m_uiShadowWordPain_Timer;
uint32 m_uiGouge_Timer;
uint32 m_uiMark_Timer;
uint32 m_uiCleave_Timer;
uint32 m_uiVanish_Timer;
uint32 m_uiVisible_Timer;
+
uint32 m_uiSummon_Timer;
uint32 m_uiSummonCount;
+
Unit* m_pMarkedTarget;
+
bool m_bIsPhaseTwo;
bool m_bIsVanished;
+
void Reset()
{
m_uiShadowWordPain_Timer = 8000;
@@ -72,50 +88,66 @@ struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
m_uiCleave_Timer = 4000;
m_uiVanish_Timer = 60000;
m_uiVisible_Timer = 6000;
+
m_uiSummon_Timer = 5000;
m_uiSummonCount = 0;
+
m_bIsPhaseTwo = false;
m_bIsVanished = false;
+
m_pMarkedTarget = NULL;
+
m_creature->SetDisplayId(MODEL_ID_NORMAL);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustReachedHome()
{
if (m_pInstance)
m_pInstance->SetData(TYPE_ARLOKK, NOT_STARTED);
+
//we should be summoned, so despawn
m_creature->ForcedDespawn();
}
+
void JustDied(Unit* pKiller)
{
DoScriptText(SAY_DEATH, m_creature);
+
m_creature->SetDisplayId(MODEL_ID_NORMAL);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_ARLOKK, DONE);
}
+
void DoSummonPhanters()
{
if (m_pMarkedTarget)
DoScriptText(SAY_FEAST_PANTHER, m_creature, m_pMarkedTarget);
+
m_creature->SummonCreature(NPC_ZULIAN_PROWLER, -11532.7998, -1649.6734, 41.4800, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
m_creature->SummonCreature(NPC_ZULIAN_PROWLER, -11532.9970, -1606.4840, 41.2979, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
}
+
void JustSummoned(Creature* pSummoned)
{
if (m_pMarkedTarget)
pSummoned->AI()->AttackStart(m_pMarkedTarget);
+
++m_uiSummonCount;
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
if (!m_bIsPhaseTwo)
{
if (m_uiShadowWordPain_Timer < uiDiff)
@@ -125,13 +157,16 @@ struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
}
else
m_uiShadowWordPain_Timer -= uiDiff;
+
if (m_uiMark_Timer < uiDiff)
{
m_pMarkedTarget = SelectUnit(SELECT_TARGET_RANDOM,0);
+
if (m_pMarkedTarget)
DoCast(m_pMarkedTarget, SPELL_MARK);
else
error_log("TSCR: boss_arlokk could not accuire m_pMarkedTarget.");
+
m_uiMark_Timer = 15000;
}
else
@@ -147,16 +182,20 @@ struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
}
else
m_uiCleave_Timer -= uiDiff;
+
//Gouge_Timer
if (m_uiGouge_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_GOUGE);
+
DoModifyThreatPercent(m_creature->getVictim(),-80);
+
m_uiGouge_Timer = 17000+rand()%10000;
}
else
m_uiGouge_Timer -= uiDiff;
}
+
if (m_uiSummonCount <= 30)
{
if (m_uiSummon_Timer < uiDiff)
@@ -167,19 +206,24 @@ struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
else
m_uiSummon_Timer -= uiDiff;
}
+
if (m_uiVanish_Timer < uiDiff)
{
//Invisble Model
m_creature->SetDisplayId(MODEL_ID_BLANK);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
m_creature->AttackStop();
DoResetThreat();
+
m_bIsVanished = true;
+
m_uiVanish_Timer = 45000;
m_uiVisible_Timer = 6000;
}
else
m_uiVanish_Timer -= uiDiff;
+
if (m_bIsVanished)
{
if (m_uiVisible_Timer < uiDiff)
@@ -187,12 +231,15 @@ struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
//The Panther Model
m_creature->SetDisplayId(MODEL_ID_PANTHER);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
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);
+
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM,0))
AttackStart(pTarget);
+
m_bIsPhaseTwo = true;
m_bIsVanished = false;
}
@@ -203,17 +250,21 @@ struct TRINITY_DLL_DECL boss_arlokkAI : public ScriptedAI
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_arlokk(Creature* pCreature)
{
return new boss_arlokkAI (pCreature);
}
+
void AddSC_boss_arlokk()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_gong_of_bethekk";
newscript->pGOHello = &GOHello_go_gong_of_bethekk;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_arlokk";
newscript->GetAI = &GetAI_boss_arlokk;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp
index abcc3d71632..3de88e3af0f 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_gahzranka.cpp
@@ -13,55 +13,67 @@
* along 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 16099
#define SPELL_MASSIVEGEYSER 22421 //Not working. Cause its a summon...
#define SPELL_SLAM 24326
+
struct TRINITY_DLL_DECL boss_gahzrankaAI : public ScriptedAI
{
boss_gahzrankaAI(Creature *c) : ScriptedAI(c) {}
uint32 Frostbreath_Timer;
uint32 MassiveGeyser_Timer;
uint32 Slam_Timer;
+
void Reset()
{
Frostbreath_Timer = 8000;
MassiveGeyser_Timer = 25000;
Slam_Timer = 17000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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();
}
};
@@ -69,6 +81,7 @@ CreatureAI* GetAI_boss_gahzranka(Creature* pCreature)
{
return new boss_gahzrankaAI (pCreature);
}
+
void AddSC_boss_gahzranka()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp
index 957105f2b75..6475f7ab4e5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_grilek.cpp
@@ -13,52 +13,67 @@
* along 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 TRINITY_DLL_DECL boss_grilekAI : public ScriptedAI
{
boss_grilekAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Avartar_Timer;
uint32 GroundTremor_Timer;
+
void Reset()
{
Avartar_Timer = 15000 + rand()%10000;
GroundTremor_Timer = 8000 + rand()%8000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Avartar_Timer
if (Avartar_Timer < diff)
{
+
DoCast(m_creature, SPELL_AVARTAR);
Unit* target = NULL;
+
target = SelectUnit(SELECT_TARGET_RANDOM,1);
+
if (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(m_creature->getVictim(),-50);
if (target)
AttackStart(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();
}
};
@@ -66,6 +81,7 @@ CreatureAI* GetAI_boss_grilek(Creature* pCreature)
{
return new boss_grilekAI (pCreature);
}
+
void AddSC_boss_grilek()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp
index 644a8018d89..be73374aafc 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hakkar.cpp
@@ -13,52 +13,64 @@
* along 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 SAY_AGGRO -1309020
#define SAY_FLEEING -1309021
#define SAY_MINION_DESTROY -1309022 //where does it belong?
#define SAY_PROTECT_ALTAR -1309023 //where does it belong?
+
#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
+
struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
{
boss_hakkarAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
ScriptedInstance *m_pInstance;
+
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;
+
bool Enraged;
+
void Reset()
{
BloodSiphon_Timer = 90000;
@@ -66,57 +78,70 @@ struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
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 EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
if (Unit* 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)
{
if (Unit* 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)
{
@@ -133,6 +158,7 @@ struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
}
CheckJeklik_Timer = 1000;
}else CheckJeklik_Timer -= diff;
+
//Checking if Venoxis is dead. If not we cast his Aspect
if (CheckVenoxis_Timer < diff)
{
@@ -149,6 +175,7 @@ struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
}
CheckVenoxis_Timer = 1000;
}else CheckVenoxis_Timer -= diff;
+
//Checking if Marli is dead. If not we cast her Aspect
if (CheckMarli_Timer < diff)
{
@@ -161,10 +188,12 @@ struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
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)
{
@@ -181,6 +210,7 @@ struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
}
CheckThekal_Timer = 1000;
}else CheckThekal_Timer -= diff;
+
//Checking if Arlokk is dead. If yes we cast her Aspect
if (CheckArlokk_Timer < diff)
{
@@ -192,19 +222,23 @@ struct TRINITY_DLL_DECL boss_hakkarAI : public ScriptedAI
{
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* pCreature)
{
return new boss_hakkarAI (pCreature);
}
+
void AddSC_boss_hakkar()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp
index f07106df754..0b11bd59d6c 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_hazzarah.cpp
@@ -13,65 +13,79 @@
* along 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 TRINITY_DLL_DECL boss_hazzarahAI : public ScriptedAI
{
boss_hazzarahAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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 (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (!target)
return;
+
Illusion = m_creature->SummonCreature(15163,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,30000);
if (Illusion)
(Illusion->AI())->AttackStart(target);
}
+
Illusions_Timer = 15000 + rand()%10000;
}else Illusions_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -79,6 +93,7 @@ CreatureAI* GetAI_boss_hazzarah(Creature* pCreature)
{
return new boss_hazzarahAI (pCreature);
}
+
void AddSC_boss_hazzarah()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp
index 39ed24ede8b..27d9fc56443 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jeklik.cpp
@@ -13,17 +13,21 @@
* along 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 SAY_AGGRO -1309002
#define SAY_RAIN_FIRE -1309003
#define SAY_DEATH -1309004
+
#define SPELL_CHARGE 22911
#define SPELL_SONICBURST 23918
#define SPELL_SCREECH 6605
@@ -32,15 +36,20 @@ EndScriptData */
#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...
+
struct TRINITY_DLL_DECL boss_jeklikAI : public ScriptedAI
{
boss_jeklikAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
ScriptedInstance *m_pInstance;
+
uint32 Charge_Timer;
uint32 SonicBurst_Timer;
uint32 Screech_Timer;
@@ -50,7 +59,9 @@ struct TRINITY_DLL_DECL boss_jeklikAI : public ScriptedAI
uint32 ChainMindFlay_Timer;
uint32 GreaterHeal_Timer;
uint32 SpawnFlyingBats_Timer;
+
bool PhaseTwo;
+
void Reset()
{
Charge_Timer = 20000;
@@ -62,23 +73,29 @@ struct TRINITY_DLL_DECL boss_jeklikAI : public ScriptedAI
ChainMindFlay_Timer = 26000;
GreaterHeal_Timer = 50000;
SpawnFlyingBats_Timer = 10000;
+
PhaseTwo = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoCast(m_creature,SPELL_BAT_FORM);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_JEKLIK, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (m_creature->getVictim() && m_creature->isAlive())
{
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 50))
@@ -90,34 +107,44 @@ struct TRINITY_DLL_DECL boss_jeklikAI : public ScriptedAI
DoCast(target,SPELL_CHARGE);
AttackStart(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;
}
@@ -133,31 +160,37 @@ struct TRINITY_DLL_DECL boss_jeklikAI : public ScriptedAI
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);
if (!target)
return;
+
Creature* FlyingBat = m_creature->SummonCreature(14965, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ()+15, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (FlyingBat)
FlyingBat->AI()->AttackStart(target);
+
SpawnFlyingBats_Timer = 10000 + rand()%5000;
}else SpawnFlyingBats_Timer -=diff;
}
@@ -168,10 +201,12 @@ struct TRINITY_DLL_DECL boss_jeklikAI : public ScriptedAI
PhaseTwo = true;
}
}
+
DoMeleeAttackIfReady();
}
}
};
+
//Flying Bat
struct TRINITY_DLL_DECL mob_batriderAI : public ScriptedAI
{
@@ -179,20 +214,27 @@ struct TRINITY_DLL_DECL mob_batriderAI : public ScriptedAI
{
m_pInstance = c->GetInstanceData();
}
+
ScriptedInstance *m_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 EnterCombat(Unit *who) {}
+
void UpdateAI (const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Bomb_Timer
if (Bomb_Timer < diff)
{
@@ -202,6 +244,7 @@ struct TRINITY_DLL_DECL mob_batriderAI : public ScriptedAI
Bomb_Timer = 5000;
}
}else Bomb_Timer -= diff;
+
//Check_Timer
if (Check_Timer < diff)
{
@@ -214,19 +257,24 @@ struct TRINITY_DLL_DECL mob_batriderAI : public ScriptedAI
return;
}
}
+
Check_Timer = 1000;
}else Check_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_jeklik(Creature* pCreature)
{
return new boss_jeklikAI (pCreature);
}
+
CreatureAI* GetAI_mob_batrider(Creature* pCreature)
{
return new mob_batriderAI (pCreature);
}
+
void AddSC_boss_jeklik()
{
Script *newscript;
@@ -234,6 +282,7 @@ void AddSC_boss_jeklik()
newscript->Name = "boss_jeklik";
newscript->GetAI = &GetAI_boss_jeklik;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_batrider";
newscript->GetAI = &GetAI_mob_batrider;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp
index 09700f4193e..3fa3090abdb 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_jindo.cpp
@@ -13,33 +13,42 @@
* along 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 SAY_AGGRO -1309014
+
#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
+
struct TRINITY_DLL_DECL boss_jindoAI : public ScriptedAI
{
boss_jindoAI(Creature *c) : ScriptedAI(c) {}
+
uint32 BrainWashTotem_Timer;
uint32 HealingWard_Timer;
uint32 Hex_Timer;
uint32 Delusions_Timer;
uint32 Teleport_Timer;
+
void Reset()
{
BrainWashTotem_Timer = 20000;
@@ -48,20 +57,24 @@ struct TRINITY_DLL_DECL boss_jindoAI : public ScriptedAI
Delusions_Timer = 10000;
Teleport_Timer = 5000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -69,26 +82,33 @@ struct TRINITY_DLL_DECL boss_jindoAI : public ScriptedAI
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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(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)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
{
DoCast(target, SPELL_DELUSIONSOFJINDO);
+
Creature *Shade = m_creature->SummonCreature(14986, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (Shade)
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)
{
@@ -97,8 +117,10 @@ struct TRINITY_DLL_DECL boss_jindoAI : public ScriptedAI
if (target && target->GetTypeId() == TYPEID_PLAYER)
{
DoTeleportPlayer(target, -11583.7783,-1249.4278,77.5471,4.745);
+
if (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(target,-100);
+
Creature *Skeletons;
Skeletons = m_creature->SummonCreature(14826, target->GetPositionX()+2, target->GetPositionY(), target->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (Skeletons)
@@ -128,11 +150,14 @@ struct TRINITY_DLL_DECL boss_jindoAI : public ScriptedAI
if (Skeletons)
Skeletons->AI()->AttackStart(target);
}
+
Teleport_Timer = 15000 + rand()%8000;
}else Teleport_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Healing Ward
struct TRINITY_DLL_DECL mob_healing_wardAI : public ScriptedAI
{
@@ -140,15 +165,20 @@ struct TRINITY_DLL_DECL mob_healing_wardAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
uint32 Heal_Timer;
+
ScriptedInstance *pInstance;
+
void Reset()
{
Heal_Timer = 2000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI (const uint32 diff)
{
//Heal_Timer
@@ -162,56 +192,71 @@ struct TRINITY_DLL_DECL mob_healing_wardAI : public ScriptedAI
}
Heal_Timer = 3000;
}else Heal_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Shade of Jindo
struct TRINITY_DLL_DECL mob_shade_of_jindoAI : public ScriptedAI
{
mob_shade_of_jindoAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowShock_Timer;
+
void Reset()
{
ShadowShock_Timer = 1000;
m_creature->CastSpell(m_creature, SPELL_INVISIBLE,true);
}
+
void EnterCombat(Unit *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* pCreature)
{
return new boss_jindoAI (pCreature);
}
+
CreatureAI* GetAI_mob_healing_ward(Creature* pCreature)
{
return new mob_healing_wardAI (pCreature);
}
+
CreatureAI* GetAI_mob_shade_of_jindo(Creature* pCreature)
{
return new mob_shade_of_jindoAI (pCreature);
}
+
void AddSC_boss_jindo()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_jindo";
newscript->GetAI = &GetAI_boss_jindo;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_healing_ward";
newscript->GetAI = &GetAI_mob_healing_ward;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shade_of_jindo";
newscript->GetAI = &GetAI_mob_shade_of_jindo;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp
index c15a9a12904..0408cdc1e95 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_mandokir.cpp
@@ -13,19 +13,23 @@
* along 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 SAY_AGGRO -1309015
#define SAY_DING_KILL -1309016
#define SAY_GRATS_JINDO -1309017
#define SAY_WATCH -1309018
#define SAY_WATCH_WHISPER -1309019 //is this text for real? easter egg?
+
#define SPELL_CHARGE 24315
#define SPELL_CLEAVE 20691
#define SPELL_FEAR 29321
@@ -34,14 +38,17 @@ EndScriptData */
#define SPELL_ENRAGE 23537
#define SPELL_WATCH 24314
#define SPELL_LEVEL_UP 24312
+
//Ohgans Spells
#define SPELL_SUNDERARMOR 24317
+
struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
{
boss_mandokirAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
uint32 KillCount;
uint32 Watch_Timer;
uint32 TargetInRange;
@@ -53,12 +60,16 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
float targetX;
float targetY;
float targetZ;
+
ScriptedInstance *m_pInstance;
+
bool endWatch;
bool someWatched;
bool RaptorDead;
bool CombatStart;
+
uint64 WatchTarget;
+
void Reset()
{
KillCount = 0;
@@ -68,25 +79,32 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
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)
{
++KillCount;
+
if (KillCount == 3)
{
DoScriptText(SAY_DING_KILL, m_creature);
+
if (m_pInstance)
{
uint64 JindoGUID = m_pInstance->GetData64(DATA_JINDO);
@@ -104,29 +122,35 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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() ||
@@ -148,6 +172,7 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
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
{
if (Unit* p = SelectUnit(SELECT_TARGET_RANDOM,0))
@@ -159,6 +184,7 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
endWatch = true;
}
}
+
if ((Watch_Timer < 1000) && endWatch) //1 sec before the debuf expire, store the target position
{
Unit* pUnit = Unit::GetUnit(*m_creature, WatchTarget);
@@ -170,6 +196,7 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
}
endWatch = false;
}
+
if (!someWatched)
{
//Cleave
@@ -178,27 +205,33 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
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<HostilReference*>::iterator i = m_creature->getThreatManager().getThreatList().begin();
- for (; i != m_creature->getThreatManager().getThreatList().end(); ++i)
+ for(; i != m_creature->getThreatManager().getThreatList().end(); ++i)
{
Unit* pUnit = Unit::GetUnit(*m_creature, (*i)->getUnitGuid());
if (pUnit && m_creature->IsWithinMeleeRange(pUnit))
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() && m_creature->getVictim()->GetHealth() < m_creature->getVictim()->GetMaxHealth()*0.5)
{
@@ -223,12 +256,15 @@ struct TRINITY_DLL_DECL boss_mandokirAI : public ScriptedAI
}
}
}
+
Check_Timer = 1000;
}else Check_Timer -= diff;
+
DoMeleeAttackIfReady();
}
}
};
+
//Ohgan
struct TRINITY_DLL_DECL mob_ohganAI : public ScriptedAI
{
@@ -236,47 +272,59 @@ struct TRINITY_DLL_DECL mob_ohganAI : public ScriptedAI
{
m_pInstance = c->GetInstanceData();
}
+
uint32 SunderArmor_Timer;
ScriptedInstance *m_pInstance;
+
void Reset()
{
SunderArmor_Timer = 5000;
}
+
void EnterCombat(Unit *who) {}
+
void JustDied(Unit* Killer)
{
if (m_pInstance)
m_pInstance->SetData(TYPE_OHGAN, DONE);
}
+
void UpdateAI (const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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* pCreature)
{
return new boss_mandokirAI (pCreature);
}
+
CreatureAI* GetAI_mob_ohgan(Creature* pCreature)
{
return new mob_ohganAI (pCreature);
}
+
void AddSC_boss_mandokir()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_mandokir";
newscript->GetAI = &GetAI_boss_mandokir;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ohgan";
newscript->GetAI = &GetAI_mob_ohgan;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp
index 6c39a2b1f10..ef29657f559 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_marli.cpp
@@ -13,32 +13,40 @@
* along 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 SAY_AGGRO -1309005
#define SAY_TRANSFORM -1309006
#define SAY_SPIDER_SPAWN -1309007
#define SAY_DEATH -1309008
+
#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.
+
struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
{
boss_marliAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
ScriptedInstance *m_pInstance;
+
uint32 SpawnStartSpiders_Timer;
uint32 PoisonVolley_Timer;
uint32 SpawnSpider_Timer;
@@ -46,9 +54,11 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
uint32 Aspect_Timer;
uint32 Transform_Timer;
uint32 TransformBack_Timer;
+
Creature *Spider;
bool Spawned;
bool PhaseTwo;
+
void Reset()
{
SpawnStartSpiders_Timer = 1000;
@@ -58,23 +68,28 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
Aspect_Timer = 12000;
Transform_Timer = 45000;
TransformBack_Timer = 25000;
+
Spawned = false;
PhaseTwo = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
if (m_pInstance)
m_pInstance->SetData(TYPE_MARLI, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (m_creature->getVictim() && m_creature->isAlive())
{
if (PoisonVolley_Timer < diff)
@@ -82,17 +97,21 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
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)
{
DoScriptText(SAY_SPIDER_SPAWN, m_creature);
+
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (!target)
return;
+
Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (Spider)
Spider->AI()->AttackStart(target);
@@ -105,18 +124,22 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (Spider)
Spider->AI()->AttackStart(target);
+
Spawned = true;
}else SpawnStartSpiders_Timer -= diff;
+
if (SpawnSpider_Timer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (!target)
return;
+
Spider = m_creature->SummonCreature(15041,target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
if (Spider)
Spider->AI()->AttackStart(target);
SpawnSpider_Timer = 12000 + rand()%5000;
}else SpawnSpider_Timer -= diff;
+
if (!PhaseTwo && Transform_Timer < diff)
{
DoScriptText(SAY_TRANSFORM, m_creature);
@@ -126,11 +149,14 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
m_creature->SetBaseWeaponDamage(BASE_ATTACK, MAXDAMAGE, (cinfo->maxdmg +((cinfo->maxdmg/100) * 35)));
m_creature->UpdateDamagePhysical(BASE_ATTACK);
DoCast(m_creature->getVictim(),SPELL_ENVOLWINGWEB);
+
if (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(m_creature->getVictim(),-100);
+
PhaseTwo = true;
Transform_Timer = 35000 + rand()%25000;
}else Transform_Timer -= diff;
+
if (PhaseTwo)
{
if (Charge_Timer < diff)
@@ -152,8 +178,10 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
//m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1);
AttackStart(target);
}
+
Charge_Timer = 8000;
}else Charge_Timer -= diff;
+
if (TransformBack_Timer < diff)
{
m_creature->SetDisplayId(15220);
@@ -161,55 +189,70 @@ struct TRINITY_DLL_DECL boss_marliAI : public ScriptedAI
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 TRINITY_DLL_DECL mob_spawn_of_marliAI : public ScriptedAI
{
mob_spawn_of_marliAI(Creature *c) : ScriptedAI(c) {}
+
uint32 LevelUp_Timer;
+
void Reset()
{
LevelUp_Timer = 3000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI (const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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* pCreature)
{
return new boss_marliAI (pCreature);
}
+
CreatureAI* GetAI_mob_spawn_of_marli(Creature* pCreature)
{
return new mob_spawn_of_marliAI (pCreature);
}
+
void AddSC_boss_marli()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_marli";
newscript->GetAI = &GetAI_boss_marli;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_spawn_of_marli";
newscript->GetAI = &GetAI_mob_spawn_of_marli;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp
index 49feb913ed8..bc6be61e253 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_renataki.cpp
@@ -13,27 +13,35 @@
* along 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
+
#define EQUIP_ID_MAIN_HAND 0 //was item display id 31818, but this id does not exist
+
struct TRINITY_DLL_DECL boss_renatakiAI : public ScriptedAI
{
boss_renatakiAI(Creature *c) : ScriptedAI(c) {}
+
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;
@@ -41,26 +49,34 @@ struct TRINITY_DLL_DECL boss_renatakiAI : public ScriptedAI
Visible_Timer = 4000;
Aggro_Timer = 15000 + rand()%10000;
ThousandBlades_Timer = 4000 + rand()%4000;
+
Invisible = false;
Ambushed = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Invisible_Timer
if (Invisible_Timer < diff)
{
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL);
+
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
m_creature->SetDisplayId(11686);
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
Invisible = true;
+
Invisible_Timer = 15000 + rand()%15000;
}else Invisible_Timer -= diff;
+
if (Invisible)
{
if (Ambush_Timer < diff)
@@ -72,40 +88,51 @@ struct TRINITY_DLL_DECL boss_renatakiAI : public ScriptedAI
DoTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
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->SetDisplayId(15268);
SetEquipmentSlots(false, EQUIP_ID_MAIN_HAND, EQUIP_NO_CHANGE, EQUIP_NO_CHANGE);
+
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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(m_creature->getVictim(),-50);
+
if (target)
AttackStart(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();
}
};
@@ -113,6 +140,7 @@ CreatureAI* GetAI_boss_renataki(Creature* pCreature)
{
return new boss_renatakiAI (pCreature);
}
+
void AddSC_boss_renataki()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp
index 3127d1e0ec0..a0693a0cdc4 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_thekal.cpp
@@ -13,16 +13,20 @@
* along 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 SAY_AGGRO -1309009
#define SAY_DEATH -1309010
+
#define SPELL_MORTALCLEAVE 22859
#define SPELL_SILENCE 23207
#define SPELL_FRENZY 23342
@@ -32,23 +36,27 @@ EndScriptData */
#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
+
struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
{
boss_thekalAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
uint32 MortalCleave_Timer;
uint32 Silence_Timer;
uint32 Frenzy_Timer;
@@ -58,10 +66,12 @@ struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
uint32 SummonTigers_Timer;
uint32 Check_Timer;
uint32 Resurrect_Timer;
+
ScriptedInstance *m_pInstance;
bool Enraged;
bool PhaseTwo;
bool WasDead;
+
void Reset()
{
MortalCleave_Timer = 4000;
@@ -73,29 +83,35 @@ struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
SummonTigers_Timer = 25000;
Check_Timer = 10000;
Resurrect_Timer = 10000;
+
Enraged = false;
PhaseTwo = false;
WasDead = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
if (m_pInstance)
m_pInstance->SetData(TYPE_THEKAL, DONE);
}
+
void JustReachedHome()
{
if (m_pInstance)
m_pInstance->SetData(TYPE_THEKAL, NOT_STARTED);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Check_Timer for the death of LorKhan and Zath.
if (!WasDead && Check_Timer < diff)
{
@@ -110,9 +126,11 @@ struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
pLorKhan->setFaction(14);
pLorKhan->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
pLorKhan->SetHealth(int(pLorKhan->GetMaxHealth()*1.0));
+
m_pInstance->SetData(TYPE_LORKHAN, DONE);
}
}
+
if (m_pInstance->GetData(TYPE_ZATH) == SPECIAL)
{
//Resurrect Zath
@@ -123,31 +141,39 @@ struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
pZath->setFaction(14);
pZath->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
pZath->SetHealth(int(pZath->GetMaxHealth()*1.0));
+
m_pInstance->SetData(TYPE_ZATH, DONE);
}
}
}
+
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->SetStandState(UNIT_STAND_STATE_SLEEP);
m_creature->AttackStop();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_THEKAL, SPECIAL);
+
WasDead=true;
}
+
//Thekal will transform to Tiger if he died and was not resurrected after 10 seconds.
if (!PhaseTwo && WasDead)
{
@@ -166,10 +192,12 @@ struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
PhaseTwo = true;
}else Resurrect_Timer -= diff;
}
+
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() == 100) && WasDead)
{
WasDead = false;
}
+
if (PhaseTwo)
{
if (Charge_Timer < diff)
@@ -180,32 +208,40 @@ struct TRINITY_DLL_DECL boss_thekalAI : public ScriptedAI
DoResetThreat();
AttackStart(target);
}
+
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 TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
{
@@ -213,13 +249,17 @@ struct TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
{
m_pInstance = c->GetInstanceData();
}
+
uint32 Shield_Timer;
uint32 BloodLust_Timer;
uint32 GreaterHeal_Timer;
uint32 Disarm_Timer;
uint32 Check_Timer;
+
bool FakeDeath;
+
ScriptedInstance *m_pInstance;
+
void Reset()
{
Shield_Timer = 1000;
@@ -227,31 +267,39 @@ struct TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
GreaterHeal_Timer = 32000;
Disarm_Timer = 6000;
Check_Timer = 10000;
+
FakeDeath = false;
+
if (m_pInstance)
m_pInstance->SetData(TYPE_LORKHAN, NOT_STARTED);
+
m_creature->SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI (const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -259,8 +307,10 @@ struct TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
{
Unit *pThekal = Unit::GetUnit((*m_creature), m_pInstance->GetData64(DATA_THEKAL));
Unit *pZath = Unit::GetUnit((*m_creature), m_pInstance->GetData64(DATA_ZATH));
+
if (!pThekal || !pZath)
return;
+
switch(rand()%2)
{
case 0:
@@ -273,14 +323,17 @@ struct TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
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)
{
@@ -297,6 +350,7 @@ struct TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
pThekal->SetHealth(int(pThekal->GetMaxHealth()*1.0));
}
}
+
if (m_pInstance->GetData(TYPE_ZATH) == SPECIAL)
{
//Resurrect Zath
@@ -309,21 +363,27 @@ struct TRINITY_DLL_DECL mob_zealot_lorkhanAI : public ScriptedAI
}
}
}
+
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->SetStandState(UNIT_STAND_STATE_SLEEP);
m_creature->setFaction(35);
m_creature->AttackStop();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_LORKHAN, SPECIAL);
+
FakeDeath = true;
}
+
DoMeleeAttackIfReady();
}
};
+
//Zealot Zath
struct TRINITY_DLL_DECL mob_zealot_zathAI : public ScriptedAI
{
@@ -331,14 +391,18 @@ struct TRINITY_DLL_DECL mob_zealot_zathAI : public ScriptedAI
{
m_pInstance = c->GetInstanceData();
}
+
uint32 SweepingStrikes_Timer;
uint32 SinisterStrike_Timer;
uint32 Gouge_Timer;
uint32 Kick_Timer;
uint32 Blind_Timer;
uint32 Check_Timer;
+
bool FakeDeath;
+
ScriptedInstance *m_pInstance;
+
void Reset()
{
SweepingStrikes_Timer = 13000;
@@ -347,51 +411,64 @@ struct TRINITY_DLL_DECL mob_zealot_zathAI : public ScriptedAI
Kick_Timer = 18000;
Blind_Timer = 5000;
Check_Timer = 10000;
+
FakeDeath = false;
+
if (m_pInstance)
m_pInstance->SetData(TYPE_ZATH, NOT_STARTED);
+
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI (const uint32 diff)
{
if (!UpdateVictim())
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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(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)
{
@@ -408,6 +485,7 @@ struct TRINITY_DLL_DECL mob_zealot_zathAI : public ScriptedAI
pLorKhan->SetHealth(int(pLorKhan->GetMaxHealth()*1.0));
}
}
+
if (m_pInstance->GetData(TYPE_THEKAL) == SPECIAL)
{
//Resurrect Thekal
@@ -420,44 +498,56 @@ struct TRINITY_DLL_DECL mob_zealot_zathAI : public ScriptedAI
}
}
}
+
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->SetStandState(UNIT_STAND_STATE_SLEEP);
m_creature->setFaction(35);
m_creature->AttackStop();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_ZATH, SPECIAL);
+
FakeDeath = true;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_thekal(Creature* pCreature)
{
return new boss_thekalAI (pCreature);
}
+
CreatureAI* GetAI_mob_zealot_lorkhan(Creature* pCreature)
{
return new mob_zealot_lorkhanAI (pCreature);
}
+
CreatureAI* GetAI_mob_zealot_zath(Creature* pCreature)
{
return new mob_zealot_zathAI (pCreature);
}
+
void AddSC_boss_thekal()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_thekal";
newscript->GetAI = &GetAI_boss_thekal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_zealot_lorkhan";
newscript->GetAI = &GetAI_mob_zealot_lorkhan;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_zealot_zath";
newscript->GetAI = &GetAI_mob_zealot_zath;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp
index 65dc89a3362..2ff7597f0fa 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_venoxis.cpp
@@ -13,16 +13,20 @@
* along 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 SAY_TRANSFORM -1309000
#define SAY_DEATH -1309001
+
#define SPELL_HOLY_FIRE 23860
#define SPELL_HOLY_WRATH 28883 //Not sure if this or 23979
#define SPELL_VENOMSPIT 23862
@@ -32,13 +36,16 @@ EndScriptData */
#define SPELL_RENEW 23895
#define SPELL_BERSERK 23537
#define SPELL_DISPELL 23859
+
struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
{
boss_venoxisAI(Creature *c) : ScriptedAI(c)
{
m_pInstance = c->GetInstanceData();
}
+
ScriptedInstance *m_pInstance;
+
uint32 HolyFire_Timer;
uint32 HolyWrath_Timer;
uint32 VenomSpit_Timer;
@@ -47,8 +54,10 @@ struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
uint32 HolyNova_Timer;
uint32 Dispell_Timer;
uint32 TargetInRange;
+
bool PhaseTwo;
bool InBerserk;
+
void Reset()
{
HolyFire_Timer = 10000;
@@ -59,22 +68,27 @@ struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
HolyNova_Timer = 5000;
Dispell_Timer = 35000;
TargetInRange = 0;
+
PhaseTwo = false;
InBerserk= false;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
if (m_pInstance)
m_pInstance->SetData(TYPE_VENOXIS, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 50))
{
if (Dispell_Timer < diff)
@@ -82,25 +96,29 @@ struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
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)
{
TargetInRange = 0;
- for (uint8 i=0; i<10; ++i)
+ for(uint8 i=0; i<10; ++i)
{
if (Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO,i))
if (m_creature->IsWithinMeleeRange(target))
TargetInRange++;
}
+
if (TargetInRange > 1)
{
DoCast(m_creature->getVictim(),SPELL_HOLY_NOVA);
@@ -110,11 +128,14 @@ struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
{
HolyNova_Timer = 2000;
}
+
}else HolyNova_Timer -= diff;
+
if (HolyFire_Timer < diff && TargetInRange < 3)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target, SPELL_HOLY_FIRE);
+
HolyFire_Timer = 8000;
}else HolyFire_Timer -= diff;
}
@@ -133,17 +154,21 @@ struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
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)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target, SPELL_VENOMSPIT);
+
VenomSpit_Timer = 15000 + rand()%5000;
}else VenomSpit_Timer -= diff;
+
if (PhaseTwo && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 11))
{
if (!InBerserk)
@@ -157,10 +182,12 @@ struct TRINITY_DLL_DECL boss_venoxisAI : public ScriptedAI
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_venoxis(Creature* pCreature)
{
return new boss_venoxisAI (pCreature);
}
+
void AddSC_boss_venoxis()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp
index bc4af57b510..4ba494a2b0a 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/boss_wushoolay.cpp
@@ -13,47 +13,59 @@
* along 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 TRINITY_DLL_DECL boss_wushoolayAI : public ScriptedAI
{
boss_wushoolayAI(Creature *c) : ScriptedAI(c) {}
+
uint32 LightningCloud_Timer;
uint32 LightningWave_Timer;
+
void Reset()
{
LightningCloud_Timer = 5000 + rand()%5000;
LightningWave_Timer = 8000 + rand()%8000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -61,6 +73,7 @@ CreatureAI* GetAI_boss_wushoolay(Creature* pCreature)
{
return new boss_wushoolayAI (pCreature);
}
+
void AddSC_boss_wushoolay()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/def_zulgurub.h b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/def_zulgurub.h
index 773076effa8..bf55a54c1d5 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/def_zulgurub.h
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/def_zulgurub.h
@@ -1,11 +1,14 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_ZULGURUB_H
#define DEF_ZULGURUB_H
+
enum eTypes
{
MAX_ENCOUNTERS = 8,
+
TYPE_ARLOKK = 1,
TYPE_JEKLIK = 2,
TYPE_VENOXIS = 3,
@@ -14,10 +17,12 @@ enum eTypes
TYPE_THEKAL = 6,
TYPE_ZATH = 7,
TYPE_LORKHAN = 8,
+
DATA_JINDO = 10,
DATA_LORKHAN = 11,
DATA_THEKAL = 12,
DATA_ZATH = 13
};
+
#endif
diff --git a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp
index 311083b270a..03b3e64c5e6 100644
--- a/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp
+++ b/src/bindings/scripts/scripts/eastern_kingdoms/zulgurub/instance_zulgurub.cpp
@@ -13,37 +13,46 @@
* along 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 TRINITY_DLL_DECL instance_zulgurub : public ScriptedInstance
{
instance_zulgurub(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
//If all High Priest bosses were killed. Lorkhan, Zath and Ohgan are added too.
uint32 m_auiEncounter[MAX_ENCOUNTERS];
+
//Storing Lorkhan, Zath and Thekal because we need to cast on them later. Jindo is needed for healfunction too.
uint64 m_uiLorKhanGUID;
uint64 m_uiZathGUID;
uint64 m_uiThekalGUID;
uint64 m_uiJindoGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiLorKhanGUID = 0;
m_uiZathGUID = 0;
m_uiThekalGUID = 0;
m_uiJindoGUID = 0;
}
+
bool IsEncounterInProgress() const
{
//not active in Zul'Gurub
return false;
}
+
void OnCreatureCreate(Creature* pCreature)
{
switch(pCreature->GetEntry())
@@ -54,6 +63,7 @@ struct TRINITY_DLL_DECL instance_zulgurub : public ScriptedInstance
case 11380: m_uiJindoGUID = pCreature->GetGUID(); break;
}
}
+
void SetData(uint32 uiType, uint32 uiData)
{
switch(uiType)
@@ -61,29 +71,37 @@ struct TRINITY_DLL_DECL instance_zulgurub : public ScriptedInstance
case TYPE_ARLOKK:
m_auiEncounter[0] = uiData;
break;
+
case TYPE_JEKLIK:
m_auiEncounter[1] = uiData;
break;
+
case TYPE_VENOXIS:
m_auiEncounter[2] = uiData;
break;
+
case TYPE_MARLI:
m_auiEncounter[3] = uiData;
break;
+
case TYPE_THEKAL:
m_auiEncounter[4] = uiData;
break;
+
case TYPE_LORKHAN:
m_auiEncounter[5] = uiData;
break;
+
case TYPE_ZATH:
m_auiEncounter[6] = uiData;
break;
+
case TYPE_OHGAN:
m_auiEncounter[7] = uiData;
break;
}
}
+
uint32 GetData(uint32 uiType)
{
switch(uiType)
@@ -107,6 +125,7 @@ struct TRINITY_DLL_DECL instance_zulgurub : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 uiData)
{
switch(uiData)
@@ -123,10 +142,12 @@ struct TRINITY_DLL_DECL instance_zulgurub : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_zulgurub(Map* pMap)
{
return new instance_zulgurub(pMap);
}
+
void AddSC_instance_zulgurub()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/examples/example_creature.cpp b/src/bindings/scripts/scripts/examples/example_creature.cpp
index bd82225f599..c932f3387f9 100644
--- a/src/bindings/scripts/scripts/examples/example_creature.cpp
+++ b/src/bindings/scripts/scripts/examples/example_creature.cpp
@@ -13,22 +13,28 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Example_Creature
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
+
enum eEnums
{
//List of text id's. The text is stored in database, also in a localized version
@@ -44,6 +50,7 @@ enum eEnums
SAY_PHASE = -1999907,
SAY_DANCE = -1999908,
SAY_SALUTE = -1999909,
+
//List of spells. Not required to define them in this way, but will make it easier to maintain in case spellId change
SPELL_BUFF = 25661,
SPELL_ONE = 12555,
@@ -52,18 +59,23 @@ enum eEnums
SPELL_THREE = 26027,
SPELL_ENRAGE = 23537,
SPELL_BESERK = 32309,
+
FACTION_WORGEN = 24
};
+
//List of gossip item texts. Items will appear in the gossip window.
#define GOSSIP_ITEM "I'm looking for a fight"
+
struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
{
//*** HANDLED FUNCTION ***
//This is the constructor, called only once when the Creature is first created
example_creatureAI(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 m_uiSay_Timer; //Timer for random chat
uint32 m_uiRebuff_Timer; //Timer for rebuffing
uint32 m_uiSpell_1_Timer; //Timer for spell 1 when in combat
@@ -72,6 +84,7 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
uint32 m_uiBeserk_Timer; //Timer until we go into Beserk (enraged) mode
uint32 m_uiPhase; //The current battle phase we are in
uint32 m_uiPhase_Timer; //Timer until phase transition
+
//*** HANDLED FUNCTION ***
//This is called whenever the core decides we need to evade
void Reset()
@@ -83,6 +96,7 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
m_uiSpell_3_Timer = 19000; //19 seconds
m_uiBeserk_Timer = 120000; //2 minutes
}
+
//*** HANDLED FUNCTION ***
//Attack Start is called whenever someone hits us.
void EnterCombat(Unit* pWho)
@@ -90,10 +104,12 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
//Say some stuff
DoScriptText(SAY_AGGRO, m_creature, pWho);
}
+
//Our Recive emote function
void ReceiveEmote(Player* pPlayer, uint32 uiTextEmote)
{
m_creature->HandleEmoteCommand(uiTextEmote);
+
switch(uiTextEmote)
{
case TEXTEMOTE_DANCE:
@@ -104,6 +120,7 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
break;
}
}
+
//*** 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 uiDiff)
@@ -116,10 +133,12 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
{
//Random switch between 5 outcomes
DoScriptText(RAND(SAY_RANDOM_0,SAY_RANDOM_1,SAY_RANDOM_2,SAY_RANDOM_3,SAY_RANDOM_4), m_creature);
+
m_uiSay_Timer = 45000; //Say something agian in 45 seconds
}
else
m_uiSay_Timer -= uiDiff;
+
//Rebuff timer
if (m_uiRebuff_Timer < uiDiff)
{
@@ -129,9 +148,11 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
else
m_uiRebuff_Timer -= uiDiff;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//Spell 1 timer
if (m_uiSpell_1_Timer < uiDiff)
{
@@ -140,10 +161,12 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_ONE_ALT);
else if (m_creature->IsWithinDist(m_creature->getVictim(), 25.0f))
DoCast(m_creature->getVictim(), SPELL_ONE);
+
m_uiSpell_1_Timer = 5000;
}
else
m_uiSpell_1_Timer -= uiDiff;
+
//Spell 2 timer
if (m_uiSpell_2_Timer < uiDiff)
{
@@ -153,6 +176,7 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
}
else
m_uiSpell_2_Timer -= uiDiff;
+
//Beserk timer
if (m_uiPhase > 1)
{
@@ -161,15 +185,18 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
{
//Cast spell one on our current target.
DoCast(m_creature->getVictim(), SPELL_THREE);
+
m_uiSpell_3_Timer = 19000;
}
else
m_uiSpell_3_Timer -= uiDiff;
+
if (m_uiBeserk_Timer < uiDiff)
{
//Say our line then cast uber death spell
DoScriptText(SAY_BESERK, m_creature, m_creature->getVictim());
DoCast(m_creature->getVictim(), SPELL_BESERK);
+
//Cast our beserk spell agian in 12 seconds if we didn't kill everyone
m_uiBeserk_Timer = 12000;
}
@@ -188,15 +215,18 @@ struct TRINITY_DLL_DECL example_creatureAI : public ScriptedAI
else
m_uiPhase_Timer -= uiDiff;
}
+
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_example_creature(Creature* pCreature)
{
return new example_creatureAI (pCreature);
}
+
//This function is called when the player clicks an option on the gossip menu
bool GossipSelect_example_creature(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
@@ -207,20 +237,25 @@ bool GossipSelect_example_creature(Player* pPlayer, Creature* pCreature, uint32
pCreature->setFaction(FACTION_WORGEN);
pCreature->AI()->AttackStart(pPlayer);
}
+
return true;
}
+
//This function is called when the player opens the gossip menu
bool GossipHello_example_creature(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(907, pCreature->GetGUID());
+
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
void AddSC_example_creature()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "example_creature";
newscript->GetAI = &GetAI_example_creature;
diff --git a/src/bindings/scripts/scripts/examples/example_escort.cpp b/src/bindings/scripts/scripts/examples/example_escort.cpp
index 973d870a9da..027268a9325 100644
--- a/src/bindings/scripts/scripts/examples/example_escort.cpp
+++ b/src/bindings/scripts/scripts/examples/example_escort.cpp
@@ -13,20 +13,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Example_Escort
SD%Complete: 100
SDComment: Script used for testing escortAI
SDCategory: Script Examples
EndScriptData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
enum eEnums
{
NPC_FELBOAR = 21878,
+
SPELL_DEATH_COIL = 33130,
SPELL_ELIXIR_OF_FORTITUDE = 3593,
SPELL_BLUE_FIREWORK = 11540,
+
SAY_AGGRO1 = -1999910,
SAY_AGGRO2 = -1999911,
SAY_WP_1 = -1999912,
@@ -40,19 +45,24 @@ enum eEnums
SAY_RAND_1 = -1999920,
SAY_RAND_2 = -1999921
};
+
#define GOSSIP_ITEM_1 "Click to Test Escort(Attack, Run)"
#define GOSSIP_ITEM_2 "Click to Test Escort(NoAttack, Walk)"
#define GOSSIP_ITEM_3 "Click to Test Escort(NoAttack, Run)"
+
struct TRINITY_DLL_DECL example_escortAI : public npc_escortAI
{
// CreatureAI functions
example_escortAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
uint32 m_uiDeathCoilTimer;
uint32 m_uiChatTimer;
+
void JustSummoned(Creature* pSummoned)
{
pSummoned->AI()->AttackStart(m_creature);
}
+
// Pure Virtual Functions (Have to be implemented)
void WaypointReached(uint32 uiWP)
{
@@ -76,6 +86,7 @@ struct TRINITY_DLL_DECL example_escortAI : public npc_escortAI
break;
}
}
+
void EnterCombat(Unit* pWho)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
@@ -86,11 +97,13 @@ struct TRINITY_DLL_DECL example_escortAI : public npc_escortAI
else
DoScriptText(SAY_AGGRO2, m_creature);
}
+
void Reset()
{
m_uiDeathCoilTimer = 4000;
m_uiChatTimer = 4000;
}
+
void JustDied(Unit* pKiller)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
@@ -109,10 +122,12 @@ struct TRINITY_DLL_DECL example_escortAI : public npc_escortAI
else
DoScriptText(SAY_DEATH_3, m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
//Must update npc_escortAI
npc_escortAI::UpdateAI(uiDiff);
+
//Combat check
if (m_creature->getVictim())
{
@@ -142,6 +157,7 @@ struct TRINITY_DLL_DECL example_escortAI : public npc_escortAI
DoScriptText(SAY_RAND_2, m_creature);
m_creature->CastSpell(m_creature, SPELL_ELIXIR_OF_FORTITUDE, false);
}
+
m_uiChatTimer = 12000;
}
else
@@ -150,45 +166,57 @@ struct TRINITY_DLL_DECL example_escortAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_example_escort(Creature* pCreature)
{
return new example_escortAI(pCreature);
}
+
bool GossipHello_example_escort(Player* pPlayer, Creature* pCreature)
{
pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
pCreature->prepareGossipMenu(pPlayer, 0);
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
+
pCreature->sendPreparedGossip(pPlayer);
+
return true;
}
+
bool GossipSelect_example_escort(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
npc_escortAI* pEscortAI = CAST_AI(example_escortAI, pCreature->AI());
+
switch(uiAction)
{
case GOSSIP_ACTION_INFO_DEF+1:
pPlayer->CLOSE_GOSSIP_MENU();
+
if (pEscortAI)
pEscortAI->Start(true, true, pPlayer->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF+2:
pPlayer->CLOSE_GOSSIP_MENU();
+
if (pEscortAI)
pEscortAI->Start(false, false, pPlayer->GetGUID());
break;
case GOSSIP_ACTION_INFO_DEF+3:
pPlayer->CLOSE_GOSSIP_MENU();
+
if (pEscortAI)
pEscortAI->Start(false, true, pPlayer->GetGUID());
break;
default:
return false; // nothing defined -> trinity core handling
}
+
return true; // no default handling -> prevent trinity core handling
}
+
void AddSC_example_escort()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/examples/example_gossip_codebox.cpp b/src/bindings/scripts/scripts/examples/example_gossip_codebox.cpp
index 80f8ff3325a..9a51d4d4c9a 100644
--- a/src/bindings/scripts/scripts/examples/example_gossip_codebox.cpp
+++ b/src/bindings/scripts/scripts/examples/example_gossip_codebox.cpp
@@ -13,32 +13,41 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Example_Gossip_Codebox
SD%Complete: 100
SDComment: Show a codebox in gossip option
SDCategory: Script Examples
EndScriptData */
+
#include "precompiled.h"
#include <cstring>
+
enum eEnums
{
SPELL_POLYMORPH = 12826,
SPELL_MARK_OF_THE_WILD = 26990,
+
SAY_NOT_INTERESTED = -1999922,
SAY_WRONG = -1999923,
SAY_CORRECT = -1999924
};
+
#define GOSSIP_ITEM_1 "A quiz: what's your name?"
#define GOSSIP_ITEM_2 "I'm not interested"
+
//This function is called when the player opens the gossip menubool
bool GossipHello_example_gossip_codebox(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1, "", 0, true);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
+
pPlayer->PlayerTalkClass->SendGossipMenu(907, pCreature->GetGUID());
+
return true;
}
+
//This function is called when the player clicks an option on the gossip menubool
bool GossipSelect_example_gossip_codebox(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
@@ -47,8 +56,10 @@ bool GossipSelect_example_gossip_codebox(Player* pPlayer, Creature* pCreature, u
DoScriptText(SAY_NOT_INTERESTED, pCreature);
pPlayer->CLOSE_GOSSIP_MENU();
}
+
return true;
}
+
bool GossipSelectWithCode_example_gossip_codebox(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction, const char* sCode)
{
if (uiSender == GOSSIP_SENDER_MAIN)
@@ -67,14 +78,18 @@ bool GossipSelectWithCode_example_gossip_codebox(Player* pPlayer, Creature* pCre
pCreature->CastSpell(pPlayer, SPELL_MARK_OF_THE_WILD, true);
}
pPlayer->CLOSE_GOSSIP_MENU();
+
return true;
}
}
+
return false;
}
+
void AddSC_example_gossip_codebox()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "example_gossip_codebox";
newscript->pGossipHello = &GossipHello_example_gossip_codebox;
diff --git a/src/bindings/scripts/scripts/examples/example_misc.cpp b/src/bindings/scripts/scripts/examples/example_misc.cpp
index a68a4a59729..667c30ad1b6 100644
--- a/src/bindings/scripts/scripts/examples/example_misc.cpp
+++ b/src/bindings/scripts/scripts/examples/example_misc.cpp
@@ -13,44 +13,54 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Example_Misc
SD%Complete: 100
SDComment: Item, Areatrigger and other small code examples
SDCategory: Script Examples
EndScriptData */
+
#include "precompiled.h"
+
enum eSay
{
SAY_HI = -1999925
};
+
bool AT_example_areatrigger(Player* pPlayer, AreaTriggerEntry *pAt)
{
DoScriptText(SAY_HI, pPlayer);
return true;
}
+
extern void LoadDatabase();
bool ItemUse_example_item(Player* pPlayer, Item* pItem, SpellCastTargets const& scTargets)
{
LoadDatabase();
return true;
}
+
bool GOHello_example_go_teleporter(Player* pPlayer, GameObject* pGo)
{
pPlayer->TeleportTo(0, 1807.07f, 336.105f, 70.3975f, 0.0f);
return false;
}
+
void AddSC_example_misc()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "example_areatrigger";
newscript->pAreaTrigger = &AT_example_areatrigger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "example_item";
newscript->pItemUse = &ItemUse_example_item;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "example_go_teleporter";
newscript->pGOHello = &GOHello_example_go_teleporter;
diff --git a/src/bindings/scripts/scripts/kalimdor/ashenvale.cpp b/src/bindings/scripts/scripts/kalimdor/ashenvale.cpp
index 1f71cc3a05a..7bb193b98be 100644
--- a/src/bindings/scripts/scripts/kalimdor/ashenvale.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ashenvale.cpp
@@ -13,44 +13,57 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Ashenvale
SD%Complete: 70
SDComment: Quest support: 6544, 6482
SDCategory: Ashenvale Forest
EndScriptData */
+
/* ContentData
npc_torek
npc_ruul_snowhoof
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*####
# npc_torek
####*/
+
#define SAY_READY -1000106
#define SAY_MOVE -1000107
#define SAY_PREPARE -1000108
#define SAY_WIN -1000109
#define SAY_END -1000110
+
#define SPELL_REND 11977
#define SPELL_THUNDERCLAP 8078
+
#define QUEST_TOREK_ASSULT 6544
+
#define ENTRY_SPLINTERTREE_RAIDER 12859
#define ENTRY_DURIEL 12860
#define ENTRY_SILVERWING_SENTINEL 12896
#define ENTRY_SILVERWING_WARRIOR 12897
+
struct TRINITY_DLL_DECL npc_torekAI : public npc_escortAI
{
npc_torekAI(Creature *c) : npc_escortAI(c) {}
+
uint32 Rend_Timer;
uint32 Thunderclap_Timer;
bool Completed;
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch (i)
{
case 1:
@@ -76,29 +89,36 @@ struct TRINITY_DLL_DECL npc_torekAI : public npc_escortAI
break;
}
}
+
void Reset()
{
Rend_Timer = 5000;
Thunderclap_Timer = 8000;
Completed = false;
}
+
void EnterCombat(Unit* who)
{
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
+
if (!UpdateVictim())
return;
+
if (Rend_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_REND);
Rend_Timer = 20000;
}else Rend_Timer -= diff;
+
if (Thunderclap_Timer < diff)
{
DoCast(m_creature,SPELL_THUNDERCLAP);
@@ -106,6 +126,7 @@ struct TRINITY_DLL_DECL npc_torekAI : public npc_escortAI
}else Thunderclap_Timer -= diff;
}
};
+
bool QuestAccept_npc_torek(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_TOREK_ASSULT)
@@ -113,28 +134,37 @@ bool QuestAccept_npc_torek(Player* pPlayer, Creature* pCreature, Quest const* qu
//TODO: find companions, make them follow Torek, at any time (possibly done by mangos/database in future?)
DoScriptText(SAY_READY, pCreature, pPlayer);
pCreature->setFaction(113);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_torekAI, pCreature->AI()))
pEscortAI->Start(true, true, pPlayer->GetGUID());
}
+
return true;
}
+
CreatureAI* GetAI_npc_torek(Creature* pCreature)
{
return new npc_torekAI(pCreature);
}
+
/*####
# npc_ruul_snowhoof
####*/
+
#define QUEST_FREEDOM_TO_RUUL 6482
#define GO_CAGE 178147
+
struct TRINITY_DLL_DECL npc_ruul_snowhoofAI : public npc_escortAI
{
npc_ruul_snowhoofAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 0: {
@@ -153,50 +183,62 @@ struct TRINITY_DLL_DECL npc_ruul_snowhoofAI : public npc_escortAI
m_creature->SummonCreature(3921, 3506.265625, -490.531006, 186.740128, 4.239277, TEMPSUMMON_DEAD_DESPAWN, 60000);
m_creature->SummonCreature(3926, 3503.682373, -489.393799, 186.629684, 4.349232, TEMPSUMMON_DEAD_DESPAWN, 60000);
break;
+
case 21:{
if (pPlayer)
pPlayer->GroupEventHappens(QUEST_FREEDOM_TO_RUUL, m_creature);
+
break; }
}
}
+
void EnterCombat(Unit* who) {}
+
void Reset()
{
GameObject* Cage = m_creature->FindNearestGameObject(GO_CAGE, 20);
if (Cage)
Cage->SetGoState(GO_STATE_READY);
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
}
};
+
bool QuestAccept_npc_ruul_snowhoof(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_FREEDOM_TO_RUUL)
{
pCreature->setFaction(113);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_ruul_snowhoofAI, (pCreature->AI())))
pEscortAI->Start(true, false, pPlayer->GetGUID());
}
return true;
}
+
CreatureAI* GetAI_npc_ruul_snowhoofAI(Creature* pCreature)
{
return new npc_ruul_snowhoofAI(pCreature);
}
+
void AddSC_ashenvale()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_torek";
newscript->GetAI = &GetAI_npc_torek;
newscript->pQuestAccept = &QuestAccept_npc_torek;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_ruul_snowhoof";
newscript->GetAI = &GetAI_npc_ruul_snowhoofAI;
diff --git a/src/bindings/scripts/scripts/kalimdor/azshara.cpp b/src/bindings/scripts/scripts/kalimdor/azshara.cpp
index c9d03c7a2a4..d0d7fad255c 100644
--- a/src/bindings/scripts/scripts/kalimdor/azshara.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/azshara.cpp
@@ -13,35 +13,44 @@
* along 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, 10994
SDCategory: Azshara
EndScriptData */
+
/* ContentData
mobs_spitelashes
npc_loramus_thalipedes
mob_rizzle_sprysprocket
mob_depth_charge
EndContentData */
+
#include "precompiled.h"
#include "World.h"
#include "WorldPacket.h"
+
/*######
## mobs_spitelashes
######*/
+
struct TRINITY_DLL_DECL mobs_spitelashesAI : public ScriptedAI
{
mobs_spitelashesAI(Creature *c) : ScriptedAI(c) {}
+
uint32 morphtimer;
bool spellhit;
+
void Reset()
{
morphtimer = 0;
spellhit = false;
}
+
void EnterCombat(Unit *who) { }
+
void SpellHit(Unit *Hitter, const SpellEntry *Spellkind)
{
if (!spellhit &&
@@ -53,6 +62,7 @@ struct TRINITY_DLL_DECL mobs_spitelashesAI : public ScriptedAI
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
@@ -73,6 +83,7 @@ struct TRINITY_DLL_DECL mobs_spitelashesAI : public ScriptedAI
}
if (!UpdateVictim())
return;
+
//TODO: add abilities for the different creatures
DoMeleeAttackIfReady();
}
@@ -81,9 +92,11 @@ CreatureAI* GetAI_mobs_spitelashes(Creature* pCreature)
{
return new mobs_spitelashesAI (pCreature);
}
+
/*######
## npc_loramus_thalipedes
######*/
+
#define GOSSIP_HELLO_LT1 "Can you help me?"
#define GOSSIP_HELLO_LT2 "Tell me your story"
#define GOSSIP_SELECT_LT1 "Please continue"
@@ -91,17 +104,23 @@ CreatureAI* GetAI_mobs_spitelashes(Creature* pCreature)
#define GOSSIP_SELECT_LT3 "Indeed"
#define GOSSIP_SELECT_LT4 "I will do this with or your help, Loramus"
#define GOSSIP_SELECT_LT5 "Yes"
+
bool GossipHello_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(2744) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_LT1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
if (pPlayer->GetQuestStatus(3141) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_LT2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -110,6 +129,7 @@ bool GossipSelect_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature, u
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->AreaExploredOrEventHappens(2744);
break;
+
case GOSSIP_ACTION_INFO_DEF+2:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SELECT_LT1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21);
pPlayer->SEND_GOSSIP_MENU(1813, pCreature->GetGUID());
@@ -137,9 +157,11 @@ bool GossipSelect_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature, u
}
return true;
}
+
/*####
# mob_rizzle_sprysprocket
####*/
+
#define MOB_DEPTH_CHARGE 23025
#define SPELL_RIZZLE_BLACKJACK 39865
#define SPELL_RIZZLE_ESCAPE 39871
@@ -147,12 +169,16 @@ bool GossipSelect_npc_loramus_thalipedes(Player* pPlayer, Creature* pCreature, u
#define SPELL_DEPTH_CHARGE_TRAP 38576
#define SPELL_PERIODIC_DEPTH_CHARGE 39912
#define SPELL_GIVE_SOUTHFURY_MOONSTONE 39886
+
#define SAY_RIZZLE_START -1000245
#define SAY_RIZZLE_GRENADE -1000246
#define SAY_RIZZLE_FINAL -1000247
+
#define GOSSIP_GET_MOONSTONE "Hand over the Southfury moonstone and I'll let you go."
+
//next message must be send to player when Rizzle jump into river, not implemented
#define MSG_ESCAPE_NOTICE "Rizzle Sprysprocket takes the Southfury moonstone and escapes into the river. Follow her!"
+
float WPs[58][4] =
{
//pos_x pos_y pos_z orien
@@ -215,20 +241,25 @@ float WPs[58][4] =
{1927.09, -3679.56, 33.9118, 3.42},
{1873.57, -3695.32, 33.9118, 3.44}
};
+
struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
{
mob_rizzle_sprysprocketAI(Creature *c) : ScriptedAI(c) {}
+
uint32 spellEscape_Timer;
uint32 Teleport_Timer;
uint32 Check_Timer;
uint32 Grenade_Timer;
uint32 Must_Die_Timer;
uint32 CurrWP;
+
uint64 PlayerGUID;
+
bool Must_Die;
bool Escape;
bool ContinueWP;
bool Reached;
+
void Reset()
{
spellEscape_Timer = 1300;
@@ -237,12 +268,15 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
Grenade_Timer = 30000;
Must_Die_Timer = 3000;
CurrWP = 0;
+
PlayerGUID = 0;
+
Must_Die = false;
Escape = false;
ContinueWP = false;
Reached = false;
}
+
void UpdateAI(const uint32 diff)
{
if (Must_Die)
@@ -251,15 +285,18 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
m_creature->ForcedDespawn();
return;
} else Must_Die_Timer -= diff;
+
if (!Escape)
{
if (!PlayerGUID)
return;
+
if (spellEscape_Timer < diff)
{
DoCast(m_creature, SPELL_RIZZLE_ESCAPE, false);
spellEscape_Timer = 10000;
} else spellEscape_Timer -= diff;
+
if (Teleport_Timer < diff)
{
//temp solution - unit can't be teleported by core using spelleffect 5, only players
@@ -279,13 +316,16 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(CurrWP, WPs[CurrWP][0], WPs[CurrWP][1], WPs[CurrWP][2]);
Escape = true;
} else Teleport_Timer -= diff;
+
return;
}
+
if (ContinueWP)
{
m_creature->GetMotionMaster()->MovePoint(CurrWP, WPs[CurrWP][0], WPs[CurrWP][1], WPs[CurrWP][2]);
ContinueWP = false;
}
+
if (Grenade_Timer < diff)
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
@@ -296,6 +336,7 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
}
Grenade_Timer = 30000;
} else Grenade_Timer -= diff;
+
if (Check_Timer < diff)
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
@@ -304,6 +345,7 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
m_creature->ForcedDespawn();
return;
}
+
if (m_creature->IsWithinDist(pPlayer, 10) && m_creature->GetPositionX() > pPlayer->GetPositionX() && !Reached)
{
DoScriptText(SAY_RIZZLE_FINAL, m_creature);
@@ -313,9 +355,12 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
m_creature->RemoveAurasDueToSpell(SPELL_PERIODIC_DEPTH_CHARGE);
Reached = true;
}
+
Check_Timer = 1000;
} else Check_Timer -= diff;
+
}
+
void SendText(const char *text, Player* pPlayer)
{
WorldPacket data(SMSG_SERVER_MESSAGE, 0); // guess size
@@ -323,10 +368,12 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
if (pPlayer)
pPlayer->GetSession()->SendPacket(&data);
}
+
void AttackStart(Unit *who)
{
if (!who || PlayerGUID)
return;
+
if (who->GetTypeId() == TYPEID_PLAYER && CAST_PLR(who)->GetQuestStatus(10994) == QUEST_STATUS_INCOMPLETE)
{
PlayerGUID = who->GetGUID();
@@ -335,20 +382,26 @@ struct TRINITY_DLL_DECL mob_rizzle_sprysprocketAI : public ScriptedAI
return;
}
}
+
void EnterCombat(Unit* who) {}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id == 57)
{
m_creature->ForcedDespawn();
return;
}
+
++CurrWP;
ContinueWP = true;
}
+
};
+
bool GossipHello_mob_rizzle_sprysprocket(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(10994) != QUEST_STATUS_INCOMPLETE)
@@ -357,6 +410,7 @@ bool GossipHello_mob_rizzle_sprysprocket(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(10811, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_mob_rizzle_sprysprocket(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1 && pPlayer->GetQuestStatus(10994) == QUEST_STATUS_INCOMPLETE)
@@ -368,18 +422,23 @@ bool GossipSelect_mob_rizzle_sprysprocket(Player* pPlayer, Creature* pCreature,
}
return true;
}
+
CreatureAI* GetAI_mob_rizzle_sprysprocket(Creature* pCreature)
{
return new mob_rizzle_sprysprocketAI (pCreature);
}
+
/*####
# mob_depth_charge
####*/
+
struct TRINITY_DLL_DECL mob_depth_chargeAI : public ScriptedAI
{
mob_depth_chargeAI(Creature *c) : ScriptedAI(c) {}
+
bool we_must_die;
uint32 must_die_timer;
+
void Reset()
{
m_creature->SetUnitMovementFlags(MOVEMENTFLAG_HOVER | MOVEMENTFLAG_SWIMMING);
@@ -387,6 +446,7 @@ struct TRINITY_DLL_DECL mob_depth_chargeAI : public ScriptedAI
we_must_die = false;
must_die_timer = 1000;
}
+
void UpdateAI(const uint32 diff)
{
if (we_must_die)
@@ -396,10 +456,12 @@ struct TRINITY_DLL_DECL mob_depth_chargeAI : public ScriptedAI
} else must_die_timer -= diff;
return;
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who)
return;
+
if (who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 5))
{
DoCast(who, SPELL_DEPTH_CHARGE_TRAP);
@@ -407,37 +469,45 @@ struct TRINITY_DLL_DECL mob_depth_chargeAI : public ScriptedAI
return;
}
}
+
void AttackStart(Unit *who)
{
return;
}
+
void EnterCombat(Unit* who)
{
return;
}
};
+
CreatureAI* GetAI_mob_depth_charge(Creature* pCreature)
{
return new mob_depth_chargeAI (pCreature);
}
+
void AddSC_azshara()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mobs_spitelashes";
newscript->GetAI = &GetAI_mobs_spitelashes;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_loramus_thalipedes";
newscript->pGossipHello = &GossipHello_npc_loramus_thalipedes;
newscript->pGossipSelect = &GossipSelect_npc_loramus_thalipedes;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_rizzle_sprysprocket";
newscript->GetAI = &GetAI_mob_rizzle_sprysprocket;
newscript->pGossipHello = &GossipHello_mob_rizzle_sprysprocket;
newscript->pGossipSelect = &GossipSelect_mob_rizzle_sprysprocket;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_depth_charge";
newscript->GetAI = &GetAI_mob_depth_charge;
diff --git a/src/bindings/scripts/scripts/kalimdor/azuremyst_isle.cpp b/src/bindings/scripts/scripts/kalimdor/azuremyst_isle.cpp
index 9dfd5f1cdfa..76d7ff0e4af 100644
--- a/src/bindings/scripts/scripts/kalimdor/azuremyst_isle.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/azuremyst_isle.cpp
@@ -13,12 +13,14 @@
* along 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, 9582, 9554, 9531, 9303(special flight path, proper model for mount missing). Injured Draenei cosmetic only
SDCategory: Azuremyst Isle
EndScriptData */
+
/* ContentData
npc_draenei_survivor
npc_engineer_spark_overgrind
@@ -27,68 +29,90 @@ npc_magwin
npc_geezle
mob_nestlewood_owlkin
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include <cmath>
+
/*######
## npc_draenei_survivor
######*/
+
enum eEnums
{
SAY_HEAL1 = -1000248,
SAY_HEAL2 = -1000249,
SAY_HEAL3 = -1000250,
SAY_HEAL4 = -1000251,
+
SAY_HELP1 = -1000252,
SAY_HELP2 = -1000253,
SAY_HELP3 = -1000254,
SAY_HELP4 = -1000255,
+
SPELL_IRRIDATION = 35046,
SPELL_STUNNED = 28630
};
+
struct TRINITY_DLL_DECL npc_draenei_survivorAI : public ScriptedAI
{
npc_draenei_survivorAI(Creature *c) : ScriptedAI(c) {}
+
uint64 pCaster;
+
uint32 SayThanksTimer;
uint32 RunAwayTimer;
uint32 SayHelpTimer;
+
bool CanSayHelp;
+
void Reset()
{
pCaster = 0;
+
SayThanksTimer = 0;
RunAwayTimer = 0;
SayHelpTimer = 10000;
+
CanSayHelp = true;
+
m_creature->CastSpell(m_creature, SPELL_IRRIDATION, true);
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
m_creature->SetHealth(int(m_creature->GetMaxHealth()*.1));
m_creature->SetStandState(UNIT_STAND_STATE_SLEEP);
}
+
void EnterCombat(Unit *who) {}
+
void MoveInLineOfSight(Unit *who)
{
if (CanSayHelp && who->GetTypeId() == TYPEID_PLAYER && m_creature->IsFriendlyTo(who) && m_creature->IsWithinDistInMap(who, 25.0f))
{
//Random switch between 4 texts
DoScriptText(RAND(SAY_HELP1, SAY_HELP2, SAY_HELP3, SAY_HELP4), m_creature, who);
+
SayHelpTimer = 20000;
CanSayHelp = false;
}
}
+
void SpellHit(Unit *Caster, const SpellEntry *Spell)
{
if (Spell->SpellFamilyFlags[2] & 0x080000000)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
m_creature->CastSpell(m_creature, SPELL_STUNNED, true);
+
pCaster = Caster->GetGUID();
+
SayThanksTimer = 5000;
}
}
+
void UpdateAI(const uint32 diff)
{
if (SayThanksTimer)
@@ -96,26 +120,34 @@ struct TRINITY_DLL_DECL npc_draenei_survivorAI : public ScriptedAI
if (SayThanksTimer <= diff)
{
m_creature->RemoveAurasDueToSpell(SPELL_IRRIDATION);
+
if (Player* pPlayer = Unit::GetPlayer(pCaster))
{
DoScriptText(RAND(SAY_HEAL1, SAY_HEAL2, SAY_HEAL3, SAY_HEAL4), m_creature, pPlayer);
+
pPlayer->TalkedToCreature(m_creature->GetEntry(),m_creature->GetGUID());
}
+
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MovePoint(0, -4115.053711f, -13754.831055f, 73.508949f);
+
RunAwayTimer = 10000;
SayThanksTimer = 0;
}else SayThanksTimer -= diff;
+
return;
}
+
if (RunAwayTimer)
{
if (RunAwayTimer <= diff)
m_creature->ForcedDespawn();
else
RunAwayTimer -= diff;
+
return;
}
+
if (SayHelpTimer < diff)
{
CanSayHelp = true;
@@ -127,47 +159,61 @@ CreatureAI* GetAI_npc_draenei_survivor(Creature* pCreature)
{
return new npc_draenei_survivorAI (pCreature);
}
+
/*######
## npc_engineer_spark_overgrind
######*/
+
enum eOvergrind
{
SAY_TEXT = -1000256,
SAY_EMOTE = -1000257,
ATTACK_YELL = -1000258,
+
AREA_COVE = 3579,
AREA_ISLE = 3639,
QUEST_GNOMERCY = 9537,
FACTION_HOSTILE = 14,
SPELL_DYNAMITE = 7978
};
+
#define GOSSIP_FIGHT "Traitor! You will be brought to justice!"
+
struct TRINITY_DLL_DECL npc_engineer_spark_overgrindAI : public ScriptedAI
{
npc_engineer_spark_overgrindAI(Creature *c) : ScriptedAI(c)
{
NormFaction = c->getFaction();
NpcFlags = c->GetUInt32Value(UNIT_NPC_FLAGS);
+
if (c->GetAreaId() == AREA_COVE || c->GetAreaId() == AREA_ISLE)
IsTreeEvent = true;
}
+
uint32 NormFaction;
uint32 NpcFlags;
+
uint32 Dynamite_Timer;
uint32 Emote_Timer;
+
bool IsTreeEvent;
+
void Reset()
{
Dynamite_Timer = 8000;
Emote_Timer = 120000 + rand()%30000;
+
m_creature->setFaction(NormFaction);
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, NpcFlags);
+
IsTreeEvent = false;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(ATTACK_YELL, m_creature, who);
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isInCombat() && !IsTreeEvent)
@@ -181,27 +227,34 @@ struct TRINITY_DLL_DECL npc_engineer_spark_overgrindAI : public ScriptedAI
}
else if (IsTreeEvent)
return;
+
if (!UpdateVictim())
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* pCreature)
{
return new npc_engineer_spark_overgrindAI (pCreature);
}
+
bool GossipHello_npc_engineer_spark_overgrind(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_GNOMERCY) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_FIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_engineer_spark_overgrind(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -212,12 +265,15 @@ bool GossipSelect_npc_engineer_spark_overgrind(Player* pPlayer, Creature* pCreat
}
return true;
}
+
/*######
## npc_injured_draenei
######*/
+
struct TRINITY_DLL_DECL npc_injured_draeneiAI : public ScriptedAI
{
npc_injured_draeneiAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
@@ -228,23 +284,29 @@ struct TRINITY_DLL_DECL npc_injured_draeneiAI : public ScriptedAI
case 1: m_creature->SetStandState(UNIT_STAND_STATE_SLEEP); break;
}
}
+
void EnterCombat(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* pCreature)
{
return new npc_injured_draeneiAI (pCreature);
}
+
/*######
## npc_magwin
######*/
+
enum eMagwin
{
SAY_START = -1000111,
@@ -253,17 +315,22 @@ enum eMagwin
SAY_END1 = -1000114,
SAY_END2 = -1000115,
EMOTE_HUG = -1000116,
+
QUEST_A_CRY_FOR_SAY_HELP = 9528
};
+
struct TRINITY_DLL_DECL npc_magwinAI : public npc_escortAI
{
npc_magwinAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 0:
@@ -282,12 +349,15 @@ struct TRINITY_DLL_DECL npc_magwinAI : public npc_escortAI
break;
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature, who);
}
+
void Reset() { }
};
+
bool QuestAccept_npc_magwin(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_A_CRY_FOR_SAY_HELP)
@@ -298,17 +368,22 @@ bool QuestAccept_npc_magwin(Player* pPlayer, Creature* pCreature, Quest const* q
}
return true;
}
+
CreatureAI* GetAI_npc_magwinAI(Creature* pCreature)
{
return new npc_magwinAI(pCreature);
}
+
/*######
## npc_geezle
######*/
+
enum eGeezle
{
QUEST_TREES_COMPANY = 9531,
+
SPELL_TREE_DISGUISE = 30298,
+
GEEZLE_SAY_1 = -1000259,
SPARK_SAY_2 = -1000260,
SPARK_SAY_3 = -1000261,
@@ -316,25 +391,35 @@ enum eGeezle
SPARK_SAY_5 = -1000263,
SPARK_SAY_6 = -1000264,
GEEZLE_SAY_7 = -1000265,
+
EMOTE_SPARK = -1000266,
+
MOB_SPARK = 17243,
GO_NAGA_FLAG = 181694
};
+
static float SparkPos[3] = {-5029.91, -11291.79, 8.096};
+
struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
{
npc_geezleAI(Creature *c) : ScriptedAI(c) {}
+
uint64 SparkGUID;
+
uint32 Step;
uint32 SayTimer;
+
bool EventStarted;
+
void Reset()
{
SparkGUID = 0;
Step = 0;
StartEvent();
}
+
void EnterCombat(Unit* who){}
+
void StartEvent()
{
Step = 0;
@@ -348,9 +433,11 @@ struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
}
SayTimer = 8000;
}
+
uint32 NextStep(uint32 Step)
{
Creature* Spark = Unit::GetCreature(*m_creature, SparkGUID);
+
switch(Step)
{
case 0:
@@ -390,6 +477,7 @@ struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
default: return 99999999;
}
}
+
// will complete Tree's company quest for all nearby players that are disguised as trees
void CompleteQuest()
{
@@ -398,7 +486,8 @@ struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
Trinity::AnyPlayerInObjectRangeCheck checker(m_creature, radius);
Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(m_creature, players, checker);
m_creature->VisitNearbyWorldObject(radius, searcher);
- for (std::list<Player*>::iterator itr = players.begin(); itr != players.end(); ++itr)
+
+ for(std::list<Player*>::iterator itr = players.begin(); itr != players.end(); ++itr)
{
if((*itr)->GetQuestStatus(QUEST_TREES_COMPANY)==QUEST_STATUS_INCOMPLETE
&&(*itr)->HasAuraEffect(SPELL_TREE_DISGUISE,3) )
@@ -407,13 +496,15 @@ struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
}
}
}
+
void DespawnNagaFlag(bool despawn)
{
std::list<GameObject*> FlagList;
m_creature->GetGameObjectListWithEntryInGrid(FlagList,GO_NAGA_FLAG, 100.0f);
+
if (!FlagList.empty())
{
- for (std::list<GameObject*>::iterator itr = FlagList.begin(); itr != FlagList.end(); ++itr)
+ for(std::list<GameObject*>::iterator itr = FlagList.begin(); itr != FlagList.end(); ++itr)
{
if (despawn)
{
@@ -424,6 +515,7 @@ struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
}
} else error_log("SD2 ERROR: FlagList is empty!");
}
+
void UpdateAI(const uint32 diff)
{
if (SayTimer < diff)
@@ -435,27 +527,34 @@ struct TRINITY_DLL_DECL npc_geezleAI : public ScriptedAI
}else SayTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_geezleAI(Creature* pCreature)
{
return new npc_geezleAI(pCreature);
}
+
/*######
## mob_nestlewood_owlkin
######*/
+
enum eOwlkin
{
SPELL_INOCULATE_OWLKIN = 29528,
ENTRY_OWLKIN = 16518,
ENTRY_OWLKIN_INOC = 16534
};
+
struct TRINITY_DLL_DECL npc_nestlewood_owlkinAI : public ScriptedAI
{
npc_nestlewood_owlkinAI(Creature *c) : ScriptedAI(c) {}
+
uint32 DespawnTimer;
+
void Reset()
{
DespawnTimer = 0;
}
+
void UpdateAI(const uint32 diff)
{
//timer gets adjusted by the triggered aura effect
@@ -468,15 +567,19 @@ struct TRINITY_DLL_DECL npc_nestlewood_owlkinAI : public ScriptedAI
return;
}else DespawnTimer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_nestlewood_owlkinAI(Creature* pCreature)
{
return new npc_nestlewood_owlkinAI (pCreature);
}
+
bool EffectDummyCreature_npc_nestlewood_owlkin(Unit *pCaster, uint32 spellId, uint32 effIndex, Creature *pCreatureTarget)
{
//always check spellid and effectindex
@@ -484,40 +587,50 @@ bool EffectDummyCreature_npc_nestlewood_owlkin(Unit *pCaster, uint32 spellId, ui
{
if (pCreatureTarget->GetEntry() != ENTRY_OWLKIN)
return true;
+
pCreatureTarget->UpdateEntry(ENTRY_OWLKIN_INOC);
+
//set despawn timer, since we want to remove Creature after a short time
CAST_AI(npc_nestlewood_owlkinAI, pCreatureTarget->AI())->DespawnTimer = 15000;
+
//always return true when we are handling this spell and effect
return true;
}
return false;
}
+
void AddSC_azuremyst_isle()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_draenei_survivor";
newscript->GetAI = &GetAI_npc_draenei_survivor;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_injured_draenei";
newscript->GetAI = &GetAI_npc_injured_draenei;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_magwin";
newscript->GetAI = &GetAI_npc_magwinAI;
newscript->pQuestAccept = &QuestAccept_npc_magwin;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_geezle";
newscript->GetAI = &GetAI_npc_geezleAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_nestlewood_owlkin";
newscript->GetAI = &GetAI_npc_nestlewood_owlkinAI;
diff --git a/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/def_blackfathom_deeps.h b/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/def_blackfathom_deeps.h
index 20cf6610a94..097554708b7 100644
--- a/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/def_blackfathom_deeps.h
+++ b/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/def_blackfathom_deeps.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_BFD_H
#define DEF_BFD_H
+
enum eEnums
{
DATA_SHRINE1 = 1,
@@ -13,7 +15,9 @@ enum eEnums
DATA_SHRINE_OF_GELIHAST = 6,
DATA_ALTAR_OF_THE_DEEPS = 7,
DATA_MAINDOOR = 8,
+
TYPE_KELRIS = 10,
TYPE_SHRINE = 11
};
+
#endif
diff --git a/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp b/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp
index 36277c56450..0845396ffa3 100644
--- a/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/blackfathom_depths/instance_blackfathom_deeps.cpp
@@ -13,22 +13,28 @@
* along 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_Blackfathom_Deeps
SD%Complete: 50
SDComment:
SDCategory: Blackfathom Deeps
EndScriptData */
+
#include "precompiled.h"
#include "def_blackfathom_deeps.h"
+
#define MAX_ENCOUNTER 2
+
/* Encounter 0 = Twilight Lord Kelris
Encounter 1 = Shrine event
Must kill twilight lord for shrine event to be possible
*/
+
struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
{
instance_blackfathom_deeps(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 m_uiTwilightLordKelrisGUID;
uint64 m_uiShrine1GUID;
uint64 m_uiShrine2GUID;
@@ -37,10 +43,13 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
uint64 m_uiShrineOfGelihastGUID;
uint64 m_uiAltarOfTheDeepsGUID;
uint64 m_uiMainDoorGUID;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiTwilightLordKelrisGUID = 0;
m_uiShrine1GUID = 0;
m_uiShrine2GUID = 0;
@@ -50,11 +59,13 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
m_uiAltarOfTheDeepsGUID = 0;
m_uiMainDoorGUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
if (pCreature->GetEntry() == 4832)
m_uiTwilightLordKelrisGUID = pCreature->GetGUID();
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -68,6 +79,7 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
case 21117: m_uiMainDoorGUID = pGo->GetGUID(); break;
}
}
+
void SetData(uint32 uiType, uint32 uiData)
{
switch(uiType)
@@ -80,6 +92,7 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 uiType)
{
switch(uiType)
@@ -89,8 +102,10 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
case TYPE_SHRINE:
return m_auiEncounter[1];
}
+
return 0;
}
+
uint64 GetData64(uint32 uiData)
{
switch(uiData)
@@ -110,8 +125,10 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
case DATA_MAINDOOR:
return m_uiMainDoorGUID;
}
+
return 0;
}
+
void CheckFires()
{
GameObject *pShrine1 = instance->GetGameObject(m_uiShrine1GUID);
@@ -125,13 +142,16 @@ struct TRINITY_DLL_DECL instance_blackfathom_deeps : public ScriptedInstance
HandleGameObject(m_uiMainDoorGUID,true);
}
};
+
InstanceData* GetInstanceData_instance_blackfathom_deeps(Map* pMap)
{
return new instance_blackfathom_deeps(pMap);
}
+
bool GoHello_fire(Player *pPlayer, GameObject* pGo)
{
ScriptedInstance *pInstance = pGo->GetInstanceData();
+
if (pInstance)
{
pGo->SetGoState(GO_STATE_ACTIVE);
@@ -139,6 +159,7 @@ bool GoHello_fire(Player *pPlayer, GameObject* pGo)
}
return false;
}
+
void AddSC_instance_blackfathom_deeps()
{
Script *newscript;
@@ -146,6 +167,7 @@ void AddSC_instance_blackfathom_deeps()
newscript->Name = "instance_blackfathom_deeps";
newscript->GetInstanceData = &GetInstanceData_instance_blackfathom_deeps;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_blackfathom_fire";
newscript->pGOHello = &GoHello_fire;
diff --git a/src/bindings/scripts/scripts/kalimdor/bloodmyst_isle.cpp b/src/bindings/scripts/scripts/kalimdor/bloodmyst_isle.cpp
index 774094b49ac..5dce0a0e9d9 100644
--- a/src/bindings/scripts/scripts/kalimdor/bloodmyst_isle.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/bloodmyst_isle.cpp
@@ -13,34 +13,44 @@
* along 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 TRINITY_DLL_DECL mob_webbed_creatureAI : public ScriptedAI
{
mob_webbed_creatureAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
}
+
void EnterCombat(Unit* who)
{
}
+
void JustDied(Unit* Killer)
{
uint32 spawnCreatureID = 0;
+
switch(rand()%3)
{
case 0:
@@ -53,6 +63,7 @@ struct TRINITY_DLL_DECL mob_webbed_creatureAI : public ScriptedAI
spawnCreatureID = possibleSpawns[rand()%31];
break;
}
+
if (spawnCreatureID)
m_creature->SummonCreature(spawnCreatureID, 0.0f, 0.0f, 0.0f, m_creature->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000);
}
@@ -61,16 +72,20 @@ CreatureAI* GetAI_mob_webbed_creature(Creature* pCreature)
{
return new mob_webbed_creatureAI (pCreature);
}
+
/*######
## npc_captured_sunhawk_agent
######*/
+
#define C_SUNHAWK_TRIGGER 17974
+
#define GOSSIP_HELLO_CSA "[PH] "
#define GOSSIP_SELECT_CSA1 "[PH] "
#define GOSSIP_SELECT_CSA2 "[PH] "
#define GOSSIP_SELECT_CSA3 "[PH] "
#define GOSSIP_SELECT_CSA4 "[PH] "
#define GOSSIP_SELECT_CSA5 "[PH] "
+
bool GossipHello_npc_captured_sunhawk_agent(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->HasAura(31609) && pPlayer->GetQuestStatus(9756) == QUEST_STATUS_INCOMPLETE)
@@ -80,8 +95,10 @@ bool GossipHello_npc_captured_sunhawk_agent(Player* pPlayer, Creature* pCreature
}
else
pPlayer->SEND_GOSSIP_MENU(9134, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_captured_sunhawk_agent(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -113,13 +130,16 @@ bool GossipSelect_npc_captured_sunhawk_agent(Player* pPlayer, Creature* pCreatur
}
return true;
}
+
void AddSC_bloodmyst_isle()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_webbed_creature";
newscript->GetAI = &GetAI_mob_webbed_creature;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_captured_sunhawk_agent";
newscript->pGossipHello = &GossipHello_npc_captured_sunhawk_agent;
diff --git a/src/bindings/scripts/scripts/kalimdor/boss_azuregos.cpp b/src/bindings/scripts/scripts/kalimdor/boss_azuregos.cpp
index 629541b0d91..56efc4f71cf 100644
--- a/src/bindings/scripts/scripts/kalimdor/boss_azuregos.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/boss_azuregos.cpp
@@ -13,14 +13,18 @@
* along 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 SAY_TELEPORT -1000100
+
#define SPELL_MARKOFFROST 23182
#define SPELL_MANASTORM 21097
#define SPELL_CHILL 21098
@@ -28,9 +32,11 @@ EndScriptData */
#define SPELL_REFLECT 22067
#define SPELL_CLEAVE 8255 //Perhaps not right ID
#define SPELL_ENRAGE 23537
+
struct TRINITY_DLL_DECL boss_azuregosAI : public ScriptedAI
{
boss_azuregosAI(Creature *c) : ScriptedAI(c) {}
+
uint32 MarkOfFrost_Timer;
uint32 ManaStorm_Timer;
uint32 Chill_Timer;
@@ -40,6 +46,7 @@ struct TRINITY_DLL_DECL boss_azuregosAI : public ScriptedAI
uint32 Cleave_Timer;
uint32 Enrage_Timer;
bool Enraged;
+
void Reset()
{
MarkOfFrost_Timer = 35000;
@@ -52,12 +59,15 @@ struct TRINITY_DLL_DECL boss_azuregosAI : public ScriptedAI
Enrage_Timer = 0;
Enraged = false;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Teleport_Timer < diff)
{
DoScriptText(SAY_TELEPORT, m_creature);
@@ -71,27 +81,32 @@ struct TRINITY_DLL_DECL boss_azuregosAI : public ScriptedAI
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)
{
@@ -99,24 +114,28 @@ struct TRINITY_DLL_DECL boss_azuregosAI : public ScriptedAI
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();
}
};
@@ -124,6 +143,7 @@ CreatureAI* GetAI_boss_azuregos(Creature* pCreature)
{
return new boss_azuregosAI (pCreature);
}
+
void AddSC_boss_azuregos()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp
index 830b3f55e08..3d9c1c3fcbf 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_epoch.cpp
@@ -5,16 +5,19 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_CURSE_OF_EXERTION 52772
#define SPELL_TIME_WARP 52766 //Time slows down, reducing attack, casting and movement speed by 70% for 6 sec.
#define SPELL_TIME_STOP 58848 //Stops time in a 50 yard sphere for 2 sec.
#define SPELL_WOUNDING_STRIKE_N 52771 //Used only on the tank
#define SPELL_WOUNDING_STRIKE_H 58830
+
//not in db
//Say
#define SAY_INTRO -1595000 //"Prince Arthas Menethil, on this day, a powerful darkness has taken hold of your soul. The death you are destined to visit upon others will this day be your own."
@@ -26,14 +29,18 @@ update creature_template set scriptname = '' where entry = '';
#define SAY_SLAY_2 -1595006 //"This is the hour of our greatest triumph!"
#define SAY_SLAY_3 -1595007 //"You were destined to fail. "
#define SAY_DEATH -1595008 //"*gurgles*"
+
struct TRINITY_DLL_DECL boss_epochAI : public ScriptedAI
{
boss_epochAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff)
@@ -41,26 +48,33 @@ struct TRINITY_DLL_DECL boss_epochAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_epoch(Creature* pCreature)
{
return new boss_epochAI (pCreature);
}
+
void AddSC_boss_epoch()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_epoch";
newscript->GetAI = &GetAI_boss_epoch;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp
index 9d6375b870a..b94d01ce4de 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_mal_ganis.cpp
@@ -5,10 +5,12 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_mal_ganis' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_CARRION_SWARM_N 52720 //A cresting wave of chaotic magic splashes over enemies in front of the caster, dealing 3230 to 3570 Shadow damage and 380 to 420 Shadow damage every 3 sec. for 15 sec.
#define SPELL_CARRION_SWARM_H 58852
@@ -16,6 +18,7 @@ update creature_template set scriptname = 'boss_mal_ganis' where entry = '';
#define SPELL_MIND_BLAST_H 58850
#define SPELL_SLEEP 52721 //Puts an enemy to sleep for up to 10 sec. Any damage caused will awaken the target.
#define SPELL_VAMPIRIC_TOUCH 52723 //Heals the caster for half the damage dealt by a melee attack.
+
//not in db
//Yell Mal'ganis
#define SAY_INTRO_1 -1595009
@@ -35,22 +38,27 @@ update creature_template set scriptname = 'boss_mal_ganis' where entry = '';
#define SAY_15HEALTH -1595023
#define SAY_ESCAPE_SPEECH_1 -1595024
#define SAY_ESCAPE_SPEECH_2 -1595025
+
struct TRINITY_DLL_DECL boss_mal_ganisAI : public ScriptedAI
{
boss_mal_ganisAI(Creature *c) : ScriptedAI(c) {}
+
bool yelled,
yelled2,
yelled3;
+
void Reset()
{
yelled = false;
yelled2 = false;
yelled3 = false;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff)
@@ -58,6 +66,7 @@ struct TRINITY_DLL_DECL boss_mal_ganisAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
if (!yelled)
{
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30)
@@ -66,6 +75,7 @@ struct TRINITY_DLL_DECL boss_mal_ganisAI : public ScriptedAI
yelled = true;
}
}
+
if (!yelled2)
{
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 15)
@@ -74,10 +84,12 @@ struct TRINITY_DLL_DECL boss_mal_ganisAI : public ScriptedAI
yelled2 = true;
}
}
+
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 1)
{
//Handle Escape Event
}
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer) {}
@@ -85,16 +97,20 @@ struct TRINITY_DLL_DECL boss_mal_ganisAI : public ScriptedAI
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3,SAY_SLAY_4), m_creature);
}
};
+
CreatureAI* GetAI_boss_mal_ganis(Creature* pCreature)
{
return new boss_mal_ganisAI (pCreature);
}
+
void AddSC_boss_mal_ganis()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_mal_ganis";
newscript->GetAI = &GetAI_boss_mal_ganis;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp
index 2a14cec9350..261ed4a5ca0 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_meathook.cpp
@@ -5,16 +5,19 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_meathook' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spell
#define SPELL_CONSTRICTING_CHAINS_N 52696 //Encases the targets in chains, dealing 1800 Physical damage every 1 sec. and stunning the target for 5 sec.
#define SPELL_CONSTRICTING_CHAINS_H 58823
#define SPELL_DISEASE_EXPULSION_N 52666 //Meathook belches out a cloud of disease, dealing 1710 to 1890 Nature damage and interrupting the spell casting of nearby enemy targets for 4 sec.
#define SPELL_DISEASE_EXPULSION_H 58824
#define SPELL_FRENZY 58841 //Increases the caster's Physical damage by 10% for 30 sec.
+
//not in db
//Yell
#define SAY_AGGRO -1595026
@@ -23,23 +26,29 @@ update creature_template set scriptname = 'boss_meathook' where entry = '';
#define SAY_SLAY_3 -1595029
#define SAY_SPAWN -1595030
#define SAY_DEATH -1595031
+
struct TRINITY_DLL_DECL boss_meathookAI : public ScriptedAI
{
boss_meathookAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Chain_Timer,
Disease_Timer,
Frenzy_Timer;
+
void Reset()
{
Chain_Timer = 12000 + rand()%5000; //seen on video 13, 17, 15, 12, 16
Disease_Timer = 2000 + rand()%1000; //approx 3s
Frenzy_Timer = 20000 + rand()%10000; //made it up
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void AttackStart(Unit* who) {}
+
std::list <Unit*>pList;
void MoveInLineOfSight(Unit* who, const uint32 diff)
{
@@ -48,62 +57,76 @@ struct TRINITY_DLL_DECL boss_meathookAI : public ScriptedAI
pList.append(who);
}*/
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Disease_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_DISEASE_EXPULSION_N);
Disease_Timer = 1500 + rand()%2500;
}else Disease_Timer -= diff;
+
if (Frenzy_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FRENZY);
Frenzy_Timer = 20000 + rand()%10000;
}else Frenzy_Timer -= diff;
+
if (Chain_Timer < diff)
{
+
/*
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator itr;
+
int st=0;
- for (itr = m_threatlist.begin(); itr != m_threatlist.end(); ++itr)
+ for(itr = m_threatlist.begin(); itr != m_threatlist.end(); ++itr)
{
//st++;
m_creature->getThreatManager().
}
Unit* targets[st];
int st2=0;
- for (int i=1; i<=st; ++i){
+ for(int i=1; i<=st; ++i){
if (!IsWithinLOSInMap(targets[i])
st2++;
}
Unit* targets_out_of_LOS[st2];*/
+
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 1), SPELL_CONSTRICTING_CHAINS_N); //anyone but the tank
Chain_Timer = 2000 + rand()%1000;
}else Chain_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_meathook(Creature* pCreature)
{
return new boss_meathookAI (pCreature);
}
+
void AddSC_boss_meathook()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_meathook";
newscript->GetAI = &GetAI_boss_meathook;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp
index 6829cd70dd2..2982eb5a1b8 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/boss_salramm.cpp
@@ -5,10 +5,12 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_salramm' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_CURSE_OF_TWISTED_FLESH 58845
#define SPELL_EXPLODE_GHOUL_N 52480
@@ -17,6 +19,7 @@ update creature_template set scriptname = 'boss_salramm' where entry = '';
#define SPELL_SHADOW_BOLT_H 58828
#define SPELL_STEAL_FLESH 52708
#define SPELL_SUMMON_GHOULS 52451
+
//not in db
//Yell
#define SAY_AGGRO -1595032
@@ -32,14 +35,17 @@ update creature_template set scriptname = 'boss_salramm' where entry = '';
#define SAY_STEAL_FLESH_3 -1595042
#define SAY_SUMMON_GHOULS_1 -1595043
#define SAY_SUMMON_GHOULS_2 -1595044
+
struct TRINITY_DLL_DECL boss_salrammAI : public ScriptedAI
{
boss_salrammAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Curse_flesh_Timer,
Explode_ghoul_Timer,
Shadow_bolt_Timer,
Steal_flesh_Timer,
Summon_ghouls_Timer;
+
void Reset()
{
Curse_flesh_Timer = 30000; //30s DBM
@@ -48,8 +54,10 @@ struct TRINITY_DLL_DECL boss_salrammAI : public ScriptedAI
Steal_flesh_Timer = 12345;
Summon_ghouls_Timer = 19000 + rand()%5000; //on a video approx 24s after aggro
}
+
void EnterCombat(Unit* who)
{DoScriptText(SAY_AGGRO, m_creature);}
+
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff)
@@ -57,13 +65,16 @@ struct TRINITY_DLL_DECL boss_salrammAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
Unit* random_target = SelectUnit(SELECT_TARGET_RANDOM, 0);
+
//Curse of twisted flesh timer
if (Curse_flesh_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CURSE_OF_TWISTED_FLESH);
Curse_flesh_Timer = 37000;
}else Curse_flesh_Timer -= diff;
+
//Shadow bolt timer
if (Shadow_bolt_Timer < diff)
{
@@ -71,6 +82,7 @@ struct TRINITY_DLL_DECL boss_salrammAI : public ScriptedAI
DoCast(random_target,SPELL_SHADOW_BOLT_N);
Shadow_bolt_Timer = 8000 + rand()%4000;
}else Shadow_bolt_Timer -= diff;
+
//Steal Flesh timer
if (Steal_flesh_Timer < diff)
{
@@ -90,6 +102,7 @@ struct TRINITY_DLL_DECL boss_salrammAI : public ScriptedAI
DoCast(random_target,SPELL_STEAL_FLESH);
Steal_flesh_Timer = 10000;
}else Steal_flesh_Timer -= diff;
+
//Summon ghouls timer
if (Summon_ghouls_Timer < diff)
{
@@ -106,24 +119,31 @@ struct TRINITY_DLL_DECL boss_salrammAI : public ScriptedAI
DoCast(random_target,SPELL_SUMMON_GHOULS);
Summon_ghouls_Timer = 10000;
}else Summon_ghouls_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{DoScriptText(SAY_DEATH, m_creature);}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_salramm(Creature* pCreature)
{
return new boss_salrammAI (pCreature);
}
+
void AddSC_boss_salramm()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_salramm";
newscript->GetAI = &GetAI_boss_salramm;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/def_culling_of_stratholme.h b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/def_culling_of_stratholme.h
index ae7e93dc32b..849549e8f99 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/def_culling_of_stratholme.h
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/def_culling_of_stratholme.h
@@ -1,3 +1,4 @@
#ifndef DEF_CULLING_OF_STRATHOLME_H
#define DEF_CULLING_OF_STRATHOLME_H
+
#endif
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp
index 81abb37776c..35ee68ea991 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/culling_of_stratholme/instance_culling_of_stratholme.cpp
@@ -1,13 +1,16 @@
#include "precompiled.h"
#include "def_culling_of_stratholme.h"
+
struct TRINITY_DLL_DECL instance_culling_of_stratholme : public ScriptedInstance
{
instance_culling_of_stratholme(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
};
+
InstanceData* GetInstanceData_instance_culling_of_stratholme(Map* pMap)
{
return new instance_culling_of_stratholme(pMap);
}
+
void AddSC_instance_culling_of_stratholme()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp
index b743501788d..ea3bdc45a6f 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_aeonus.cpp
@@ -13,14 +13,17 @@
* along 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: 80
SDComment: Some spells not implemented
SDCategory: Caverns of Time, The Dark Portal
EndScriptData */
+
#include "precompiled.h"
#include "def_dark_portal.h"
+
enum eEnums
{
SAY_ENTER = -1269012,
@@ -30,12 +33,14 @@ enum eEnums
SAY_SLAY2 = -1269016,
SAY_DEATH = -1269017,
EMOTE_FRENZY = -1269018,
+
SPELL_CLEAVE = 40504,
SPELL_TIME_STOP = 31422,
SPELL_ENRAGE = 37605,
SPELL_SAND_BREATH = 31473,
H_SPELL_SAND_BREATH = 39049
};
+
struct TRINITY_DLL_DECL boss_aeonusAI : public ScriptedAI
{
boss_aeonusAI(Creature *c) : ScriptedAI(c)
@@ -43,21 +48,26 @@ struct TRINITY_DLL_DECL boss_aeonusAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
bool HeroicMode;
+
uint32 SandBreath_Timer;
uint32 TimeStop_Timer;
uint32 Frenzy_Timer;
+
void Reset()
{
SandBreath_Timer = 15000+rand()%15000;
TimeStop_Timer = 10000+rand()%5000;
Frenzy_Timer = 30000+rand()%15000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void MoveInLineOfSight(Unit *who)
{
//Despawn Time Keeper
@@ -69,38 +79,46 @@ struct TRINITY_DLL_DECL boss_aeonusAI : public ScriptedAI
m_creature->DealDamage(who, who->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(TYPE_RIFT,DONE);
pInstance->SetData(TYPE_MEDIVH,DONE);//FIXME: later should be removed
}
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Sand Breath
if (SandBreath_Timer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_SAND_BREATH, H_SPELL_SAND_BREATH));
SandBreath_Timer = 15000+rand()%10000;
}else SandBreath_Timer -= diff;
+
//Time Stop
if (TimeStop_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_TIME_STOP);
TimeStop_Timer = 20000+rand()%15000;
}else TimeStop_Timer -= diff;
+
//Frenzy
if (Frenzy_Timer < diff)
{
@@ -108,13 +126,16 @@ struct TRINITY_DLL_DECL boss_aeonusAI : public ScriptedAI
DoCast(m_creature, SPELL_ENRAGE);
Frenzy_Timer = 20000+rand()%15000;
}else Frenzy_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_aeonus(Creature* pCreature)
{
return new boss_aeonusAI (pCreature);
}
+
void AddSC_boss_aeonus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp
index f5dce6ba27c..eb0471fc278 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_chrono_lord_deja.cpp
@@ -13,14 +13,17 @@
* along 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: 65
SDComment: All abilities not implemented
SDCategory: Caverns of Time, The Dark Portal
EndScriptData */
+
#include "precompiled.h"
#include "def_dark_portal.h"
+
enum eEnums
{
SAY_ENTER = -1269006,
@@ -29,6 +32,7 @@ enum eEnums
SAY_SLAY1 = -1269009,
SAY_SLAY2 = -1269010,
SAY_DEATH = -1269011,
+
SPELL_ARCANE_BLAST = 31457,
H_SPELL_ARCANE_BLAST = 38538,
SPELL_ARCANE_DISCHARGE = 31472,
@@ -36,6 +40,7 @@ enum eEnums
SPELL_TIME_LAPSE = 31467,
SPELL_ATTRACTION = 38540 //Not Implemented (Heroic mode)
};
+
struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
{
boss_chrono_lord_dejaAI(Creature *c) : ScriptedAI(c)
@@ -43,12 +48,15 @@ struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
bool HeroicMode;
+
uint32 ArcaneBlast_Timer;
uint32 TimeLapse_Timer;
uint32 Attraction_Timer;
uint32 ArcaneDischarge_Timer;
+
void Reset()
{
ArcaneBlast_Timer = 18000+rand()%5000;
@@ -56,10 +64,12 @@ struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
ArcaneDischarge_Timer = 20000+rand()%10000;
Attraction_Timer = 25000+rand()%10000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void MoveInLineOfSight(Unit *who)
{
//Despawn Time Keeper
@@ -71,29 +81,36 @@ struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
m_creature->DealDamage(who, who->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_RIFT,SPECIAL);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Arcane Blast
if (ArcaneBlast_Timer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_ARCANE_BLAST, H_SPELL_ARCANE_BLAST));
ArcaneBlast_Timer = 15000+rand()%10000;
}else ArcaneBlast_Timer -= diff;
+
//Arcane Discharge
if (ArcaneDischarge_Timer < diff)
{
@@ -101,6 +118,7 @@ struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
DoCast(target,HEROIC(SPELL_ARCANE_DISCHARGE, H_SPELL_ARCANE_DISCHARGE));
ArcaneDischarge_Timer = 20000+rand()%10000;
}else ArcaneDischarge_Timer -= diff;
+
//Time Lapse
if (TimeLapse_Timer < diff)
{
@@ -108,6 +126,7 @@ struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
DoCast(m_creature, SPELL_TIME_LAPSE);
TimeLapse_Timer = 15000+rand()%10000;
}else TimeLapse_Timer -= diff;
+
if (HeroicMode)
{
if (Attraction_Timer < diff)
@@ -116,13 +135,16 @@ struct TRINITY_DLL_DECL boss_chrono_lord_dejaAI : public ScriptedAI
Attraction_Timer = 25000+rand()%10000;
}else Attraction_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_chrono_lord_deja(Creature* pCreature)
{
return new boss_chrono_lord_dejaAI (pCreature);
}
+
void AddSC_boss_chrono_lord_deja()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp
index fda6c49ae94..5b09e4cb117 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/boss_temporus.cpp
@@ -13,14 +13,17 @@
* along 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: 75
SDComment: More abilities need to be implemented
SDCategory: Caverns of Time, The Dark Portal
EndScriptData */
+
#include "precompiled.h"
#include "def_dark_portal.h"
+
enum eEnums
{
SAY_ENTER = -1269000,
@@ -29,12 +32,14 @@ enum eEnums
SAY_SLAY1 = -1269003,
SAY_SLAY2 = -1269004,
SAY_DEATH = -1269005,
+
SPELL_HASTE = 31458,
SPELL_MORTAL_WOUND = 31464,
SPELL_WING_BUFFET = 31475,
H_SPELL_WING_BUFFET = 38593,
SPELL_REFLECT = 38592 //Not Implemented (Heroic mod)
};
+
struct TRINITY_DLL_DECL boss_temporusAI : public ScriptedAI
{
boss_temporusAI(Creature *c) : ScriptedAI(c)
@@ -42,12 +47,15 @@ struct TRINITY_DLL_DECL boss_temporusAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
bool HeroicMode;
+
uint32 Haste_Timer;
uint32 SpellReflection_Timer;
uint32 MortalWound_Timer;
uint32 WingBuffet_Timer;
+
void Reset()
{
Haste_Timer = 15000+rand()%8000;
@@ -55,20 +63,25 @@ struct TRINITY_DLL_DECL boss_temporusAI : public ScriptedAI
MortalWound_Timer = 8000;
WingBuffet_Timer = 25000+rand()%10000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_RIFT,SPECIAL);
}
+
void MoveInLineOfSight(Unit *who)
{
//Despawn Time Keeper
@@ -77,34 +90,41 @@ struct TRINITY_DLL_DECL boss_temporusAI : public ScriptedAI
if (m_creature->IsWithinDistInMap(who,20.0f))
{
DoScriptText(SAY_BANISH, m_creature);
+
m_creature->DealDamage(who, who->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Attack Haste
if (Haste_Timer < diff)
{
DoCast(m_creature, SPELL_HASTE);
Haste_Timer = 20000+rand()%5000;
}else Haste_Timer -= diff;
+
//MortalWound_Timer
if (MortalWound_Timer < diff)
{
DoCast(m_creature, SPELL_MORTAL_WOUND);
MortalWound_Timer = 10000+rand()%10000;
}else MortalWound_Timer -= diff;
+
//Wing ruffet
if (WingBuffet_Timer < diff)
{
DoCast(m_creature,HEROIC(SPELL_WING_BUFFET, H_SPELL_WING_BUFFET));
WingBuffet_Timer = 20000+rand()%10000;
}else WingBuffet_Timer -= diff;
+
if (HeroicMode)
{
if (SpellReflection_Timer < diff)
@@ -113,13 +133,16 @@ struct TRINITY_DLL_DECL boss_temporusAI : public ScriptedAI
SpellReflection_Timer = 25000+rand()%10000;
}else SpellReflection_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_temporus(Creature* pCreature)
{
return new boss_temporusAI (pCreature);
}
+
void AddSC_boss_temporus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp
index b8721cd8666..6984f287950 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/dark_portal.cpp
@@ -13,19 +13,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Dark_Portal
SD%Complete: 30
SDComment: Misc NPC's and mobs for instance. Most here far from complete.
SDCategory: Caverns of Time, The Dark Portal
EndScriptData */
+
/* ContentData
npc_medivh_bm
npc_time_rift
npc_saat
EndContentData */
+
#include "precompiled.h"
#include "def_dark_portal.h"
+
#define SAY_ENTER -1269020 //where does this belong?
#define SAY_INTRO -1269021
#define SAY_WEAK75 -1269022
@@ -35,46 +39,62 @@ EndContentData */
#define SAY_WIN -1269026
#define SAY_ORCS_ENTER -1269027
#define SAY_ORCS_ANSWER -1269028
+
#define SPELL_CHANNEL 31556
#define SPELL_PORTAL_RUNE 32570 //aura(portal on ground effect)
+
#define SPELL_BLACK_CRYSTAL 32563 //aura
#define SPELL_PORTAL_CRYSTAL 32564 //summon
+
#define SPELL_BANISH_PURPLE 32566 //aura
#define SPELL_BANISH_GREEN 32567 //aura
+
#define SPELL_CORRUPT 31326
#define SPELL_CORRUPT_AEONUS 37853
+
#define C_COUNCIL_ENFORCER 17023
+
struct TRINITY_DLL_DECL npc_medivh_bmAI : public ScriptedAI
{
npc_medivh_bmAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 SpellCorrupt_Timer;
uint32 Check_Timer;
+
bool Life75;
bool Life50;
bool Life25;
+
void Reset()
{
SpellCorrupt_Timer = 0;
+
if (!pInstance)
return;
+
if (pInstance->GetData(TYPE_MEDIVH) == IN_PROGRESS)
m_creature->CastSpell(m_creature,SPELL_CHANNEL,true);
else if (m_creature->HasAura(SPELL_CHANNEL))
m_creature->RemoveAura(SPELL_CHANNEL);
+
m_creature->CastSpell(m_creature,SPELL_PORTAL_RUNE,true);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!pInstance)
return;
+
if (who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 10.0f))
{
if (pInstance->GetData(TYPE_MEDIVH) == IN_PROGRESS || pInstance->GetData(TYPE_MEDIVH) == DONE)
return;
+
DoScriptText(SAY_INTRO, m_creature);
pInstance->SetData(TYPE_MEDIVH,IN_PROGRESS);
m_creature->CastSpell(m_creature,SPELL_CHANNEL,false);
@@ -84,6 +104,7 @@ struct TRINITY_DLL_DECL npc_medivh_bmAI : public ScriptedAI
{
if (pInstance->GetData(TYPE_MEDIVH) != IN_PROGRESS)
return;
+
uint32 entry = who->GetEntry();
if (entry == C_ASSAS || entry == C_WHELP || entry == C_CHRON || entry == C_EXECU || entry == C_VANQU)
{
@@ -97,37 +118,48 @@ struct TRINITY_DLL_DECL npc_medivh_bmAI : public ScriptedAI
}
}
}
+
void AttackStart(Unit *who)
{
//if (pInstance && pInstance->GetData(TYPE_MEDIVH) == IN_PROGRESS)
//return;
+
//ScriptedAI::AttackStart(who);
}
+
void EnterCombat(Unit *who) {}
+
void SpellHit(Unit* caster, const SpellEntry* spell)
{
if (SpellCorrupt_Timer)
return;
+
if (spell->Id == SPELL_CORRUPT_AEONUS)
SpellCorrupt_Timer = 1000;
+
if (spell->Id == SPELL_CORRUPT)
SpellCorrupt_Timer = 3000;
}
+
void JustDied(Unit* Killer)
{
if (Killer->GetEntry() == m_creature->GetEntry())
return;
+
DoScriptText(SAY_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance)
return;
+
if (SpellCorrupt_Timer)
{
if (SpellCorrupt_Timer <= diff)
{
pInstance->SetData(TYPE_MEDIVH,SPECIAL);
+
if (m_creature->HasAura(SPELL_CORRUPT_AEONUS))
SpellCorrupt_Timer = 1000;
else if (m_creature->HasAura(SPELL_CORRUPT))
@@ -136,12 +168,15 @@ struct TRINITY_DLL_DECL npc_medivh_bmAI : public ScriptedAI
SpellCorrupt_Timer = 0;
}else SpellCorrupt_Timer -= diff;
}
+
if (Check_Timer)
{
if (Check_Timer <= diff)
{
uint32 pct = pInstance->GetData(DATA_SHIELD);
+
Check_Timer = 5000;
+
if (Life25 && pct <= 25)
{
DoScriptText(SAY_WEAK25, m_creature);
@@ -157,6 +192,7 @@ struct TRINITY_DLL_DECL npc_medivh_bmAI : public ScriptedAI
DoScriptText(SAY_WEAK75, m_creature);
Life75 = false;
}
+
//if we reach this it means event was running but at some point reset.
if (pInstance->GetData(TYPE_MEDIVH) == NOT_STARTED)
{
@@ -165,122 +201,159 @@ struct TRINITY_DLL_DECL npc_medivh_bmAI : public ScriptedAI
m_creature->Respawn();
return;
}
+
if (pInstance->GetData(TYPE_RIFT) == DONE)
{
DoScriptText(SAY_WIN, m_creature);
Check_Timer = 0;
+
if (m_creature->HasAura(SPELL_CHANNEL))
m_creature->RemoveAura(SPELL_CHANNEL);
+
//TODO: start the post-event here
pInstance->SetData(TYPE_MEDIVH,DONE);
}
}else Check_Timer -= diff;
}
+
//if (!UpdateVictim())
//return;
+
//DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_medivh_bm(Creature* pCreature)
{
return new npc_medivh_bmAI (pCreature);
}
+
struct Wave
{
uint32 PortalMob[4]; //spawns for portal waves (in order)
};
+
static Wave PortalWaves[]=
{
{C_ASSAS, C_WHELP, C_CHRON, 0},
{C_EXECU, C_CHRON, C_WHELP, C_ASSAS},
{C_EXECU, C_VANQU, C_CHRON, C_ASSAS}
};
+
struct TRINITY_DLL_DECL npc_time_riftAI : public ScriptedAI
{
npc_time_riftAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 TimeRiftWave_Timer;
uint8 mRiftWaveCount;
uint8 mPortalCount;
uint8 mWaveId;
+
void Reset()
{
+
TimeRiftWave_Timer = 15000;
mRiftWaveCount = 0;
+
if (!pInstance)
return;
+
mPortalCount = pInstance->GetData(DATA_PORTAL_COUNT);
+
if (mPortalCount < 6)
mWaveId = 0;
else if (mPortalCount > 12)
mWaveId = 2;
else mWaveId = 1;
+
}
void EnterCombat(Unit *who) {}
+
void DoSummonAtRift(uint32 creature_entry)
{
if (!creature_entry)
return;
+
if (pInstance && pInstance->GetData(TYPE_MEDIVH) != IN_PROGRESS)
{
m_creature->InterruptNonMeleeSpells(true);
m_creature->RemoveAllAuras();
return;
}
+
Position pos;
m_creature->GetRandomNearPosition(pos, 10.0f);
+
//normalize Z-level if we can, if rift is not at ground level.
pos.m_positionZ = std::max(m_creature->GetMap()->GetHeight(pos.m_positionX, pos.m_positionY, MAX_HEIGHT), m_creature->GetMap()->GetWaterLevel(pos.m_positionX, pos.m_positionY));
+
if(Unit *Summon = DoSummon(creature_entry, pos, 30000, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT))
if (Unit *temp = Unit::GetUnit(*m_creature, pInstance ? pInstance->GetData64(DATA_MEDIVH) : 0))
Summon->AddThreat(temp,0.0f);
}
+
void DoSelectSummon()
{
uint32 entry = 0;
+
if ((mRiftWaveCount > 2 && mWaveId < 1) || mRiftWaveCount > 3)
mRiftWaveCount = 0;
+
entry = PortalWaves[mWaveId].PortalMob[mRiftWaveCount];
debug_log("TSCR: npc_time_rift: summoning wave Creature (Wave %u, Entry %u).",mRiftWaveCount,entry);
+
++mRiftWaveCount;
+
if (entry == C_WHELP)
{
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
DoSummonAtRift(entry);
}else DoSummonAtRift(entry);
}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance)
return;
+
if (TimeRiftWave_Timer < diff)
{
DoSelectSummon();
TimeRiftWave_Timer = 15000;
}else TimeRiftWave_Timer -= diff;
+
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
debug_log("TSCR: npc_time_rift: not casting anylonger, i need to die.");
m_creature->setDeathState(JUST_DIED);
+
if (pInstance->GetData(TYPE_RIFT) == IN_PROGRESS)
pInstance->SetData(TYPE_RIFT,SPECIAL);
}
};
+
CreatureAI* GetAI_npc_time_rift(Creature* pCreature)
{
return new npc_time_riftAI (pCreature);
}
+
#define SAY_SAAT_WELCOME -1269019
+
#define GOSSIP_ITEM_OBTAIN "[PH] Obtain Chrono-Beacon"
#define SPELL_CHRONO_BEACON 34975
#define ITEM_CHRONO_BEACON 24289
+
bool GossipHello_npc_saat(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE && !pPlayer->HasItemCount(ITEM_CHRONO_BEACON,1))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT,GOSSIP_ITEM_OBTAIN,GOSSIP_SENDER_MAIN,GOSSIP_ACTION_INFO_DEF+1);
@@ -293,9 +366,11 @@ bool GossipHello_npc_saat(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(10001, pCreature->GetGUID());
return true;
}
+
pPlayer->SEND_GOSSIP_MENU(10002, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_saat(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -305,17 +380,21 @@ bool GossipSelect_npc_saat(Player* pPlayer, Creature* pCreature, uint32 uiSender
}
return true;
}
+
void AddSC_dark_portal()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_medivh_bm";
newscript->GetAI = &GetAI_npc_medivh_bm;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_time_rift";
newscript->GetAI = &GetAI_npc_time_rift;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_saat";
newscript->pGossipHello = &GossipHello_npc_saat;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/def_dark_portal.h b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/def_dark_portal.h
index 6c2572c4fe8..7bfd8c917d7 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/def_dark_portal.h
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/def_dark_portal.h
@@ -1,18 +1,24 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_DARKPORTAL_H
#define DEF_DARKPORTAL_H
+
#define TYPE_MEDIVH 1
#define TYPE_RIFT 2
+
#define DATA_MEDIVH 10
#define DATA_PORTAL_COUNT 11
#define DATA_SHIELD 12
+
#define WORLD_STATE_BM 2541
#define WORLD_STATE_BM_SHIELD 2540
#define WORLD_STATE_BM_RIFT 2784
+
#define QUEST_OPENING_PORTAL 10297
#define QUEST_MASTER_TOUCH 9836
+
#define C_TIME_KEEPER 17918
#define C_RKEEP 21104
#define C_RLORD 17839
@@ -24,5 +30,6 @@
#define C_CHRON 17892
#define C_EXECU 18994
#define C_VANQU 18995
+
#endif
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp
index f12a20e929c..b6d08ee82d8 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/dark_portal/instance_dark_portal.cpp
@@ -13,20 +13,28 @@
* along 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_Dark_Portal
SD%Complete: 50
SDComment: Quest support: 9836, 10297. Currently in progress.
SDCategory: Caverns of Time, The Dark Portal
EndScriptData */
+
#include "precompiled.h"
#include "def_dark_portal.h"
+
#define MAX_ENCOUNTER 2
+
#define C_MEDIVH 15608
#define C_TIME_RIFT 17838
+
#define SPELL_RIFT_CHANNEL 31387
+
#define RIFT_BOSS 1
+
inline uint32 RandRiftBoss() { return ((rand()%2) ? C_RKEEP : C_RLORD); }
+
float PortalLocation[4][4]=
{
{-2041.06, 7042.08, 29.99, 1.30},
@@ -34,11 +42,13 @@ float PortalLocation[4][4]=
{-1885.82, 7107.36, 22.32, 3.07},
{-1928.11, 7175.95, 22.11, 3.44}
};
+
struct Wave
{
uint32 PortalBoss; //protector of current portal
uint32 NextPortalTime; //time to next portal, or 0 if portal boss need to be killed
};
+
static Wave RiftWaves[]=
{
{RIFT_BOSS, 0},
@@ -48,62 +58,81 @@ static Wave RiftWaves[]=
{RIFT_BOSS, 120000},
{C_AEONUS, 0}
};
+
struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
{
instance_dark_portal(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
uint32 mRiftPortalCount;
uint32 mShieldPercent;
uint8 mRiftWaveCount;
uint8 mRiftWaveId;
+
uint32 NextPortal_Timer;
+
uint64 MedivhGUID;
uint8 CurrentRiftId;
+
void Initialize()
{
MedivhGUID = 0;
Clear();
}
+
void Clear()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
mRiftPortalCount = 0;
mShieldPercent = 100;
mRiftWaveCount = 0;
mRiftWaveId = 0;
+
CurrentRiftId = 0;
+
NextPortal_Timer = 0;
}
+
void InitWorldState(bool Enable = true)
{
DoUpdateWorldState(WORLD_STATE_BM,Enable ? 1 : 0);
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, 100);
DoUpdateWorldState(WORLD_STATE_BM_RIFT, 0);
}
+
bool IsEncounterInProgress()
{
if (GetData(TYPE_MEDIVH) == IN_PROGRESS)
return true;
+
return false;
}
+
void OnPlayerEnter(Player* pPlayer)
{
if (GetData(TYPE_MEDIVH) == IN_PROGRESS)
return;
+
pPlayer->SendUpdateWorldState(WORLD_STATE_BM,0);
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
if (pCreature->GetEntry() == C_MEDIVH)
MedivhGUID = pCreature->GetGUID();
}
+
//what other conditions to check?
bool CanProgressEvent()
{
if (instance->GetPlayers().isEmpty())
return false;
+
return true;
}
+
uint8 GetRiftWaveId()
{
switch(mRiftPortalCount)
@@ -120,6 +149,7 @@ struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
return mRiftWaveId;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -128,7 +158,9 @@ struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
if (data == SPECIAL && m_auiEncounter[0] == IN_PROGRESS)
{
--mShieldPercent;
+
DoUpdateWorldState(WORLD_STATE_BM_SHIELD, mShieldPercent);
+
if (!mShieldPercent)
{
if (Creature* pMedivh = instance->GetCreature(MedivhGUID))
@@ -151,25 +183,29 @@ struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
m_auiEncounter[1] = IN_PROGRESS;
NextPortal_Timer = 15000;
}
+
if (data == DONE)
{
//this may be completed further out in the post-event
debug_log("TSCR: Instance Dark Portal: Event completed.");
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* pPlayer = itr->getSource())
{
if (pPlayer->GetQuestStatus(QUEST_OPENING_PORTAL) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(QUEST_OPENING_PORTAL);
+
if (pPlayer->GetQuestStatus(QUEST_MASTER_TOUCH) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(QUEST_MASTER_TOUCH);
}
}
}
}
+
m_auiEncounter[0] = data;
}
break;
@@ -184,6 +220,7 @@ struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -199,44 +236,59 @@ struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
if (data == DATA_MEDIVH)
return MedivhGUID;
+
return 0;
}
+
Creature* SummonedPortalBoss(Creature* m_creature)
{
uint32 entry = RiftWaves[GetRiftWaveId()].PortalBoss;
+
if (entry == RIFT_BOSS)
entry = RandRiftBoss();
+
debug_log("TSCR: Instance Dark Portal: Summoning rift boss entry %u.",entry);
+
Position pos;
m_creature->GetRandomNearPosition(pos, 10.0f);
+
//normalize Z-level if we can, if rift is not at ground level.
pos.m_positionZ = std::max(m_creature->GetMap()->GetHeight(pos.m_positionX, pos.m_positionY, MAX_HEIGHT), m_creature->GetMap()->GetWaterLevel(pos.m_positionX, pos.m_positionY));
+
if(Creature *summon = m_creature->SummonCreature(entry, pos, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 600000))
return summon;
+
debug_log("TSCR: Instance Dark Portal: what just happened there? No boss, no loot, no fun...");
return NULL;
}
+
void DoSpawnPortal()
{
if (Creature* pMedivh = instance->GetCreature(MedivhGUID))
{
int tmp = rand()%(4-1);
+
if (tmp >= CurrentRiftId)
tmp++;
debug_log("TSCR: Instance Dark Portal: Creating Time Rift at locationId %i (old locationId was %u).",tmp,CurrentRiftId);
+
CurrentRiftId = tmp;
+
Creature* pTemp = pMedivh->SummonCreature(C_TIME_RIFT,
PortalLocation[tmp][0],PortalLocation[tmp][1],PortalLocation[tmp][2],PortalLocation[tmp][3],
TEMPSUMMON_CORPSE_DESPAWN,0);
if (pTemp)
{
+
pTemp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
pTemp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
if (Creature* pBoss = SummonedPortalBoss(pTemp))
{
if (pBoss->GetEntry() == C_AEONUS)
@@ -252,35 +304,43 @@ struct TRINITY_DLL_DECL instance_dark_portal : public ScriptedInstance
}
}
}
+
void Update(uint32 diff)
{
if (m_auiEncounter[1] != IN_PROGRESS)
return;
+
//add delay timer?
if (!CanProgressEvent())
{
Clear();
return;
}
+
if (NextPortal_Timer)
{
if (NextPortal_Timer <= diff)
{
++mRiftPortalCount;
+
DoUpdateWorldState(WORLD_STATE_BM_RIFT, mRiftPortalCount);
+
DoSpawnPortal();
NextPortal_Timer = RiftWaves[GetRiftWaveId()].NextPortalTime;
}else NextPortal_Timer -= diff;
}
}
};
+
InstanceData* GetInstanceData_instance_dark_portal(Map* pMap)
{
return new instance_dark_portal(pMap);
}
+
void AddSC_instance_dark_portal()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "instance_dark_portal";
newscript->GetInstanceData = &GetInstanceData_instance_dark_portal;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp
index b0a299f9c09..96e56462385 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_anetheron.cpp
@@ -2,32 +2,40 @@
#include "precompiled.h"
#include "def_hyjal.h"
#include "hyjal_trash.h"
+
#define SPELL_CARRION_SWARM 31306
#define SPELL_SLEEP 31298
#define SPELL_VAMPIRIC_AURA 38196
#define SPELL_INFERNO 31299
+
#define SAY_ONDEATH "The clock... is still... ticking."
#define SOUND_ONDEATH 10982
+
#define SAY_ONSLAY1 "Your hopes are lost!"
#define SAY_ONSLAY2 "Scream for me!"
#define SAY_ONSLAY3 "Pity, no time for a slow death!"
#define SOUND_ONSLAY1 10981
#define SOUND_ONSLAY2 11038
#define SOUND_ONSLAY3 11039
+
#define SAY_SWARM1 "The swarm is eager to feed!"
#define SAY_SWARM2 "Pestilence upon you!"
#define SOUND_SWARM1 10979
#define SOUND_SWARM2 11037
+
#define SAY_SLEEP1 "You look tired..."
#define SAY_SLEEP2 "Sweet dreams..."
#define SOUND_SLEEP1 10978
#define SOUND_SLEEP2 11545
+
#define SAY_INFERNO1 "Let fire rain from above!"
#define SAY_INFERNO2 "Earth and sky shall burn!"
#define SOUND_INFERNO1 10980
#define SOUND_INFERNO2 11036
+
#define SAY_ONAGGRO "You are defenders of a doomed world! Flee here, and perhaps you will prolong your pathetic lives!"
#define SOUND_ONAGGRO 10977
+
struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
{
boss_anetheronAI(Creature *c) : hyjal_trashAI(c)
@@ -42,12 +50,14 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
TempSpell->EffectImplicitTargetB[0] = 0;
}
}
+
uint32 SwarmTimer;
uint32 SleepTimer;
uint32 AuraTimer;
uint32 InfernoTimer;
bool pGo;
uint32 pos;
+
void Reset()
{
damageTaken = 0;
@@ -55,9 +65,11 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
SleepTimer = 60000;
AuraTimer = 5000;
InfernoTimer = 45000;
+
if (pInstance && IsEvent)
pInstance->SetData(DATA_ANETHERONEVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance && IsEvent)
@@ -65,6 +77,7 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
DoPlaySoundToSet(m_creature, SOUND_ONAGGRO);
m_creature->MonsterYell(SAY_ONAGGRO, LANG_UNIVERSAL, 0);
}
+
void KilledUnit(Unit *victim)
{
switch(rand()%3)
@@ -83,6 +96,7 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
break;
}
}
+
void WaypointReached(uint32 i)
{
pos = i;
@@ -93,6 +107,7 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
m_creature->AddThreat(target,0.0);
}
}
+
void JustDied(Unit *victim)
{
hyjal_trashAI::JustDied(victim);
@@ -101,6 +116,7 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
DoPlaySoundToSet(m_creature, SOUND_ONDEATH);
m_creature->MonsterYell(SAY_ONDEATH, LANG_UNIVERSAL, 0);
}
+
void UpdateAI(const uint32 diff)
{
if (IsEvent)
@@ -125,13 +141,16 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
}
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (SwarmTimer < diff)
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
DoCast(target,SPELL_CARRION_SWARM);
+
SwarmTimer = 45000+rand()%15000;
switch(rand()%2)
{
@@ -145,9 +164,10 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
break;
}
}else SwarmTimer -= diff;
+
if (SleepTimer < diff)
{
- for (uint8 i=0; i<3; ++i)
+ for(uint8 i=0;i<3; ++i)
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
target->CastSpell(target,SPELL_SLEEP,true);
@@ -186,15 +206,19 @@ struct TRINITY_DLL_DECL boss_anetheronAI : public hyjal_trashAI
break;
}
}else InfernoTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_anetheron(Creature* pCreature)
{
return new boss_anetheronAI (pCreature);
}
+
#define SPELL_IMMOLATION 31303
#define SPELL_INFERNO_EFFECT 31302
+
struct TRINITY_DLL_DECL mob_towering_infernalAI : public ScriptedAI
{
mob_towering_infernalAI(Creature *c) : ScriptedAI(c)
@@ -203,30 +227,40 @@ struct TRINITY_DLL_DECL mob_towering_infernalAI : public ScriptedAI
if (pInstance)
AnetheronGUID = pInstance->GetData64(DATA_ANETHERON);
}
+
uint32 ImmolationTimer;
uint32 CheckTimer;
uint64 AnetheronGUID;
ScriptedInstance* pInstance;
+
void Reset()
{
DoCast(m_creature, SPELL_INFERNO_EFFECT);
ImmolationTimer = 5000;
CheckTimer = 5000;
}
+
void EnterCombat(Unit *who)
{
+
}
+
void KilledUnit(Unit *victim)
{
+
}
+
void JustDied(Unit *victim)
{
+
}
+
void MoveInLineOfSight(Unit *who)
{
if (m_creature->IsWithinDist(who, 50) && !m_creature->isInCombat() && m_creature->IsHostileTo(who))
AttackStart(who);
}
+
void UpdateAI(const uint32 diff)
{
if (CheckTimer < diff)
@@ -243,21 +277,26 @@ struct TRINITY_DLL_DECL mob_towering_infernalAI : public ScriptedAI
}
CheckTimer = 5000;
}else CheckTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (ImmolationTimer < diff)
{
DoCast(m_creature, SPELL_IMMOLATION);
ImmolationTimer = 5000;
}else ImmolationTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_towering_infernal(Creature* pCreature)
{
return new mob_towering_infernalAI (pCreature);
}
+
void AddSC_boss_anetheron()
{
Script *newscript;
@@ -265,6 +304,7 @@ void AddSC_boss_anetheron()
newscript->Name = "boss_anetheron";
newscript->GetAI = &GetAI_boss_anetheron;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_towering_infernal";
newscript->GetAI = &GetAI_mob_towering_infernal;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp
index b2c4d377b8a..fa4c779d9b8 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_archimonde.cpp
@@ -13,16 +13,19 @@
* along 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: 85
SDComment: Doomfires not completely offlike due to core limitations for random moving. Tyrande and second phase not fully implemented.
SDCategory: Caverns of Time, Mount Hyjal
EndScriptData */
+
#include "precompiled.h"
#include "def_hyjal.h"
#include "SpellAuras.h"
#include "hyjal_trash.h"
+
//text id -1534018 are the text used when previous events complete. Not part of this script.
#define SAY_AGGRO -1534019
#define SAY_DOOMFIRE1 -1534020
@@ -36,11 +39,14 @@ EndScriptData */
#define SAY_DEATH -1534028
#define SAY_SOUL_CHARGE1 -1534029
#define SAY_SOUL_CHARGE2 -1534030
+
#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
@@ -55,14 +61,17 @@ EndScriptData */
#define SPELL_UNLEASH_SOUL_GREEN 32057
#define SPELL_UNLEASH_SOUL_RED 32053
#define SPELL_FEAR 31970
+
#define CREATURE_ARCHIMONDE 17968
#define CREATURE_DOOMFIRE 18095
#define CREATURE_DOOMFIRE_SPIRIT 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)
@@ -70,18 +79,25 @@ struct mob_ancient_wispAI : public ScriptedAI
pInstance = c->GetInstanceData();
ArchimondeGUID = 0;
}
+
ScriptedInstance* pInstance;
uint64 ArchimondeGUID;
uint32 CheckTimer;
+
void Reset()
{
CheckTimer = 1000;
+
if (pInstance)
ArchimondeGUID = pInstance->GetData64(DATA_ARCHIMONDE);
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
void EnterCombat(Unit* who) {}
+
void DamageTaken(Unit* done_by, uint32 &damage) { damage = 0; }
+
void UpdateAI(const uint32 diff)
{
if (CheckTimer < diff)
@@ -97,28 +113,35 @@ struct mob_ancient_wispAI : public ScriptedAI
}else CheckTimer -= diff;
}
};
+
/* This script is merely a placeholder for the Doomfire that triggers Doomfire spell. It will
MoveChase the Doomfire Spirit always, until despawn (AttackStart is called upon it's spawn) */
struct TRINITY_DLL_DECL mob_doomfireAI : public ScriptedAI
{
mob_doomfireAI(Creature* c) : ScriptedAI(c) {}
+
void Reset() { }
+
void MoveInLineOfSight(Unit* who) { }
void EnterCombat(Unit* who) { }
void DamageTaken(Unit *done_by, uint32 &damage) { damage = 0; }
};
+
/* This is the script for the Doomfire Spirit Mob. This mob simply follow players or
travels in random directions if target cannot be found. */
struct TRINITY_DLL_DECL mob_doomfire_targettingAI : public ScriptedAI
{
mob_doomfire_targettingAI(Creature* c) : ScriptedAI(c) {}
+
uint64 TargetGUID;
uint32 ChangeTargetTimer;
+
void Reset()
{
TargetGUID = 0;
ChangeTargetTimer = 5000;
}
+
void MoveInLineOfSight(Unit* who)
{
//will update once TargetGUID is 0. In case noone actually moves(not likely) and this is 0
@@ -126,8 +149,11 @@ struct TRINITY_DLL_DECL mob_doomfire_targettingAI : public ScriptedAI
if (!TargetGUID && who->GetTypeId() == TYPEID_PLAYER)
TargetGUID = who->GetGUID();
}
+
void EnterCombat(Unit* who) {}
+
void DamageTaken(Unit *done_by, uint32 &damage) { damage = 0; }
+
void UpdateAI(const uint32 diff)
{
if (ChangeTargetTimer < diff)
@@ -143,10 +169,12 @@ struct TRINITY_DLL_DECL mob_doomfire_targettingAI : public ScriptedAI
m_creature->GetRandomNearPosition(pos, 40);
m_creature->GetMotionMaster()->MovePoint(0, pos.m_positionX, pos.m_positionY, pos.m_positionZ);
}
+
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
@@ -154,15 +182,19 @@ struct TRINITY_DLL_DECL mob_doomfire_targettingAI : public ScriptedAI
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 Spirit) for the Doomfire mob to follow. It's spirit will
randomly select it's target to follow and then we create the random movement making it unpredictable. */
+
struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
{
boss_archimondeAI(Creature *c) : hyjal_trashAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint64 DoomfireSpiritGUID;
uint64 WorldTreeGUID;
+
uint32 DrainNordrassilTimer;
uint32 FearTimer;
uint32 AirBurstTimer;
@@ -176,17 +208,21 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
uint32 WispCount;
uint32 EnrageTimer;
uint32 CheckDistanceTimer;
+
bool Enraged;
bool BelowTenPercent;
bool HasProtected;
bool IsChanneling;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_ARCHIMONDEEVENT, NOT_STARTED);
+
DoomfireSpiritGUID = 0;
damageTaken = 0;
WorldTreeGUID = 0;
+
DrainNordrassilTimer = 0;
FearTimer = 42000;
AirBurstTimer = 30000;
@@ -200,25 +236,31 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
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
SummonWispTimer = 0;
+
Enraged = false;
BelowTenPercent = false;
HasProtected = false;
IsChanneling = false;
}
+
void EnterCombat(Unit *who)
{
m_creature->InterruptSpell(CURRENT_CHANNELED_SPELL);
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
+
if (pInstance)
pInstance->SetData(DATA_ARCHIMONDEEVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
+
if (victim && (victim->GetTypeId() == TYPEID_PLAYER))
GainSoulCharge(CAST_PLR(victim));
}
+
void GainSoulCharge(Player* victim)
{
switch(victim->getClass())
@@ -239,35 +281,43 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
victim->CastSpell(m_creature, SPELL_SOUL_CHARGE_GREEN, true);
break;
}
+
SoulChargeTimer = 2000 + rand()%28000;
++SoulChargeCount;
}
+
void JustDied(Unit *victim)
{
hyjal_trashAI::JustDied(victim);
DoScriptText(SAY_DEATH, m_creature);
+
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<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
if (m_threatlist.empty())
return false;
+
std::list<Unit*> targets;
std::list<HostilReference*>::iterator itr = m_threatlist.begin();
- for (; itr != m_threatlist.end(); ++itr)
+ 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(ObjectDistanceOrder(m_creature));
Unit* target = targets.front();
if (target)
@@ -277,8 +327,10 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
else // This target is closest, he is our new tank
m_creature->AddThreat(target, m_creature->getThreatManager().getThreat(m_creature->getVictim()));
}
+
return false;
}
+
void JustSummoned(Creature *summoned)
{
if (summoned->GetEntry() == CREATURE_ANCIENT_WISP)
@@ -289,14 +341,17 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
if (summoned->GetEntry() == CREATURE_DOOMFIRE_SPIRIT)
{
DoomfireSpiritGUID = summoned->GetGUID();
}
+
if (summoned->GetEntry() == CREATURE_DOOMFIRE)
{
summoned->CastSpell(summoned,SPELL_DOOMFIRE_SPAWN,false);
summoned->CastSpell(summoned,SPELL_DOOMFIRE,true,0,0,m_creature->GetGUID());
+
if (Unit *DoomfireSpirit = Unit::GetUnit(*m_creature, DoomfireSpiritGUID))
{
summoned->GetMotionMaster()->MoveFollow(DoomfireSpirit,0.0f,0.0f);
@@ -304,22 +359,27 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
}
}
}
+
//this is code doing close to what the summoning spell would do (spell 31903)
void SummonDoomfire(Unit* target)
{
m_creature->SummonCreature(CREATURE_DOOMFIRE_SPIRIT,
target->GetPositionX()+15.0,target->GetPositionY()+15.0,target->GetPositionZ(),0,
TEMPSUMMON_TIMED_DESPAWN, 27000);
+
m_creature->SummonCreature(CREATURE_DOOMFIRE,
target->GetPositionX()-15.0,target->GetPositionY()-15.0,target->GetPositionZ(),0,
TEMPSUMMON_TIMED_DESPAWN, 27000);
}
+
void UnleashSoulCharge()
{
m_creature->InterruptNonMeleeSpells(false);
+
bool HasCast = false;
uint32 chargeSpell = 0;
uint32 unleashSpell = 0;
+
switch(rand()%3)
{
case 0:
@@ -335,6 +395,7 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
unleashSpell = SPELL_UNLEASH_SOUL_GREEN;
break;
}
+
if (m_creature->HasAura(chargeSpell))
{
m_creature->RemoveAuraFromStack(chargeSpell);
@@ -342,9 +403,11 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
HasCast = true;
SoulChargeCount--;
}
+
if (HasCast)
SoulChargeTimer = 2000 + rand()%28000;
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isInCombat())
@@ -363,13 +426,16 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
m_creature->SetVisibility(VISIBILITY_ON);
}
}
+
if (DrainNordrassilTimer < diff)
{
if (!IsChanneling)
{
Creature *temp = m_creature->SummonCreature(CREATURE_CHANNEL_TARGET, NORDRASSIL_X, NORDRASSIL_Y, NORDRASSIL_Z, 0, TEMPSUMMON_TIMED_DESPAWN, 1200000);
+
if (temp)
WorldTreeGUID = temp->GetGUID();
+
if (Unit *Nordrassil = Unit::GetUnit(*m_creature, WorldTreeGUID))
{
Nordrassil->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -378,6 +444,7 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
IsChanneling = true;
}
}
+
if (Unit *Nordrassil = Unit::GetUnit(*m_creature, WorldTreeGUID))
{
Nordrassil->CastSpell(m_creature, SPELL_DRAIN_WORLD_TREE_2, true);
@@ -385,10 +452,13 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
}
}else DrainNordrassilTimer -= diff;
}
+
if (!UpdateVictim())
return;
+
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !BelowTenPercent && !Enraged)
BelowTenPercent = true;
+
if (!Enraged)
{
if (EnrageTimer < diff)
@@ -401,6 +471,7 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
DoScriptText(SAY_ENRAGE, m_creature);
}
}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
@@ -408,6 +479,7 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
if (Check)
{
Check->SetVisibility(VISIBILITY_OFF);
+
if (m_creature->IsWithinDistInMap(Check, 75))
{
m_creature->GetMotionMaster()->Clear(false);
@@ -419,26 +491,31 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
CheckDistanceTimer = 5000;
}else CheckDistanceTimer -= diff;
}
+
if (BelowTenPercent)
{
if (!HasProtected)
{
m_creature->GetMotionMaster()->Clear(false);
m_creature->GetMotionMaster()->MoveIdle();
+
//all members of raid must get this buff
DoCast(m_creature->getVictim(), SPELL_PROTECTION_OF_ELUNE);
HasProtected = true;
Enraged = true;
}
+
if (SummonWispTimer < diff)
{
DoSpawnCreature(CREATURE_ANCIENT_WISP, rand()%40, rand()%40, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 15000);
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)
@@ -448,45 +525,55 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
}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)
DoScriptText(SAY_AIR_BURST1, m_creature);
else
DoScriptText(SAY_AIR_BURST2, m_creature);
+
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 1), SPELL_AIR_BURST);//not on tank
AirBurstTimer = 25000 + rand()%15000;
}else AirBurstTimer -= diff;
+
if (FearTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FEAR);
FearTimer = 42000;
}else FearTimer -= diff;
+
if (DoomfireTimer < diff)
{
if (rand()%2 == 0)
DoScriptText(SAY_DOOMFIRE1, m_creature);
else
DoScriptText(SAY_DOOMFIRE2, m_creature);
+
Unit *temp = SelectUnit(SELECT_TARGET_RANDOM, 1);
if (!temp)
temp = m_creature->getVictim();
+
//replace with spell cast 31903 once implicitTarget 73 implemented
SummonDoomfire(temp);
+
//supposedly three doomfire can be up at the same time
DoomfireTimer = 20000;
}else DoomfireTimer -= diff;
+
if (MeleeRangeCheckTimer < diff)
{
if (CanUseFingerOfDeath())
@@ -494,28 +581,35 @@ struct TRINITY_DLL_DECL boss_archimondeAI : public hyjal_trashAI
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FINGER_OF_DEATH);
MeleeRangeCheckTimer = 1000;
}
+
MeleeRangeCheckTimer = 5000;
}else MeleeRangeCheckTimer -= diff;
+
DoMeleeAttackIfReady();
}
void WaypointReached(uint32 i){}
};
+
CreatureAI* GetAI_boss_archimonde(Creature* pCreature)
{
return new boss_archimondeAI (pCreature);
}
+
CreatureAI* GetAI_mob_doomfire(Creature* pCreature)
{
return new mob_doomfireAI(pCreature);
}
+
CreatureAI* GetAI_mob_doomfire_targetting(Creature* pCreature)
{
return new mob_doomfire_targettingAI(pCreature);
}
+
CreatureAI* GetAI_mob_ancient_wisp(Creature* pCreature)
{
return new mob_ancient_wispAI(pCreature);
}
+
void AddSC_boss_archimonde()
{
Script *newscript;
@@ -523,14 +617,17 @@ void AddSC_boss_archimonde()
newscript->Name = "boss_archimonde";
newscript->GetAI = &GetAI_boss_archimonde;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_doomfire";
newscript->GetAI = &GetAI_mob_doomfire;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_doomfire_targetting";
newscript->GetAI = &GetAI_mob_doomfire_targetting;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ancient_wisp";
newscript->GetAI = &GetAI_mob_ancient_wisp;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp
index 1e4280c182e..a61d009284f 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_azgalor.cpp
@@ -2,25 +2,31 @@
#include "precompiled.h"
#include "def_hyjal.h"
#include "hyjal_trash.h"
+
#define SPELL_RAIN_OF_FIRE 31340
#define SPELL_DOOM 31347
#define SPELL_HOWL_OF_AZGALOR 31344
#define SPELL_CLEAVE 31345
#define SPELL_BERSERK 26662
+
#define SAY_ONDEATH "Your time is almost... up"
#define SOUND_ONDEATH 11002
+
#define SAY_ONSLAY1 "Reesh, hokta!"
#define SAY_ONSLAY2 "Don't fight it"
#define SAY_ONSLAY3 "No one is going to save you"
#define SOUND_ONSLAY1 11001
#define SOUND_ONSLAY2 11048
#define SOUND_ONSLAY3 11047
+
#define SAY_DOOM1 "Just a taste... of what awaits you"
#define SAY_DOOM2 "Suffer you despicable insect!"
#define SOUND_DOOM1 11046
#define SOUND_DOOM2 11000
+
#define SAY_ONAGGRO "Abandon all hope! The legion has returned to finish what was begun so many years ago. This time there will be no escape!"
#define SOUND_ONAGGRO 10999
+
struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
{
boss_azgalorAI(Creature *c) : hyjal_trashAI(c)
@@ -32,14 +38,17 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
if (TempSpell)
TempSpell->EffectRadiusIndex[0] = 12;//100yards instead of 50000?!
}
+
uint32 RainTimer;
uint32 DoomTimer;
uint32 HowlTimer;
uint32 CleaveTimer;
uint32 EnrageTimer;
bool enraged;
+
bool pGo;
uint32 pos;
+
void Reset()
{
damageTaken = 0;
@@ -49,9 +58,11 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
CleaveTimer = 10000;
EnrageTimer = 600000;
enraged = false;
+
if (pInstance && IsEvent)
pInstance->SetData(DATA_AZGALOREVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance && IsEvent)
@@ -59,6 +70,7 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
DoPlaySoundToSet(m_creature, SOUND_ONAGGRO);
m_creature->MonsterYell(SAY_ONAGGRO, LANG_UNIVERSAL, NULL);
}
+
void KilledUnit(Unit *victim)
{
switch(rand()%3)
@@ -77,6 +89,7 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
break;
}
}
+
void WaypointReached(uint32 i)
{
pos = i;
@@ -87,6 +100,7 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
m_creature->AddThreat(target,0.0);
}
}
+
void JustDied(Unit *victim)
{
hyjal_trashAI::JustDied(victim);
@@ -94,6 +108,7 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
pInstance->SetData(DATA_AZGALOREVENT, DONE);
DoPlaySoundToSet(m_creature, SOUND_ONDEATH);
}
+
void UpdateAI(const uint32 diff)
{
if (IsEvent)
@@ -118,29 +133,35 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
}
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (RainTimer < diff)
{
DoCast(SelectTarget(SELECT_TARGET_RANDOM,0,30,true), SPELL_RAIN_OF_FIRE);
RainTimer = 20000+rand()%15000;
}else RainTimer -= diff;
+
if (DoomTimer < diff)
{
DoCast(SelectTarget(SELECT_TARGET_RANDOM,1,100,true), SPELL_DOOM);//never on tank
DoomTimer = 45000+rand()%5000;
}else DoomTimer -= diff;
+
if (HowlTimer < diff)
{
DoCast(m_creature, SPELL_HOWL_OF_AZGALOR);
HowlTimer = 30000;
}else HowlTimer -= diff;
+
if (CleaveTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_CLEAVE);
CleaveTimer = 10000+rand()%5000;
}else CleaveTimer -= diff;
+
if (EnrageTimer < diff && !enraged)
{
m_creature->InterruptNonMeleeSpells(false);
@@ -148,16 +169,20 @@ struct TRINITY_DLL_DECL boss_azgalorAI : public hyjal_trashAI
enraged = true;
EnrageTimer = 600000;
}else EnrageTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_azgalor(Creature* pCreature)
{
return new boss_azgalorAI (pCreature);
}
+
#define SPELL_THRASH 12787
#define SPELL_CRIPPLE 31406
#define SPELL_WARSTOMP 31408
+
struct TRINITY_DLL_DECL mob_lesser_doomguardAI : public hyjal_trashAI
{
mob_lesser_doomguardAI(Creature *c) : hyjal_trashAI(c)
@@ -166,11 +191,13 @@ struct TRINITY_DLL_DECL mob_lesser_doomguardAI : public hyjal_trashAI
if (pInstance)
AzgalorGUID = pInstance->GetData64(DATA_AZGALOR);
}
+
uint32 CrippleTimer;
uint32 WarstompTimer;
uint32 CheckTimer;
uint64 AzgalorGUID;
ScriptedInstance* pInstance;
+
void Reset()
{
CrippleTimer = 50000;
@@ -178,23 +205,32 @@ struct TRINITY_DLL_DECL mob_lesser_doomguardAI : public hyjal_trashAI
DoCast(m_creature, SPELL_THRASH);
CheckTimer = 5000;
}
+
void EnterCombat(Unit *who)
{
}
+
void KilledUnit(Unit *victim)
{
+
}
+
void WaypointReached(uint32 i)
{
+
}
+
void MoveInLineOfSight(Unit *who)
{
if (m_creature->IsWithinDist(who, 50) && !m_creature->isInCombat() && m_creature->IsHostileTo(who))
AttackStart(who);
}
+
void JustDied(Unit *victim)
{
+
}
+
void UpdateAI(const uint32 diff)
{
if (CheckTimer < diff)
@@ -211,26 +247,32 @@ struct TRINITY_DLL_DECL mob_lesser_doomguardAI : public hyjal_trashAI
}
CheckTimer = 5000;
}else CheckTimer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (WarstompTimer < diff)
{
DoCast(m_creature, SPELL_WARSTOMP);
WarstompTimer = 10000+rand()%5000;
}else WarstompTimer -= diff;
+
if (CrippleTimer < diff)
{
DoCast(SelectTarget(SELECT_TARGET_RANDOM,0,100,true), SPELL_CRIPPLE);
CrippleTimer = 25000+rand()%5000;
}else CrippleTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_lesser_doomguard(Creature* pCreature)
{
return new mob_lesser_doomguardAI (pCreature);
}
+
void AddSC_boss_azgalor()
{
Script *newscript;
@@ -238,6 +280,7 @@ void AddSC_boss_azgalor()
newscript->Name = "boss_azgalor";
newscript->GetAI = &GetAI_boss_azgalor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_lesser_doomguard";
newscript->GetAI = &GetAI_mob_lesser_doomguard;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp
index 57d88073fa1..d3f87daec28 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_kazrogal.cpp
@@ -2,22 +2,28 @@
#include "precompiled.h"
#include "def_hyjal.h"
#include "hyjal_trash.h"
+
#define SPELL_CLEAVE 31436
#define SPELL_WARSTOMP 31480
#define SPELL_MARK 31447
+
#define SOUND_ONDEATH 11018
+
#define SAY_ONSLAY1 "Shaza-Kiel!"
#define SAY_ONSLAY2 "You... are nothing!"
#define SAY_ONSLAY3 "Miserable nuisance!"
#define SOUND_ONSLAY1 11017
#define SOUND_ONSLAY2 11053
#define SOUND_ONSLAY3 11054
+
#define SAY_MARK1 "Your death will be a painful one."
#define SAY_MARK2 "You... are marked."
#define SOUND_MARK1 11016
#define SOUND_MARK2 11052
+
#define SAY_ONAGGRO "Cry for mercy! Your meaningless lives will soon be forfeit."
#define SOUND_ONAGGRO 11015
+
struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
{
boss_kazrogalAI(Creature *c) : hyjal_trashAI(c)
@@ -32,12 +38,14 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
TempSpell->EffectImplicitTargetB[0] = 0;
}
}
+
uint32 CleaveTimer;
uint32 WarStompTimer;
uint32 MarkTimer;
uint32 MarkTimerBase;
bool pGo;
uint32 pos;
+
void Reset()
{
damageTaken = 0;
@@ -45,9 +53,11 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
WarStompTimer = 15000;
MarkTimer = 45000;
MarkTimerBase = 45000;
+
if (pInstance && IsEvent)
pInstance->SetData(DATA_KAZROGALEVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance && IsEvent)
@@ -55,6 +65,7 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
DoPlaySoundToSet(m_creature, SOUND_ONAGGRO);
m_creature->MonsterYell(SAY_ONAGGRO, LANG_UNIVERSAL, NULL);
}
+
void KilledUnit(Unit *victim)
{
switch(rand()%3)
@@ -73,6 +84,7 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
break;
}
}
+
void WaypointReached(uint32 i)
{
pos = i;
@@ -83,6 +95,7 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
m_creature->AddThreat(target,0.0);
}
}
+
void JustDied(Unit *victim)
{
hyjal_trashAI::JustDied(victim);
@@ -90,6 +103,7 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
pInstance->SetData(DATA_KAZROGALEVENT, DONE);
DoPlaySoundToSet(m_creature, SOUND_ONDEATH);
}
+
void UpdateAI(const uint32 diff)
{
if (IsEvent)
@@ -114,27 +128,32 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
}
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (CleaveTimer < diff)
{
DoCast(m_creature, SPELL_CLEAVE);
CleaveTimer = 6000+rand()%15000;
}else CleaveTimer -= diff;
+
if (WarStompTimer < diff)
{
DoCast(m_creature, SPELL_WARSTOMP);
WarStompTimer = 60000;
}else WarStompTimer -= diff;
+
if (m_creature->HasAura(SPELL_MARK))
m_creature->RemoveAurasDueToSpell(SPELL_MARK);
if (MarkTimer < diff)
{
//cast dummy, useful for bos addons
m_creature->CastCustomSpell(m_creature, SPELL_MARK, NULL, NULL, NULL, false, NULL, NULL, m_creature->GetGUID());
+
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::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->getPowerType() == POWER_MANA)
@@ -158,13 +177,16 @@ struct TRINITY_DLL_DECL boss_kazrogalAI : public hyjal_trashAI
break;
}
}else MarkTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_kazrogal(Creature* pCreature)
{
return new boss_kazrogalAI (pCreature);
}
+
void AddSC_boss_kazrogal()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp
index 856b8b374cb..da0d6c2dd5a 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/boss_rage_winterchill.cpp
@@ -2,26 +2,34 @@
#include "precompiled.h"
#include "def_hyjal.h"
#include "hyjal_trash.h"
+
#define SPELL_FROST_ARMOR 31256
#define SPELL_DEATH_AND_DECAY 31258
+
#define SPELL_FROST_NOVA 31250
#define SPELL_ICEBOLT 31249
+
#define SAY_ONDEATH "You have won this battle, but not... the... war"
#define SOUND_ONDEATH 11026
+
#define SAY_ONSLAY1 "All life must perish!"
#define SAY_ONSLAY2 "Victory to the Legion!"
#define SOUND_ONSLAY1 11025
#define SOUND_ONSLAY2 11057
+
#define SAY_DECAY1 "Crumble and rot!"
#define SAY_DECAY2 "Ashes to ashes, dust to dust"
#define SOUND_DECAY1 11023
#define SOUND_DECAY2 11055
+
#define SAY_NOVA1 "Succumb to the icy chill... of death!"
#define SAY_NOVA2 "It will be much colder in your grave"
#define SOUND_NOVA1 11024
#define SOUND_NOVA2 11058
+
#define SAY_ONAGGRO "The Legion's final conquest has begun! Once again the subjugation of this world is within our grasp. Let none survive!"
#define SOUND_ONAGGRO 11022
+
struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
{
boss_rage_winterchillAI(Creature *c) : hyjal_trashAI(c)
@@ -30,12 +38,14 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
pGo = false;
pos = 0;
}
+
uint32 FrostArmorTimer;
uint32 DecayTimer;
uint32 NovaTimer;
uint32 IceboltTimer;
bool pGo;
uint32 pos;
+
void Reset()
{
damageTaken = 0;
@@ -43,9 +53,11 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
DecayTimer = 45000;
NovaTimer = 15000;
IceboltTimer = 10000;
+
if (pInstance && IsEvent)
pInstance->SetData(DATA_RAGEWINTERCHILLEVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance && IsEvent)
@@ -53,6 +65,7 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
DoPlaySoundToSet(m_creature, SOUND_ONAGGRO);
m_creature->MonsterYell(SAY_ONAGGRO, LANG_UNIVERSAL, 0);
}
+
void KilledUnit(Unit *victim)
{
switch(rand()%2)
@@ -67,6 +80,7 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
break;
}
}
+
void WaypointReached(uint32 i)
{
pos = i;
@@ -77,6 +91,7 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
m_creature->AddThreat(target,0.0);
}
}
+
void JustDied(Unit *victim)
{
hyjal_trashAI::JustDied(victim);
@@ -85,6 +100,7 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
DoPlaySoundToSet(m_creature, SOUND_ONDEATH);
m_creature->MonsterYell(SAY_ONDEATH, LANG_UNIVERSAL, NULL);
}
+
void UpdateAI(const uint32 diff)
{
if (IsEvent)
@@ -109,9 +125,11 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
}
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (FrostArmorTimer < diff)
{
DoCast(m_creature, SPELL_FROST_ARMOR);
@@ -154,13 +172,16 @@ struct TRINITY_DLL_DECL boss_rage_winterchillAI : public hyjal_trashAI
DoCast(SelectTarget(SELECT_TARGET_RANDOM,0,40,true), SPELL_ICEBOLT);
IceboltTimer = 11000+rand()%20000;
}else IceboltTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_rage_winterchill(Creature* pCreature)
{
return new boss_rage_winterchillAI (pCreature);
}
+
void AddSC_boss_rage_winterchill()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/def_hyjal.h b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/def_hyjal.h
index 7aa8c9d5517..bf2d6d99f90 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/def_hyjal.h
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/def_hyjal.h
@@ -1,14 +1,18 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_HYJAL_H
#define DEF_HYJAL_H
+
#define ERROR_INST_DATA "TSCR: Instance data not set properly for Mount Hyjal. Encounters will be buggy"
+
enum eTypes
{
WORLD_STATE_WAVES = 2842,
WORLD_STATE_ENEMY = 2453,
WORLD_STATE_ENEMYCOUNT = 2454,
+
DATA_ANETHERON = 1,
DATA_ANETHERONEVENT = 2,
DATA_ARCHIMONDE = 3,
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp
index e34eb7446fd..eb6e72452c0 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal.cpp
@@ -13,49 +13,65 @@
* along 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: 80
SDComment: gossip text id's unknown
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 "My companions and I are with you, Lady Proudmoore."
#define GOSSIP_ITEM_ANETHERON "We are ready for whatever Archimonde might send our way, Lady Proudmoore."
+
#define GOSSIP_ITEM_BEGIN_HORDE "I am with you, Thrall."
#define GOSSIP_ITEM_AZGALOR "We have nothing to fear."
+
#define GOSSIP_ITEM_RETREAT "We can't keep this up. Let's retreat!"
+
#define GOSSIP_ITEM_TYRANDE "Aid us in defending Nordrassil"
#define ITEM_TEAR_OF_GODDESS 24494
+
#define GOSSIP_ITEM_GM1 "[GM] Toggle Debug Timers"
+
CreatureAI* GetAI_npc_jaina_proudmoore(Creature* pCreature)
{
hyjalAI* ai = new hyjalAI(pCreature);
+
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 = 5500 + rand()%4000;
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* pPlayer, Creature* pCreature)
{
hyjalAI* ai = CAST_AI(hyjalAI, pCreature->AI());
if (ai->EventBegun)
return false;
+
uint32 RageEncounter = ai->GetInstanceData(DATA_RAGEWINTERCHILLEVENT);
uint32 AnetheronEncounter = ai->GetInstanceData(DATA_ANETHERONEVENT);
if (RageEncounter == NOT_STARTED)
@@ -64,11 +80,14 @@ bool GossipHello_npc_jaina_proudmoore(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_ANETHERON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
else if (RageEncounter == DONE && AnetheronEncounter == DONE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RETREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
+
if (pPlayer->isGameMaster())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_ITEM_GM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(907, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_jaina_proudmoore(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
hyjalAI* ai = CAST_AI(hyjalAI, pCreature->AI());
@@ -92,24 +111,31 @@ bool GossipSelect_npc_jaina_proudmoore(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
CreatureAI* GetAI_npc_thrall(Creature* pCreature)
{
hyjalAI* ai = new hyjalAI(pCreature);
+
ai->Reset();
ai->EnterEvadeMode();
+
ai->Spell[0].SpellId = SPELL_CHAIN_LIGHTNING;
ai->Spell[0].Cooldown = 3000 + 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* pPlayer, Creature* pCreature)
{
hyjalAI* ai = CAST_AI(hyjalAI, pCreature->AI());
if (ai->EventBegun)
return false;
+
uint32 AnetheronEvent = ai->GetInstanceData(DATA_ANETHERONEVENT);
// Only let them start the Horde phases if Anetheron is dead.
if (AnetheronEvent == DONE && ai->GetInstanceData(DATA_ALLIANCE_RETREAT))
@@ -123,11 +149,14 @@ bool GossipHello_npc_thrall(Player* pPlayer, Creature* pCreature)
else if (AzgalorEvent == DONE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RETREAT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
}
+
if (pPlayer->isGameMaster())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_ITEM_GM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(907, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_thrall(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
hyjalAI* ai = CAST_AI(hyjalAI, pCreature->AI());
@@ -152,6 +181,7 @@ bool GossipSelect_npc_thrall(Player* pPlayer, Creature* pCreature, uint32 uiSend
}
return true;
}
+
CreatureAI* GetAI_npc_tyrande_whisperwind(Creature* pCreature)
{
hyjalAI* ai = new hyjalAI(pCreature);
@@ -159,16 +189,19 @@ CreatureAI* GetAI_npc_tyrande_whisperwind(Creature* pCreature)
ai->EnterEvadeMode();
return ai;
}
+
bool GossipHello_npc_tyrande_whisperwind(Player* pPlayer, Creature* pCreature)
{
hyjalAI* ai = CAST_AI(hyjalAI, pCreature->AI());
uint32 AzgalorEvent = ai->GetInstanceData(DATA_AZGALOREVENT);
+
// Only let them get item if Azgalor is dead.
if (AzgalorEvent == DONE && !pPlayer->HasItemCount(ITEM_TEAR_OF_GODDESS,1))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TYRANDE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(907, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_tyrande_whisperwind(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -186,21 +219,25 @@ bool GossipSelect_npc_tyrande_whisperwind(Player* pPlayer, Creature* pCreature,
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_thrall";
newscript->GetAI = &GetAI_npc_thrall;
newscript->pGossipHello = &GossipHello_npc_thrall;
newscript->pGossipSelect = &GossipSelect_npc_thrall;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tyrande_whisperwind";
newscript->pGossipHello = &GossipHello_npc_tyrande_whisperwind;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp
index f408f7b3bf1..e8bf7d8ceeb 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.cpp
@@ -13,12 +13,14 @@
* along 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: 90
SDComment:
SDCategory: Caverns of Time, Mount Hyjal
EndScriptData */
+
#include "precompiled.h"
#include "hyjalAI.h"
#include "hyjal_trash.h"
@@ -26,10 +28,13 @@ EndScriptData */
#include "Language.h"
#include "Chat.h"
#include "Object.h"
+
#define SPAWN_GARG_GATE 0
#define SPAWN_WYRM_GATE 1
#define SPAWN_NEAR_TOWER 2
+
#define YELL_HURRY "Hurry, we don't have much time"
+
// Locations for summoning gargoyls and frost wyrms in special cases
float SpawnPointSpecial[3][3]=
{
@@ -37,6 +42,7 @@ float SpawnPointSpecial[3][3]=
{5624.53, -2548.12, 1551.54}, //spawn point for the frost wyrm near the horde gate
{5604.41, -2811.98, 1547.77} //spawn point for the gargoyles and wyrms near the horde tower
};
+
// Locations for summoning waves in Alliance base
float AllianceBase[4][3]=
{
@@ -45,12 +51,14 @@ float AllianceBase[4][3]=
{4928.41, -1510.35, 1327.99},
{4938.35, -1521.00, 1326.69}
};
+
float JainaDummySpawn[2][4]=
{
{5497.01, -2719.03, 1483.08, 2.90426},
{5484.98, -2721.69, 1483.39, 6.00656}
};
+
// Locations for summoning waves in Horde base
float HordeBase[4][3]=
{
@@ -59,12 +67,14 @@ float HordeBase[4][3]=
{5468.45, -2355.13, 1459.99},
{5479.06, -2344.16, 1461.74}
};
+
// Lady Jaina's waypoints when retreathing
float JainaWPs[2][3]=
{
{5078.56, -1789.79, 1320.73},//next to the small stairs
{5037.38, -1778.39, 1322.61},//center of alliance base
};
+
float InfernalPos[8][3]=//spawn points for the infernals in the horde base
{
{5453.59, -2764.52, 1493.50},
@@ -76,6 +86,7 @@ float InfernalPos[8][3]=//spawn points for the infernals in the horde base
{5510.16, -2691.75, 1479.66},
{5482.39, -2689.19, 1481.09}
};
+
float InfernalSPWP[26][4]=//spawn points for the infernals in the horde base used in the cleaning wave
{
{5490.96, -2718.72, 1482.96, 0.49773},
@@ -105,6 +116,7 @@ float InfernalSPWP[26][4]=//spawn points for the infernals in the horde base use
{5549.76, -2692.93, 1482.68, 2.19414},
{5459.78, -2755.71, 1490.68, 1.05139}
};
+
float VeinPos[14][8]=//spawn points of the ancient gem veins
{
{5184.84, -1982.59, 1382.66, 2.58079, 0, 0, 0.960944, 0.276742}, //alliance
@@ -122,6 +134,7 @@ float VeinPos[14][8]=//spawn points of the ancient gem veins
{5374.3, -3420.59, 1653.43, 1.45762, 0, 0, 0.665981, 0.745969}, //horde
{5441.54, -3321.59, 1651.55, 0.258306, 0, 0, 0.128794, 0.991671} //horde
};
+
float AllianceOverrunGargPos[5][4]=//gargoyle spawn points in the alliance overrun
{
{5279.94, -2049.68, 1311.38, 0},//garg1
@@ -130,6 +143,7 @@ float AllianceOverrunGargPos[5][4]=//gargoyle spawn points in the alliance overr
{5071.52, -2425.63, 1454.48, 5.54},//garg4
{5120.65, -2467.92, 1463.93, 2.54}//garg5
};
+
float AllianceFirePos[92][8]=//spawn points for the fire visuals (GO) in the alliance base
{
{5039.9, -1796.84, 1323.88, 2.59222, 0, 0, 0.962511, 0.271243},
@@ -225,6 +239,7 @@ float AllianceFirePos[92][8]=//spawn points for the fire visuals (GO) in the all
{5332.5 , -2181.28, 1279.95, 4.6906, 0, 0, 0.714768, -0.699362},
{5108.2 , -2429.84, 1427.73, 4.5194, 0, 0, 0.771943, -0.635691}
};
+
float HordeFirePos[65][8]=//spawn points for the fire visuals (GO) in the horde base
{
{5524.11, -2612.73, 1483.38, 1.96198, 0, 0, 0.831047, 0.556202},
@@ -293,12 +308,13 @@ float HordeFirePos[65][8]=//spawn points for the fire visuals (GO) in the horde
{5534.15, -2679.35, 1483.61, 0.428685, 0, 0, 0.212705, 0.977116},
{5545.43, -2647.82, 1483.05, 5.38848, 0, 0, 0.432578, -0.901596}
};
+
hyjalAI::hyjalAI(Creature *c) : npc_escortAI(c), Summons(m_creature)
{
pInstance = c->GetInstanceData();
VeinsSpawned[0] = false;
VeinsSpawned[1] = false;
- for (uint8 i=0; i<14; ++i)
+ for(uint8 i=0;i<14; ++i)
VeinGUID[i] = 0;
InfernalCount = 0;
TeleportTimer = 1000;
@@ -314,14 +330,17 @@ hyjalAI::hyjalAI(Creature *c) : npc_escortAI(c), Summons(m_creature)
MassTeleportTimer = 0;
DoMassTeleport = false;
}
+
void hyjalAI::JustSummoned(Creature *summoned)
{
Summons.Summon(summoned);
}
+
void hyjalAI::SummonedCreatureDespawn(Creature* summoned)
{
Summons.Despawn(summoned);
}
+
void hyjalAI::Reset()
{
IsDummy = false;
@@ -330,13 +349,16 @@ void hyjalAI::Reset()
PlayerGUID = 0;
BossGUID[0] = 0;
BossGUID[1] = 0;
+
// Timers
NextWaveTimer = 10000;
CheckTimer = 0;
RetreatTimer = 1000;
+
// Misc
WaveCount = 0;
EnemyCount = 0;
+
// Set faction properly based on Creature entry
switch(m_creature->GetEntry())
{
@@ -344,13 +366,16 @@ void hyjalAI::Reset()
Faction = 0;
DoCast(m_creature, SPELL_BRILLIANCE_AURA, true);
break;
+
case THRALL:
Faction = 1;
break;
+
case TYRANDE:
Faction = 2;
break;
}
+
//Bools
EventBegun = false;
FirstBossDead = false;
@@ -359,11 +384,15 @@ void hyjalAI::Reset()
bRetreat = false;
Debug = false;
+
//Flags
m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+
//Initialize spells
memset(Spell, 0, sizeof(Spell));
+
+
//Reset Instance Data for trash count
if (pInstance)
{
@@ -376,9 +405,11 @@ void hyjalAI::Reset()
pInstance->SetData(DATA_RESET_TRASH_COUNT, 0);
}
}else error_log(ERROR_INST_DATA);
+
//Visibility
DoHide = true;
}
+
void hyjalAI::EnterEvadeMode()
{
if (m_creature->GetEntry() != JAINA)
@@ -386,30 +417,37 @@ void hyjalAI::EnterEvadeMode()
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
m_creature->LoadCreaturesAddon();
+
if (m_creature->isAlive())
m_creature->GetMotionMaster()->MoveTargetedHome();
+
m_creature->SetLootRecipient(NULL);
}
+
void hyjalAI::EnterCombat(Unit *who)
{
- if (IsDummy)
- return;
- for (uint8 i = 0; i < 3; ++i)
+ if (IsDummy)return;
+ for(uint8 i = 0; i < 3; ++i)
if (Spell[i].Cooldown)
SpellTimer[i] = Spell[i].Cooldown;
+
Talk(ATTACKED);
}
+
void hyjalAI::MoveInLineOfSight(Unit *who)
{
if (IsDummy)
return;
+
npc_escortAI::MoveInLineOfSight(who);
}
+
void hyjalAI::SummonCreature(uint32 entry, float Base[4][3])
{
uint32 random = rand()%4;
float SpawnLoc[3];
- for (uint8 i = 0; i < 3; ++i)
+
+ for(uint8 i = 0; i < 3; ++i)
{
SpawnLoc[i] = Base[random][i];
}
@@ -417,14 +455,13 @@ void hyjalAI::SummonCreature(uint32 entry, float Base[4][3])
switch(entry)
{
case 17906: //GARGOYLE
+
if (!FirstBossDead && (WaveCount == 1 || WaveCount == 3))
{//summon at tower
pCreature = m_creature->SummonCreature(entry, SpawnPointSpecial[SPAWN_NEAR_TOWER][0]+irand(-20,20), SpawnPointSpecial[SPAWN_NEAR_TOWER][1]+irand(-20,20), SpawnPointSpecial[SPAWN_NEAR_TOWER][2]+irand(-10,10), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000);
if (pCreature)
CAST_AI(hyjal_trashAI, pCreature->AI())->useFlyPath = true;
- }
- else
- {//summon at gate
+ }else{//summon at gate
pCreature = m_creature->SummonCreature(entry, SpawnPointSpecial[SPAWN_GARG_GATE][0]+irand(-10,10), SpawnPointSpecial[SPAWN_GARG_GATE][1]+irand(-10,10), SpawnPointSpecial[SPAWN_GARG_GATE][2]+irand(-10,10), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000);
}
break;
@@ -432,9 +469,7 @@ void hyjalAI::SummonCreature(uint32 entry, float Base[4][3])
if (FirstBossDead && WaveCount == 1)
{//summon at gate
pCreature = m_creature->SummonCreature(entry, SpawnPointSpecial[SPAWN_WYRM_GATE][0],SpawnPointSpecial[SPAWN_WYRM_GATE][1],SpawnPointSpecial[SPAWN_WYRM_GATE][2], 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000);
- }
- else
- {
+ }else{
pCreature = m_creature->SummonCreature(entry, SpawnPointSpecial[SPAWN_NEAR_TOWER][0], SpawnPointSpecial[SPAWN_NEAR_TOWER][1],SpawnPointSpecial[SPAWN_NEAR_TOWER][2], 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000);
if (pCreature)
CAST_AI(hyjal_trashAI, pCreature->AI())->useFlyPath = true;
@@ -448,11 +483,14 @@ void hyjalAI::SummonCreature(uint32 entry, float Base[4][3])
default:
pCreature = m_creature->SummonCreature(entry, SpawnLoc[0], SpawnLoc[1], SpawnLoc[2], 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 120000);
break;
+
}
+
if (pCreature)
{
// Increment Enemy Count to be used in World States and instance script
++EnemyCount;
+
pCreature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pCreature->setActive(true);
switch(entry)
@@ -487,33 +525,40 @@ void hyjalAI::SummonCreature(uint32 entry, float Base[4][3])
}
}
}
+
void hyjalAI::SummonNextWave(Wave wave[18], uint32 Count, float Base[4][3])
{
// 1 in 4 chance we give a rally yell. Not sure if the chance is offilike.
if (rand()%4 == 0)
Talk(RALLY);
+
if (!pInstance)
{
error_log(ERROR_INST_DATA);
return;
}
InfernalCount = 0;//reset infernal count every new wave
+
EnemyCount = pInstance->GetData(DATA_TRASH);
- for (uint8 i = 0; i < 18; ++i)
+ 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
+
// Set world state to our current wave number
pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, stateValue); // Set world state to our current wave number
// Enable world state
pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 1); // Enable world state
+
pInstance->SetData(DATA_TRASH, EnemyCount); // Send data for instance script to update count
+
if (!Debug)
NextWaveTimer = wave[Count].WaveTimer;
else
@@ -527,39 +572,51 @@ void hyjalAI::SummonNextWave(Wave wave[18], uint32 Count, float Base[4][3])
// Set world state for waves to 0 to disable it.
pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, 0);
pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 1);
+
// Set World State for enemies invading to 1.
pInstance->DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, 1);
+
Summon = false;
}
CheckTimer = 5000;
}
+
void hyjalAI::StartEvent(Player* pPlayer)
{
if (!pPlayer || IsDummy || !pInstance)
return;
+
Talk(BEGIN);
+
EventBegun = true;
Summon = true;
+
NextWaveTimer = 15000;
CheckTimer = 5000;
PlayerGUID = pPlayer->GetGUID();
+
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+
pInstance->DoUpdateWorldState(WORLD_STATE_WAVES, 0);
pInstance->DoUpdateWorldState(WORLD_STATE_ENEMY, 0);
pInstance->DoUpdateWorldState(WORLD_STATE_ENEMYCOUNT, 0);
+
DeSpawnVeins();
}
+
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<uint8> index;
- for (uint8 i = 0; i < 9; ++i)
+ for(uint8 i = 0; i < 9; ++i)
{
if (Faction == 0) // Alliance
{
@@ -572,9 +629,12 @@ void hyjalAI::Talk(uint32 id)
index.push_back(i);
}
}
+
if (index.empty())
return; // No quotes found, no use to continue
+
uint8 ind = *(index.begin()) + rand()%index.size();
+
int32 YellId = 0;
if (Faction == 0) // Alliance
{
@@ -584,14 +644,17 @@ void hyjalAI::Talk(uint32 id)
{
YellId = ThrallQuotes[ind].textid;
}
+
if (YellId)
DoScriptText(YellId, m_creature);
}
+
void hyjalAI::Retreat()
{
if (pInstance)
{
pInstance->SetData(TYPE_RETREAT,SPECIAL);
+
if (Faction == 0)
{
pInstance->SetData(DATA_ALLIANCE_RETREAT, 1);
@@ -619,11 +682,12 @@ void hyjalAI::Retreat()
Overrun = true;
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);//cant talk after overrun event started
}
+
void hyjalAI::SpawnVeins()
{
if (Faction == 0)
{
- if (VeinsSpawned[0]) //prevent any buggers
+ if (VeinsSpawned[0])//prevent any buggers
return;
for (uint8 i = 0; i<7; ++i)
{
@@ -632,9 +696,7 @@ void hyjalAI::SpawnVeins()
VeinGUID[i]=gem->GetGUID();
}
VeinsSpawned[0] = true;
- }
- else
- {
+ }else{
if (VeinsSpawned[1])
return;
for (uint8 i = 7; i<14; ++i)
@@ -646,6 +708,7 @@ void hyjalAI::SpawnVeins()
VeinsSpawned[1] = true;
}
}
+
void hyjalAI::DeSpawnVeins()
{
if (!pInstance)
@@ -674,6 +737,7 @@ void hyjalAI::DeSpawnVeins()
}
}
}
+
void hyjalAI::UpdateAI(const uint32 diff)
{
if (IsDummy)
@@ -696,8 +760,9 @@ void hyjalAI::UpdateAI(const uint32 diff)
m_creature->SetVisibility(VISIBILITY_OFF);
HideNearPos(m_creature->GetPositionX(), m_creature->GetPositionY());
HideNearPos(5037.76, -1889.71);
- for (uint8 i = 0; i < 92; ++i) //summon fires
+ for(uint8 i = 0; i < 92; ++i)//summon fires
m_creature->SummonGameObject(FLAMEOBJECT,AllianceFirePos[i][0],AllianceFirePos[i][1],AllianceFirePos[i][2],AllianceFirePos[i][3],AllianceFirePos[i][4],AllianceFirePos[i][5],AllianceFirePos[i][6],AllianceFirePos[i][7],0);
+
}
else m_creature->SetVisibility(VISIBILITY_ON);
break;
@@ -708,8 +773,9 @@ void hyjalAI::UpdateAI(const uint32 diff)
HideNearPos(m_creature->GetPositionX(), m_creature->GetPositionY());
HideNearPos(5563, -2763.19);
HideNearPos(5542.2, -2629.36);
- for (uint8 i = 0; i < 65; ++i) //summon fires
+ for(uint8 i = 0; i < 65; ++i)//summon fires
m_creature->SummonGameObject(FLAMEOBJECT,HordeFirePos[i][0],HordeFirePos[i][1],HordeFirePos[i][2],HordeFirePos[i][3],HordeFirePos[i][4],HordeFirePos[i][5],HordeFirePos[i][6],HordeFirePos[i][7],0);
+
}
else m_creature->SetVisibility(VISIBILITY_ON);
break;
@@ -730,9 +796,7 @@ void hyjalAI::UpdateAI(const uint32 diff)
RespawnNearPos(5542.2, -2629.36);
}
m_creature->SetVisibility(VISIBILITY_ON);
- }
- else
- {
+ }else{
RespawnTimer -= diff;
m_creature->SetVisibility(VISIBILITY_OFF);
}
@@ -761,8 +825,10 @@ void hyjalAI::UpdateAI(const uint32 diff)
m_creature->SetVisibility(VISIBILITY_OFF);
}else RetreatTimer -= diff;
}
+
if (!EventBegun)
return;
+
if (Summon)
{
if (pInstance && EnemyCount)
@@ -771,6 +837,7 @@ void hyjalAI::UpdateAI(const uint32 diff)
if (!EnemyCount)
NextWaveTimer = 5000;
}
+
if (NextWaveTimer < diff)
{
if (Faction == 0)
@@ -780,9 +847,10 @@ void hyjalAI::UpdateAI(const uint32 diff)
++WaveCount;
}else NextWaveTimer -= diff;
}
+
if (CheckTimer < diff)
{
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
if (BossGUID[i])
{
@@ -810,9 +878,11 @@ void hyjalAI::UpdateAI(const uint32 diff)
}
CheckTimer = 5000;
}else CheckTimer -= diff;
+
if (!UpdateVictim())
return;
- for (uint8 i = 0; i < 3; ++i)
+
+ for(uint8 i = 0; i < 3; ++i)
{
if (Spell[i].SpellId)
{
@@ -820,13 +890,16 @@ void hyjalAI::UpdateAI(const uint32 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);
@@ -835,6 +908,7 @@ void hyjalAI::UpdateAI(const uint32 diff)
}else SpellTimer[i] -= diff;
}
}
+
DoMeleeAttackIfReady();
}
void hyjalAI::JustDied(Unit* killer)
@@ -865,6 +939,7 @@ void hyjalAI::HideNearPos(float x, float y)
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
// First get all creatures.
std::list<Creature*> creatures;
Trinity::AllFriendlyCreaturesInGrid creature_check(m_creature);
@@ -872,12 +947,14 @@ void hyjalAI::HideNearPos(float x, float y)
TypeContainerVisitor
<Trinity::CreatureListSearcher<Trinity::AllFriendlyCreaturesInGrid>,
GridTypeMapContainer> creature_visitor(creature_searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
// Get Creatures
cell_lock->Visit(cell_lock, creature_visitor, *(m_creature->GetMap()));
+
if (!creatures.empty())
{
- for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
+ for(std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
{
(*itr)->SetVisibility(VISIBILITY_OFF);
(*itr)->setFaction(35);//make them friendly so mobs won't attack them
@@ -890,6 +967,7 @@ void hyjalAI::RespawnNearPos(float x, float y)
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::RespawnDo u_do;
Trinity::WorldObjectWorker<Trinity::RespawnDo> worker(m_creature, u_do);
TypeContainerVisitor<Trinity::WorldObjectWorker<Trinity::RespawnDo>, GridTypeMapContainer > obj_worker(worker);
@@ -921,6 +999,7 @@ void hyjalAI::WaypointReached(uint32 i)
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
// First get all creatures.
std::list<Creature*> creatures;
Trinity::AllFriendlyCreaturesInGrid creature_check(m_creature);
@@ -928,11 +1007,13 @@ void hyjalAI::WaypointReached(uint32 i)
TypeContainerVisitor
<Trinity::CreatureListSearcher<Trinity::AllFriendlyCreaturesInGrid>,
GridTypeMapContainer> creature_visitor(creature_searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, creature_visitor, *(m_creature->GetMap()));
+
if (!creatures.empty())
{
- for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
+ for(std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
{
if ((*itr) && (*itr)->isAlive() && (*itr) != m_creature && (*itr)->GetEntry() != JAINA)
{
@@ -961,17 +1042,20 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
std::list<Creature*> creatures;
Trinity::AllFriendlyCreaturesInGrid creature_check(m_creature);
Trinity::CreatureListSearcher<Trinity::AllFriendlyCreaturesInGrid> creature_searcher(m_creature, creatures, creature_check);
TypeContainerVisitor
<Trinity::CreatureListSearcher<Trinity::AllFriendlyCreaturesInGrid>,
GridTypeMapContainer> creature_visitor(creature_searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, creature_visitor, *(m_creature->GetMap()));
+
if (!creatures.empty())
{
- for (std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
+ for(std::list<Creature*>::iterator itr = creatures.begin(); itr != creatures.end(); ++itr)
{
if ((*itr) && (*itr)->isAlive())
{
@@ -984,6 +1068,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
bRetreat = true;
RetreatTimer = 1000;
}
+
WaitForTeleport = false;
Teleported = true;
}TeleportTimer -= diff;
@@ -994,9 +1079,10 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
switch(faction)
{
case 0://alliance
- for (uint8 i = 0; i < 92; ++i) //summon fires
+ for(uint8 i = 0; i < 92; ++i)//summon fires
m_creature->SummonGameObject(FLAMEOBJECT,AllianceFirePos[i][0],AllianceFirePos[i][1],AllianceFirePos[i][2],AllianceFirePos[i][3],AllianceFirePos[i][4],AllianceFirePos[i][5],AllianceFirePos[i][6],AllianceFirePos[i][7],0);
- for (uint8 i = 0; i < 25; ++i) //summon 25 ghouls
+
+ for(uint8 i = 0; i < 25; ++i)//summon 25 ghouls
{
uint8 r = rand()%4;
Creature* pUnit = m_creature->SummonCreature(GHOUL, AllianceBase[r][0]+irand(-15,15), AllianceBase[r][1]+irand(-15,15), AllianceBase[r][2], 0, TEMPSUMMON_MANUAL_DESPAWN, 2*60*1000);
@@ -1008,7 +1094,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
pUnit->setActive(true);
}
}
- for (uint8 i = 0; i < 3; ++i) //summon 3 abominations
+ for(uint8 i = 0; i < 3; ++i)//summon 3 abominations
{
uint8 r = rand()%4;
Creature* pUnit = m_creature->SummonCreature(ABOMINATION, AllianceBase[r][0]+irand(-15,15), AllianceBase[r][1]+irand(-15,15), AllianceBase[r][2], 0, TEMPSUMMON_MANUAL_DESPAWN, 2*60*1000);
@@ -1020,7 +1106,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
pUnit->setActive(true);
}
}
- for (uint8 i = 0; i < 5; ++i) //summon 5 gargoyles
+ for(uint8 i = 0; i < 5; ++i)//summon 5 gargoyles
{
Creature* pUnit = m_creature->SummonCreature(GARGOYLE, AllianceOverrunGargPos[i][0], AllianceOverrunGargPos[i][1], AllianceOverrunGargPos[i][2], AllianceOverrunGargPos[i][3], TEMPSUMMON_MANUAL_DESPAWN, 2*60*1000);
if (pUnit)
@@ -1034,9 +1120,10 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
}
break;
case 1://horde
- for (uint8 i = 0; i < 65; ++i) //summon fires
+ for(uint8 i = 0; i < 65; ++i)//summon fires
m_creature->SummonGameObject(FLAMEOBJECT,HordeFirePos[i][0],HordeFirePos[i][1],HordeFirePos[i][2],HordeFirePos[i][3],HordeFirePos[i][4],HordeFirePos[i][5],HordeFirePos[i][6],HordeFirePos[i][7],0);
- for (uint8 i = 0; i < 26; ++i) //summon infernals
+
+ for(uint8 i = 0; i < 26; ++i)//summon infernals
{
Creature* pUnit = m_creature->SummonCreature(GIANT_INFERNAL, InfernalSPWP[i][0], InfernalSPWP[i][1], InfernalSPWP[i][2], InfernalSPWP[i][3], TEMPSUMMON_MANUAL_DESPAWN, 2*60*1000);
if (pUnit)
@@ -1048,7 +1135,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
pUnit->setActive(true);
}
}
- for (uint8 i = 0; i < 25; ++i) //summon 25 ghouls
+ for(uint8 i = 0; i < 25; ++i)//summon 25 ghouls
{
uint8 r = rand()%4;
Creature* pUnit = m_creature->SummonCreature(GHOUL, HordeBase[r][0]+irand(-15,15), HordeBase[r][1]+irand(-15,15), HordeBase[r][2], 0, TEMPSUMMON_MANUAL_DESPAWN, 2*60*1000);
@@ -1060,7 +1147,7 @@ void hyjalAI::DoOverrun(uint32 faction, const uint32 diff)
pUnit->setActive(true);
}
}
- for (uint8 i = 0; i < 5; ++i) //summon 5 abominations
+ for(uint8 i = 0; i < 5; ++i)//summon 5 abominations
{
uint8 r = rand()%4;
Creature* pUnit = m_creature->SummonCreature(ABOMINATION, HordeBase[r][0]+irand(-15,15), HordeBase[r][1]+irand(-15,15), HordeBase[r][2], 0, TEMPSUMMON_MANUAL_DESPAWN, 2*60*1000);
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h
index 3a2a189d311..c646c47699c 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjalAI.h
@@ -1,10 +1,13 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_HYJALAI_H
#define SC_HYJALAI_H
+
#include "def_hyjal.h"
#include "escort_ai.h"
+
// Trash Mobs summoned in waves
#define NECROMANCER 17899//done
#define ABOMINATION 17898//done
@@ -15,33 +18,41 @@
#define FROST_WYRM 17907//done
#define GIANT_INFERNAL 17908//done
#define FEL_STALKER 17916//done
+
#define JAINA 17772
#define THRALL 17852
#define TYRANDE 17948
+
#define ANCIENT_VEIN 185557
#define FLAMEOBJECT 182592
+
// 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 SPELL_MASS_TELEPORT 16807
+
//Spells for Jaina
#define SPELL_BRILLIANCE_AURA 31260 // The database must handle this spell via creature_addon(it should, but is removed in evade..)
#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
{ // Rage Winterchill Wave 1-8
{GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, GHOUL, 0, 0, 0, 0, 0, 0, 0, 0, 120000, false},
@@ -66,6 +77,7 @@ static Wave AllianceWaves[]= // Waves that will b
// 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
{ // Kaz'Rogal Wave 1-8
{GHOUL, GHOUL, GHOUL, GHOUL, ABOMINATION, ABOMINATION, ABOMINATION, ABOMINATION, BANSHEE, BANSHEE, NECROMANCER, NECROMANCER, 0, 0, 0, 0, 0, 0, 180000, false},
@@ -90,17 +102,20 @@ static Wave HordeWaves[]= // Waves that are su
// 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)
int32 textid; // The text id to be yelled
};
+
enum YellId
{
ATTACKED = 0, // Used when attacked and set in combat
@@ -111,6 +126,7 @@ enum YellId
SUCCESS = 5, // Used when the raid has sucessfully defeated a wave phase
DEATH = 6, // Used on death
};
+
static Yells JainaQuotes[]=
{
{ATTACKED, -1534000},
@@ -123,6 +139,7 @@ static Yells JainaQuotes[]=
{SUCCESS, -1534007},
{DEATH, -1534008},
};
+
static Yells ThrallQuotes[]=
{
{ATTACKED, -1534009},
@@ -135,19 +152,28 @@ static Yells ThrallQuotes[]=
{SUCCESS, -1534016},
{DEATH, -1534017},
};
+
struct TRINITY_DLL_DECL hyjalAI : public npc_escortAI
{
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 EnterCombat(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.
+
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 SpawnVeins();
void DeSpawnVeins();
void JustSummoned(Creature *summoned);
@@ -157,23 +183,32 @@ struct TRINITY_DLL_DECL hyjalAI : public npc_escortAI
void WaypointReached(uint32 i);
void DoOverrun(uint32 faction, const uint32 diff);
void MoveInLineOfSight(Unit *who);
+
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
+
public:
ScriptedInstance* pInstance;
+
uint64 PlayerGUID;
uint64 BossGUID[2];
uint64 VeinGUID[14];
+
uint32 NextWaveTimer;
uint32 WaveCount;
uint32 CheckTimer;
uint32 Faction;
uint32 EnemyCount;
uint32 RetreatTimer;
+
bool EventBegun;
bool FirstBossDead;
bool SecondBossDead;
@@ -197,12 +232,14 @@ struct TRINITY_DLL_DECL hyjalAI : public npc_escortAI
uint32 MassTeleportTimer;
bool DoMassTeleport;
uint64 DummyGuid;
+
struct Spell
{
uint32 SpellId;
uint32 Cooldown;
uint32 TargetType;
}Spell[3];
+
private:
uint32 SpellTimer[3];
//std::list<uint64> CreatureList;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp
index a0216c3c608..705039a6d09 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.cpp
@@ -3,11 +3,13 @@
#include "def_hyjal.h"
#include "hyjal_trash.h"
#include "hyjalAI.h"
+
#define SPELL_METEOR 33814 //infernal visual
#define SPELL_IMMOLATION 37059
#define SPELL_FLAME_BUFFET 31724
#define NPC_TRIGGER 21987 //World Trigger (Tiny)
#define MODEL_INVIS 11686 //invisible model
+
float HordeWPs[8][3]=//basic waypoints from spawn to leader
{
{5492.91, -2404.61, 1462.63},
@@ -30,24 +32,28 @@ float AllianceWPs[8][3]=//basic waypoints from spawn to leader
{5037.77, -1770.56, 1324.36},
{5067.23, -1789.95, 1321.17}
};
+
float FrostWyrmWPs[3][3]=//waypoints for the frost wyrms in horde base
{
{5580.82, -2628.83, 1528.28},
{5550.90, -2667.16, 1505.45},
{5459.64, -2725.91, 1484.83}
};
+
float GargoyleWPs[3][3]=//waypoints for the gargoyles in horde base
{
{5533.66, -2634.32, 1495.33},
{5517.88, -2712.05, 1490.54},
{5459.64, -2725.91, 1484.83}
};
+
float FlyPathWPs[3][3]=//waypoints for the gargoyls and frost wyrms in horde base in wave 1/3
{
{5531.96, -2772.83, 1516.68},
{5498.32, -2734.84, 1497.01},
{5456.67, -2725.48, 1493.08}
};
+
float AllianceOverrunWP[55][3]=//waypoints in the alliance base used in the end in the cleaning wave
{
{4976.37,-1708.02,1339.43},//0spawn
@@ -105,7 +111,9 @@ float AllianceOverrunWP[55][3]=//waypoints in the alliance base used in the end
{5180.41, -2121.87, 1292.62},//52 garg target3
{5088.68, -2432.04, 1441.73},//53 garg target4
{5111.26, -2454.73, 1449.63}//54 garg target5
+
};
+
float HordeOverrunWP[21][3]=//waypoints in the horde base used in the end in the cleaning wave
{
{5490.72,-2702.94,1482.14},//0 start
@@ -130,6 +138,7 @@ float HordeOverrunWP[21][3]=//waypoints in the horde base used in the end in the
{5433.25,-2712.93,1493.02},//19 end 1
{5429.91,-2718.44,1493.42}//20 end 2
};
+
hyjal_trashAI::hyjal_trashAI(Creature *c) : npc_escortAI(c)
{
pInstance = c->GetInstanceData();
@@ -144,6 +153,7 @@ hyjal_trashAI::hyjal_trashAI(Creature *c) : npc_escortAI(c)
damageTaken = 0;
Reset();
}
+
void hyjal_trashAI::DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetTypeId() == TYPEID_PLAYER || (done_by->GetTypeId() == TYPEID_UNIT && CAST_CRE(done_by)->isPet()))
@@ -153,6 +163,7 @@ void hyjal_trashAI::DamageTaken(Unit *done_by, uint32 &damage)
pInstance->SetData(DATA_RAIDDAMAGE,damage);//store raid's damage
}
}
+
void hyjal_trashAI::UpdateAI(const uint32 diff)
{
if (IsOverrun && !SetupOverrun)
@@ -168,7 +179,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
}
if (m_creature->GetEntry() == ABOMINATION)
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
AddWaypoint(i, AllianceWPs[i][0]+irand(-3,3), AllianceWPs[i][1]+irand(-3,3), AllianceWPs[i][2]);
switch(OverrunType)
{
@@ -180,6 +191,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
AddWaypoint(8, AllianceOverrunWP[26][0]+irand(-3,3), AllianceOverrunWP[26][1]+irand(-3,3), AllianceOverrunWP[26][2]);
AddWaypoint(9, AllianceOverrunWP[27][0]+irand(-3,3), AllianceOverrunWP[27][1]+irand(-3,3), AllianceOverrunWP[27][2]);
AddWaypoint(10, AllianceOverrunWP[28][0]+irand(-3,3), AllianceOverrunWP[28][1]+irand(-3,3), AllianceOverrunWP[28][2]);
+
AddWaypoint(11, AllianceOverrunWP[36][0]+irand(-3,3), AllianceOverrunWP[36][1]+irand(-3,3), AllianceOverrunWP[36][2]);
AddWaypoint(12, AllianceOverrunWP[37][0]+irand(-3,3), AllianceOverrunWP[37][1]+irand(-3,3), AllianceOverrunWP[37][2]);
AddWaypoint(13, AllianceOverrunWP[38][0]+irand(-3,3), AllianceOverrunWP[38][1]+irand(-3,3), AllianceOverrunWP[38][2]);
@@ -201,6 +213,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
AddWaypoint(8, AllianceOverrunWP[26][0]+irand(-3,3), AllianceOverrunWP[26][1]+irand(-3,3), AllianceOverrunWP[26][2]);
AddWaypoint(9, AllianceOverrunWP[27][0]+irand(-3,3), AllianceOverrunWP[27][1]+irand(-3,3), AllianceOverrunWP[27][2]);
AddWaypoint(10, AllianceOverrunWP[28][0]+irand(-3,3), AllianceOverrunWP[28][1]+irand(-3,3), AllianceOverrunWP[28][2]);
+
AddWaypoint(11, AllianceOverrunWP[36][0]+irand(-3,3), AllianceOverrunWP[36][1]+irand(-3,3), AllianceOverrunWP[36][2]);
AddWaypoint(12, AllianceOverrunWP[37][0]+irand(-3,3), AllianceOverrunWP[37][1]+irand(-3,3), AllianceOverrunWP[37][2]);
AddWaypoint(13, AllianceOverrunWP[38][0]+irand(-3,3), AllianceOverrunWP[38][1]+irand(-3,3), AllianceOverrunWP[38][2]);
@@ -215,8 +228,9 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
Start(true, true);
break;
default:
- for (uint8 i = 22; i < 36; ++i)
+ for(uint8 i = 22; i < 36; ++i)
AddWaypoint(i-18, AllianceOverrunWP[i][0]+irand(-3,3), AllianceOverrunWP[i][1]+irand(-3,3), AllianceOverrunWP[i][2]);
+
SetDespawnAtEnd(true);
LastOverronPos = 17;
Start(true, true);
@@ -225,7 +239,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
}
if (m_creature->GetEntry() == GHOUL)
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
AddWaypoint(i, AllianceWPs[i][0]+irand(-3,3), AllianceWPs[i][1]+irand(-3,3), AllianceWPs[i][2]);
switch(OverrunType)
{
@@ -287,7 +301,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
Start(true, true);
break;
default:
- for (uint8 i = 22; i < 36; ++i)
+ for(uint8 i = 22; i < 36; ++i)
AddWaypoint(i-18, AllianceOverrunWP[i][0]+irand(-3,3), AllianceOverrunWP[i][1]+irand(-3,3), AllianceOverrunWP[i][2]);
SetDespawnAtEnd(true);
LastOverronPos = 17;
@@ -300,7 +314,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
{
if (m_creature->GetEntry() == GHOUL)
{
- for (uint8 i = 0; i < 6; ++i)
+ for(uint8 i = 0; i < 6; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
switch(OverrunType)
{
@@ -325,7 +339,7 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
Start(true, true);
break;
default:
- for (uint8 i = 0; i < 16; ++i)
+ for(uint8 i = 0; i < 16; ++i)
AddWaypoint(i+6, HordeOverrunWP[i][0]+irand(-10,10), HordeOverrunWP[i][1]+irand(-10,10), HordeOverrunWP[i][2]);
SetDespawnAtEnd(true);
LastOverronPos = 21;
@@ -335,9 +349,9 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
}
if (m_creature->GetEntry() == ABOMINATION)
{
- for (uint8 i = 0; i < 6; ++i)
+ for(uint8 i = 0; i < 6; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-10,10), HordeWPs[i][1]+irand(-10,10), HordeWPs[i][2]);
- for (uint8 i = 0; i < 16; ++i)
+ for(uint8 i = 0; i < 16; ++i)
AddWaypoint(i+6, HordeOverrunWP[i][0]+irand(-10,10), HordeOverrunWP[i][1]+irand(-10,10), HordeOverrunWP[i][2]);
SetDespawnAtEnd(true);
LastOverronPos = 21;
@@ -346,15 +360,18 @@ void hyjal_trashAI::UpdateAI(const uint32 diff)
}
}
}
+
void hyjal_trashAI::JustDied(Unit *victim)
{
if (!pInstance)
return;
if (IsEvent && !m_creature->isWorldBoss())
pInstance->SetData(DATA_TRASH, 0);//signal trash is dead
+
if ((pInstance->GetData(DATA_RAIDDAMAGE) < MINRAIDDAMAGE && !m_creature->isWorldBoss()) || (damageTaken < m_creature->GetMaxHealth()/4 && m_creature->isWorldBoss()))
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);//no loot
}
+
struct mob_giant_infernalAI : public hyjal_trashAI
{
mob_giant_infernalAI(Creature* c) : hyjal_trashAI(c)
@@ -370,6 +387,7 @@ struct mob_giant_infernalAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool meteor;
bool CanMove;
bool WpEnabled;
@@ -378,19 +396,22 @@ struct mob_giant_infernalAI : public hyjal_trashAI
uint32 spawnTimer;
uint32 FlameBuffetTimer;
bool imol;
+
void Reset()
{
spawnTimer = 2000;
FlameBuffetTimer= 2000;
imol = false;
}
+
void EnterCombat(Unit* who) {}
+
void WaypointReached(uint32 i)
{
pos = i;
if (i == 0 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
@@ -398,13 +419,14 @@ struct mob_giant_infernalAI : public hyjal_trashAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
- if (Delay < diff)
- Delay = 0;
- else
+ if (Delay<diff)
{
- Delay -= diff;
+ Delay=0;
+ }else{
+ Delay-=diff;
return;
}
if (!meteor)
@@ -421,9 +443,7 @@ struct mob_giant_infernalAI : public hyjal_trashAI
}
m_creature->GetMotionMaster()->Clear();
meteor = true;
- }
- else if (!CanMove)
- {
+ }else if (!CanMove){
if (spawnTimer<diff)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -437,16 +457,13 @@ struct mob_giant_infernalAI : public hyjal_trashAI
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else if (pInstance->GetData(DATA_ALLIANCE_RETREAT) && pInstance->GetData(DATA_HORDE_RETREAT))
- {
+ }else if (pInstance->GetData(DATA_ALLIANCE_RETREAT) && pInstance->GetData(DATA_HORDE_RETREAT)){
//do overrun
}
}
}else spawnTimer -= diff;
}
- if (!CanMove)
- return;
+ if (!CanMove)return;
hyjal_trashAI::UpdateAI(diff);
if (IsEvent || IsOverrun)
npc_escortAI::UpdateAI(diff);
@@ -463,6 +480,7 @@ struct mob_giant_infernalAI : public hyjal_trashAI
}
}
}
+
if (!UpdateVictim())
return;
if (!imol)
@@ -479,12 +497,15 @@ struct mob_giant_infernalAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_giant_infernal(Creature* pCreature)
{
return new mob_giant_infernalAI(pCreature);
}
+
#define SPELL_DISEASE_CLOUD 31607
#define SPELL_KNOCKDOWN 31610
+
struct mob_abominationAI : public hyjal_trashAI
{
mob_abominationAI(Creature* c) : hyjal_trashAI(c)
@@ -494,6 +515,7 @@ struct mob_abominationAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool pGo;
uint32 KnockDownTimer;
uint32 pos;
@@ -501,19 +523,18 @@ struct mob_abominationAI : public hyjal_trashAI
{
KnockDownTimer = 10000;
}
+
void WaypointReached(uint32 i)
{
pos = i;
if (i == 7 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else
- {
+ }else{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JAINAPROUDMOORE));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
@@ -528,7 +549,9 @@ struct mob_abominationAI : public hyjal_trashAI
}
}
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -541,7 +564,7 @@ struct mob_abominationAI : public hyjal_trashAI
pGo = true;
if (pInstance)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, use horde WPs
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, use horde WPs
{
for (uint8 i = 0; i < 8; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
@@ -570,11 +593,14 @@ struct mob_abominationAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_abomination(Creature* pCreature)
{
return new mob_abominationAI(pCreature);
}
+
#define SPELL_FRENZY 31540
+
struct mob_ghoulAI : public hyjal_trashAI
{
mob_ghoulAI(Creature* c) : hyjal_trashAI(c)
@@ -584,6 +610,7 @@ struct mob_ghoulAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool pGo;
uint32 FrenzyTimer;
uint32 pos;
@@ -595,19 +622,18 @@ struct mob_ghoulAI : public hyjal_trashAI
MoveTimer = 2000;
RandomMove = false;
}
+
void WaypointReached(uint32 i)
{
pos = i;
if (i == 7 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else
- {
+ }else{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JAINAPROUDMOORE));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
@@ -621,9 +647,12 @@ struct mob_ghoulAI : public hyjal_trashAI
m_creature->setDeathState(DEAD);
m_creature->RemoveCorpse();
}
+
}
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -636,7 +665,7 @@ struct mob_ghoulAI : public hyjal_trashAI
pGo = true;
if (pInstance)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, use horde WPs
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, use horde WPs
{
for (uint8 i = 0; i < 8; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
@@ -659,18 +688,22 @@ struct mob_ghoulAI : public hyjal_trashAI
}else FrenzyTimer -= diff;
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_ghoul(Creature* pCreature)
{
return new mob_ghoulAI(pCreature);
}
+
#define SPELL_RAISE_DEAD_1 31617
#define SPELL_RAISE_DEAD_2 31624
#define SPELL_RAISE_DEAD_3 31625
#define SPELL_SHADOW_BOLT 31627
+
struct mob_necromancerAI : public hyjal_trashAI
{
mob_necromancerAI(Creature* c) : hyjal_trashAI(c), summons(m_creature)
@@ -689,6 +722,7 @@ struct mob_necromancerAI : public hyjal_trashAI
ShadowBoltTimer = 1000+rand()%5000;
summons.DespawnAll();
}
+
void JustSummoned(Creature* summon)
{
Unit* target = SelectTarget(SELECT_TARGET_RANDOM,0,30,true);
@@ -702,20 +736,19 @@ struct mob_necromancerAI : public hyjal_trashAI
pos = i;
if (i == 7 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else
- {
+ }else{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JAINAPROUDMOORE));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
}
}
}
+
void KilledUnit(Unit* victim)
{
switch (rand()%3)
@@ -736,7 +769,9 @@ struct mob_necromancerAI : public hyjal_trashAI
break;
}
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -749,7 +784,7 @@ struct mob_necromancerAI : public hyjal_trashAI
pGo = true;
if (pInstance)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, use horde WPs
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, use horde WPs
{
for (uint8 i = 0; i < 8; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
@@ -772,17 +807,21 @@ struct mob_necromancerAI : public hyjal_trashAI
DoCast(m_creature->getVictim(),SPELL_SHADOW_BOLT);
ShadowBoltTimer = 20000+rand()%10000;
}else ShadowBoltTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_necromancer(Creature* pCreature)
{
return new mob_necromancerAI(pCreature);
}
+
#define SPELL_BANSHEE_CURSE 31651
#define SPELL_BANSHEE_WAIL 38183
#define SPELL_ANTI_MAGIC_SHELL 31662
+
struct mob_bansheeAI : public hyjal_trashAI
{
mob_bansheeAI(Creature* c) : hyjal_trashAI(c)
@@ -792,6 +831,7 @@ struct mob_bansheeAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool pGo;
uint32 CourseTimer;
uint32 WailTimer;
@@ -803,26 +843,27 @@ struct mob_bansheeAI : public hyjal_trashAI
WailTimer = 15000+rand()%5000;
ShellTimer = 50000+rand()%10000;
}
+
void WaypointReached(uint32 i)
{
pos = i;
if (i == 7 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else
- {
+ }else{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JAINAPROUDMOORE));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
}
}
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -835,7 +876,7 @@ struct mob_bansheeAI : public hyjal_trashAI
pGo = true;
if (pInstance)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, use horde WPs
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, use horde WPs
{
for (uint8 i = 0; i < 8; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
@@ -872,11 +913,14 @@ struct mob_bansheeAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_banshee(Creature* pCreature)
{
return new mob_bansheeAI(pCreature);
}
+
#define SPELL_WEB 28991
+
struct mob_crypt_fiendAI : public hyjal_trashAI
{
mob_crypt_fiendAI(Creature* c) : hyjal_trashAI(c)
@@ -886,6 +930,7 @@ struct mob_crypt_fiendAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool pGo;
uint32 WebTimer;
uint32 pos;
@@ -893,26 +938,27 @@ struct mob_crypt_fiendAI : public hyjal_trashAI
{
WebTimer = 20000+rand()%5000;
}
+
void WaypointReached(uint32 i)
{
pos = i;
if (i == 7 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else
- {
+ }else{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JAINAPROUDMOORE));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
}
}
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -925,7 +971,7 @@ struct mob_crypt_fiendAI : public hyjal_trashAI
pGo = true;
if (pInstance)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, use horde WPs
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, use horde WPs
{
for (uint8 i = 0; i < 8; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
@@ -938,6 +984,7 @@ struct mob_crypt_fiendAI : public hyjal_trashAI
Start(false, true);
SetDespawnAtEnd(false);
}
+
}
}
}
@@ -952,11 +999,14 @@ struct mob_crypt_fiendAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_crypt_fiend(Creature* pCreature)
{
return new mob_crypt_fiendAI(pCreature);
}
+
#define SPELL_MANA_BURN 31729
+
struct mob_fel_stalkerAI : public hyjal_trashAI
{
mob_fel_stalkerAI(Creature* c) : hyjal_trashAI(c)
@@ -966,6 +1016,7 @@ struct mob_fel_stalkerAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool pGo;
uint32 ManaBurnTimer;
uint32 pos;
@@ -973,26 +1024,27 @@ struct mob_fel_stalkerAI : public hyjal_trashAI
{
ManaBurnTimer = 9000+rand()%5000;
}
+
void WaypointReached(uint32 i)
{
pos = i;
if (i == 7 && pInstance && !IsOverrun)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, attack thrall
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, attack thrall
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_THRALL));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
- }
- else
- {
+ }else{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_JAINAPROUDMOORE));
if (target && target->isAlive())
m_creature->AddThreat(target,0.0);
}
}
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -1005,7 +1057,7 @@ struct mob_fel_stalkerAI : public hyjal_trashAI
pGo = true;
if (pInstance)
{
- if (pInstance->GetData(DATA_ALLIANCE_RETREAT)) //2.alliance boss down, use horde WPs
+ if (pInstance->GetData(DATA_ALLIANCE_RETREAT))//2.alliance boss down, use horde WPs
{
for (uint8 i = 0; i < 8; ++i)
AddWaypoint(i, HordeWPs[i][0]+irand(-3,3), HordeWPs[i][1]+irand(-3,3), HordeWPs[i][2]);
@@ -1018,6 +1070,7 @@ struct mob_fel_stalkerAI : public hyjal_trashAI
Start(false, true);
SetDespawnAtEnd(false);
}
+
}
}
}
@@ -1032,11 +1085,14 @@ struct mob_fel_stalkerAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_fel_stalker(Creature* pCreature)
{
return new mob_fel_stalkerAI(pCreature);
}
+
#define SPELL_FROST_BREATH 31688
+
struct mob_frost_wyrmAI : public hyjal_trashAI
{
mob_frost_wyrmAI(Creature* c) : hyjal_trashAI(c)
@@ -1046,16 +1102,19 @@ struct mob_frost_wyrmAI : public hyjal_trashAI
pos = 0;
Reset();
}
+
bool pGo;
uint32 FrostBreathTimer;
uint32 pos;
uint32 MoveTimer;
+
void Reset()
{
FrostBreathTimer = 5000;
MoveTimer = 0;
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
}
+
void WaypointReached(uint32 i)
{
pos = i;
@@ -1069,17 +1128,21 @@ struct mob_frost_wyrmAI : public hyjal_trashAI
}
}
}
+
void JustDied(Unit *victim)
{
if (pInstance && IsEvent)
pInstance->SetData(DATA_TRASH, 0);//signal trash is dead
+
float x,y,z;
m_creature->GetPosition(x,y,z);
z = m_creature->GetMap()->GetVmapHeight(x, y, z, true);
m_creature->GetMotionMaster()->MovePoint(0,x,y,z);
m_creature->GetMap()->CreatureRelocation(m_creature, x,y,z,0);
}
+
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -1101,9 +1164,7 @@ struct mob_frost_wyrmAI : public hyjal_trashAI
AddWaypoint(i, FrostWyrmWPs[i][0], FrostWyrmWPs[i][1], FrostWyrmWPs[i][2]);
Start(false, true);
SetDespawnAtEnd(false);
- }
- else
- {//fly path FlyPathWPs
+ }else{//fly path FlyPathWPs
for (uint8 i = 0; i < 3; ++i)
AddWaypoint(i, FlyPathWPs[i][0]+irand(-10,10), FlyPathWPs[i][1]+irand(-10,10), FlyPathWPs[i][2]);
Start(false, true);
@@ -1121,6 +1182,7 @@ struct mob_frost_wyrmAI : public hyjal_trashAI
MoveTimer = 2000;
}else MoveTimer-=diff;
}
+
if (FrostBreathTimer<diff)
{
if (!m_creature->IsWithinDist(m_creature->getVictim(), 25))
@@ -1134,11 +1196,14 @@ struct mob_frost_wyrmAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_frost_wyrm(Creature* pCreature)
{
return new mob_frost_wyrmAI(pCreature);
}
+
#define SPELL_GARGOYLE_STRIKE 31664
+
struct mob_gargoyleAI : public hyjal_trashAI
{
mob_gargoyleAI(Creature* c) : hyjal_trashAI(c)
@@ -1149,12 +1214,14 @@ struct mob_gargoyleAI : public hyjal_trashAI
DummyTarget[0] = 0;DummyTarget[1] = 0;DummyTarget[2] = 0;
Reset();
}
+
bool pGo;
uint32 StrikeTimer;
uint32 pos;
uint32 MoveTimer;
float Zpos;
bool forcemove;
+
void Reset()
{
forcemove = true;
@@ -1163,6 +1230,7 @@ struct mob_gargoyleAI : public hyjal_trashAI
MoveTimer = 0;
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
}
+
void WaypointReached(uint32 i)
{
pos = i;
@@ -1176,6 +1244,7 @@ struct mob_gargoyleAI : public hyjal_trashAI
}
}
}
+
void JustDied(Unit *victim)
{
float x,y,z;
@@ -1185,6 +1254,7 @@ struct mob_gargoyleAI : public hyjal_trashAI
m_creature->GetMap()->CreatureRelocation(m_creature, x,y,z,0);
hyjal_trashAI::JustDied(victim);
}
+
void UpdateAI(const uint32 diff)
{
hyjal_trashAI::UpdateAI(diff);
@@ -1206,9 +1276,7 @@ struct mob_gargoyleAI : public hyjal_trashAI
AddWaypoint(i, GargoyleWPs[i][0]+irand(-10,10), GargoyleWPs[i][1]+irand(-10,10), GargoyleWPs[i][2]);
Start(false, true);
SetDespawnAtEnd(false);
- }
- else
- {//fly path FlyPathWPs
+ }else{//fly path FlyPathWPs
for (uint8 i = 0; i < 3; ++i)
AddWaypoint(i, FlyPathWPs[i][0]+irand(-10,10), FlyPathWPs[i][1]+irand(-10,10), FlyPathWPs[i][2]);
Start(false, true);
@@ -1219,7 +1287,7 @@ struct mob_gargoyleAI : public hyjal_trashAI
}
if (IsOverrun && !UpdateVictim())
{
- if (faction == 0) //alliance
+ if (faction == 0)//alliance
{
if (StrikeTimer<diff)
{
@@ -1262,29 +1330,37 @@ struct mob_gargoyleAI : public hyjal_trashAI
}
};
+
CreatureAI* GetAI_mob_gargoyle(Creature* pCreature)
{
return new mob_gargoyleAI(pCreature);
}
+
#define SPELL_EXPLODING_SHOT 7896
+
struct TRINITY_DLL_DECL alliance_riflemanAI : public Scripted_NoMovementAI
{
alliance_riflemanAI(Creature *c) : Scripted_NoMovementAI(c)
{
Reset();
}
+
uint32 ExplodeTimer;
+
void JustDied(Unit* who)
{
}
+
void Reset()
{
ExplodeTimer = 5000+rand()%5000;
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim())
return;
+
if (who->isTargetableForAttack() && m_creature->IsHostileTo(who))
{
float attackRadius = m_creature->GetAttackDistance(who);
@@ -1294,9 +1370,11 @@ struct TRINITY_DLL_DECL alliance_riflemanAI : public Scripted_NoMovementAI
}
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Check if we have a target
@@ -1316,48 +1394,59 @@ struct TRINITY_DLL_DECL alliance_riflemanAI : public Scripted_NoMovementAI
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_alliance_rifleman(Creature* pCreature)
{
return new alliance_riflemanAI(pCreature);
}
+
void AddSC_hyjal_trash()
{
Script *newscript = new Script;
newscript->Name = "mob_giant_infernal";
newscript->GetAI = &GetAI_mob_giant_infernal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_abomination";
newscript->GetAI = &GetAI_mob_abomination;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ghoul";
newscript->GetAI = &GetAI_mob_ghoul;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_necromancer";
newscript->GetAI = &GetAI_mob_necromancer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_banshee";
newscript->GetAI = &GetAI_mob_banshee;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_crypt_fiend";
newscript->GetAI = &GetAI_mob_crypt_fiend;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_fel_stalker";
newscript->GetAI = &GetAI_mob_fel_stalker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_frost_wyrm";
newscript->GetAI = &GetAI_mob_frost_wyrm;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_gargoyle";
newscript->GetAI = &GetAI_mob_gargoyle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "alliance_rifleman";
newscript->GetAI = &GetAI_alliance_rifleman;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h
index ed5031ef1a8..17fdbad5fda 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/hyjal_trash.h
@@ -1,15 +1,22 @@
#ifndef SC_HYJAL_TRASH_AI_H
#define SC_HYJAL_TRASH_AI_H
+
#include "def_hyjal.h"
#include "escort_ai.h"
+
#define MINRAIDDAMAGE 700000//minimal damage before trash can drop loot and reputation, resets if faction leader dies
+
struct TRINITY_DLL_DECL hyjal_trashAI : public npc_escortAI
{
hyjal_trashAI(Creature *c);
+
void UpdateAI(const uint32 diff);
+
void JustDied(Unit* killer);
+
void DamageTaken(Unit *done_by, uint32 &damage);
+
public:
ScriptedInstance* pInstance;
bool IsEvent;
@@ -22,6 +29,7 @@ struct TRINITY_DLL_DECL hyjal_trashAI : public npc_escortAI
bool useFlyPath;
uint32 damageTaken;
float DummyTarget[3];
+
//private:
};
-#endif
+#endif \ No newline at end of file
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp
index 2a69e02caea..81b51507463 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/hyjal/instance_hyjal.cpp
@@ -13,18 +13,22 @@
* along 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 "hyjal_trash.h"
+
enum eEnums
{
MAX_ENCOUNTER = 5,
+
GO_ANCIENT_GEM = 185557
};
/* Battle of Mount Hyjal encounters:
@@ -34,12 +38,16 @@ enum eEnums
3 - Azgalor event
4 - Archimonde event
*/
+
struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
{
instance_mount_hyjal(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
+
std::list<uint64> m_uiAncientGemGUID;
+
uint64 RageWinterchill;
uint64 Anetheron;
uint64 Kazrogal;
@@ -50,18 +58,25 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
uint64 TyrandeWhisperwind;
uint64 HordeGate;
uint64 ElfGate;
+
uint32 Trash;
+
uint32 hordeRetreat;
uint32 allianceRetreat;
bool ArchiYell;
+
uint32 RaidDamage;
+
#define YELL_EFFORTS "All of your efforts have been in vain, for the draining of the World Tree has already begun. Soon the heart of your world will beat no more."
#define YELL_EFFORTS_NAME "Archimonde"
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiAncientGemGUID.clear();
+
RageWinterchill = 0;
Anetheron = 0;
Kazrogal = 0;
@@ -74,16 +89,21 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
ElfGate = 0;
ArchiYell = false;
RaidDamage = 0;
+
Trash = 0;
+
hordeRetreat = 0;
allianceRetreat = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -107,6 +127,7 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -121,6 +142,7 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
case 17948: TyrandeWhisperwind = pCreature->GetGUID(); break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -134,8 +156,10 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
case DATA_THRALL: return Thrall;
case DATA_TYRANDEWHISPERWIND: return TyrandeWhisperwind;
}
+
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -152,10 +176,12 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
{
if (ArchiYell)break;
ArchiYell = true;
+
Creature* pCreature = instance->GetCreature(Azgalor);
if (pCreature)
{
Creature* pUnit = pCreature->SummonCreature(21987,pCreature->GetPositionX(),pCreature->GetPositionY(),pCreature->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN,10000);
+
Map* pMap = pCreature->GetMap();
if (pMap->IsDungeon() && pUnit)
{
@@ -163,6 +189,7 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
Map::PlayerList const &PlayerList = pMap->GetPlayers();
if (PlayerList.isEmpty())
return;
+
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (i->getSource())
@@ -170,6 +197,7 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
WorldPacket data(SMSG_MESSAGECHAT, 200);
pUnit->BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,YELL_EFFORTS,0,YELL_EFFORTS_NAME,i->getSource()->GetGUID());
i->getSource()->GetSession()->SendPacket(&data);
+
WorldPacket data2(SMSG_PLAY_SOUND, 4);
data2 << 10986;
i->getSource()->GetSession()->SendPacket(&data2);
@@ -182,6 +210,7 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
break;
case DATA_ARCHIMONDEEVENT: m_auiEncounter[4] = data; break;
case DATA_RESET_TRASH_COUNT: Trash = 0; break;
+
case DATA_TRASH:
if (data) Trash = data;
else Trash--;
@@ -192,7 +221,7 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
{
if (!m_uiAncientGemGUID.empty())
{
- for (std::list<uint64>::iterator itr = m_uiAncientGemGUID.begin(); itr != m_uiAncientGemGUID.end(); ++itr)
+ for(std::list<uint64>::iterator itr = m_uiAncientGemGUID.begin(); itr != m_uiAncientGemGUID.end(); ++itr)
{
//don't know how long it expected
DoRespawnGameObject(*itr,DAY);
@@ -219,20 +248,27 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
RaidDamage = 0;
break;
}
+
debug_log("TSCR: Instance Hyjal: Instance data updated for event %u (Data=%u)",type,data);
+
if (data == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " "
<< m_auiEncounter[3] << " " << m_auiEncounter[4]
<< " " << allianceRetreat << " " << hordeRetreat
<< " " << RaidDamage;
+
str_data = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
+
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -249,10 +285,12 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -260,19 +298,22 @@ struct TRINITY_DLL_DECL instance_mount_hyjal : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3] >> m_auiEncounter[4] >> allianceRetreat >> hordeRetreat >> RaidDamage;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as IN_PROGRESS - reset it instead.
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_mount_hyjal(Map* pMap)
{
return new instance_mount_hyjal(pMap);
}
+
void AddSC_instance_mount_hyjal()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp
index 130b34e0ee3..6582e114049 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_captain_skarloc.cpp
@@ -13,39 +13,47 @@
* along 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 SAY_ENTER -1560000
#define SAY_TAUNT1 -1560001
#define SAY_TAUNT2 -1560002
#define SAY_SLAY1 -1560003
#define SAY_SLAY2 -1560004
#define SAY_DEATH -1560005
+
#define SPELL_HOLY_LIGHT 29427
#define SPELL_CLEANSE 29380
#define SPELL_HAMMER_OF_JUSTICE 13005
#define SPELL_HOLY_SHIELD 31904
#define SPELL_DEVOTION_AURA 8258
#define SPELL_CONSECRATION 38385
+
struct TRINITY_DLL_DECL boss_captain_skarlocAI : public ScriptedAI
{
boss_captain_skarlocAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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 = 20000 + rand()%10000;
@@ -55,70 +63,84 @@ struct TRINITY_DLL_DECL boss_captain_skarlocAI : public ScriptedAI
DevotionAura_Timer = 3000;
Consecration_Timer = 8000;
}
+
void EnterCombat(Unit *who)
{
//This is not correct. Should taunt Thrall before engage in combat
DoScriptText(SAY_TAUNT1, m_creature);
DoScriptText(SAY_TAUNT2, m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
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 (!UpdateVictim())
return;
+
//Holy_Light
if (Holy_Light_Timer < diff)
{
DoCast(m_creature, SPELL_HOLY_LIGHT);
Holy_Light_Timer = 30000;
}else Holy_Light_Timer -= diff;
+
//Cleanse
if (Cleanse_Timer < diff)
{
DoCast(m_creature, SPELL_CLEANSE);
Cleanse_Timer = 10000;
} else Cleanse_Timer -= diff;
+
//Hammer of Justice
if (HammerOfJustice_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_HAMMER_OF_JUSTICE);
HammerOfJustice_Timer = 60000;
}else HammerOfJustice_Timer -= diff;
+
//Holy Shield
if (HolyShield_Timer < diff)
{
DoCast(m_creature, SPELL_HOLY_SHIELD);
HolyShield_Timer = 240000;
}else HolyShield_Timer -= diff;
+
//Devotion_Aura
if (DevotionAura_Timer < diff)
{
DoCast(m_creature, SPELL_DEVOTION_AURA);
DevotionAura_Timer = 45000 + rand()%10000;
}else DevotionAura_Timer -= diff;
+
//Consecration
if (Consecration_Timer < diff)
{
//DoCast(m_creature->getVictim(), SPELL_CONSECRATION);
Consecration_Timer = 5000 + rand()%5000;
}else Consecration_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_captain_skarloc(Creature* pCreature)
{
return new boss_captain_skarlocAI (pCreature);
}
+
void AddSC_boss_captain_skarloc()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp
index bb8012bfa48..d430cf92863 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_epoch_hunter.cpp
@@ -13,14 +13,17 @@
* along 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 -1560013
#define SAY_ENTER2 -1560014
#define SAY_ENTER3 -1560015
@@ -31,21 +34,26 @@ EndScriptData */
#define SAY_BREATH1 -1560020
#define SAY_BREATH2 -1560021
#define SAY_DEATH -1560022
+
#define SPELL_SAND_BREATH 31914
#define SPELL_IMPENDING_DEATH 31916
#define SPELL_MAGIC_DISRUPTION_AURA 33834
#define SPELL_WING_BUFFET 31475
+
struct TRINITY_DLL_DECL boss_epoch_hunterAI : public ScriptedAI
{
boss_epoch_hunterAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 SandBreath_Timer;
uint32 ImpendingDeath_Timer;
uint32 WingBuffet_Timer;
uint32 Mda_Timer;
+
void Reset()
{
SandBreath_Timer = 8000 + rand()%8000;
@@ -53,57 +61,72 @@ struct TRINITY_DLL_DECL boss_epoch_hunterAI : public ScriptedAI
WingBuffet_Timer = 35000;
Mda_Timer = 40000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2), m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
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 (!UpdateVictim())
return;
+
//Sand Breath
if (SandBreath_Timer < diff)
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
DoCast(m_creature->getVictim(),SPELL_SAND_BREATH);
+
DoScriptText(RAND(SAY_BREATH1,SAY_BREATH2), m_creature);
+
SandBreath_Timer = 10000 + rand()%10000;
}else SandBreath_Timer -= diff;
+
if (ImpendingDeath_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_IMPENDING_DEATH);
ImpendingDeath_Timer = 25000+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* pCreature)
{
return new boss_epoch_hunterAI (pCreature);
}
+
void AddSC_boss_epoch_hunter()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp
index 3a607c0ff6a..f396c556dae 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/boss_leutenant_drake.cpp
@@ -13,31 +13,39 @@
* along 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. Script for GO (barrels) quest 10283
SDCategory: Caverns of Time, Old Hillsbrad Foothills
EndScriptData */
+
#include "precompiled.h"
#include "def_old_hillsbrad.h"
#include "escort_ai.h"
+
/*######
## go_barrel_old_hillsbrad
######*/
+
bool GOHello_go_barrel_old_hillsbrad(Player* pPlayer, GameObject* pGO)
{
if (ScriptedInstance* pInstance = pGO->GetInstanceData())
{
if (pInstance->GetData(TYPE_BARREL_DIVERSION) == DONE)
return false;
+
pInstance->SetData(TYPE_BARREL_DIVERSION, IN_PROGRESS);
}
+
return false;
}
+
/*######
## boss_lieutenant_drake
######*/
+
#define SAY_ENTER -1560006
#define SAY_AGGRO -1560007
#define SAY_SLAY1 -1560008
@@ -45,10 +53,12 @@ bool GOHello_go_barrel_old_hillsbrad(Player* pPlayer, GameObject* pGO)
#define SAY_MORTAL -1560010
#define SAY_SHOUT -1560011
#define SAY_DEATH -1560012
+
#define SPELL_WHIRLWIND 31909
#define SPELL_HAMSTRING 9080
#define SPELL_MORTAL_STRIKE 31911
#define SPELL_FRIGHTENING_SHOUT 33789
+
struct Location
{
uint32 wpId;
@@ -56,6 +66,7 @@ struct Location
float y;
float z;
};
+
static Location DrakeWP[]=
{
{0, 2125.84, 88.2535, 54.8830},
@@ -78,36 +89,45 @@ static Location DrakeWP[]=
{17, 2125.50, 88.9481, 54.7953},
{18, 2128.20, 70.9763, 64.4221}
};
+
struct TRINITY_DLL_DECL boss_lieutenant_drakeAI : public ScriptedAI
{
boss_lieutenant_drakeAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//TODO: make this work
@@ -116,15 +136,18 @@ struct TRINITY_DLL_DECL boss_lieutenant_drakeAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(DrakeWP[0].wpId, DrakeWP[0].x, DrakeWP[0].y, DrakeWP[0].z);
wpId++;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//Whirlwind
if (Whirlwind_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_WHIRLWIND);
Whirlwind_Timer = 20000+rand()%5000;
}else Whirlwind_Timer -= diff;
+
//Fear
if (Fear_Timer < diff)
{
@@ -132,6 +155,7 @@ struct TRINITY_DLL_DECL boss_lieutenant_drakeAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_FRIGHTENING_SHOUT);
Fear_Timer = 25000+rand()%10000;
}else Fear_Timer -= diff;
+
//Mortal Strike
if (MortalStrike_Timer < diff)
{
@@ -139,20 +163,25 @@ struct TRINITY_DLL_DECL boss_lieutenant_drakeAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_MORTAL_STRIKE);
MortalStrike_Timer = 20000+rand()%10000;
}else MortalStrike_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_lieutenant_drake(Creature* pCreature)
{
return new boss_lieutenant_drakeAI (pCreature);
}
+
void AddSC_boss_lieutenant_drake()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_barrel_old_hillsbrad";
newscript->pGOHello = &GOHello_go_barrel_old_hillsbrad;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lieutenant_drake";
newscript->GetAI = &GetAI_boss_lieutenant_drake;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h
index d57c5e9d313..3253a384217 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/def_old_hillsbrad.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_OLD_HILLSBRAD_H
#define DEF_OLD_HILLSBRAD_H
+
#define TYPE_BARREL_DIVERSION 1
#define TYPE_THRALL_EVENT 2
#define TYPE_THRALL_PART1 3
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp
index 0f976484190..b8ec8f2e111 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/instance_old_hillsbrad.cpp
@@ -13,65 +13,82 @@
* along 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: 75
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 */
+
#include "precompiled.h"
#include "def_old_hillsbrad.h"
+
#define MAX_ENCOUNTER 6
+
#define THRALL_ENTRY 17876
#define TARETHA_ENTRY 18887
#define EPOCH_ENTRY 18096
+
#define DRAKE_ENTRY 17848
+
#define QUEST_ENTRY_DIVERSION 10283
#define LODGE_QUEST_TRIGGER 20155
+
struct TRINITY_DLL_DECL instance_old_hillsbrad : public ScriptedInstance
{
instance_old_hillsbrad(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint32 mBarrelCount;
uint32 mThrallEventCount;
+
uint64 ThrallGUID;
uint64 TarethaGUID;
uint64 EpochGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
mBarrelCount = 0;
mThrallEventCount = 0;
ThrallGUID = 0;
TarethaGUID = 0;
EpochGUID = 0;
}
+
Player* GetPlayerInMap()
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* plr = itr->getSource())
return plr;
}
}
+
debug_log("TSCR: Instance Old Hillsbrad: GetPlayerInMap, but PlayerList is empty!");
return NULL;
}
+
void UpdateQuestCredit()
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* pPlayer = itr->getSource())
pPlayer->KilledMonsterCredit(LODGE_QUEST_TRIGGER,0);
}
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -87,14 +104,17 @@ struct TRINITY_DLL_DECL instance_old_hillsbrad : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
Player* pPlayer = GetPlayerInMap();
+
if (!pPlayer)
{
debug_log("TSCR: Instance Old Hillsbrad: SetData (Type: %u Data %u) cannot find any player.", type, data);
return;
}
+
switch(type)
{
case TYPE_BARREL_DIVERSION:
@@ -103,10 +123,14 @@ struct TRINITY_DLL_DECL instance_old_hillsbrad : public ScriptedInstance
{
if (mBarrelCount >= 5)
return;
+
++mBarrelCount;
DoUpdateWorldState(WORLD_STATE_OH, mBarrelCount);
+
debug_log("TSCR: Instance Old Hillsbrad: go_barrel_old_hillsbrad count %u",mBarrelCount);
+
m_auiEncounter[0] = IN_PROGRESS;
+
if (mBarrelCount == 5)
{
UpdateQuestCredit();
@@ -163,6 +187,7 @@ struct TRINITY_DLL_DECL instance_old_hillsbrad : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 data)
{
switch(data)
@@ -182,6 +207,7 @@ struct TRINITY_DLL_DECL instance_old_hillsbrad : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -200,6 +226,7 @@ InstanceData* GetInstanceData_instance_old_hillsbrad(Map* pMap)
{
return new instance_old_hillsbrad(pMap);
}
+
void AddSC_instance_old_hillsbrad()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp
index 5408fdd8308..9fdd713e639 100644
--- a/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/caverns_of_time/old_hillsbrad/old_hillsbrad.cpp
@@ -13,42 +13,54 @@
* along 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: Quest support: 10283, 10284. 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_erozion
npc_thrall_old_hillsbrad
npc_taretha
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.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
+
#define GOSSIP_HELLO_EROZION1 "I need a pack of Incendiary Bombs."
#define GOSSIP_HELLO_EROZION2 "[PH] Teleport please, i'm tired."
+
/*######
## npc_erozion
######*/
+
bool GossipHello_npc_erozion(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
ScriptedInstance* pInstance = pCreature->GetInstanceData();
if (pInstance && pInstance->GetData(TYPE_BARREL_DIVERSION) != DONE && !pPlayer->HasItemCount(ITEM_ENTRY_BOMBS,1))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_EROZION1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
if (!pPlayer->GetQuestRewardStatus(QUEST_ENTRY_RETURN) && pPlayer->GetQuestStatus(QUEST_ENTRY_RETURN) == QUEST_STATUS_COMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_EROZION2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
+
pPlayer->SEND_GOSSIP_MENU(9778, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_erozion(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -67,9 +79,11 @@ bool GossipSelect_npc_erozion(Player* pPlayer, Creature* pCreature, uint32 uiSen
}
return true;
}
+
/*######
## npc_thrall_old_hillsbrad
######*/
+
//Thrall texts
#define SAY_TH_START_EVENT_PART1 -1560023
#define SAY_TH_ARMORY -1560024
@@ -82,57 +96,73 @@ bool GossipSelect_npc_erozion(Player* pPlayer, Creature* pCreature, uint32 uiSen
#define SAY_TH_EPOCH_WONDER -1560031
#define SAY_TH_EPOCH_KILL_TARETHA -1560032
#define SAY_TH_EVENT_COMPLETE -1560033
+
#define SAY_TH_RANDOM_LOW_HP1 -1560034
#define SAY_TH_RANDOM_LOW_HP2 -1560035
+
#define SAY_TH_RANDOM_DIE1 -1560036
#define SAY_TH_RANDOM_DIE2 -1560037
+
#define SAY_TH_RANDOM_AGGRO1 -1560038
#define SAY_TH_RANDOM_AGGRO2 -1560039
#define SAY_TH_RANDOM_AGGRO3 -1560040
#define SAY_TH_RANDOM_AGGRO4 -1560041
+
#define SAY_TH_RANDOM_KILL1 -1560042
#define SAY_TH_RANDOM_KILL2 -1560043
#define SAY_TH_RANDOM_KILL3 -1560044
+
#define SAY_TH_LEAVE_COMBAT1 -1560045
#define SAY_TH_LEAVE_COMBAT2 -1560046
#define SAY_TH_LEAVE_COMBAT3 -1560047
+
//Taretha texts
#define SAY_TA_FREE -1560048
#define SAY_TA_ESCAPED -1560049
+
//Misc for Thrall
#define SPELL_STRIKE 14516
#define SPELL_SHIELD_BLOCK 12169
#define SPELL_SUMMON_EROZION_IMAGE 33954 //if thrall dies during escort?
+
#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
+
//Misc Creature entries
#define ENTRY_ARMORER 18764
#define ENTRY_SCARLOC 17862
+
#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 ENTRY_EPOCH 18096
+
//gossip items
#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...
@@ -140,10 +170,14 @@ bool GossipSelect_npc_erozion(Player* pPlayer, Creature* pCreature, uint32 uiSen
#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 GOSSIP_ITEM_WALKING "[PH] Start walking."
+
struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
{
npc_thrall_old_hillsbradAI(Creature *c) : npc_escortAI(c)
@@ -152,14 +186,19 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
HadMount = false;
m_creature->setActive(true);
}
+
ScriptedInstance *pInstance;
+
uint64 TarethaGUID;
+
bool LowHp;
bool HadMount;
+
void WaypointReached(uint32 i)
{
if (!pInstance)
return;
+
switch(i)
{
case 8:
@@ -292,6 +331,7 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
//trigger epoch Yell("Thrall! Come outside and face your fate! ....")
//from here, thrall should not never be allowed to move to point 106 which he currently does.
break;
+
case 106:
{
//trigger taretha to run down outside
@@ -300,17 +340,19 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
if (Player* pPlayer = GetPlayerForEscort())
CAST_AI(npc_escortAI, (Taretha->AI()))->Start(false, true, pPlayer->GetGUID());
}
+
//kill credit Creature for quest
Map* pMap = m_creature->GetMap();
Map::PlayerList const& players = pMap->GetPlayers();
if (!players.isEmpty() && pMap->IsDungeon())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* pPlayer = itr->getSource())
pPlayer->KilledMonsterCredit(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);
}
@@ -322,11 +364,14 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
}
}
+
void Reset()
{
LowHp = false;
+
if (HadMount)
DoMount();
+
if (!HasEscortState(STATE_ESCORT_ESCORTING))
{
DoUnmount();
@@ -364,6 +409,7 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
HadMount = true;
}
}
+
void JustSummoned(Creature* summoned)
{
switch(summoned->GetEntry())
@@ -380,6 +426,7 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
break;
}
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_TH_RANDOM_KILL1,SAY_TH_RANDOM_KILL2,SAY_TH_RANDOM_KILL3), m_creature);
@@ -388,16 +435,21 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
{
if (pInstance)
pInstance->SetData(TYPE_THRALL_EVENT,FAIL);
+
// Don't do a yell if he kills self (if player goes too far or at the end).
if (slayer == m_creature)
return;
+
DoScriptText(RAND(SAY_TH_RANDOM_DIE1,SAY_TH_RANDOM_DIE2), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
+
if (!UpdateVictim())
return;
+
//TODO: add his abilities'n-crap here
if (!LowHp && ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20))
{
@@ -406,10 +458,12 @@ struct TRINITY_DLL_DECL npc_thrall_old_hillsbradAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_npc_thrall_old_hillsbrad(Creature* pCreature)
{
return new npc_thrall_old_hillsbradAI(pCreature);
}
+
bool GossipHello_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
@@ -417,6 +471,7 @@ bool GossipHello_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature)
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
pPlayer->SendPreparedQuest(pCreature->GetGUID());
}
+
ScriptedInstance* pInstance = pCreature->GetInstanceData();
if (pInstance)
{
@@ -425,11 +480,13 @@ bool GossipHello_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_WALKING, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_ID_START, pCreature->GetGUID());
}
+
if (pInstance->GetData(TYPE_THRALL_PART1) == DONE && !pInstance->GetData(TYPE_THRALL_PART2))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SKARLOC1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_ID_SKARLOC1, pCreature->GetGUID());
}
+
if (pInstance->GetData(TYPE_THRALL_PART2) == DONE && !pInstance->GetData(TYPE_THRALL_PART3))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TARREN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+3);
@@ -438,6 +495,7 @@ bool GossipHello_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
bool GossipSelect_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
@@ -450,25 +508,33 @@ bool GossipSelect_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature,
pInstance->SetData(TYPE_THRALL_EVENT,IN_PROGRESS);
pInstance->SetData(TYPE_THRALL_PART1,IN_PROGRESS);
}
+
DoScriptText(SAY_TH_START_EVENT_PART1, pCreature);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_thrall_old_hillsbradAI, pCreature->AI()))
pEscortAI->Start(true, true, pPlayer->GetGUID());
+
CAST_AI(npc_escortAI, (pCreature->AI()))->SetMaxPlayerDistance(100.0f);//not really needed, because it will not despawn if player is too far
CAST_AI(npc_escortAI, (pCreature->AI()))->SetDespawnAtEnd(false);
CAST_AI(npc_escortAI, (pCreature->AI()))->SetDespawnAtFar(false);
break;
+
case GOSSIP_ACTION_INFO_DEF+2:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_SKARLOC2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+20);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_ID_SKARLOC2, pCreature->GetGUID());
break;
+
case GOSSIP_ACTION_INFO_DEF+20:
pPlayer->SEND_GOSSIP_MENU(GOSSIP_ID_SKARLOC3, pCreature->GetGUID());
pCreature->SummonCreature(SKARLOC_MOUNT,2038.81,270.26,63.20,5.41,TEMPSUMMON_TIMED_DESPAWN,12000);
if (pInstance)
pInstance->SetData(TYPE_THRALL_PART2,IN_PROGRESS);
+
DoScriptText(SAY_TH_START_EVENT_PART2, pCreature);
+
CAST_AI(npc_thrall_old_hillsbradAI, pCreature->AI())->StartWP();
break;
+
case GOSSIP_ACTION_INFO_DEF+3:
pPlayer->CLOSE_GOSSIP_MENU();
if (pInstance)
@@ -478,20 +544,25 @@ bool GossipSelect_npc_thrall_old_hillsbrad(Player* pPlayer, Creature* pCreature,
}
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."
+
struct TRINITY_DLL_DECL npc_tarethaAI : public npc_escortAI
{
npc_tarethaAI(Creature *c) : npc_escortAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
void WaypointReached(uint32 i)
{
switch(i)
@@ -506,6 +577,7 @@ struct TRINITY_DLL_DECL npc_tarethaAI : public npc_escortAI
}
void Reset() {}
void EnterCombat(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
@@ -515,6 +587,7 @@ CreatureAI* GetAI_npc_taretha(Creature* pCreature)
{
return new npc_tarethaAI(pCreature);
}
+
bool GossipHello_npc_taretha(Player* pPlayer, Creature* pCreature)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
@@ -525,6 +598,7 @@ bool GossipHello_npc_taretha(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
bool GossipSelect_npc_taretha(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
@@ -536,11 +610,13 @@ bool GossipSelect_npc_taretha(Player* pPlayer, Creature* pCreature, uint32 uiSen
if (uiAction == GOSSIP_ACTION_INFO_DEF+2)
{
pPlayer->CLOSE_GOSSIP_MENU();
+
if (pInstance && pInstance->GetData(TYPE_THRALL_EVENT) == IN_PROGRESS)
{
pInstance->SetData(TYPE_THRALL_PART4,IN_PROGRESS);
if (pInstance->GetData64(DATA_EPOCH) == 0)
pCreature->SummonCreature(ENTRY_EPOCH,2639.13,698.55,65.43,4.59,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,120000);
+
if (uint64 ThrallGUID = pInstance->GetData64(DATA_THRALL))
{
Creature* Thrall = (Unit::GetCreature((*pCreature), ThrallGUID));
@@ -551,23 +627,28 @@ bool GossipSelect_npc_taretha(Player* pPlayer, Creature* pCreature, uint32 uiSen
}
return true;
}
+
/*######
## AddSC
######*/
+
void AddSC_old_hillsbrad()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_erozion";
newscript->pGossipHello = &GossipHello_npc_erozion;
newscript->pGossipSelect = &GossipSelect_npc_erozion;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_taretha";
newscript->pGossipHello = &GossipHello_npc_taretha;
diff --git a/src/bindings/scripts/scripts/kalimdor/darkshore.cpp b/src/bindings/scripts/scripts/kalimdor/darkshore.cpp
index 4c1485586e5..1354c2bc861 100644
--- a/src/bindings/scripts/scripts/kalimdor/darkshore.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/darkshore.cpp
@@ -13,55 +13,71 @@
* along 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: 100
SDComment: Quest support: 731, 2078, 5321
SDCategory: Darkshore
EndScriptData */
+
/* ContentData
npc_kerlonian
npc_prospector_remtravel
npc_threshwackonator
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "follower_ai.h"
+
/*####
# npc_kerlonian
####*/
+
enum eKerlonian
{
SAY_KER_START = -1000434,
+
EMOTE_KER_SLEEP_1 = -1000435,
EMOTE_KER_SLEEP_2 = -1000436,
EMOTE_KER_SLEEP_3 = -1000437,
+
SAY_KER_SLEEP_1 = -1000438,
SAY_KER_SLEEP_2 = -1000439,
SAY_KER_SLEEP_3 = -1000440,
SAY_KER_SLEEP_4 = -1000441,
+
EMOTE_KER_AWAKEN = -1000445,
+
SAY_KER_ALERT_1 = -1000442,
SAY_KER_ALERT_2 = -1000443,
+
SAY_KER_END = -1000444,
+
SPELL_SLEEP_VISUAL = 25148,
SPELL_AWAKEN = 17536,
QUEST_SLEEPER_AWAKENED = 5321,
NPC_LILADRIS = 11219, //attackers entries unknown
FACTION_KER_ESCORTEE = 113
};
+
//TODO: make concept similar as "ringo" -escort. Find a way to run the scripted attacks, _if_ player are choosing road.
struct TRINITY_DLL_DECL npc_kerlonianAI : public FollowerAI
{
npc_kerlonianAI(Creature* pCreature) : FollowerAI(pCreature) { }
+
uint32 m_uiFallAsleepTimer;
+
void Reset()
{
m_uiFallAsleepTimer = urand(10000, 45000);
}
+
void MoveInLineOfSight(Unit *pWho)
{
FollowerAI::MoveInLineOfSight(pWho);
+
if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_LILADRIS)
{
if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE*5))
@@ -70,38 +86,50 @@ struct TRINITY_DLL_DECL npc_kerlonianAI : public FollowerAI
{
if (pPlayer->GetQuestStatus(QUEST_SLEEPER_AWAKENED) == QUEST_STATUS_INCOMPLETE)
pPlayer->GroupEventHappens(QUEST_SLEEPER_AWAKENED, m_creature);
+
DoScriptText(SAY_KER_END, m_creature);
}
+
SetFollowComplete();
}
}
}
+
void SpellHit(Unit* pCaster, const SpellEntry* pSpell)
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS | STATE_FOLLOW_PAUSED) && pSpell->Id == SPELL_AWAKEN)
ClearSleeping();
}
+
void SetSleeping()
{
SetFollowPaused(true);
+
DoScriptText(RAND(EMOTE_KER_SLEEP_1,EMOTE_KER_SLEEP_2,EMOTE_KER_SLEEP_3), m_creature);
+
DoScriptText(RAND(SAY_KER_SLEEP_1,SAY_KER_SLEEP_2,SAY_KER_SLEEP_3,SAY_KER_SLEEP_4), m_creature);
+
m_creature->SetStandState(UNIT_STAND_STATE_SLEEP);
m_creature->CastSpell(m_creature, SPELL_SLEEP_VISUAL, false);
}
+
void ClearSleeping()
{
m_creature->RemoveAurasDueToSpell(SPELL_SLEEP_VISUAL);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
DoScriptText(EMOTE_KER_AWAKEN, m_creature);
+
SetFollowPaused(false);
}
+
void UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
{
if (!HasFollowState(STATE_FOLLOW_INPROGRESS))
return;
+
if (!HasFollowState(STATE_FOLLOW_PAUSED))
{
if (m_uiFallAsleepTimer < uiDiff)
@@ -112,15 +140,19 @@ struct TRINITY_DLL_DECL npc_kerlonianAI : public FollowerAI
else
m_uiFallAsleepTimer -= uiDiff;
}
+
return;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_kerlonian(Creature* pCreature)
{
return new npc_kerlonianAI(pCreature);
}
+
bool QuestAccept_npc_kerlonian(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_SLEEPER_AWAKENED)
@@ -132,11 +164,14 @@ bool QuestAccept_npc_kerlonian(Player* pPlayer, Creature* pCreature, const Quest
pKerlonianAI->StartFollow(pPlayer, FACTION_KER_ESCORTEE, pQuest);
}
}
+
return true;
}
+
/*####
# npc_prospector_remtravel
####*/
+
enum eRemtravel
{
SAY_REM_START = -1000415, // signed for 4966
@@ -152,20 +187,25 @@ enum eRemtravel
SAY_REM_PROGRESS = -1000424, // signed for 9999
SAY_REM_REMEMBER = -1000425, // signed for 9999
EMOTE_REM_END = -1000426, // signed for 9999
+
FACTION_ESCORTEE = 10,
QUEST_ABSENT_MINDED_PT2 = 731,
NPC_GRAVEL_SCOUT = 2158,
NPC_GRAVEL_BONE = 2159,
NPC_GRAVEL_GEO = 2160
};
+
struct TRINITY_DLL_DECL npc_prospector_remtravelAI : public npc_escortAI
{
npc_prospector_remtravelAI(Creature* pCreature) : npc_escortAI(pCreature) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 0:
@@ -221,35 +261,44 @@ struct TRINITY_DLL_DECL npc_prospector_remtravelAI : public npc_escortAI
break;
}
}
+
void Reset() {}
+
void EnterCombat(Unit* who)
{
if (rand()%2)
DoScriptText(SAY_REM_AGGRO, me, who);
}
+
void JustSummoned(Creature* pSummoned)
{
//unsure if it should be any
//pSummoned->AI()->AttackStart(m_creature);
}
};
+
CreatureAI* GetAI_npc_prospector_remtravel(Creature* pCreature)
{
return new npc_prospector_remtravelAI(pCreature);
}
+
bool QuestAccept_npc_prospector_remtravel(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_ABSENT_MINDED_PT2)
{
if (npc_escortAI* pEscortAI = CAST_AI(npc_prospector_remtravelAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID());
+
pCreature->setFaction(FACTION_ESCORTEE);
}
+
return true;
}
+
/*####
# npc_threshwackonator
####*/
+
enum eThreshwackonator
{
EMOTE_START = -1000413, //signed for 4966
@@ -258,14 +307,19 @@ enum eThreshwackonator
NPC_GELKAK = 6667,
FACTION_HOSTILE = 14
};
+
#define GOSSIP_ITEM_INSERT_KEY "[PH] Insert key"
+
struct TRINITY_DLL_DECL npc_threshwackonatorAI : public FollowerAI
{
npc_threshwackonatorAI(Creature* pCreature) : FollowerAI(pCreature) { }
+
void Reset() { }
+
void MoveInLineOfSight(Unit* pWho)
{
FollowerAI::MoveInLineOfSight(pWho);
+
if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_GELKAK)
{
if (m_creature->IsWithinDistInMap(pWho, 10.0f))
@@ -275,51 +329,64 @@ struct TRINITY_DLL_DECL npc_threshwackonatorAI : public FollowerAI
}
}
}
+
void DoAtEnd()
{
me->setFaction(FACTION_HOSTILE);
+
if (Player* pHolder = GetLeaderForFollower())
me->AI()->AttackStart(pHolder);
+
SetFollowComplete();
}
};
+
CreatureAI* GetAI_npc_threshwackonator(Creature* pCreature)
{
return new npc_threshwackonatorAI(pCreature);
}
+
bool GossipHello_npc_threshwackonator(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_GYROMAST_REV) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_INSERT_KEY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_threshwackonator(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
{
pPlayer->CLOSE_GOSSIP_MENU();
+
if (npc_threshwackonatorAI* pThreshAI = CAST_AI(npc_threshwackonatorAI, pCreature->AI()))
{
DoScriptText(EMOTE_START, pCreature);
pThreshAI->StartFollow(pPlayer);
}
}
+
return true;
}
+
void AddSC_darkshore()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_kerlonian";
newscript->GetAI = &GetAI_npc_kerlonian;
newscript->pQuestAccept = &QuestAccept_npc_kerlonian;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_prospector_remtravel";
newscript->GetAI = &GetAI_npc_prospector_remtravel;
newscript->pQuestAccept = &QuestAccept_npc_prospector_remtravel;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_threshwackonator";
newscript->GetAI = &GetAI_npc_threshwackonator;
diff --git a/src/bindings/scripts/scripts/kalimdor/desolace.cpp b/src/bindings/scripts/scripts/kalimdor/desolace.cpp
index 8555b3190f0..02702f77c15 100644
--- a/src/bindings/scripts/scripts/kalimdor/desolace.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/desolace.cpp
@@ -13,55 +13,70 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Desolace
SD%Complete: 100
SDComment: Quest support: 5561
SDCategory: Desolace
EndScriptData */
+
/* ContentData
npc_aged_dying_ancient_kodo
EndContentData */
+
#include "precompiled.h"
+
enum eDyingKodo
{
// signed for 9999
SAY_SMEED_HOME_1 = -1000428,
SAY_SMEED_HOME_2 = -1000429,
SAY_SMEED_HOME_3 = -1000430,
+
QUEST_KODO = 5561,
+
NPC_SMEED = 11596,
NPC_AGED_KODO = 4700,
NPC_DYING_KODO = 4701,
NPC_ANCIENT_KODO = 4702,
NPC_TAMED_KODO = 11627,
+
SPELL_KODO_KOMBO_ITEM = 18153,
SPELL_KODO_KOMBO_PLAYER_BUFF = 18172, //spells here have unclear function, but using them at least for visual parts and checks
SPELL_KODO_KOMBO_DESPAWN_BUFF = 18377,
SPELL_KODO_KOMBO_GOSSIP = 18362
+
};
+
struct TRINITY_DLL_DECL npc_aged_dying_ancient_kodoAI : public ScriptedAI
{
npc_aged_dying_ancient_kodoAI(Creature* pCreature) : ScriptedAI(pCreature) { Reset(); }
+
uint32 m_uiDespawnTimer;
+
void Reset()
{
m_uiDespawnTimer = 0;
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (pWho->GetEntry() == NPC_SMEED)
{
if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
return;
+
if (m_creature->IsWithinDistInMap(pWho, 10.0f))
{
DoScriptText(RAND(SAY_SMEED_HOME_1,SAY_SMEED_HOME_2,SAY_SMEED_HOME_3), pWho);
+
//spell have no implemented effect (dummy), so useful to notify spellHit
m_creature->CastSpell(m_creature,SPELL_KODO_KOMBO_GOSSIP,true);
}
}
}
+
void SpellHit(Unit* pCaster, SpellEntry const* pSpell)
{
if (pSpell->Id == SPELL_KODO_KOMBO_GOSSIP)
@@ -70,6 +85,7 @@ struct TRINITY_DLL_DECL npc_aged_dying_ancient_kodoAI : public ScriptedAI
m_uiDespawnTimer = 60000;
}
}
+
void UpdateAI(const uint32 diff)
{
//timer should always be == 0 unless we already updated entry of creature. Then not expect this updated to ever be in combat.
@@ -83,15 +99,19 @@ struct TRINITY_DLL_DECL npc_aged_dying_ancient_kodoAI : public ScriptedAI
return;
}
} else m_uiDespawnTimer -= diff;
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_aged_dying_ancient_kodo(Creature* pCreature)
{
return new npc_aged_dying_ancient_kodoAI(pCreature);
}
+
bool EffectDummyCreature_npc_aged_dying_ancient_kodo(Unit *pCaster, uint32 spellId, uint32 effIndex, Creature *pCreatureTarget)
{
//always check spellid and effectindex
@@ -100,37 +120,47 @@ bool EffectDummyCreature_npc_aged_dying_ancient_kodo(Unit *pCaster, uint32 spell
//no effect if player/creature already have aura from spells
if (pCaster->HasAura(SPELL_KODO_KOMBO_PLAYER_BUFF) || pCreatureTarget->HasAura(SPELL_KODO_KOMBO_DESPAWN_BUFF))
return true;
+
if (pCreatureTarget->GetEntry() == NPC_AGED_KODO ||
pCreatureTarget->GetEntry() == NPC_DYING_KODO ||
pCreatureTarget->GetEntry() == NPC_ANCIENT_KODO)
{
pCaster->CastSpell(pCaster,SPELL_KODO_KOMBO_PLAYER_BUFF,true);
+
pCreatureTarget->UpdateEntry(NPC_TAMED_KODO);
pCreatureTarget->CastSpell(pCreatureTarget,SPELL_KODO_KOMBO_DESPAWN_BUFF,false);
+
if (pCreatureTarget->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
pCreatureTarget->GetMotionMaster()->MoveIdle();
+
pCreatureTarget->GetMotionMaster()->MoveFollow(pCaster, PET_FOLLOW_DIST, pCreatureTarget->GetFollowAngle());
}
+
//always return true when we are handling this spell and effect
return true;
}
return false;
}
+
bool GossipHello_npc_aged_dying_ancient_kodo(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->HasAura(SPELL_KODO_KOMBO_PLAYER_BUFF) && pCreature->HasAura(SPELL_KODO_KOMBO_DESPAWN_BUFF))
{
//the expected quest objective
pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
+
pPlayer->RemoveAurasDueToSpell(SPELL_KODO_KOMBO_PLAYER_BUFF);
pCreature->GetMotionMaster()->MoveIdle();
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
void AddSC_desolace()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_aged_dying_ancient_kodo";
newscript->GetAI = &GetAI_npc_aged_dying_ancient_kodo;
diff --git a/src/bindings/scripts/scripts/kalimdor/dustwallow_marsh.cpp b/src/bindings/scripts/scripts/kalimdor/dustwallow_marsh.cpp
index 1a278abb78c..8f33a569c3e 100644
--- a/src/bindings/scripts/scripts/kalimdor/dustwallow_marsh.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/dustwallow_marsh.cpp
@@ -13,12 +13,14 @@
* along 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, 11142, 11180. Vendor Nat Pagle
SDCategory: Dustwallow Marsh
EndScriptData */
+
/* ContentData
mobs_risen_husk_spirit
npc_restless_apparition
@@ -28,10 +30,13 @@ npc_nat_pagle
npc_private_hendel
npc_cassa_crimsonwing - handled by npc_taxi
EndContentData */
+
#include "precompiled.h"
+
/*######
## mobs_risen_husk_spirit
######*/
+
enum eHuskSpirit
{
QUEST_WHATS_HAUNTING_WITCH_HILL = 11180,
@@ -41,16 +46,20 @@ enum eHuskSpirit
NPC_RISEN_HUSK = 23555,
NPC_RISEN_SPIRIT = 23554
};
+
struct TRINITY_DLL_DECL mobs_risen_husk_spiritAI : public ScriptedAI
{
mobs_risen_husk_spiritAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiConsumeFlesh_Timer;
uint32 m_uiIntangiblePresence_Timer;
+
void Reset()
{
m_uiConsumeFlesh_Timer = 10000;
m_uiIntangiblePresence_Timer = 5000;
}
+
void DamageTaken(Unit* pDoneBy, uint32 &damage)
{
if (pDoneBy->GetTypeId() == TYPEID_PLAYER)
@@ -59,63 +68,80 @@ struct TRINITY_DLL_DECL mobs_risen_husk_spiritAI : public ScriptedAI
m_creature->CastSpell(pDoneBy, SPELL_SUMMON_RESTLESS_APPARITION, false);
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
if (m_uiConsumeFlesh_Timer < uiDiff)
{
if (m_creature->GetEntry() == NPC_RISEN_HUSK)
DoCast(m_creature->getVictim(), SPELL_CONSUME_FLESH);
+
m_uiConsumeFlesh_Timer = 15000;
}
else
m_uiConsumeFlesh_Timer -= uiDiff;
+
if (m_uiIntangiblePresence_Timer < uiDiff)
{
if (m_creature->GetEntry() == NPC_RISEN_SPIRIT)
DoCast(m_creature->getVictim(), SPELL_INTANGIBLE_PRESENCE);
+
m_uiIntangiblePresence_Timer = 20000;
}
else
m_uiIntangiblePresence_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mobs_risen_husk_spirit(Creature* pCreature)
{
return new mobs_risen_husk_spiritAI (pCreature);
}
+
/*######
## npc_restless_apparition
######*/
+
bool GossipHello_npc_restless_apparition(Player* pPlayer, Creature* pCreature)
{
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
pCreature->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
return true;
}
+
/*######
## npc_deserter_agitator
######*/
+
enum eAgitator
{
QUEST_TRAITORS_AMONG_US = 11126,
FACTION_THER_DESERTER = 1883
};
+
struct TRINITY_DLL_DECL npc_deserter_agitatorAI : public ScriptedAI
{
npc_deserter_agitatorAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
void Reset()
{
me->RestoreFaction();
}
};
+
CreatureAI* GetAI_npc_deserter_agitator(Creature* pCreature)
{
return new npc_deserter_agitatorAI (pCreature);
}
+
bool GossipHello_npc_deserter_agitator(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_TRAITORS_AMONG_US) == QUEST_STATUS_INCOMPLETE)
@@ -125,26 +151,35 @@ bool GossipHello_npc_deserter_agitator(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_lady_jaina_proudmoore
######*/
+
enum eLadyJaina
{
QUEST_JAINAS_AUTOGRAPH = 558,
SPELL_JAINAS_AUTOGRAPH = 23122
};
+
#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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_JAINAS_AUTOGRAPH) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_JAINA, GOSSIP_SENDER_MAIN, GOSSIP_SENDER_INFO);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lady_jaina_proudmoore(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_SENDER_INFO)
@@ -154,17 +189,21 @@ bool GossipSelect_npc_lady_jaina_proudmoore(Player* pPlayer, Creature* pCreature
}
return true;
}
+
/*######
## npc_nat_pagle
######*/
+
enum eNatPagle
{
QUEST_NATS_MEASURING_TAPE = 8227
};
+
bool GossipHello_npc_nat_pagle(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetQuestRewardStatus(QUEST_NATS_MEASURING_TAPE))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
@@ -172,17 +211,22 @@ bool GossipHello_npc_nat_pagle(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(7638, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_nat_pagle(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_private_hendel
######*/
+
enum eHendel
{
// looks like all this text ids are wrong.
@@ -191,79 +235,100 @@ enum eHendel
SAY_PROGRESS_3_TER = -1000413,
SAY_PROGRESS_4_TER = -1000414,
EMOTE_SURRENDER = -1000415,
+
QUEST_MISSING_DIPLO_PT16 = 1324,
FACTION_HOSTILE = 168, //guessed, may be different
+
NPC_SENTRY = 5184, //helps hendel
NPC_JAINA = 4968, //appears once hendel gives up
NPC_TERVOSH = 4967
};
+
//TODO: develop this further, end event not created
struct TRINITY_DLL_DECL npc_private_hendelAI : public ScriptedAI
{
npc_private_hendelAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
void Reset()
{
me->RestoreFaction();
}
+
void AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim())
return;
+
if (m_creature->IsFriendlyTo(pAttacker))
return;
+
AttackStart(pAttacker);
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (uiDamage > m_creature->GetHealth() || ((m_creature->GetHealth() - uiDamage)*100 / m_creature->GetMaxHealth() < 20))
{
uiDamage = 0;
+
if (Player* pPlayer = pDoneBy->GetCharmerOrOwnerPlayerOrPlayerItself())
pPlayer->GroupEventHappens(QUEST_MISSING_DIPLO_PT16, m_creature);
+
DoScriptText(EMOTE_SURRENDER, m_creature);
EnterEvadeMode();
}
}
};
+
bool QuestAccept_npc_private_hendel(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_MISSING_DIPLO_PT16)
pCreature->setFaction(FACTION_HOSTILE);
+
return true;
}
+
CreatureAI* GetAI_npc_private_hendel(Creature* pCreature)
{
return new npc_private_hendelAI(pCreature);
}
+
/*######
##
######*/
+
void AddSC_dustwallow_marsh()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mobs_risen_husk_spirit";
newscript->GetAI = &GetAI_mobs_risen_husk_spirit;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_restless_apparition";
newscript->pGossipHello = &GossipHello_npc_restless_apparition;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_deserter_agitator";
newscript->GetAI = &GetAI_npc_deserter_agitator;
newscript->pGossipHello = &GossipHello_npc_deserter_agitator;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lady_jaina_proudmoore";
newscript->pGossipHello = &GossipHello_npc_lady_jaina_proudmoore;
newscript->pGossipSelect = &GossipSelect_npc_lady_jaina_proudmoore;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_nat_pagle";
newscript->pGossipHello = &GossipHello_npc_nat_pagle;
newscript->pGossipSelect = &GossipSelect_npc_nat_pagle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_private_hendel";
newscript->GetAI = &GetAI_npc_private_hendel;
diff --git a/src/bindings/scripts/scripts/kalimdor/felwood.cpp b/src/bindings/scripts/scripts/kalimdor/felwood.cpp
index 1baee7b02bb..4a676ec5366 100644
--- a/src/bindings/scripts/scripts/kalimdor/felwood.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/felwood.cpp
@@ -13,25 +13,33 @@
* along 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: 4101, 4102
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* pPlayer, Creature* pCreature)
{
uint32 eCreature = pCreature->GetEntry();
+
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (eCreature==9528)
{
if (pPlayer->GetQuestRewardStatus(4101))
@@ -43,6 +51,7 @@ bool GossipHello_npcs_riverbreeze_and_silversky(Player* pPlayer, Creature* pCrea
else
pPlayer->SEND_GOSSIP_MENU(2844, pCreature->GetGUID());
}
+
if (eCreature==9529)
{
if (pPlayer->GetQuestRewardStatus(4102))
@@ -54,8 +63,10 @@ bool GossipHello_npcs_riverbreeze_and_silversky(Player* pPlayer, Creature* pCrea
else
pPlayer->SEND_GOSSIP_MENU(2842, pCreature->GetGUID());
}
+
return true;
}
+
bool GossipSelect_npcs_riverbreeze_and_silversky(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction ==GOSSIP_ACTION_INFO_DEF+1)
@@ -65,9 +76,11 @@ bool GossipSelect_npcs_riverbreeze_and_silversky(Player* pPlayer, Creature* pCre
}
return true;
}
+
void AddSC_felwood()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npcs_riverbreeze_and_silversky";
newscript->pGossipHello = &GossipHello_npcs_riverbreeze_and_silversky;
diff --git a/src/bindings/scripts/scripts/kalimdor/feralas.cpp b/src/bindings/scripts/scripts/kalimdor/feralas.cpp
index aec906510e8..9a3df7c06e2 100644
--- a/src/bindings/scripts/scripts/kalimdor/feralas.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/feralas.cpp
@@ -13,27 +13,35 @@
* along 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, 2767, Special vendor Gregan Brewspewer
SDCategory: Feralas
EndScriptData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_gregan_brewspewer
######*/
+
#define GOSSIP_HELLO "Buy somethin', will ya?"
+
bool GossipHello_npc_gregan_brewspewer(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetQuestStatus(3909) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(2433, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_gregan_brewspewer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -45,9 +53,11 @@ bool GossipSelect_npc_gregan_brewspewer(Player* pPlayer, Creature* pCreature, ui
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
return true;
}
+
/*######
## npc_oox22fe
######*/
+
enum eOOX
{
//signed for 7806
@@ -56,19 +66,23 @@ enum eOOX
SAY_OOX_AGGRO2 = -1000289,
SAY_OOX_AMBUSH = -1000290,
SAY_OOX_END = -1000292,
+
NPC_YETI = 7848,
NPC_GORILLA = 5260,
NPC_WOODPAW_REAVER = 5255,
NPC_WOODPAW_BRUTE = 5253,
NPC_WOODPAW_ALPHA = 5258,
NPC_WOODPAW_MYSTIC = 5254,
+
QUEST_RESCUE_OOX22FE = 2767,
FACTION_ESCORTEE_A = 774,
FACTION_ESCORTEE_H = 775
};
+
struct TRINITY_DLL_DECL npc_oox22feAI : public npc_escortAI
{
npc_oox22feAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void WaypointReached(uint32 i)
{
switch (i)
@@ -105,26 +119,31 @@ struct TRINITY_DLL_DECL npc_oox22feAI : public npc_escortAI
break;
}
}
+
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
}
+
void EnterCombat(Unit* who)
{
//For an small probability the npc says something when he get aggro
if (urand(0,9) > 7)
DoScriptText(RAND(SAY_OOX_AGGRO1,SAY_OOX_AGGRO2), m_creature);
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
};
+
CreatureAI* GetAI_npc_oox22fe(Creature* pCreature)
{
return new npc_oox22feAI(pCreature);
}
+
bool QuestAccept_npc_oox22fe(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_RESCUE_OOX22FE)
@@ -132,41 +151,53 @@ bool QuestAccept_npc_oox22fe(Player* pPlayer, Creature* pCreature, const Quest*
DoScriptText(SAY_OOX_START, pCreature);
//change that the npc is not lying dead on the ground
pCreature->SetStandState(UNIT_STAND_STATE_STAND);
+
if (pPlayer->GetTeam() == ALLIANCE)
pCreature->setFaction(FACTION_ESCORTEE_A);
+
if (pPlayer->GetTeam() == HORDE)
pCreature->setFaction(FACTION_ESCORTEE_H);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_oox22feAI, pCreature->AI()))
pEscortAI->Start(true, false, pPlayer->GetGUID());
+
}
return true;
}
+
/*######
## npc_screecher_spirit
######*/
+
bool GossipHello_npc_screecher_spirit(Player* pPlayer, Creature* pCreature)
{
pPlayer->SEND_GOSSIP_MENU(2039, pCreature->GetGUID());
pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
pCreature->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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_oox22fe";
newscript->GetAI = &GetAI_npc_oox22fe;
newscript->pQuestAccept = &QuestAccept_npc_oox22fe;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_screecher_spirit";
newscript->pGossipHello = &GossipHello_npc_screecher_spirit;
diff --git a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp
index 8eabe72c3fc..de33f5253df 100644
--- a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_celebras_the_cursed.cpp
@@ -13,37 +13,47 @@
* along 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 TRINITY_DLL_DECL celebras_the_cursedAI : public ScriptedAI
{
celebras_the_cursedAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Wrath_Timer;
uint32 EntanglingRoots_Timer;
uint32 CorruptForces_Timer;
+
void Reset()
{
Wrath_Timer = 8000;
EntanglingRoots_Timer = 2000;
CorruptForces_Timer = 30000;
}
+
void EnterCombat(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 (!UpdateVictim())
return;
+
//Wrath
if (Wrath_Timer < diff)
{
@@ -53,12 +63,14 @@ struct TRINITY_DLL_DECL celebras_the_cursedAI : public ScriptedAI
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)
{
@@ -66,6 +78,7 @@ struct TRINITY_DLL_DECL celebras_the_cursedAI : public ScriptedAI
DoCast(m_creature,SPELL_CORRUPT_FORCES);
CorruptForces_Timer = 20000;
}else CorruptForces_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -73,6 +86,7 @@ CreatureAI* GetAI_celebras_the_cursed(Creature* pCreature)
{
return new celebras_the_cursedAI (pCreature);
}
+
void AddSC_boss_celebras_the_cursed()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_landslide.cpp b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_landslide.cpp
index 4babfab7223..736636b69ee 100644
--- a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_landslide.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_landslide.cpp
@@ -13,47 +13,58 @@
* along 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 TRINITY_DLL_DECL boss_landslideAI : public ScriptedAI
{
boss_landslideAI(Creature *c) : ScriptedAI(c) {}
+
uint32 KnockAway_Timer;
uint32 Trample_Timer;
uint32 Landslide_Timer;
+
void Reset()
{
KnockAway_Timer = 8000;
Trample_Timer = 2000;
Landslide_Timer = 0;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
@@ -64,6 +75,7 @@ struct TRINITY_DLL_DECL boss_landslideAI : public ScriptedAI
Landslide_Timer = 60000;
} else Landslide_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
@@ -71,6 +83,7 @@ CreatureAI* GetAI_boss_landslide(Creature* pCreature)
{
return new boss_landslideAI (pCreature);
}
+
void AddSC_boss_landslide()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_noxxion.cpp b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_noxxion.cpp
index fd26e8126b5..add73e2c60f 100644
--- a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_noxxion.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_noxxion.cpp
@@ -13,18 +13,23 @@
* along 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 TRINITY_DLL_DECL boss_noxxionAI : public ScriptedAI
{
boss_noxxionAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ToxicVolley_Timer;
uint32 Uppercut_Timer;
uint32 Adds_Timer;
@@ -34,6 +39,7 @@ struct TRINITY_DLL_DECL boss_noxxionAI : public ScriptedAI
int RandX;
int RandY;
Creature* Summoned;
+
void Reset()
{
ToxicVolley_Timer = 7000;
@@ -42,9 +48,11 @@ struct TRINITY_DLL_DECL boss_noxxionAI : public ScriptedAI
Invisible_Timer = 15000; //Too much too low?
Invisible = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void SummonAdds(Unit* victim)
{
Rand = rand()%8;
@@ -65,6 +73,7 @@ struct TRINITY_DLL_DECL boss_noxxionAI : public ScriptedAI
if (Summoned)
(Summoned->AI())->AttackStart(victim);
}
+
void UpdateAI(const uint32 diff)
{
if (Invisible && Invisible_Timer < diff)
@@ -82,21 +91,25 @@ struct TRINITY_DLL_DECL boss_noxxionAI : public ScriptedAI
//Do nothing while invisible
return;
}
+
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -114,8 +127,10 @@ struct TRINITY_DLL_DECL boss_noxxionAI : public ScriptedAI
SummonAdds(m_creature->getVictim());
Invisible = true;
Invisible_Timer = 15000;
+
Adds_Timer = 40000;
}else Adds_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -123,6 +138,7 @@ CreatureAI* GetAI_boss_noxxion(Creature* pCreature)
{
return new boss_noxxionAI (pCreature);
}
+
void AddSC_boss_noxxion()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_princess_theradras.cpp b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_princess_theradras.cpp
index 3e799b88ad7..53544d9dcdb 100644
--- a/src/bindings/scripts/scripts/kalimdor/maraudon/boss_princess_theradras.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/maraudon/boss_princess_theradras.cpp
@@ -13,24 +13,30 @@
* along 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 TRINITY_DLL_DECL boss_ptheradrasAI : public ScriptedAI
{
boss_ptheradrasAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Dustfield_Timer;
uint32 Boulder_Timer;
uint32 Thrash_Timer;
uint32 RepulsiveGaze_Timer;
+
void Reset()
{
Dustfield_Timer = 8000;
@@ -38,23 +44,28 @@ struct TRINITY_DLL_DECL boss_ptheradrasAI : public ScriptedAI
Thrash_Timer = 5000;
RepulsiveGaze_Timer = 23000;
}
+
void EnterCombat(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 (!UpdateVictim())
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)
{
@@ -64,18 +75,21 @@ struct TRINITY_DLL_DECL boss_ptheradrasAI : public ScriptedAI
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();
}
};
@@ -83,6 +97,7 @@ CreatureAI* GetAI_boss_ptheradras(Creature* pCreature)
{
return new boss_ptheradrasAI (pCreature);
}
+
void AddSC_boss_ptheradras()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/moonglade.cpp b/src/bindings/scripts/scripts/kalimdor/moonglade.cpp
index b50a3b3e932..7e52c16b74d 100644
--- a/src/bindings/scripts/scripts/kalimdor/moonglade.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/moonglade.cpp
@@ -13,12 +13,14 @@
* along 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, 10965. Special Flight Paths for Druid class.
SDCategory: Moonglade
EndScriptData */
+
/* ContentData
npc_bunthen_plainswind
npc_great_bear_spirit
@@ -26,11 +28,14 @@ npc_silva_filnaveth
npc_clintar_spirit
npc_clintar_dreamwalker
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_bunthen_plainswind
######*/
+
enum eBunthen
{
QUEST_SEA_LION_HORDE = 30,
@@ -38,8 +43,10 @@ enum eBunthen
TAXI_PATH_ID_ALLY = 315,
TAXI_PATH_ID_HORDE = 316
};
+
#define GOSSIP_ITEM_THUNDER "I'd like to fly to Thunder Bluff."
#define GOSSIP_ITEM_AQ_END "Do you know where I can find Half Pendant of Aquatic Endurance?"
+
bool GossipHello_npc_bunthen_plainswind(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->getClass() != CLASS_DRUID)
@@ -48,17 +55,21 @@ bool GossipHello_npc_bunthen_plainswind(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_END, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
+
pPlayer->SEND_GOSSIP_MENU(4917, pCreature->GetGUID());
}
else if (pPlayer->getClass() == CLASS_DRUID && pPlayer->GetTeam() == HORDE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_THUNDER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
if (pPlayer->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_END, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
+
pPlayer->SEND_GOSSIP_MENU(4918, pCreature->GetGUID());
}
return true;
}
+
bool GossipSelect_npc_bunthen_plainswind(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -77,13 +88,16 @@ bool GossipSelect_npc_bunthen_plainswind(Player* pPlayer, Creature* pCreature, u
}
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* pPlayer, Creature* pCreature)
{
//ally or horde quest
@@ -94,8 +108,10 @@ bool GossipHello_npc_great_bear_spirit(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(4718, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_great_bear_spirit(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -122,11 +138,14 @@ bool GossipSelect_npc_great_bear_spirit(Player* pPlayer, Creature* pCreature, ui
}
return true;
}
+
/*######
## npc_silva_filnaveth
######*/
+
#define GOSSIP_ITEM_RUTHERAN "I'd like to fly to Rut'theran Village."
#define GOSSIP_ITEM_AQ_AGI "Do you know where I can find Half Pendant of Aquatic Agility?"
+
bool GossipHello_npc_silva_filnaveth(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->getClass() != CLASS_DRUID)
@@ -135,17 +154,21 @@ bool GossipHello_npc_silva_filnaveth(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_SEA_LION_HORDE) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_AGI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
+
pPlayer->SEND_GOSSIP_MENU(4915, pCreature->GetGUID());
}
else if (pPlayer->getClass() == CLASS_DRUID && pPlayer->GetTeam() == ALLIANCE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_RUTHERAN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
if (pPlayer->GetQuestStatus(QUEST_SEA_LION_ALLY) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AQ_AGI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
+
pPlayer->SEND_GOSSIP_MENU(4914, pCreature->GetGUID());
}
return true;
}
+
bool GossipSelect_npc_silva_filnaveth(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -164,9 +187,11 @@ bool GossipSelect_npc_silva_filnaveth(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
/*######
## npc_clintar_spirit
######*/
+
float Clintar_spirit_WP[41][5] =
{
//pos_x pos_y pos_z orien waitTime
@@ -212,7 +237,9 @@ float Clintar_spirit_WP[41][5] =
{7518.37, -3057.42, 445.584, 0.74, 0},
{7517.51, -3056.3, 444.568, 2.49, 4500}
};
+
#define ASPECT_RAVEN 22915
+
#define ASPECT_RAVEN_SUMMON_X 7472.96
#define ASPECT_RAVEN_SUMMON_Y -3074.18
#define ASPECT_RAVEN_SUMMON_Z 427.566
@@ -220,6 +247,7 @@ float Clintar_spirit_WP[41][5] =
#define CLINTAR_SPIRIT_SUMMON_Y -3122.5632
#define CLINTAR_SPIRIT_SUMMON_Z 438.9842
#define CLINTAR_SPIRIT_SUMMON_O 0.8594
+
//from -1000292 to -1000287 are signed for 7806. but all this texts ids wrong.
#define CLINTAR_SPIRIT_SAY_START -1000286
#define CLINTAR_SPIRIT_SAY_UNDER_ATTACK_1 -1000287
@@ -228,16 +256,21 @@ float Clintar_spirit_WP[41][5] =
#define CLINTAR_SPIRIT_SAY_GET_TWO -1000290
#define CLINTAR_SPIRIT_SAY_GET_THREE -1000291
#define CLINTAR_SPIRIT_SAY_GET_FINAL -1000292
+
struct TRINITY_DLL_DECL npc_clintar_spiritAI : public npc_escortAI
{
public:
npc_clintar_spiritAI(Creature *c) : npc_escortAI(c) {}
+
uint32 Step;
uint32 CurrWP;
uint32 Event_Timer;
uint32 checkPlayer_Timer;
+
uint64 PlayerGUID;
+
bool Event_onWait;
+
void Reset()
{
if (!PlayerGUID)
@@ -250,10 +283,12 @@ public:
Event_onWait = false;
}
}
+
void JustDied(Unit *killer)
{
if (!PlayerGUID)
return;
+
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
if (pPlayer && pPlayer->GetQuestStatus(10965) == QUEST_STATUS_INCOMPLETE)
{
@@ -262,6 +297,7 @@ public:
Reset();
}
}
+
void EnterEvadeMode()
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
@@ -272,6 +308,7 @@ public:
}
npc_escortAI::EnterEvadeMode();
}
+
void EnterCombat(Unit* who)
{
uint32 rnd = rand()%2;
@@ -281,13 +318,14 @@ public:
case 1: DoScriptText(CLINTAR_SPIRIT_SAY_UNDER_ATTACK_2, m_creature, who); break;
}
}
+
void StartEvent(Player* pPlayer)
{
if (!pPlayer)
return;
if (pPlayer->GetQuestStatus(10965) == QUEST_STATUS_INCOMPLETE)
{
- for (uint8 i = 0; i < 41; ++i)
+ for(uint8 i = 0; i < 41; ++i)
{
AddWaypoint(i, Clintar_spirit_WP[i][0], Clintar_spirit_WP[i][1], Clintar_spirit_WP[i][2], (uint32)Clintar_spirit_WP[i][4]);
}
@@ -296,14 +334,17 @@ public:
}
return;
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
+
if (!PlayerGUID)
{
m_creature->setDeathState(JUST_DIED);
return;
}
+
if (!m_creature->isInCombat() && !Event_onWait)
{
if (checkPlayer_Timer < diff)
@@ -314,14 +355,17 @@ public:
checkPlayer_Timer = 1000;
}else checkPlayer_Timer -= diff;
}
+
if (Event_onWait && Event_Timer < diff)
{
+
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
if (!pPlayer || (pPlayer && pPlayer->GetQuestStatus(10965) == QUEST_STATUS_NONE))
{
m_creature->setDeathState(JUST_DIED);
return;
}
+
switch(CurrWP)
{
case 0:
@@ -453,8 +497,10 @@ public:
Event_onWait = false;
break;
}
+
} else if (Event_onWait) Event_Timer -= diff;
}
+
void WaypointReached(uint32 id)
{
CurrWP = id;
@@ -463,14 +509,18 @@ public:
Event_onWait = true;
}
};
+
CreatureAI* GetAI_npc_clintar_spirit(Creature* pCreature)
{
return new npc_clintar_spiritAI (pCreature);
}
+
/*####
# npc_clintar_dreamwalker
####*/
+
#define CLINTAR_SPIRIT 22916
+
bool QuestAccept_npc_clintar_dreamwalker(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if (quest->GetQuestId() == 10965)
@@ -481,31 +531,38 @@ bool QuestAccept_npc_clintar_dreamwalker(Player* pPlayer, Creature* pCreature, Q
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_great_bear_spirit";
newscript->pGossipHello = &GossipHello_npc_great_bear_spirit;
newscript->pGossipSelect = &GossipSelect_npc_great_bear_spirit;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_silva_filnaveth";
newscript->pGossipHello = &GossipHello_npc_silva_filnaveth;
newscript->pGossipSelect = &GossipSelect_npc_silva_filnaveth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_clintar_dreamwalker";
newscript->pQuestAccept = &QuestAccept_npc_clintar_dreamwalker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_clintar_spirit";
newscript->GetAI = &GetAI_npc_clintar_spirit;
diff --git a/src/bindings/scripts/scripts/kalimdor/mulgore.cpp b/src/bindings/scripts/scripts/kalimdor/mulgore.cpp
index 974402849a0..0fcd62cd8df 100644
--- a/src/bindings/scripts/scripts/kalimdor/mulgore.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/mulgore.cpp
@@ -13,60 +13,77 @@
* along 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: Support for quest: 11129, 772
SDCategory: Mulgore
EndScriptData */
+
/* ContentData
npc_skorn_whitecloud
npc_kyle_frenzied
npc_plains_vision
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
# npc_skorn_whitecloud
######*/
+
#define GOSSIP_SW "Tell me a story, Skorn."
+
bool GossipHello_npc_skorn_whitecloud(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (!pPlayer->GetQuestRewardStatus(770))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SW, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(522, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_skorn_whitecloud(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
pPlayer->SEND_GOSSIP_MENU(523, pCreature->GetGUID());
+
return true;
}
+
/*#####
# npc_kyle_frenzied
######*/
+
enum eKyleFrenzied
{
//emote signed for 7780 but propably thats wrong id.
EMOTE_SEE_LUNCH = -1000407,
EMOTE_EAT_LUNCH = -1000408,
EMOTE_DANCE = -1000409,
+
SPELL_LUNCH = 42222,
NPC_KYLE_FRENZIED = 23616,
NPC_KYLE_FRIENDLY = 23622,
POINT_ID = 1
};
+
struct TRINITY_DLL_DECL npc_kyle_frenziedAI : public ScriptedAI
{
npc_kyle_frenziedAI(Creature *c) : ScriptedAI(c) {}
+
bool bEvent;
bool m_bIsMovingToLunch;
uint64 uiPlayerGUID;
uint32 uiEventTimer;
uint8 uiEventPhase;
+
void Reset()
{
bEvent = false;
@@ -74,43 +91,52 @@ struct TRINITY_DLL_DECL npc_kyle_frenziedAI : public ScriptedAI
uiPlayerGUID = 0;
uiEventTimer = 5000;
uiEventPhase = 0;
+
if (m_creature->GetEntry() == NPC_KYLE_FRIENDLY)
m_creature->UpdateEntry(NPC_KYLE_FRENZIED);
}
+
void SpellHit(Unit* pCaster, SpellEntry const* pSpell)
{
if (!m_creature->getVictim() && !bEvent && pSpell->Id == SPELL_LUNCH)
{
if (pCaster->GetTypeId() == TYPEID_PLAYER)
uiPlayerGUID = pCaster->GetGUID();
+
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
{
m_creature->GetMotionMaster()->MovementExpired();
m_creature->GetMotionMaster()->MoveIdle();
m_creature->StopMoving();
}
+
bEvent = true;
DoScriptText(EMOTE_SEE_LUNCH, m_creature);
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_CREATURE_SPECIAL);
}
}
+
void MovementInform(uint32 uiType, uint32 uiPointId)
{
if (uiType != POINT_MOTION_TYPE || !bEvent)
return;
+
if (uiPointId == POINT_ID)
m_bIsMovingToLunch = false;
}
+
void UpdateAI(const uint32 diff)
{
if (bEvent)
{
if (m_bIsMovingToLunch)
return;
+
if (uiEventTimer < diff)
{
uiEventTimer = 5000;
++uiEventPhase;
+
switch(uiEventPhase)
{
case 1:
@@ -130,6 +156,7 @@ struct TRINITY_DLL_DECL npc_kyle_frenziedAI : public ScriptedAI
case 3:
if (Player* pUnit = Unit::GetPlayer(uiPlayerGUID))
pUnit->TalkedToCreature(m_creature->GetEntry(), m_creature->GetGUID());
+
m_creature->UpdateEntry(NPC_KYLE_FRIENDLY);
break;
case 4:
@@ -149,13 +176,16 @@ struct TRINITY_DLL_DECL npc_kyle_frenziedAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_kyle_frenzied(Creature* pCreature)
{
return new npc_kyle_frenziedAI (pCreature);
}
+
/*#####
# npc_plains_vision
######*/
+
float wp_plain_vision[50][3] =
{
{-2226.32, -408.095, -9.36235},
@@ -209,23 +239,29 @@ float wp_plain_vision[50][3] =
{-1511.39, 362.537, 62.4539},
{-1508.68, 366.822, 62.733}
};
+
struct TRINITY_DLL_DECL npc_plains_visionAI : public ScriptedAI
{
npc_plains_visionAI(Creature *c) : ScriptedAI(c) {}
+
bool newWaypoint;
uint8 WayPointId;
uint8 amountWP;
+
void Reset()
{
WayPointId = 0;
newWaypoint = true;
amountWP = 49;
}
+
void EnterCombat(Unit* who){}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id < amountWP)
{
++WayPointId;
@@ -237,6 +273,7 @@ struct TRINITY_DLL_DECL npc_plains_visionAI : public ScriptedAI
m_creature->RemoveCorpse();
}
}
+
void UpdateAI(const uint32 diff)
{
if (newWaypoint)
@@ -246,25 +283,31 @@ struct TRINITY_DLL_DECL npc_plains_visionAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_plains_vision(Creature* pCreature)
{
return new npc_plains_visionAI (pCreature);
}
+
/*#####
#
######*/
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_kyle_frenzied";
newscript->GetAI = &GetAI_npc_kyle_frenzied;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_plains_vision";
newscript->GetAI = &GetAI_npc_plains_vision;
diff --git a/src/bindings/scripts/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp b/src/bindings/scripts/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp
index fe1b71f0b04..3c635526d2e 100644
--- a/src/bindings/scripts/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/onyxias_lair/boss_onyxia.cpp
@@ -13,30 +13,39 @@
* along 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: 90
SDComment: Spell Heated Ground is wrong, flying animation, visual for area effect
SDCategory: Onyxia's Lair
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO -1249000
#define SAY_KILL -1249001
#define SAY_PHASE_2_TRANS -1249002
#define SAY_PHASE_3_TRANS -1249003
#define EMOTE_BREATH -1249004
+
#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 //Wrong Spell
+
#define SPELL_SUMMONWHELP 17646
+
#define CREATURE_WHELP 11262
+
static float MovementLocations[4][3]=
{
{-64.0523, -213.0619, -68.2985},
@@ -44,6 +53,7 @@ static float MovementLocations[4][3]=
{-38.8391, -182.3220, -68.9457},
{-37.0390, -244.8760, -68.1278}
};
+
static float SpawnLocations[4][3]=
{
{-30.127, -254.463, -89.440},
@@ -51,10 +61,13 @@ static float SpawnLocations[4][3]=
{14.480, -241.560, -85.6300},
{17.372, -190.840, -85.2810},
};
+
struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
{
boss_onyxiaAI(Creature* c) : ScriptedAI(c) {}
+
uint32 Phase;
+
uint32 FlameBreathTimer;
uint32 CleaveTimer;
uint32 TailSweepTimer;
@@ -65,10 +78,13 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
uint32 WingBuffetTimer;
uint32 KnockAwayTimer;
uint32 FireballTimer;
+
bool InitialSpawn;
+
void Reset()
{
Phase = 1;
+
FlameBreathTimer = 20000;
TailSweepTimer = 2000;
CleaveTimer = 15000;
@@ -79,24 +95,30 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
WingBuffetTimer = 17000;
KnockAwayTimer = 15000;
FireballTimer = 18000;
+
InitialSpawn = true;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
}
+
void JustDied(Unit* Killer)
{
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 60) && (Phase == 1))
{
Phase = 2;
@@ -106,6 +128,7 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
m_creature->GetMotionMaster()->MoveIdle();
DoScriptText(SAY_PHASE_2_TRANS, m_creature);
}
+
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 40) && (Phase == 2))
{
Phase = 3;
@@ -117,6 +140,7 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
DoScriptText(SAY_PHASE_3_TRANS, m_creature);
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
}
+
if (Phase == 1 || Phase == 3)
{
if (FlameBreathTimer < diff)
@@ -124,23 +148,28 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
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;
+
if (KnockAwayTimer < diff)
{
if (rand() <= 30)
@@ -149,27 +178,34 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
}
KnockAwayTimer = 15000;
}else KnockAwayTimer -= diff;
+
if (Phase == 3)
{
if (BellowingRoarTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_BELLOWINGROAR);
+
BellowingRoarTimer = 30000;
}else BellowingRoarTimer -= diff;
+
if (SummonWhelpsTimer < diff)
{
SummonWhelps(Phase);
+
SummonWhelpsTimer = 45000;
}else SummonWhelpsTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
if (Phase == 2)
{
if (InitialSpawn)
{
InitialSpawn = false;
- for (uint32 i = 0; i < 10; ++i)
+
+ for(uint32 i = 0; i < 10; ++i)
{
uint32 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);
@@ -177,19 +213,24 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
Whelp->AI()->AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0));
}
}
+
if (EngulfingFlamesTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_ENGULFINGFLAMES);
m_creature->HandleEmoteCommand(ANIM_FLY);
+
EngulfingFlamesTimer = 10000;
}
else EngulfingFlamesTimer -= diff;
+
if (FireballTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
+
FireballTimer = 18000;
}
else FireballTimer -= diff;
+
if (MovementTimer < diff)
{
if (rand()%100 < 30)
@@ -198,28 +239,33 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_DEEPBREATH);
}
else ChangePosition();
+
MovementTimer = 25000;
}else MovementTimer -= diff;
+
if (SummonWhelpsTimer < diff)
{
SummonWhelps(Phase);
+
SummonWhelpsTimer = 45000;
}
else SummonWhelpsTimer -= diff;
}
}
+
void ChangePosition()
{
uint32 random = rand() % 4;
if (random<4){
m_creature->GetMotionMaster()->MovePoint(0, MovementLocations[random][0], MovementLocations[random][1], MovementLocations[random][2]);}
}
+
void SummonWhelps(uint32 Phase)
{
if (Phase == 2)
{
uint32 max = rand()%10;
- for (uint32 i = 0; i < max; ++i)
+ for(uint32 i = 0; i < max; ++i)
{
uint32 random = rand()%3;
Creature* Whelp = m_creature->SummonCreature(CREATURE_WHELP, SpawnLocations[random][0], SpawnLocations[random][1], SpawnLocations[random][2], 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
@@ -227,12 +273,13 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
Whelp->AI()->AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0));
}
}
+
if (Phase == 3)
{
uint32 max = rand() % 10 +1;
if (max < 5)
{
- for (uint32 i = 0; i < max; ++i)
+ for(uint32 i = 0; i < max; ++i)
{
uint32 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);
@@ -243,10 +290,12 @@ struct TRINITY_DLL_DECL boss_onyxiaAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_boss_onyxiaAI(Creature* pCreature)
{
return new boss_onyxiaAI (pCreature);
}
+
void AddSC_boss_onyxia()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/orgrimmar.cpp b/src/bindings/scripts/scripts/kalimdor/orgrimmar.cpp
index 9ace405eefc..30129b7e037 100644
--- a/src/bindings/scripts/scripts/kalimdor/orgrimmar.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/orgrimmar.cpp
@@ -13,33 +13,42 @@
* along 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
+
#define GOSSIP_HNF "You may speak frankly, Neeru..."
#define GOSSIP_SNF "[PH] ..."
bool GossipHello_npc_neeru_fireblade(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_5727) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HNF, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(4513, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_neeru_fireblade(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -55,21 +64,26 @@ bool GossipSelect_npc_neeru_fireblade(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
/*######
## npc_shenthul
######*/
+
enum eShenthul
{
QUEST_SHATTERED_SALUTE = 2460
};
+
struct TRINITY_DLL_DECL npc_shenthulAI : public ScriptedAI
{
npc_shenthulAI(Creature* c) : ScriptedAI(c) {}
+
bool CanTalk;
bool CanEmote;
uint32 Salute_Timer;
uint32 Reset_Timer;
uint64 PlayerGUID;
+
void Reset()
{
CanTalk = false;
@@ -78,7 +92,9 @@ struct TRINITY_DLL_DECL npc_shenthulAI : public ScriptedAI
Reset_Timer = 0;
PlayerGUID = 0;
}
+
void EnterCombat(Unit* who) { }
+
void UpdateAI(const uint32 diff)
{
if (CanEmote)
@@ -93,6 +109,7 @@ struct TRINITY_DLL_DECL npc_shenthulAI : public ScriptedAI
Reset();
} else Reset_Timer -= diff;
}
+
if (CanTalk && !CanEmote)
{
if (Salute_Timer < diff)
@@ -102,10 +119,13 @@ struct TRINITY_DLL_DECL npc_shenthulAI : public ScriptedAI
Reset_Timer = 60000;
} else Salute_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void ReceiveEmote(Player* pPlayer, uint32 emote)
{
if (emote == TEXTEMOTE_SALUTE && pPlayer->GetQuestStatus(QUEST_SHATTERED_SALUTE) == QUEST_STATUS_INCOMPLETE)
@@ -118,10 +138,12 @@ struct TRINITY_DLL_DECL npc_shenthulAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_shenthul(Creature* pCreature)
{
return new npc_shenthulAI (pCreature);
}
+
bool QuestAccept_npc_shenthul(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_SHATTERED_SALUTE)
@@ -131,12 +153,16 @@ bool QuestAccept_npc_shenthul(Player* pPlayer, Creature* pCreature, Quest const*
}
return true;
}
+
/*######
## npc_thrall_warchief
######*/
+
#define QUEST_6566 6566
+
#define SPELL_CHAIN_LIGHTNING 16033
#define SPELL_SHOCK 16034
+
#define GOSSIP_HTW "Please share your wisdom with me, Warchief."
#define GOSSIP_STW1 "What discoveries?"
#define GOSSIP_STW2 "Usurper?"
@@ -144,32 +170,40 @@ bool QuestAccept_npc_shenthul(Player* pPlayer, Creature* pCreature, Quest const*
#define GOSSIP_STW4 "I... I did not think of it that way, Warchief."
#define GOSSIP_STW5 "I live only to serve, Warchief! My life is empty and meaningless without your guidance."
#define GOSSIP_STW6 "Of course, Warchief!"
+
//TODO: verify abilities/timers
struct TRINITY_DLL_DECL npc_thrall_warchiefAI : public ScriptedAI
{
npc_thrall_warchiefAI(Creature* c) : ScriptedAI(c) {}
+
uint32 ChainLightning_Timer;
uint32 Shock_Timer;
+
void Reset()
{
ChainLightning_Timer = 2000;
Shock_Timer = 8000;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -177,15 +211,19 @@ CreatureAI* GetAI_npc_thrall_warchief(Creature* pCreature)
{
return new npc_thrall_warchiefAI (pCreature);
}
+
bool GossipHello_npc_thrall_warchief(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_6566) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HTW, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_thrall_warchief(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -221,19 +259,23 @@ bool GossipSelect_npc_thrall_warchief(Player* pPlayer, Creature* pCreature, uint
}
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_shenthul";
newscript->GetAI = &GetAI_npc_shenthul;
newscript->pQuestAccept = &QuestAccept_npc_shenthul;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_thrall_warchief";
newscript->GetAI = &GetAI_npc_thrall_warchief;
diff --git a/src/bindings/scripts/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp b/src/bindings/scripts/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp
index 435a38bf477..10f211666a1 100644
--- a/src/bindings/scripts/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/razorfen_downs/boss_amnennar_the_coldbringer.cpp
@@ -13,31 +13,38 @@
* along 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_AGGRO -1129000
#define SAY_SUMMON60 -1129001
#define SAY_SUMMON30 -1129002
#define SAY_HP -1129003
#define SAY_KILL -1129004
+
#define SPELL_AMNENNARSWRATH 13009
#define SPELL_FROSTBOLT 15530
#define SPELL_FROST_NOVA 15531
#define SPELL_FROST_SPECTRES 12642
+
struct TRINITY_DLL_DECL boss_amnennar_the_coldbringerAI : public ScriptedAI
{
boss_amnennar_the_coldbringerAI(Creature *c) : ScriptedAI(c) {}
+
uint32 AmnenarsWrath_Timer;
uint32 FrostBolt_Timer;
uint32 FrostNova_Timer;
bool Spectrals60;
bool Spectrals30;
bool Hp;
+
void Reset()
{
AmnenarsWrath_Timer = 8000;
@@ -47,59 +54,71 @@ struct TRINITY_DLL_DECL boss_amnennar_the_coldbringerAI : public ScriptedAI
Spectrals60 = false;
Hp = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit()
{
DoScriptText(SAY_KILL, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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)
{
DoCast(m_creature->getVictim(),SPELL_FROSTBOLT);
FrostBolt_Timer = 8000;
} else FrostBolt_Timer -= diff;
+
if (FrostNova_Timer < diff)
{
DoCast(m_creature,SPELL_FROST_NOVA);
FrostNova_Timer = 15000;
} else FrostNova_Timer -= diff;
+
if (!Spectrals60 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 60)
{
DoScriptText(SAY_SUMMON60, m_creature);
DoCast(m_creature->getVictim(),SPELL_FROST_SPECTRES);
Spectrals60 = true;
}
+
if (!Hp && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50)
{
DoScriptText(SAY_HP, m_creature);
Hp = true;
}
+
if (!Spectrals30 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 30)
{
DoScriptText(SAY_SUMMON30, m_creature);
DoCast(m_creature->getVictim(),SPELL_FROST_SPECTRES);
Spectrals30 = true;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_amnennar_the_coldbringer(Creature* pCreature)
{
return new boss_amnennar_the_coldbringerAI (pCreature);
}
+
void AddSC_boss_amnennar_the_coldbringer()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp b/src/bindings/scripts/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp
index 4f580fa7efd..0d133a0db0b 100644
--- a/src/bindings/scripts/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/razorfen_downs/razorfen_downs.cpp
@@ -13,19 +13,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Razorfen_Downs
SD%Complete: 100
SDComment: Support for Henry Stern(2 recipes)
SDCategory: Razorfen Downs
EndScriptData */
+
/* ContentData
npc_henry_stern
EndContentData */
+
#include "precompiled.h"
+
/*###
# npc_henry_stern
####*/
+
enum eEnums
{
SPELL_GOLDTHORN_TEA = 13028,
@@ -35,17 +40,22 @@ enum eEnums
GOSSIP_TEXT_TEA_ANSWER = 2114,
GOSSIP_TEXT_POTION_ANSWER = 2115,
};
+
#define GOSSIP_ITEM_TEA "Teach me the cooking recipe"
#define GOSSIP_ITEM_POTION "Teach me the alchemy recipe"
+
bool GossipHello_npc_henry_stern (Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetBaseSkillValue(SKILL_COOKING) >= 175 && !pPlayer->HasSpell(SPELL_GOLDTHORN_TEA))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TEA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
if (pPlayer->GetBaseSkillValue(SKILL_ALCHEMY) >= 180 && !pPlayer->HasSpell(SPELL_MIGHT_TROLLS_BLOOD_POTION))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_POTION, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 2);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_henry_stern (Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -53,16 +63,20 @@ bool GossipSelect_npc_henry_stern (Player* pPlayer, Creature* pCreature, uint32
pCreature->CastSpell(pPlayer, SPELL_TEACHING_GOLDTHORN_TEA, true);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_TEA_ANSWER, pCreature->GetGUID());
}
+
if (uiAction == GOSSIP_ACTION_INFO_DEF + 2)
{
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXT_POTION_ANSWER, pCreature->GetGUID());
pCreature->CastSpell(pPlayer, SPELL_TEACHING_MIGHTY_TROLLS_BLOOD_POTION, true);
}
+
return true;
}
+
void AddSC_razorfen_downs()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "npc_henry_stern";
newscript->pGossipHello = &GossipHello_npc_henry_stern;
diff --git a/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/def_razorfen_kraul.h b/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/def_razorfen_kraul.h
index f9bc63e7c0a..d40abd7f2fd 100644
--- a/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/def_razorfen_kraul.h
+++ b/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/def_razorfen_kraul.h
@@ -13,7 +13,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DEF_RAZORFEN_KRAUL_H
#define DEF_RAZORFEN_KRAUL_H
+
#define TYPE_WARD_KEEPERS 1
-#endif
+#endif \ No newline at end of file
diff --git a/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp b/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp
index 2899d279f77..ed139ea45e9 100644
--- a/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/instance_razorfen_kraul.cpp
@@ -13,34 +13,42 @@
* along 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_Razorfen_Kraul
SD%Complete:
SDComment:
SDCategory: Razorfen Kraul
EndScriptData */
+
#include "precompiled.h"
#include "def_razorfen_kraul.h"
+
#define WARD_KEEPERS_NR 2
+
struct TRINITY_DLL_DECL instance_razorfen_kraul : public ScriptedInstance
{
instance_razorfen_kraul(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 DoorWardGUID;
uint32 WardCheck_Timer;
int WardKeeperAlive;
+
void Initialize()
{
WardKeeperAlive = 1;
WardCheck_Timer = 4000;
DoorWardGUID = 0;
}
+
Player* GetPlayerInMap()
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* plr = itr->getSource())
return plr;
@@ -49,6 +57,7 @@ struct TRINITY_DLL_DECL instance_razorfen_kraul : public ScriptedInstance
debug_log("TSCR: Instance Razorfen Kraul: GetPlayerInMap, but PlayerList is empty!");
return NULL;
}
+
void OnGameObjectCreate(GameObject* pGo, bool apply)
{
switch(pGo->GetEntry())
@@ -56,6 +65,7 @@ struct TRINITY_DLL_DECL instance_razorfen_kraul : public ScriptedInstance
case 21099: DoorWardGUID = pGo->GetGUID(); break;
}
}
+
void Update(uint32 diff)
{
if (WardCheck_Timer < diff)
@@ -66,6 +76,7 @@ struct TRINITY_DLL_DECL instance_razorfen_kraul : public ScriptedInstance
}else
WardCheck_Timer -= diff;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -76,11 +87,14 @@ struct TRINITY_DLL_DECL instance_razorfen_kraul : public ScriptedInstance
break;
}
}
+
};
+
InstanceData* GetInstanceData_instance_razorfen_kraul(Map* pMap)
{
return new instance_razorfen_kraul(pMap);
}
+
void AddSC_instance_razorfen_kraul()
{
Script *newscript;
@@ -88,4 +102,4 @@ void AddSC_instance_razorfen_kraul()
newscript->Name = "instance_razorfen_kraul";
newscript->GetInstanceData = &GetInstanceData_instance_razorfen_kraul;
newscript->RegisterSelf();
-}
+} \ No newline at end of file
diff --git a/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp b/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp
index 42de72fb63c..fec2cf711da 100644
--- a/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/razorfen_kraul/razorfen_kraul.cpp
@@ -13,18 +13,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Razorfen Kraul
SD%Complete: 100
SDComment: Quest support: 1144
SDCategory: Razorfen Kraul
EndScriptData */
+
/* ContentData
npc_willix
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "def_razorfen_kraul.h"
+
#define SAY_READY -1047000
#define SAY_POINT -10470001
#define SAY_AGGRO1 -1047002
@@ -36,17 +40,22 @@ EndContentData */
#define SAY_FINALY -1047008
#define SAY_WIN -1047009
#define SAY_END -1047010
+
#define QUEST_WILLIX_THE_IMPORTER 1144
#define ENTRY_BOAR 4514
#define SPELL_QUILLBOAR_CHANNELING 7083
+
struct TRINITY_DLL_DECL npc_willixAI : public npc_escortAI
{
npc_willixAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch (i)
{
case 3:
@@ -90,21 +99,26 @@ struct TRINITY_DLL_DECL npc_willixAI : public npc_escortAI
break;
}
}
+
void Reset() {}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO1, m_creature, NULL);
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void JustDied(Unit* killer)
{
if (Player* pPlayer = GetPlayerForEscort())
CAST_PLR(pPlayer)->FailQuest(QUEST_WILLIX_THE_IMPORTER);
}
};
+
bool QuestAccept_npc_willix(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_WILLIX_THE_IMPORTER)
@@ -113,26 +127,33 @@ bool QuestAccept_npc_willix(Player* pPlayer, Creature* pCreature, Quest const* q
DoScriptText(SAY_READY, pCreature, pPlayer);
pCreature->setFaction(113);
}
+
return true;
}
+
struct TRINITY_DLL_DECL npc_deaths_head_ward_keeperAI : public ScriptedAI
{
npc_deaths_head_ward_keeperAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 QuillboarChanneling_Timer;
+
void Reset()
{
QuillboarChanneling_Timer = 1500;
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isAlive())
return;
+
if (pInstance)
pInstance->SetData(TYPE_WARD_KEEPERS, NOT_STARTED);
+
if (QuillboarChanneling_Timer < diff)
{
if (m_creature->IsNonMeleeSpellCasted(false))
@@ -140,24 +161,30 @@ struct TRINITY_DLL_DECL npc_deaths_head_ward_keeperAI : public ScriptedAI
DoCast(m_creature, SPELL_QUILLBOAR_CHANNELING);
QuillboarChanneling_Timer = 1100;
}else QuillboarChanneling_Timer -= diff;
+
}
};
+
CreatureAI* GetAI_npc_deaths_head_ward_keeper(Creature* pCreature)
{
return new npc_deaths_head_ward_keeperAI(pCreature);
}
+
CreatureAI* GetAI_npc_willix(Creature* pCreature)
{
return new npc_willixAI(pCreature);
}
+
void AddSC_razorfen_kraul()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_willix";
newscript->GetAI = &GetAI_npc_willix;
newscript->pQuestAccept = &QuestAccept_npc_willix;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_deaths_head_ward_keeper";
newscript->GetAI = &GetAI_npc_deaths_head_ward_keeper;
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp
index d6bb995dbd6..84e34924958 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ayamiss.cpp
@@ -13,29 +13,36 @@
* along 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 TRINITY_DLL_DECL boss_ayamissAI : public ScriptedAI
{
boss_ayamissAI(Creature *c) : ScriptedAI(c) {}
+
Unit *pTarget;
uint32 STINGERSPRAY_Timer;
uint32 POISONSTINGER_Timer;
uint32 SUMMONSWARMER_Timer;
uint32 phase;
+
void Reset()
{
pTarget = NULL;
@@ -44,37 +51,44 @@ struct TRINITY_DLL_DECL boss_ayamissAI : public ScriptedAI
SUMMONSWARMER_Timer = 60000;
phase=1;
}
+
void EnterCombat(Unit *who)
{
pTarget = who;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -82,6 +96,7 @@ CreatureAI* GetAI_boss_ayamiss(Creature* pCreature)
{
return new boss_ayamissAI (pCreature);
}
+
void AddSC_boss_ayamiss()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp
index 80dd3bff06f..58c48540362 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_buru.cpp
@@ -13,12 +13,15 @@
* along 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"
+
#define EMOTE_TARGET -1509002
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp
index 03222740da2..5096b60ccdf 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_kurinnaxx.cpp
@@ -13,23 +13,29 @@
* along 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 TRINITY_DLL_DECL boss_kurinnaxxAI : public ScriptedAI
{
boss_kurinnaxxAI(Creature *c) : ScriptedAI(c) {}
+
Unit *pTarget;
uint32 MORTALWOUND_Timer;
uint32 SANDTRAP_Timer;
uint32 i;
+
void Reset()
{
i=0;
@@ -37,32 +43,38 @@ struct TRINITY_DLL_DECL boss_kurinnaxxAI : public ScriptedAI
MORTALWOUND_Timer = 30000;
SANDTRAP_Timer = 30000;
}
+
void EnterCombat(Unit *who)
{
pTarget = who;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
};
@@ -70,6 +82,7 @@ CreatureAI* GetAI_boss_kurinnaxx(Creature* pCreature)
{
return new boss_kurinnaxxAI (pCreature);
}
+
void AddSC_boss_kurinnaxx()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp
index 7be14a98f3c..1e5ca42d044 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_moam.cpp
@@ -13,29 +13,36 @@
* along 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 EMOTE_AGGRO -1509000
#define EMOTE_MANA_FULL -1509001
+
#define SPELL_TRAMPLE 15550
#define SPELL_DRAINMANA 27256
#define SPELL_ARCANEERUPTION 25672
#define SPELL_SUMMONMANA 25681
#define SPELL_GRDRSLEEP 24360 //Greater Dreamless Sleep
+
struct TRINITY_DLL_DECL boss_moamAI : public ScriptedAI
{
boss_moamAI(Creature *c) : ScriptedAI(c) {}
+
Unit *pTarget;
uint32 TRAMPLE_Timer;
uint32 DRAINMANA_Timer;
uint32 SUMMONMANA_Timer;
uint32 i;
uint32 j;
+
void Reset()
{
i=0;
@@ -44,21 +51,25 @@ struct TRINITY_DLL_DECL boss_moamAI : public ScriptedAI
TRAMPLE_Timer = 30000;
DRAINMANA_Timer = 30000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(EMOTE_AGGRO, m_creature);
pTarget = who;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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);
DoScriptText(EMOTE_MANA_FULL, m_creature);
}
+
//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))
{
@@ -66,25 +77,30 @@ struct TRINITY_DLL_DECL boss_moamAI : public ScriptedAI
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();
}
};
@@ -92,6 +108,7 @@ CreatureAI* GetAI_boss_moam(Creature* pCreature)
{
return new boss_moamAI (pCreature);
}
+
void AddSC_boss_moam()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp
index cd5e65a29bd..5652b706917 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_ossirian.cpp
@@ -13,20 +13,26 @@
* along 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"
+
#define SAY_SURPREME2 -1509019
#define SAY_SURPREME3 -1509020
+
#define SAY_RAND_INTRO1 -1509021
#define SAY_RAND_INTRO2 -1509022
#define SAY_RAND_INTRO3 -1509023
#define SAY_RAND_INTRO4 -1509024 //possibly old?
+
#define SAY_AGGRO -1509025
+
#define SAY_SLAY -1509026
#define SAY_DEATH -1509027
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp
index 6587e92e342..fb2c92ff59f 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/boss_rajaxx.cpp
@@ -13,26 +13,33 @@
* along 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"
+
#define SAY_ANDOROV_INTRO -1509003
#define SAY_ANDOROV_ATTACK -1509004
+
#define SAY_WAVE3 -1509005
#define SAY_WAVE4 -1509006
#define SAY_WAVE5 -1509007
#define SAY_WAVE6 -1509008
#define SAY_WAVE7 -1509009
#define SAY_INTRO -1509010
+
#define SAY_UNK1 -1509011
#define SAY_UNK2 -1509012
#define SAY_UNK3 -1509013
#define SAY_UNK4 -1509014
+
#define SAY_DEAGGRO -1509015
#define SAY_KILLS_ANDOROV -1509016
+
#define SAY_COMPLETE_QUEST -1509017 //Yell when realm complete quest 8743 for world event
diff --git a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp
index 3c42cb587c8..b94e6f16a80 100644
--- a/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ruins_of_ahnqiraj/instance_ruins_of_ahnqiraj.cpp
@@ -13,11 +13,13 @@
* along 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/kalimdor/silithus.cpp b/src/bindings/scripts/scripts/kalimdor/silithus.cpp
index c9d2ae36aec..45313fbbc73 100644
--- a/src/bindings/scripts/scripts/kalimdor/silithus.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/silithus.cpp
@@ -13,21 +13,26 @@
* along 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: 7785, 8304, 8507.
SDCategory: Silithus
EndScriptData */
+
/* ContentData
npc_highlord_demitrian
npcs_rutgar_and_frankal
quest_a_pawn_on_the_eternal_pawn
EndContentData */
+
#include "precompiled.h"
+
/*###
## npc_highlord_demitrian
###*/
+
#define GOSSIP_DEMITRIAN1 "What do you know of it?"
#define GOSSIP_DEMITRIAN2 "I am listening , Demitrian."
#define GOSSIP_DEMITRIAN3 "Continue, please."
@@ -35,16 +40,20 @@ EndContentData */
#define GOSSIP_DEMITRIAN5 "<Nod>"
#define GOSSIP_DEMITRIAN6 "Caught unaware? How?"
#define GOSSIP_DEMITRIAN7 "So what did Ragnaros do next?"
+
bool GossipHello_npc_highlord_demitrian(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(7785) == QUEST_STATUS_NONE &&
(pPlayer->HasItemCount(18563,1,false) || pPlayer->HasItemCount(18564,1,false)))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DEMITRIAN1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(6812, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_highlord_demitrian(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -75,6 +84,7 @@ bool GossipSelect_npc_highlord_demitrian(Player* pPlayer, Creature* pCreature, u
break;
case GOSSIP_ACTION_INFO_DEF+6:
pPlayer->SEND_GOSSIP_MENU(6870, pCreature->GetGUID());
+
ItemPosCountVec dest;
uint8 msg = pPlayer->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, 19016, 1);
if (msg == EQUIP_ERR_OK)
@@ -83,40 +93,51 @@ bool GossipSelect_npc_highlord_demitrian(Player* pPlayer, Creature* pCreature, u
}
return true;
}
+
/*###
## 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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE &&
pCreature->GetEntry() == 15170 &&
!pPlayer->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
if (pPlayer->GetQuestStatus(8304) == QUEST_STATUS_INCOMPLETE &&
pCreature->GetEntry() == 15171 &&
pPlayer->GetReqKillOrCastCurrentCount(8304, TRIGGER_RUTGAR))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9);
+
pPlayer->SEND_GOSSIP_MENU(7754, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npcs_rutgar_and_frankal(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -150,6 +171,7 @@ bool GossipSelect_npcs_rutgar_and_frankal(Player* pPlayer, Creature* pCreature,
//'kill' our trigger to update quest status
pPlayer->KilledMonsterCredit(TRIGGER_RUTGAR, pCreature->GetGUID());
break;
+
case GOSSIP_ACTION_INFO_DEF + 9:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM11, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
pPlayer->SEND_GOSSIP_MENU(7762, pCreature->GetGUID());
@@ -178,19 +200,23 @@ bool GossipSelect_npcs_rutgar_and_frankal(Player* pPlayer, Creature* pCreature,
}
return true;
}
+
/*####
# quest_a_pawn_on_the_eternal_board (Defines)
####*/
enum eEternalBoard
{
QUEST_A_PAWN_ON_THE_ETERNAL_BOARD = 8519,
+
FACTION_HOSTILE = 14,
FACTION_FRIENDLY = 35,
+
C_ANACHRONOS = 15381,
C_FANDRAL_STAGHELM = 15382,
C_ARYGOS = 15380,
C_MERITHRA = 15378,
C_CAELESTRASZ = 15379,
+
ANACHRONOS_SAY_1 = -1350000,
ANACHRONOS_SAY_2 = -1350001,
ANACHRONOS_SAY_3 = -1350002,
@@ -204,6 +230,7 @@ enum eEternalBoard
ANACHRONOS_EMOTE_1 = -1350010,
ANACHRONOS_EMOTE_2 = -1350011,
ANACHRONOS_EMOTE_3 = -1350012,
+
FANDRAL_SAY_1 = -1350013,
FANDRAL_SAY_2 = -1350014,
FANDRAL_SAY_3 = -1350015,
@@ -212,16 +239,20 @@ enum eEternalBoard
FANDRAL_SAY_6 = -1350018,
FANDRAL_EMOTE_1 = -1350019,
FANDRAL_EMOTE_2 = -1350020,
+
CAELESTRASZ_SAY_1 = -1350021,
CAELESTRASZ_SAY_2 = -1350022,
CAELESTRASZ_YELL_1 = -1350023,
+
ARYGOS_SAY_1 = -1350024,
ARYGOS_YELL_1 = -1350025,
ARYGOS_EMOTE_1 = -1350026,
+
MERITHRA_SAY_1 = -1350027,
MERITHRA_SAY_2 = -1350028,
MERITHRA_YELL_1 = -1350029,
MERITHRA_EMOTE_1 = -1350030,
+
GO_GATE_OF_AHN_QIRAJ = 176146,
GO_GLYPH_OF_AHN_QIRAJ = 176148,
GO_ROOTS_OF_AHN_QIRAJ = 176147
@@ -229,6 +260,7 @@ enum eEternalBoard
/*#####
# Quest: A Pawn on the Eternal Board
#####*/
+
/* ContentData
A Pawn on the Eternal Board - creatures, gameobjects and defines
mob_qiraj_war_spawn : Adds that are summoned in the Qiraj gates battle.
@@ -238,14 +270,17 @@ go_crystalline_tear : GameObject that begins the event and hands out quest
TO DO: get correct spell IDs and timings for spells cast upon dragon transformations
TO DO: Dragons should use the HandleEmoteCommand(EMOTE_ONESHOT_LIFTOFF) after transformation,but for some unknown reason it doesnt work.
EndContentData */
+
#define QUEST_A_PAWN_ON_THE_ETERNAL_BOARD 8519
#define EVENT_AREA_RADIUS 65 //65yds
#define EVENT_COOLDOWN 500000 //in ms. appear after event completed or failed (should be = Adds despawn time)
+
struct QuestCinematic
{
int32 TextId;
uint32 Creature, Timer;
};
+
// Creature 0 - Anachronos, 1 - Fandral, 2 - Arygos, 3 - Merithra, 4 - Caelestrasz
static QuestCinematic EventAnim[]=
{
@@ -318,10 +353,12 @@ static QuestCinematic EventAnim[]=
{NULL, 0, 5000},
{NULL, 0, NULL}
};
+
struct Location
{
float x, y, z, o;
};
+
//Cordinates for Spawns
static Location SpawnLocation[]=
{
@@ -330,11 +367,13 @@ static Location SpawnLocation[]=
{-8085, 1524, 2.61, 3.141592},//Kaldorei Infantry
{-8080, 1522, 2.61, 3.141592},//Kaldorei Infantry
{-8085, 1520, 2.61, 3.141592},//Kaldorei Infantry
+
{-8085, 1524, 2.61, 3.141592},//Kaldorei Infantry
{-8080, 1522, 2.61, 3.141592},//Kaldorei Infantry
{-8085, 1520, 2.61, 3.141592},//Kaldorei Infantry
{-8080, 1518, 2.61, 3.141592},//Kaldorei Infantry
{-8085, 1516, 2.61, 3.141592},//Kaldorei Infantry
+
{-8085, 1518, 2.61, 3.141592},//Kaldorei Infantry
{-8080, 1516, 2.61, 3.141592},//Kaldorei Infantry
{-8080, 1520, 2.61, 3.141592},//Kaldorei Infantry
@@ -346,52 +385,62 @@ static Location SpawnLocation[]=
{-8082, 1524, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1526, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1527, 2.61, 3.141592},//Kaldorei Infantry
+
{-8082, 1524, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1522, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1520, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1518, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1516, 2.61, 3.141592},//Kaldorei Infantry
+
{-8082, 1523, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1521, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1528, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1519, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1526, 2.61, 3.141592},//Kaldorei Infantry
+
{-8082, 1524, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1522, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1520, 2.61, 3.141592},//Kaldorei Infantry
{-8078, 1518, 2.61, 3.141592},//Kaldorei Infantry
{-8082, 1516, 2.61, 3.141592},//Kaldorei Infantry
+
{-8088, 1510, 2.61, 0},//Anubisath Conqueror
{-8084, 1520, 2.61, 0},//Anubisath Conqueror
{-8088, 1530, 2.61, 0},//Anubisath Conqueror
+
{-8080, 1513, 2.61, 0},//Qiraj Wasp
{-8082, 1523, 2.61, 0},//Qiraj Wasp
{-8085, 1518, 2.61, 0},//Qiraj Wasp
{-8082, 1516, 2.61, 0},//Qiraj Wasp
{-8085, 1520, 2.61, 0},//Qiraj Wasp
{-8080, 1528, 2.61, 0},//Qiraj Wasp
+
{-8082, 1513, 2.61, 0},//Qiraj Wasp
{-8079, 1523, 2.61, 0},//Qiraj Wasp
{-8080, 1531, 2.61, 0},//Qiraj Wasp
{-8079, 1516, 2.61, 0},//Qiraj Wasp
{-8082, 1520, 2.61, 0},//Qiraj Wasp
{-8080, 1518, 2.61, 0},//Qiraj Wasp
+
{-8081, 1514, 2.61, 0},//Qiraj Tank
{-8081, 1520, 2.61, 0},//Qiraj Tank
{-8081, 1526, 2.61, 0},//Qiraj Tank
{-8081, 1512, 2.61, 0},//Qiraj Tank
{-8082, 1520, 2.61, 0},//Qiraj Tank
{-8081, 1528, 2.61, 0},//Qiraj Tank
+
{-8082, 1513, 2.61, 3.141592},//Anubisath Conqueror
{-8082, 1520, 2.61, 3.141592},//Anubisath Conqueror
{-8082, 1527, 2.61, 3.141592},//Anubisath Conqueror
};
+
struct WaveData
{
uint8 SpawnCount, UsedSpawnPoint;
uint32 CreatureId, SpawnTimer, YellTimer, DespTimer;
int32 WaveTextId;
};
+
static WaveData WavesInfo[] =
{
{30, 0, 15423, 0, 0,24000, NULL}, //Kaldorei Soldier
@@ -399,11 +448,14 @@ static WaveData WavesInfo[] =
{12, 38, 15414, 0, 0,24000, NULL}, //Qiraji Wasps
{6, 50, 15422, 0, 0,24000, NULL}, //Qiraji Tanks
{15, 15, 15423, 0, 0,24000, NULL} //Kaldorei Soldier
+
};
+
struct SpawnSpells
{
uint32 Timer1, Timer2, SpellId;
};
+
static SpawnSpells SpawnCast[]=//
{
{100000, 2000, 33652}, // Stop Time
@@ -417,8 +469,10 @@ static SpawnSpells SpawnCast[]=//
struct TRINITY_DLL_DECL npc_anachronos_the_ancientAI : public ScriptedAI
{
npc_anachronos_the_ancientAI(Creature* c) : ScriptedAI(c) {}
+
uint32 AnimationTimer;
uint8 AnimationCount;
+
uint64 AnachronosQuestTriggerGUID;
uint64 MerithraGUID;
uint64 ArygosGUID;
@@ -426,6 +480,7 @@ struct TRINITY_DLL_DECL npc_anachronos_the_ancientAI : public ScriptedAI
uint64 FandralGUID;
uint64 PlayerGUID;
bool eventEnd;
+
void Reset()
{
AnimationTimer = 1500;
@@ -437,19 +492,24 @@ struct TRINITY_DLL_DECL npc_anachronos_the_ancientAI : public ScriptedAI
FandralGUID = 0;
PlayerGUID = 0;
eventEnd = false;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
void HandleAnimation()
{
Player* plr = Unit::GetPlayer(PlayerGUID);
if(!plr)
return;
+
Unit* Fandral = plr->FindNearestCreature(C_FANDRAL_STAGHELM, 100, m_creature);
Unit* Arygos = plr->FindNearestCreature(C_ARYGOS, 100,m_creature);
Unit* Caelestrasz = plr->FindNearestCreature(C_CAELESTRASZ, 100, m_creature);
Unit* Merithra = plr->FindNearestCreature(C_MERITHRA, 100,m_creature);
+
if(!Fandral || !Arygos || !Caelestrasz || !Merithra)
return;
+
Unit* mob;
AnimationTimer = EventAnim[AnimationCount].Timer;
if (eventEnd == false)
@@ -710,12 +770,15 @@ struct TRINITY_DLL_DECL npc_anachronos_the_ancientAI : public ScriptedAI
m_creature->AI()->EnterEvadeMode();
}
};
+
/*######
# mob_qiraj_war_spawn
######*/
+
struct TRINITY_DLL_DECL mob_qiraj_war_spawnAI : public ScriptedAI
{
mob_qiraj_war_spawnAI(Creature* c) : ScriptedAI(c) {}
+
uint64 MobGUID;
uint64 PlayerGUID;
uint32 SpellTimer1, SpellTimer2, SpellTimer3,SpellTimer4;
@@ -729,12 +792,15 @@ struct TRINITY_DLL_DECL mob_qiraj_war_spawnAI : public ScriptedAI
Timers = false;
hasTarget = false;
}
+
void EnterCombat(Unit* who) {}
void JustDied(Unit* slayer);
+
void UpdateAI(const uint32 diff)
{
Unit* target;
Player* plr = m_creature->GetPlayer(PlayerGUID);
+
if(!Timers)
{
if(m_creature->GetEntry() == 15424 || m_creature->GetEntry() == 15422 || m_creature->GetEntry() == 15414) //all but Kaldorei Soldiers
@@ -784,6 +850,7 @@ struct TRINITY_DLL_DECL mob_qiraj_war_spawnAI : public ScriptedAI
{
uint8 tar;
tar = rand()%3;
+
if (tar == 0)
target = m_creature->FindNearestCreature(15422,20,true);
else if (tar == 1)
@@ -797,40 +864,53 @@ struct TRINITY_DLL_DECL mob_qiraj_war_spawnAI : public ScriptedAI
}
if (!(trigger = m_creature->FindNearestCreature(15379,100)))
DoCast(m_creature, 33652);
+
if (!UpdateVictim())
{
hasTarget = false;
return;
}
+
DoMeleeAttackIfReady();
}
};
+
/*#####
# npc_anachronos_quest_trigger
#####*/
+
struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
{
npc_anachronos_quest_triggerAI(Creature* c) : ScriptedAI(c) {}
+
uint64 PlayerGUID;
+
uint32 WaveTimer;
uint32 AnnounceTimer;
+
int8 LiveCount;
uint8 WaveCount;
+
bool EventStarted;
bool Announced;
bool Failed;
+
void Reset()
{
PlayerGUID = 0;
+
WaveTimer = 2000;
AnnounceTimer = 1000;
LiveCount = 0;
WaveCount = 0;
+
EventStarted = false;
Announced = false;
Failed = false;
+
m_creature->SetVisibility(VISIBILITY_OFF);
}
+
void SummonNextWave()
{
uint8 count = WavesInfo[WaveCount].SpawnCount;
@@ -839,7 +919,7 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
uint8 KaldoreiSoldierCount = 0;
uint8 AnubisathConquerorCount = 0;
uint8 QirajiWaspCount = 0;
- for (uint8 i = 0; i < 67; ++i)
+ for(uint8 i = 0; i < 67; ++i)
{
Creature* Spawn = NULL;
float X = SpawnLocation[locIndex + i].x;
@@ -848,6 +928,7 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
float O = SpawnLocation[locIndex + i].o;
uint32 desptimer = WavesInfo[WaveCount].DespTimer;
Spawn = m_creature->SummonCreature(WavesInfo[WaveCount].CreatureId, X, Y, Z, O, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, desptimer);
+
if(Spawn)
{
Spawn->LoadCreaturesAddon();
@@ -857,6 +938,7 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
if (i >= 33) WaveCount = 2;
if (i >= 45) WaveCount = 3;
if (i >= 51) WaveCount = 4;
+
if(WaveCount < 5) //1-4 Wave
{
((mob_qiraj_war_spawnAI*)Spawn->AI())->MobGUID = m_creature->GetGUID();
@@ -867,19 +949,25 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
WaveTimer = WavesInfo[WaveCount].SpawnTimer;
AnnounceTimer = WavesInfo[WaveCount].YellTimer;
}
+
void CheckEventFail()
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
if(!pPlayer)
return;
+
if(Group *EventGroup = pPlayer->GetGroup())
{
Player* GroupMember;
+
uint8 GroupMemberCount = 0;
uint8 DeadMemberCount = 0;
uint8 FailedMemberCount = 0;
+
const Group::MemberSlotList members = EventGroup->GetMemberSlots();
- for (Group::member_citerator itr = members.begin(); itr!= members.end(); itr++)
+
+ for(Group::member_citerator itr = members.begin(); itr!= members.end(); itr++)
{
GroupMember = (Unit::GetPlayer(itr->guid));
if(!GroupMember)
@@ -891,23 +979,28 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
++FailedMemberCount;
}
++GroupMemberCount;
+
if(GroupMember->isDead())
++DeadMemberCount;
}
+
if(GroupMemberCount == FailedMemberCount || !pPlayer->IsWithinDistInMap(m_creature, EVENT_AREA_RADIUS))
Failed = true; //only so event can restart
}
}
+
void LiveCounter()
{
--LiveCount;
if(!LiveCount)
Announced = false;
}
+
void UpdateAI(const uint32 diff)
{
if(!PlayerGUID || !EventStarted)
return;
+
if(WaveCount < 4)
{
if(!Announced && AnnounceTimer < diff)
@@ -915,6 +1008,7 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
DoScriptText(WavesInfo[WaveCount].WaveTextId, m_creature);
Announced = true;
} else AnnounceTimer -= diff;
+
if(WaveTimer < diff)
SummonNextWave();
else WaveTimer -= diff;
@@ -922,28 +1016,33 @@ struct TRINITY_DLL_DECL npc_anachronos_quest_triggerAI : public ScriptedAI
CheckEventFail();
if (WaveCount == 4 || Failed)
EnterEvadeMode();
- };
+ };
};
void mob_qiraj_war_spawnAI::JustDied(Unit* slayer)
{
m_creature->RemoveCorpse();
if(Creature* Mob = (Unit::GetCreature(*m_creature, MobGUID)))
((npc_anachronos_quest_triggerAI*)Mob->AI())->LiveCounter();
+
};
/*#####
# go_crystalline_tear
######*/
+
bool GOQuestAccept_GO_crystalline_tear(Player* plr, GameObject* go, Quest const* quest)
{
if(quest->GetQuestId() == QUEST_A_PAWN_ON_THE_ETERNAL_BOARD)
{
+
if(Unit* Anachronos_Quest_Trigger = go->FindNearestCreature(15454, 100, plr))
{
+
Unit *Merithra = Anachronos_Quest_Trigger->SummonCreature(15378,-8034.535,1535.14,2.61,0,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,150000);
Unit *Caelestrasz = Anachronos_Quest_Trigger->SummonCreature(15379,-8032.767, 1533.148,2.61, 1.5,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,150000);
Unit *Arygos = Anachronos_Quest_Trigger->SummonCreature(15380,-8034.52, 1537.843, 2.61, 5.7,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,150000);
Unit *Fandral = Anachronos_Quest_Trigger->SummonCreature(15382,-8028.462, 1535.843, 2.61, 3.141592,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,215000);
Creature *Anachronos = Anachronos_Quest_Trigger->SummonCreature(15381,-8028.75, 1538.795, 2.61, 4,TEMPSUMMON_TIMED_OR_DEAD_DESPAWN,220000);
+
if(Merithra)
{
Merithra->SetUInt32Value(UNIT_NPC_FLAGS, 0);
@@ -951,6 +1050,7 @@ bool GOQuestAccept_GO_crystalline_tear(Player* plr, GameObject* go, Quest const*
Merithra->SetUInt32Value(UNIT_FIELD_DISPLAYID,15420);
Merithra->setFaction(35);
}
+
if(Caelestrasz)
{
Caelestrasz->SetUInt32Value(UNIT_NPC_FLAGS, 0);
@@ -958,6 +1058,7 @@ bool GOQuestAccept_GO_crystalline_tear(Player* plr, GameObject* go, Quest const*
Caelestrasz->SetUInt32Value(UNIT_FIELD_DISPLAYID,15419);
Caelestrasz->setFaction(35);
}
+
if(Arygos)
{
Arygos->SetUInt32Value(UNIT_NPC_FLAGS, 0);
@@ -965,6 +1066,7 @@ bool GOQuestAccept_GO_crystalline_tear(Player* plr, GameObject* go, Quest const*
Arygos->SetUInt32Value(UNIT_FIELD_DISPLAYID,15418);
Arygos->setFaction(35);
}
+
if(Anachronos)
{
((npc_anachronos_the_ancientAI*)Anachronos->AI())->PlayerGUID = plr->GetGUID();
@@ -977,42 +1079,52 @@ bool GOQuestAccept_GO_crystalline_tear(Player* plr, GameObject* go, Quest const*
}
return true;
}
+
CreatureAI* GetAI_npc_anachronos_quest_trigger(Creature* c)
{
return new npc_anachronos_quest_triggerAI(c);
}
+
CreatureAI* GetAI_mob_qiraj_war_spawn(Creature* c)
{
return new mob_qiraj_war_spawnAI(c);
}
+
CreatureAI* GetAI_npc_anachronos_the_ancient(Creature* c)
{
return new npc_anachronos_the_ancientAI(c);
}
+
void AddSC_silithus()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_crystalline_tear";
newscript->pGOQuestAccept = &GOQuestAccept_GO_crystalline_tear;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_anachronos_quest_trigger";
newscript->GetAI = &GetAI_npc_anachronos_quest_trigger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_anachronos_the_ancient";
newscript->GetAI = &GetAI_npc_anachronos_the_ancient;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_qiraj_war_spawn";
newscript->GetAI = &GetAI_mob_qiraj_war_spawn;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_highlord_demitrian";
newscript->pGossipHello = &GossipHello_npc_highlord_demitrian;
newscript->pGossipSelect = &GossipSelect_npc_highlord_demitrian;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npcs_rutgar_and_frankal";
newscript->pGossipHello = &GossipHello_npcs_rutgar_and_frankal;
diff --git a/src/bindings/scripts/scripts/kalimdor/stonetalon_mountains.cpp b/src/bindings/scripts/scripts/kalimdor/stonetalon_mountains.cpp
index 3c93aeb32b9..2e1ebc683fd 100644
--- a/src/bindings/scripts/scripts/kalimdor/stonetalon_mountains.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/stonetalon_mountains.cpp
@@ -13,30 +13,37 @@
* along 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, 6523
SDCategory: Stonetalon Mountains
EndScriptData */
+
/* ContentData
npc_braug_dimspirit
npc_kaya_flathoof
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_braug_dimspirit
######*/
+
#define GOSSIP_HBD1 "Ysera"
#define GOSSIP_HBD2 "Neltharion"
#define GOSSIP_HBD3 "Nozdormu"
#define GOSSIP_HBD4 "Alexstrasza"
#define GOSSIP_HBD5 "Malygos"
+
bool GossipHello_npc_braug_dimspirit(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(6627) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HBD1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
@@ -44,18 +51,22 @@ bool GossipHello_npc_braug_dimspirit(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HBD3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HBD4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HBD5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(5820, pCreature->GetGUID());
}
else
pPlayer->SEND_GOSSIP_MENU(5819, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_braug_dimspirit(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
{
pPlayer->CLOSE_GOSSIP_MENU();
pCreature->CastSpell(pPlayer,6766,false);
+
}
if (uiAction == GOSSIP_ACTION_INFO_DEF+2)
{
@@ -64,28 +75,37 @@ bool GossipSelect_npc_braug_dimspirit(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
/*######
## npc_kaya_flathoof
######*/
+
enum eKaya
{
FACTION_ESCORTEE_H = 775,
+
NPC_GRIMTOTEM_RUFFIAN = 11910,
NPC_GRIMTOTEM_BRUTE = 11912,
NPC_GRIMTOTEM_SORCERER = 11913,
+
SAY_START = -1000347,
SAY_AMBUSH = -1000348,
SAY_END = -1000349,
+
QUEST_PROTECT_KAYA = 6523
};
+
struct TRINITY_DLL_DECL npc_kaya_flathoofAI : public npc_escortAI
{
npc_kaya_flathoofAI(Creature* c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 16:
@@ -101,39 +121,48 @@ struct TRINITY_DLL_DECL npc_kaya_flathoofAI : public npc_escortAI
break;
}
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void Reset(){}
};
+
bool QuestAccept_npc_kaya_flathoof(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_PROTECT_KAYA)
{
if (npc_escortAI* pEscortAI = CAST_AI(npc_kaya_flathoofAI, pCreature->AI()))
pEscortAI->Start(true, false, pPlayer->GetGUID());
+
DoScriptText(SAY_START, pCreature);
pCreature->setFaction(113);
pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
return true;
}
+
CreatureAI* GetAI_npc_kaya_flathoofAI(Creature* pCreature)
{
return new npc_kaya_flathoofAI(pCreature);
}
+
/*######
## 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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_kaya_flathoof";
newscript->GetAI = &GetAI_npc_kaya_flathoofAI;
diff --git a/src/bindings/scripts/scripts/kalimdor/tanaris.cpp b/src/bindings/scripts/scripts/kalimdor/tanaris.cpp
index a9998c6f533..2b21db67794 100644
--- a/src/bindings/scripts/scripts/kalimdor/tanaris.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/tanaris.cpp
@@ -13,12 +13,14 @@
* along 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: 648, 1560, 2954, 4005, 10277, 10279(Special flight path). Noggenfogger vendor
SDCategory: Tanaris
EndScriptData */
+
/* ContentData
mob_aquementas
npc_custodian_of_time
@@ -28,32 +30,42 @@ npc_stone_watcher_of_norgannon
npc_OOX17
npc_tooga
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "follower_ai.h"
+
/*######
## mob_aquementas
######*/
+
#define AGGRO_YELL_AQUE -1000350
+
#define SPELL_AQUA_JET 13586
#define SPELL_FROST_SHOCK 15089
+
struct TRINITY_DLL_DECL mob_aquementasAI : public ScriptedAI
{
mob_aquementasAI(Creature *c) : ScriptedAI(c) {}
+
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 (CAST_PLR(receiver)->HasItemCount(11169,1,false) &&
@@ -67,10 +79,12 @@ struct TRINITY_DLL_DECL mob_aquementasAI : public ScriptedAI
CAST_PLR(receiver)->StoreNewItem(dest, 11522, 1, true);
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(AGGRO_YELL_AQUE, m_creature, who);
}
+
void UpdateAI(const uint32 diff)
{
if (isFriendly)
@@ -81,8 +95,10 @@ struct TRINITY_DLL_DECL mob_aquementasAI : public ScriptedAI
isFriendly = false;
}else SwitchFaction_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
if (!isFriendly)
{
if (SendItem_Timer < diff)
@@ -92,16 +108,19 @@ struct TRINITY_DLL_DECL mob_aquementasAI : public ScriptedAI
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();
}
};
@@ -109,9 +128,11 @@ CreatureAI* GetAI_mob_aquementas(Creature* pCreature)
{
return new mob_aquementasAI (pCreature);
}
+
/*######
## npc_custodian_of_time
######*/
+
#define WHISPER_CUSTODIAN_1 -1000150
#define WHISPER_CUSTODIAN_2 -1000151
#define WHISPER_CUSTODIAN_3 -1000152
@@ -126,14 +147,17 @@ CreatureAI* GetAI_mob_aquementas(Creature* pCreature)
#define WHISPER_CUSTODIAN_12 -1000161
#define WHISPER_CUSTODIAN_13 -1000162
#define WHISPER_CUSTODIAN_14 -1000163
+
struct TRINITY_DLL_DECL npc_custodian_of_timeAI : public npc_escortAI
{
npc_custodian_of_timeAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player *pPlayer = GetPlayerForEscort();
if (!pPlayer)
return;
+
switch(i)
{
case 0: DoScriptText(WHISPER_CUSTODIAN_1, m_creature, pPlayer); break;
@@ -161,10 +185,12 @@ struct TRINITY_DLL_DECL npc_custodian_of_timeAI : public npc_escortAI
break;
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (who->GetTypeId() == TYPEID_PLAYER)
{
if (who->HasAura(34877) && CAST_PLR(who)->GetQuestStatus(10277) == QUEST_STATUS_INCOMPLETE)
@@ -177,43 +203,57 @@ struct TRINITY_DLL_DECL npc_custodian_of_timeAI : public npc_escortAI
}
}
}
+
void EnterCombat(Unit* who) { }
void Reset() { }
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_custodian_of_time(Creature* pCreature)
{
return new npc_custodian_of_timeAI(pCreature);
}
+
/*######
## npc_marin_noggenfogger
######*/
+
bool GossipHello_npc_marin_noggenfogger(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetQuestRewardStatus(2662))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_marin_noggenfogger(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(10279) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestRewardStatus(10279))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -221,38 +261,50 @@ bool GossipHello_npc_steward_of_time(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(9977, pCreature->GetGUID());
+
return true;
}
+
bool QuestAccept_npc_steward_of_time(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if (quest->GetQuestId() == 10279) //Quest: To The Master's Lair
pPlayer->CastSpell(pPlayer,34891,true); //(Flight through Caverns)
+
return false;
}
+
bool GossipSelect_npc_steward_of_time(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
pPlayer->CastSpell(pPlayer,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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(2954) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NORGANNON_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(1674, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_stone_watcher_of_norgannon(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -284,9 +336,11 @@ bool GossipSelect_npc_stone_watcher_of_norgannon(Player* pPlayer, Creature* pCre
}
return true;
}
+
/*######
## npc_OOX17
######*/
+
enum e00X17
{
//texts are signed for 7806
@@ -296,19 +350,24 @@ enum e00X17
SAY_OOX_AMBUSH = -1000290,
SAY_OOX17_AMBUSH_REPLY = -1000291,
SAY_OOX_END = -1000292,
+
Q_OOX17 = 648,
SPAWN_FIRST = 7803,
SPAWN_SECOND_1 = 5617,
SPAWN_SECOND_2 = 7805
};
+
struct TRINITY_DLL_DECL npc_OOX17AI : public npc_escortAI
{
npc_OOX17AI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i) {
case 23:
m_creature->SummonCreature(SPAWN_FIRST, -8350.96, -4445.79, 10.10, 6.20, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
@@ -316,6 +375,7 @@ struct TRINITY_DLL_DECL npc_OOX17AI : public npc_escortAI
m_creature->SummonCreature(SPAWN_FIRST, -8353.96, -4442.79, 10.10, 6.08, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
DoScriptText(SAY_OOX_AMBUSH, m_creature);
break;
+
case 56:
m_creature->SummonCreature(SPAWN_SECOND_1, -7510.07, -4795.50, 9.35, 6.06, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
m_creature->SummonCreature(SPAWN_SECOND_2, -7515.07, -4797.50, 9.35, 6.22, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
@@ -324,6 +384,7 @@ struct TRINITY_DLL_DECL npc_OOX17AI : public npc_escortAI
if (Unit* scoff = me->FindNearestCreature(SPAWN_SECOND_2, 30))
DoScriptText(SAY_OOX17_AMBUSH_REPLY, scoff);
break;
+
case 86:
if (pPlayer)
{
@@ -333,16 +394,20 @@ struct TRINITY_DLL_DECL npc_OOX17AI : public npc_escortAI
break;
}
}
+
void Reset(){}
+
void EnterCombat(Unit* who)
{
DoScriptText(RAND(SAY_OOX_AGGRO1,SAY_OOX_AGGRO2), m_creature);
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
};
+
bool QuestAccept_npc_OOX17(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == Q_OOX17)
@@ -352,18 +417,22 @@ bool QuestAccept_npc_OOX17(Player* pPlayer, Creature* pCreature, Quest const* qu
pCreature->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
DoScriptText(SAY_OOX_START, pCreature);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_OOX17AI, pCreature->AI()))
pEscortAI->Start(true, false, pPlayer->GetGUID());
}
return true;
}
+
CreatureAI* GetAI_npc_OOX17(Creature* pCreature)
{
return new npc_OOX17AI(pCreature);
}
+
/*####
# npc_tooga
####*/
+
enum eTooga
{
SAY_TOOG_THIRST = -1000391,
@@ -374,29 +443,39 @@ enum eTooga
SAY_TORT_POST_4 = -1000396,
SAY_TOOG_POST_5 = -1000397,
SAY_TORT_POST_6 = -1000398,
+
QUEST_TOOGA = 1560,
NPC_TORTA = 6015,
+
POINT_ID_TO_WATER = 1,
FACTION_TOOG_ESCORTEE = 113
};
+
const float m_afToWaterLoc[] = {-7032.664551, -4906.199219, -1.606446};
+
struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
{
npc_toogaAI(Creature* pCreature) : FollowerAI(pCreature) { }
+
uint32 m_uiCheckSpeechTimer;
uint32 m_uiPostEventTimer;
uint32 m_uiPhasePostEvent;
+
Unit* pTorta;
+
void Reset()
{
m_uiCheckSpeechTimer = 2500;
m_uiPostEventTimer = 1000;
m_uiPhasePostEvent = 0;
+
pTorta = NULL;
}
+
void MoveInLineOfSight(Unit *pWho)
{
FollowerAI::MoveInLineOfSight(pWho);
+
if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE | STATE_FOLLOW_POSTEVENT) && pWho->GetEntry() == NPC_TORTA)
{
if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE))
@@ -406,19 +485,24 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
if (pPlayer->GetQuestStatus(QUEST_TOOGA) == QUEST_STATUS_INCOMPLETE)
pPlayer->GroupEventHappens(QUEST_TOOGA, m_creature);
}
+
pTorta = pWho;
SetFollowComplete(true);
}
}
}
+
void MovementInform(uint32 uiMotionType, uint32 uiPointId)
{
FollowerAI::MovementInform(uiMotionType, uiPointId);
+
if (uiMotionType != POINT_MOTION_TYPE)
return;
+
if (uiPointId == POINT_ID_TO_WATER)
SetFollowComplete();
}
+
void UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
@@ -429,12 +513,14 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
if (m_uiPostEventTimer < uiDiff)
{
m_uiPostEventTimer = 5000;
+
if (!pTorta || !pTorta->isAlive())
{
//something happened, so just complete
SetFollowComplete();
return;
}
+
switch(m_uiPhasePostEvent)
{
case 1:
@@ -457,6 +543,7 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
m_creature->GetMotionMaster()->MovePoint(POINT_ID_TO_WATER, m_afToWaterLoc[0], m_afToWaterLoc[1], m_afToWaterLoc[2]);
break;
}
+
++m_uiPhasePostEvent;
}
else
@@ -468,21 +555,26 @@ struct TRINITY_DLL_DECL npc_toogaAI : public FollowerAI
if (m_uiCheckSpeechTimer < uiDiff)
{
m_uiCheckSpeechTimer = 5000;
+
if (urand(0,9) > 8)
DoScriptText(RAND(SAY_TOOG_THIRST,SAY_TOOG_WORRIED), m_creature);
}
else
m_uiCheckSpeechTimer -= uiDiff;
}
+
return;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_tooga(Creature* pCreature)
{
return new npc_toogaAI(pCreature);
}
+
bool QuestAccept_npc_tooga(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_TOOGA)
@@ -490,40 +582,49 @@ bool QuestAccept_npc_tooga(Player* pPlayer, Creature* pCreature, const Quest* pQ
if (npc_toogaAI* pToogaAI = CAST_AI(npc_toogaAI, pCreature->AI()))
pToogaAI->StartFollow(pPlayer, FACTION_TOOG_ESCORTEE, pQuest);
}
+
return true;
}
+
void AddSC_tanaris()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_aquementas";
newscript->GetAI = &GetAI_mob_aquementas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_custodian_of_time";
newscript->GetAI = &GetAI_npc_custodian_of_time;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_marin_noggenfogger";
newscript->pGossipHello = &GossipHello_npc_marin_noggenfogger;
newscript->pGossipSelect = &GossipSelect_npc_marin_noggenfogger;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_OOX17";
newscript->GetAI = &GetAI_npc_OOX17;
newscript->pQuestAccept = &QuestAccept_npc_OOX17;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tooga";
newscript->GetAI = &GetAI_npc_tooga;
diff --git a/src/bindings/scripts/scripts/kalimdor/teldrassil.cpp b/src/bindings/scripts/scripts/kalimdor/teldrassil.cpp
index b1a6e130b79..8ab8d98218a 100644
--- a/src/bindings/scripts/scripts/kalimdor/teldrassil.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/teldrassil.cpp
@@ -13,20 +13,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Teldrassil
SD%Complete: 100
SDComment: Quest support: 938
SDCategory: Teldrassil
EndScriptData */
+
/* ContentData
npc_mist
EndContentData */
+
#include "precompiled.h"
#include "follower_ai.h"
+
/*####
# npc_mist
####*/
+
enum eMist
{
SAY_AT_HOME = -1000411,
@@ -35,13 +40,17 @@ enum eMist
NPC_ARYNIA = 3519,
FACTION_DARNASSUS = 79
};
+
struct TRINITY_DLL_DECL npc_mistAI : public FollowerAI
{
npc_mistAI(Creature* pCreature) : FollowerAI(pCreature) { }
+
void Reset() { }
+
void MoveInLineOfSight(Unit *pWho)
{
FollowerAI::MoveInLineOfSight(pWho);
+
if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_ARYNIA)
{
if (m_creature->IsWithinDistInMap(pWho, 10.0f))
@@ -51,29 +60,36 @@ struct TRINITY_DLL_DECL npc_mistAI : public FollowerAI
}
}
}
+
void DoComplete()
{
DoScriptText(EMOTE_AT_HOME, m_creature);
+
if (Player* pPlayer = GetLeaderForFollower())
{
if (pPlayer->GetQuestStatus(QUEST_MIST) == QUEST_STATUS_INCOMPLETE)
pPlayer->GroupEventHappens(QUEST_MIST, m_creature);
}
+
//The follow is over (and for later development, run off to the woods before really end)
SetFollowComplete();
}
+
//call not needed here, no known abilities
/*void UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}*/
};
+
CreatureAI* GetAI_npc_mist(Creature* pCreature)
{
return new npc_mistAI(pCreature);
}
+
bool QuestAccept_npc_mist(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
if (pQuest->GetQuestId() == QUEST_MIST)
@@ -81,11 +97,14 @@ bool QuestAccept_npc_mist(Player* pPlayer, Creature* pCreature, Quest const* pQu
if (npc_mistAI* pMistAI = CAST_AI(npc_mistAI, pCreature->AI()))
pMistAI->StartFollow(pPlayer, FACTION_DARNASSUS, pQuest);
}
+
return true;
}
+
void AddSC_teldrassil()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_mist";
newscript->GetAI = &GetAI_npc_mist;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp
index 9619a4398dd..d8ce45872e5 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_bug_trio.cpp
@@ -13,45 +13,58 @@
* along 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 TRINITY_DLL_DECL boss_kriAI : public ScriptedAI
{
boss_kriAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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 EnterCombat(Unit *who)
{
}
+
void JustDied(Unit* killer)
{
if (pInstance)
@@ -59,6 +72,7 @@ struct TRINITY_DLL_DECL boss_kriAI : public ScriptedAI
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);
}
}
@@ -67,23 +81,27 @@ struct TRINITY_DLL_DECL boss_kriAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
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.
@@ -97,27 +115,35 @@ struct TRINITY_DLL_DECL boss_kriAI : public ScriptedAI
Check_Timer = 2000;
}else Check_Timer -=diff;
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_vemAI : public ScriptedAI
{
boss_vemAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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)
@@ -129,14 +155,17 @@ struct TRINITY_DLL_DECL boss_vemAI : public ScriptedAI
pInstance->SetData(DATA_BUG_TRIO_DEATH, 1);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Charge_Timer
if (Charge_Timer < diff)
{
@@ -148,8 +177,10 @@ struct TRINITY_DLL_DECL boss_vemAI : public ScriptedAI
//m_creature->SendMonsterMove(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0, true,1);
AttackStart(target);
}
+
Charge_Timer = 8000 + rand()%8000;
}else Charge_Timer -= diff;
+
//KnockBack_Timer
if (KnockBack_Timer < diff)
{
@@ -158,33 +189,42 @@ struct TRINITY_DLL_DECL boss_vemAI : public ScriptedAI
DoModifyThreatPercent(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 TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
{
boss_yaujAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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)
@@ -194,7 +234,8 @@ struct TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
pInstance->SetData(DATA_BUG_TRIO_DEATH, 1);
}
- for (uint8 i = 0; i < 10; ++i)
+
+ for(uint8 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);
@@ -202,14 +243,17 @@ struct TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
(Summoned->AI())->AttackStart(target);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Fear_Timer
if (Fear_Timer < diff)
{
@@ -217,6 +261,7 @@ struct TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
DoResetThreat();
Fear_Timer = 20000;
}else Fear_Timer -= diff;
+
//Casting Heal to other twins or herself.
if (Heal_Timer < diff)
{
@@ -224,6 +269,7 @@ struct TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
{
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:
@@ -239,8 +285,10 @@ struct TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
break;
}
}
+
Heal_Timer = 15000+rand()%15000;
}else Heal_Timer -= diff;
+
//Checking if Vem is dead. If yes we will enrage.
if (Check_Timer < diff)
{
@@ -257,21 +305,26 @@ struct TRINITY_DLL_DECL boss_yaujAI : public ScriptedAI
}
Check_Timer = 2000;
}else Check_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_yauj(Creature* pCreature)
{
return new boss_yaujAI (pCreature);
}
+
CreatureAI* GetAI_boss_vem(Creature* pCreature)
{
return new boss_vemAI (pCreature);
}
+
CreatureAI* GetAI_boss_kri(Creature* pCreature)
{
return new boss_kriAI (pCreature);
}
+
void AddSC_bug_trio()
{
Script *newscript;
@@ -279,10 +332,12 @@ void AddSC_bug_trio()
newscript->Name = "boss_kri";
newscript->GetAI = &GetAI_boss_kri;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_vem";
newscript->GetAI = &GetAI_boss_vem;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_yauj";
newscript->GetAI = &GetAI_boss_yauj;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp
index a758c1ff1ea..541d16b7f2b 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_cthun.cpp
@@ -13,98 +13,128 @@
* along 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"
+
//Text emote
#define EMOTE_WEAKENED -1531011
+
#define PI 3.14
+
//****** Out of Combat ******
//Random Wispers - No txt only sound
#define RANDOM_SOUND_WHISPER 8663
+
//***** 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
+
//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 TRINITY_DLL_DECL flesh_tentacleAI : public ScriptedAI
{
flesh_tentacleAI(Creature *c) : ScriptedAI(c), Parent(0)
{
SetCombatMovement(false);
}
+
uint64 Parent;
uint32 CheckTimer;
+
void SpawnedByCthun(uint64 p)
{
Parent = p;
}
+
void Reset()
{
CheckTimer = 1000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff);
+
void JustDied(Unit* killer);
};
+
struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
{
eye_of_cthunAI(Creature *c) : Scripted_NoMovementAI(c)
@@ -113,56 +143,70 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
if (!pInst)
error_log("TSCR: No Instance eye_of_cthunAI");
}
+
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 EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void SpawnEyeTentacle(float x, float y)
{
if (Creature* Spawned = me->SummonCreature(MOB_EYE_TENTACLE,m_creature->GetPositionX()+x,m_creature->GetPositionY()+y,m_creature->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,500))
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
Spawned->AI()->AttackStart(target);
}
+
void UpdateAI(const uint32 diff)
{
//Check if we have a target
if (!UpdateVictim())
return;
+
//No instance
if (!pInst)
return;
+
switch (pInst->GetData(DATA_CTHUN_PHASE))
{
case 0:
@@ -177,12 +221,15 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
{
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)
{
@@ -191,14 +238,18 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
if (target)
{
Creature* Spawned = NULL;
+
//Spawn claw tentacle on the random target
Spawned = me->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)
{
@@ -207,39 +258,50 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
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
+
//Darkbeam for 35 seconds
PhaseTimer = 35000;
}else PhaseTimer -= diff;
+
}
break;
case 1:
@@ -250,37 +312,49 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
{
//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(m_creature, 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->SetUInt32Value(UNIT_FIELD_FLAGS, 0);
+
//Eye Beam for 50 seconds
PhaseTimer = 50000;
} else PhaseTimer -= diff;
}
break;
+
//Transition phase
case 2:
{
@@ -288,6 +362,7 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0);
m_creature->SetHealth(0);
}
+
//Dead phase
case 5:
{
@@ -295,11 +370,13 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
}
}
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
//No instance
if (!pInst)
return;
+
switch (pInst->GetData(DATA_CTHUN_PHASE))
{
case 0:
@@ -308,27 +385,36 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
//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->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
@@ -339,23 +425,31 @@ struct TRINITY_DLL_DECL eye_of_cthunAI : public Scripted_NoMovementAI
}
}
};
+
struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
{
cthunAI(Creature *c) : ScriptedAI(c)
{
SetCombatMovement(false);
+
pInst = c->GetInstanceData();
if (!pInst)
error_log("TSCR: No Instance eye_of_cthunAI");
}
+
ScriptedInstance* pInst;
+
//Out of combat whisper timer
uint32 WisperTimer;
+
//Global variables
uint32 PhaseTimer;
+
//-------------------
+
//Phase transition
uint64 HoldPlayer;
+
//Body Phase
uint32 EyeTentacleTimer;
uint8 FleshTentaclesKilled;
@@ -365,16 +459,21 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
uint32 StomachEnterTimer;
uint32 StomachEnterVisTimer;
uint64 StomachEnterTarget;
+
//Stomach map, bool = true then in stomach
UNORDERED_MAP<uint64, bool> 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;
@@ -384,18 +483,23 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
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 EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void SpawnEyeTentacle(float x, float y)
{
Creature* Spawned;
@@ -403,36 +507,49 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
if (Spawned)
{
Unit* target;
+
target = SelectRandomNotStomach();
+
if (target)
Spawned->AI()->AttackStart(target);
}
}
+
Unit* SelectRandomNotStomach()
{
if (Stomach_Map.empty())
return NULL;
+
UNORDERED_MAP<uint64, bool>::iterator i = Stomach_Map.begin();
+
std::list<Unit*> temp;
std::list<Unit*>::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
@@ -444,25 +561,32 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
{
Map* pMap = m_creature->GetMap();
if (!pMap->IsDungeon()) return;
+
//Play random sound to the zone
Map::PlayerList const &PlayerList = pMap->GetPlayers();
+
if (!PlayerList.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
{
if (Player* pPlr = itr->getSource())
pPlr->PlayDirectSound(RANDOM_SOUND_WHISPER,pPlr);
}
}
+
//One random wisper every 90 - 300 seconds
WisperTimer = urand(90000,300000);
} else WisperTimer -= diff;
+
return;
}
+
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0);
+
//No instance
if (!pInst)
return;
+
switch (pInst->GetData(DATA_CTHUN_PHASE))
{
//Transition phase
@@ -473,109 +597,143 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
{
//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, HoldpPlayer));
DoZoneInCombat();
+
//Place all units in threat list on outside of stomach
Stomach_Map.clear();
+
std::list<HostilReference*>::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 = me->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS1_X, TENTACLE_POS1_Y, TENTACLE_POS1_Z, TENTACLE_POS1_O, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (!Spawned)
FleshTentaclesKilled++;
else
CAST_AI(flesh_tentacleAI, (Spawned->AI()))->SpawnedByCthun(m_creature->GetGUID());
+
//Spawn flesh tentacle
Spawned = me->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS2_X, TENTACLE_POS2_Y, TENTACLE_POS2_Z, TENTACLE_POS2_O, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (!Spawned)
FleshTentaclesKilled++;
else
CAST_AI(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);
+
DoScriptText(EMOTE_WEAKENED, m_creature);
PhaseTimer = 45000;
+
DoCast(m_creature, SPELL_RED_COLORATION, true);
+
UNORDERED_MAP<uint64, bool>::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
UNORDERED_MAP<uint64, bool>::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->IsWithinDist3d(KICK_X, KICK_Y, KICK_Z, 15.0f))
{
//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
@@ -585,20 +743,25 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
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)
{
@@ -607,14 +770,18 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
if (target)
{
Creature* Spawned = NULL;
+
//Spawn claw tentacle on the random target
Spawned = me->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)
{
@@ -622,15 +789,20 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
target = SelectRandomNotStomach();
if (target)
{
+
Creature* Spawned = NULL;
+
//Spawn claw tentacle on the random target
Spawned = me->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)
{
@@ -639,14 +811,18 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
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:
{
@@ -655,39 +831,50 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
{
//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 = me->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS1_X, TENTACLE_POS1_Y, TENTACLE_POS1_Z, TENTACLE_POS1_O, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (!Spawned)
++FleshTentaclesKilled;
else
CAST_AI(flesh_tentacleAI, (Spawned->AI()))->SpawnedByCthun(m_creature->GetGUID());
+
//Spawn flesh tentacle
Spawned = me->SummonCreature(MOB_FLESH_TENTACLE, TENTACLE_POS2_X, TENTACLE_POS2_Y, TENTACLE_POS2_Z, TENTACLE_POS2_O, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (!Spawned)
++FleshTentaclesKilled;
else
CAST_AI(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:
@@ -695,93 +882,114 @@ struct TRINITY_DLL_DECL cthunAI : public ScriptedAI
//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 TRINITY_DLL_DECL eye_tentacleAI : public ScriptedAI
{
eye_tentacleAI(Creature *c) : ScriptedAI(c)
{
SetCombatMovement(false);
+
if (Unit* pPortal = m_creature->SummonCreature(MOB_SMALL_PORTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0))
Portal = pPortal->GetGUID();
}
+
uint32 MindflayTimer;
uint32 KillSelfTimer;
uint64 Portal;
+
void JustDied(Unit* who)
{
if (Unit* p = Unit::GetUnit(*m_creature, Portal))
p->Kill(p);
}
+
void Reset()
{
//Mind flay half a second after we spawn
MindflayTimer = 500;
+
//This prevents eyes from overlapping
KillSelfTimer = 35000;
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
//Check if we have a target
if (!UpdateVictim())
return;
+
//KillSelfTimer
if (KillSelfTimer < diff)
{
m_creature->Kill(m_creature);
return;
} else KillSelfTimer -= diff;
+
//MindflayTimer
if (MindflayTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (target && !target->HasAura(SPELL_DIGESTIVE_ACID))
DoCast(target,SPELL_MIND_FLAY);
+
//Mindflay every 10 seconds
MindflayTimer = 10000;
} else MindflayTimer -= diff;
}
};
+
struct TRINITY_DLL_DECL claw_tentacleAI : public ScriptedAI
{
claw_tentacleAI(Creature *c) : ScriptedAI(c)
{
SetCombatMovement(false);
+
if (Unit* pPortal = m_creature->SummonCreature(MOB_SMALL_PORTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0))
Portal = pPortal->GetGUID();
}
+
uint32 GroundRuptureTimer;
uint32 HamstringTimer;
uint32 EvadeTimer;
uint64 Portal;
+
void JustDied(Unit* who)
{
if (Unit* p = Unit::GetUnit(*m_creature, Portal))
p->Kill(p);
}
+
void Reset()
{
//First rupture should happen half a second after we spawn
@@ -789,74 +997,90 @@ struct TRINITY_DLL_DECL claw_tentacleAI : public ScriptedAI
HamstringTimer = 2000;
EvadeTimer = 5000;
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
//Check if we have a target
if (!UpdateVictim())
return;
+
//EvadeTimer
if (!m_creature->IsWithinMeleeRange(m_creature->getVictim()))
if (EvadeTimer < diff)
{
if (Unit* p = Unit::GetUnit(*m_creature, Portal))
p->Kill(p);
+
//Dissapear and reappear at new position
m_creature->SetVisibility(VISIBILITY_OFF);
+
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (!target)
{
m_creature->Kill(m_creature);
return;
}
+
if (!target->HasAura(SPELL_DIGESTIVE_ACID))
{
m_creature->GetMap()->CreatureRelocation(m_creature, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
if (Unit* pPortal = m_creature->SummonCreature(MOB_SMALL_PORTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0))
Portal = pPortal->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 TRINITY_DLL_DECL giant_claw_tentacleAI : public ScriptedAI
{
giant_claw_tentacleAI(Creature *c) : ScriptedAI(c)
{
SetCombatMovement(false);
+
if (Unit* pPortal = m_creature->SummonCreature(MOB_GIANT_PORTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0))
Portal = pPortal->GetGUID();
}
+
uint32 GroundRuptureTimer;
uint32 ThrashTimer;
uint32 HamstringTimer;
uint32 EvadeTimer;
uint64 Portal;
+
void JustDied(Unit* who)
{
if (Unit* p = Unit::GetUnit(*m_creature, Portal))
p->Kill(p);
}
+
void Reset()
{
//First rupture should happen half a second after we spawn
@@ -865,123 +1089,151 @@ struct TRINITY_DLL_DECL giant_claw_tentacleAI : public ScriptedAI
ThrashTimer = 5000;
EvadeTimer = 5000;
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
//Check if we have a target
if (!UpdateVictim())
return;
+
//EvadeTimer
if (!m_creature->IsWithinMeleeRange(m_creature->getVictim()))
if (EvadeTimer < diff)
{
if (Unit* p = Unit::GetUnit(*m_creature, Portal))
p->Kill(p);
+
//Dissapear and reappear at new position
m_creature->SetVisibility(VISIBILITY_OFF);
+
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (!target)
{
m_creature->Kill(m_creature);
return;
}
+
if (!target->HasAura(SPELL_DIGESTIVE_ACID))
{
m_creature->GetMap()->CreatureRelocation(m_creature, target->GetPositionX(), target->GetPositionY(), target->GetPositionZ(), 0);
if (Unit* pPortal = m_creature->SummonCreature(MOB_GIANT_PORTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0))
Portal = pPortal->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 TRINITY_DLL_DECL giant_eye_tentacleAI : public ScriptedAI
{
giant_eye_tentacleAI(Creature *c) : ScriptedAI(c)
{
SetCombatMovement(false);
+
if (Unit* pPortal = m_creature->SummonCreature(MOB_GIANT_PORTAL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_CORPSE_DESPAWN, 0))
Portal = pPortal->GetGUID();
}
+
uint32 BeamTimer;
uint64 Portal;
+
void JustDied(Unit* who)
{
if (Unit* p = Unit::GetUnit(*m_creature, Portal))
p->Kill(p);
}
+
void Reset()
{
//Green Beam half a second after we spawn
BeamTimer = 500;
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
//Check if we have a target
if (!UpdateVictim())
return;
+
//BeamTimer
if (BeamTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
if (target && !target->HasAura(SPELL_DIGESTIVE_ACID))
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 (!UpdateVictim())
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)
@@ -989,68 +1241,85 @@ void flesh_tentacleAI::JustDied(Unit* killer)
error_log("TSCR: flesh_tentacle: No Parent variable");
return;
}
+
Creature* Cthun = Unit::GetCreature(*m_creature, Parent);
+
if (Cthun)
CAST_AI(cthunAI, (Cthun->AI()))->FleshTentcleKilled();
else error_log("TSCR: flesh_tentacle: No Cthun");
}
+
//GetAIs
CreatureAI* GetAI_eye_of_cthun(Creature* pCreature)
{
return new eye_of_cthunAI (pCreature);
}
+
CreatureAI* GetAI_cthun(Creature* pCreature)
{
return new cthunAI (pCreature);
}
+
CreatureAI* GetAI_eye_tentacle(Creature* pCreature)
{
return new eye_tentacleAI (pCreature);
}
+
CreatureAI* GetAI_claw_tentacle(Creature* pCreature)
{
return new claw_tentacleAI (pCreature);
}
+
CreatureAI* GetAI_giant_claw_tentacle(Creature* pCreature)
{
return new giant_claw_tentacleAI (pCreature);
}
+
CreatureAI* GetAI_giant_eye_tentacle(Creature* pCreature)
{
return new giant_eye_tentacleAI (pCreature);
}
+
CreatureAI* GetAI_flesh_tentacle(Creature* pCreature)
{
return new flesh_tentacleAI (pCreature);
}
+
void AddSC_boss_cthun()
{
Script *newscript;
+
//Eye
newscript = new Script;
newscript->Name = "boss_eye_of_cthun";
newscript->GetAI = &GetAI_eye_of_cthun;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_cthun";
newscript->GetAI = &GetAI_cthun;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_eye_tentacle";
newscript->GetAI = &GetAI_eye_tentacle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_claw_tentacle";
newscript->GetAI = &GetAI_claw_tentacle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_giant_claw_tentacle";
newscript->GetAI = &GetAI_giant_claw_tentacle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_giant_eye_tentacle";
newscript->GetAI = &GetAI_giant_eye_tentacle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_giant_flesh_tentacle";
newscript->GetAI = &GetAI_flesh_tentacle;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp
index 3ee6d881994..2dce14cb9ec 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_fankriss.cpp
@@ -13,43 +13,54 @@
* along 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 TRINITY_DLL_DECL boss_fankrissAI : public ScriptedAI
{
boss_fankrissAI(Creature *c) : ScriptedAI(c) {}
+
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)
{
if (!victim)
return;
+
Rand = 10 + (rand()%10);
switch (rand()%2)
{
@@ -68,20 +79,24 @@ struct TRINITY_DLL_DECL boss_fankrissAI : public ScriptedAI
if (Spawn)
(Spawn->AI())->AttackStart(victim);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -102,6 +117,7 @@ struct TRINITY_DLL_DECL boss_fankrissAI : public ScriptedAI
}
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)
@@ -113,8 +129,10 @@ struct TRINITY_DLL_DECL boss_fankrissAI : public ScriptedAI
if (target && target->GetTypeId() == TYPEID_PLAYER)
{
DoCast(target, SPELL_ROOT);
+
if (DoGetThreat(target))
DoModifyThreatPercent(target, -100);
+
switch(rand()%3)
{
case 0:
@@ -167,13 +185,16 @@ struct TRINITY_DLL_DECL boss_fankrissAI : public ScriptedAI
SpawnHatchlings_Timer = 45000 + rand()%15000;
}else SpawnHatchlings_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_fankriss(Creature* pCreature)
{
return new boss_fankrissAI (pCreature);
}
+
void AddSC_boss_fankriss()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp
index 83b189f4b4f..9df6912a3df 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_huhuran.cpp
@@ -13,32 +13,40 @@
* along 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 EMOTE_GENERIC_FRENZY_KILL -1000001
#define EMOTE_GENERIC_BERSERK -1000004
+
#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 TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
{
boss_huhuranAI(Creature *c) : ScriptedAI(c) {}
+
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;
@@ -47,17 +55,21 @@ struct TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
PoisonBolt_Timer = 4000;
NoxiousPoison_Timer = 10000 + rand()%10000;
FrenzyBack_Timer = 15000;
+
Frenzy = false;
Berserk = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Frenzy_Timer
if (!Frenzy && Frenzy_Timer < diff)
{
@@ -67,6 +79,7 @@ struct TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
PoisonBolt_Timer = 3000;
Frenzy_Timer = 25000 + rand()%10000;
}else Frenzy_Timer -= diff;
+
// Wyvern Timer
if (Wyvern_Timer < diff)
{
@@ -74,18 +87,21 @@ struct TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
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)
{
@@ -95,6 +111,7 @@ struct TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
PoisonBolt_Timer = 3000;
}else PoisonBolt_Timer -= diff;
}
+
//FrenzyBack_Timer
if (Frenzy && FrenzyBack_Timer < diff)
{
@@ -102,6 +119,7 @@ struct TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
Frenzy = false;
FrenzyBack_Timer = 15000;
}else FrenzyBack_Timer -= diff;
+
if (!Berserk && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 31)
{
m_creature->InterruptNonMeleeSpells(false);
@@ -109,13 +127,16 @@ struct TRINITY_DLL_DECL boss_huhuranAI : public ScriptedAI
DoCast(m_creature, SPELL_BERSERK);
Berserk = true;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_huhuran(Creature* pCreature)
{
return new boss_huhuranAI (pCreature);
}
+
void AddSC_boss_huhuran()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp
index 079a5367903..13bf15fe66a 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_ouro.cpp
@@ -13,30 +13,38 @@
* along 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 TRINITY_DLL_DECL boss_ouroAI : public ScriptedAI
{
boss_ouroAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Sweep_Timer;
uint32 SandBlast_Timer;
uint32 Submerge_Timer;
uint32 Back_Timer;
uint32 ChangeTarget_Timer;
uint32 Spawn_Timer;
+
bool Enrage;
bool Submerged;
+
void Reset()
{
Sweep_Timer = 5000 + rand()%5000;
@@ -45,30 +53,36 @@ struct TRINITY_DLL_DECL boss_ouroAI : public ScriptedAI
Back_Timer = 30000 + rand()%15000;
ChangeTarget_Timer = 5000 + rand()%3000;
Spawn_Timer = 10000 + rand()%10000;
+
Enrage = false;
Submerged = false;
}
+
void EnterCombat(Unit *who)
{
DoCast(m_creature->getVictim(), SPELL_BIRTH);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -77,34 +91,44 @@ struct TRINITY_DLL_DECL boss_ouroAI : public ScriptedAI
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);
+
if (target)
DoTeleportTo(target->GetPositionX(), target->GetPositionY(), target->GetPositionZ());
+
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* pCreature)
{
return new boss_ouroAI (pCreature);
}
+
void AddSC_boss_ouro()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp
index 1e6dde30323..0494cc2fdd1 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_sartura.cpp
@@ -13,36 +13,45 @@
* along 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: 95
SDComment:
SDCategory: Temple of Ahn'Qiraj
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO -1531008
#define SAY_SLAY -1531009
#define SAY_DEATH -1531010
+
#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 TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
{
boss_sarturaAI(Creature *c) : ScriptedAI(c) {}
+
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;
@@ -51,28 +60,35 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
AggroReset_Timer = 45000 + rand()%10000;
AggroResetEnd_Timer = 5000;
EnrageHard_Timer = 10*60000;
+
WhirlWind = false;
AggroReset = false;
Enraged = false;
EnragedHard = false;
+
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_SLAY, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (WhirlWind)
{
if (WhirlWindRandom_Timer < diff)
@@ -84,14 +100,17 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
m_creature->AddThreat(target, 1.0f);
m_creature->TauntApply(target);
AttackStart(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)
@@ -100,6 +119,7 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
WhirlWind = true;
WhirlWindEnd_Timer = 15000;
}else WhirlWind_Timer -= diff;
+
if (AggroReset_Timer < diff)
{
//Attack random Gamers
@@ -109,9 +129,11 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
m_creature->AddThreat(target, 1.0f);
m_creature->TauntApply(target);
AttackStart(target);
+
AggroReset = true;
AggroReset_Timer = 2000 + rand()%3000;
}else AggroReset_Timer -= diff;
+
if (AggroReset)
{
if (AggroResetEnd_Timer <diff)
@@ -121,6 +143,7 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
AggroReset_Timer = 35000 + rand()%10000;
} else AggroResetEnd_Timer -= diff;
}
+
//If she is 20% enrage
if (!Enraged)
{
@@ -130,6 +153,7 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
Enraged = true;
}
}
+
//After 10 minutes hard enrage
if (!EnragedHard)
{
@@ -139,21 +163,26 @@ struct TRINITY_DLL_DECL boss_sarturaAI : public ScriptedAI
EnragedHard = true;
} else EnrageHard_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
}
};
+
struct TRINITY_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI
{
mob_sartura_royal_guardAI(Creature *c) : ScriptedAI(c) {}
+
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;
@@ -162,17 +191,21 @@ struct TRINITY_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI
AggroReset_Timer = 45000 + rand()%10000;
AggroResetEnd_Timer = 5000;
KnockBack_Timer = 10000;
+
WhirlWind = false;
AggroReset = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (!WhirlWind && WhirlWind_Timer < diff)
{
DoCast(m_creature, SPELL_WHIRLWINDADD);
@@ -180,6 +213,7 @@ struct TRINITY_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI
WhirlWind_Timer = 25000 + rand()%15000;
WhirlWindEnd_Timer = 15000;
}else WhirlWind_Timer -= diff;
+
if (WhirlWind)
{
if (WhirlWindRandom_Timer < diff)
@@ -191,13 +225,16 @@ struct TRINITY_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI
m_creature->AddThreat(target, 1.0f);
m_creature->TauntApply(target);
AttackStart(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)
@@ -209,15 +246,18 @@ struct TRINITY_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI
m_creature->AddThreat(target, 1.0f);
m_creature->TauntApply(target);
AttackStart(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 <diff)
@@ -227,17 +267,21 @@ struct TRINITY_DLL_DECL mob_sartura_royal_guardAI : public ScriptedAI
AggroReset_Timer = 30000 + rand()%10000;
} else AggroResetEnd_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_sartura(Creature* pCreature)
{
return new boss_sarturaAI (pCreature);
}
+
CreatureAI* GetAI_mob_sartura_royal_guard(Creature* pCreature)
{
return new mob_sartura_royal_guardAI (pCreature);
}
+
void AddSC_boss_sartura()
{
Script *newscript;
@@ -245,6 +289,7 @@ void AddSC_boss_sartura()
newscript->Name = "boss_sartura";
newscript->GetAI = &GetAI_boss_sartura;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_sartura_royal_guard";
newscript->GetAI = &GetAI_mob_sartura_royal_guard;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp
index 8ec42dee092..1c988387993 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_skeram.cpp
@@ -13,15 +13,18 @@
* along 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 SAY_AGGRO1 -1531000
#define SAY_AGGRO2 -1531001
#define SAY_AGGRO3 -1531002
@@ -30,10 +33,12 @@ EndScriptData */
#define SAY_SLAY3 -1531005
#define SAY_SPLIT -1531006
#define SAY_DEATH -1531007
+
#define SPELL_ARCANE_EXPLOSION 25679
#define SPELL_EARTH_SHOCK 26194
#define SPELL_TRUE_FULFILLMENT4 26526
#define SPELL_BLINK 28391
+
class ov_mycoordinates
{
public:
@@ -43,23 +48,28 @@ class ov_mycoordinates
x = cx; y = cy; z = cz; r = cr;
}
};
+
struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
{
boss_skeramAI(Creature *c) : ScriptedAI(c)
{
IsImage = false;
}
+
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;
@@ -67,41 +77,50 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
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)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit* Killer)
{
if (!IsImage)
DoScriptText(SAY_DEATH, m_creature);
}
+
void EnterCombat(Unit *who)
{
if (IsImage || Images75)
return;
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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->IsWithinMeleeRange(m_creature->getVictim()))
{
@@ -120,6 +139,7 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
EarthShock_Timer = 1000;
}else EarthShock_Timer -= diff;
}
+
//Blink_Timer
if (Blink_Timer < diff)
{
@@ -140,19 +160,25 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
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)
{
@@ -161,19 +187,25 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
//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 */)
{
DoScriptText(SAY_SPLIT, m_creature);
+
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:
@@ -192,6 +224,7 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
i2=place2;
break;
}
+
for (int tryi = 0; tryi < 41; tryi ++)
{
Unit *targetpl = SelectUnit(SELECT_TARGET_RANDOM, 0);
@@ -209,6 +242,7 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
break;
}
}
+
m_creature->RemoveAllAuras();
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetVisibility(VISIBILITY_OFF);
@@ -219,13 +253,16 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
delete place3;
DoResetThreat();
DoStopAttack();
+
switch (atPercent)
{
case 75: Images75 = true; break;
case 50: Images50 = true; break;
case 25: Images25 = true; break;
}
+
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
+
Image1 = m_creature->SummonCreature(15263, i1->x, i1->y, i1->z, i1->r, TEMPSUMMON_CORPSE_DESPAWN, 30000);
if (Image1)
{
@@ -235,6 +272,7 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
Image1->AI()->AttackStart(target);
CAST_AI(boss_skeramAI, Image1->AI())->IsImage = true;
}
+
Image2 = m_creature->SummonCreature(15263,i2->x, i2->y, i2->z, i2->r, TEMPSUMMON_CORPSE_DESPAWN, 30000);
if (Image2)
{
@@ -246,11 +284,14 @@ struct TRINITY_DLL_DECL boss_skeramAI : public ScriptedAI
}
Invisible = true;
}
+
};
+
CreatureAI* GetAI_boss_skeram(Creature* pCreature)
{
return new boss_skeramAI (pCreature);
}
+
void AddSC_boss_skeram()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp
index a477c30950d..72dfcdb08f3 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_twinemperors.cpp
@@ -13,45 +13,59 @@
* along 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 TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
{
boss_twinemperorsAI(Creature *c): ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 Heal_Timer;
uint32 Teleport_Timer;
bool AfterTeleport;
@@ -60,9 +74,11 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
uint32 Abuse_Bug_Timer, BugsTimer;
bool tspellcasted;
uint32 EnrageTimer;
+
virtual bool IAmVeklor() = 0;
virtual void Reset() = 0;
virtual void CastSpellOnBug(Creature *target) = 0;
+
void TwinReset()
{
Heal_Timer = 0; // first heal immediately when they get close together
@@ -76,6 +92,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
DontYellWhenDead = false;
EnrageTimer = 15*60000;
}
+
Creature *GetOtherBoss()
{
if (pInstance)
@@ -83,6 +100,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
else
return NULL;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
Unit *pOtherBoss = GetOtherBoss();
@@ -99,6 +117,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* Killer)
{
Creature *pOtherBoss = GetOtherBoss();
@@ -112,10 +131,12 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
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 EnterCombat(Unit *who)
{
DoZoneInCombat();
@@ -133,13 +154,16 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
}
}
}
+
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);
@@ -147,6 +171,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
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)
@@ -160,10 +185,12 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
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();
@@ -174,13 +201,17 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
}
} else Heal_Timer -= diff;
}
+
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)
{
@@ -189,14 +220,17 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
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();
CAST_AI(boss_twinemperorsAI, pOtherBoss->AI())->SetAfterTeleport();
}
}
+
void SetAfterTeleport()
{
m_creature->InterruptNonMeleeSpells(false);
@@ -208,6 +242,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
AfterTeleportTimer = 2000;
tspellcasted = false;
}
+
bool TryActivateAfterTTelep(uint32 diff)
{
if (AfterTeleport)
@@ -218,7 +253,9 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
DoCast(m_creature, SPELL_TWIN_TELEPORT);
m_creature->addUnitState(UNIT_STAT_STUNNED);
}
+
tspellcasted = true;
+
if (AfterTeleportTimer < diff)
{
AfterTeleport = false;
@@ -252,10 +289,12 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
return true;
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim())
return;
+
if (who->isTargetableForAttack() && who->isInAccessiblePlaceFor(m_creature) && m_creature->IsHostileTo(who))
{
float attackRadius = m_creature->GetAttackDistance(who);
@@ -269,15 +308,19 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
}
}
}
+
Creature *RespawnNearbyBugsAndGetOne()
{
std::list<Creature*> lUnitList;
m_creature->GetCreatureListWithEntryInGrid(lUnitList,15316,150.0f);
m_creature->GetCreatureListWithEntryInGrid(lUnitList,15317,150.0f);
+
if (lUnitList.empty())
return NULL;
+
Creature *nearb = NULL;
- for (std::list<Creature*>::iterator iter = lUnitList.begin(); iter != lUnitList.end(); ++iter)
+
+ for(std::list<Creature*>::iterator iter = lUnitList.begin(); iter != lUnitList.end(); ++iter)
{
Creature *c = *iter;
if (c)
@@ -297,6 +340,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
}
return nearb;
}
+
void HandleBugs(uint32 diff)
{
if (BugsTimer < diff || Abuse_Bug_Timer < diff)
@@ -326,6 +370,7 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
Abuse_Bug_Timer -= diff;
}
}
+
void CheckEnrage(uint32 diff)
{
if (EnrageTimer < diff)
@@ -338,26 +383,32 @@ struct TRINITY_DLL_DECL boss_twinemperorsAI : public ScriptedAI
} else EnrageTimer-=diff;
}
};
+
struct TRINITY_DLL_DECL boss_veknilashAI : public boss_twinemperorsAI
{
bool IAmVeklor() {return false;}
boss_veknilashAI(Creature *c) : boss_twinemperorsAI(c) {}
+
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);
@@ -373,19 +424,23 @@ struct TRINITY_DLL_DECL boss_veknilashAI : public boss_twinemperorsAI
target->AddAura(new Aura(spell, eff_mask, target, target, target));
target->SetHealth(target->GetMaxHealth());
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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 = SelectTarget(SELECT_TARGET_RANDOM, 0, NOMINAL_MELEE_RANGE, true);
@@ -393,22 +448,29 @@ struct TRINITY_DLL_DECL boss_veknilashAI : public boss_twinemperorsAI
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 TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
{
bool IAmVeklor() {return true;}
boss_veklorAI(Creature *c) : boss_twinemperorsAI(c) {}
+
uint32 ShadowBolt_Timer;
uint32 Blizzard_Timer;
uint32 ArcaneBurst_Timer;
@@ -416,7 +478,9 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
int Rand;
int RandX;
int RandY;
+
Creature* Summoned;
+
void Reset()
{
TwinReset();
@@ -424,11 +488,13 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
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);
@@ -443,11 +509,13 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
target->AddAura(new Aura(spell, eff_mask, target, target, target));
target->SetHealth(target->GetMaxHealth());
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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
@@ -455,6 +523,7 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
ArcaneBurst_Timer = 5000;
if (!TryActivateAfterTTelep(diff))
return;
+
//ShadowBolt_Timer
if (ShadowBolt_Timer < diff)
{
@@ -464,6 +533,7 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
DoCast(m_creature->getVictim(),SPELL_SHADOWBOLT);
ShadowBolt_Timer = 2000;
}else ShadowBolt_Timer -= diff;
+
//Blizzard_Timer
if (Blizzard_Timer < diff)
{
@@ -473,6 +543,7 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
DoCast(target,SPELL_BLIZZARD);
Blizzard_Timer = 15000+rand()%15000;
}else Blizzard_Timer -= diff;
+
if (ArcaneBurst_Timer < diff)
{
Unit *mvic;
@@ -482,22 +553,29 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
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
@@ -509,21 +587,26 @@ struct TRINITY_DLL_DECL boss_veklorAI : public boss_twinemperorsAI
}
}
};
+
CreatureAI* GetAI_boss_veknilash(Creature* pCreature)
{
return new boss_veknilashAI (pCreature);
}
+
CreatureAI* GetAI_boss_veklor(Creature* pCreature)
{
return new boss_veklorAI (pCreature);
}
+
void AddSC_boss_twinemperors()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_veknilash";
newscript->GetAI = &GetAI_boss_veknilash;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_veklor";
newscript->GetAI = &GetAI_boss_veklor;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp
index 9b782f23644..2bb9f1bfb0b 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/boss_viscidus.cpp
@@ -13,14 +13,18 @@
* along 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/kalimdor/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h
index 2d41894e32d..5d545ed7c74 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/def_temple_of_ahnqiraj.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_TEMPLE_OF_AHNQIRAJ_H
#define DEF_TEMPLE_OF_AHNQIRAJ_H
+
#define DATA_SKERAM 1
#define DATA_KRI 2
#define DATA_VEM 3
@@ -15,6 +17,7 @@
#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/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp
index 75c2a218cbb..489a415cf81 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/instance_temple_of_ahnqiraj.cpp
@@ -13,40 +13,52 @@
* along 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 TRINITY_DLL_DECL instance_temple_of_ahnqiraj : public ScriptedInstance
{
instance_temple_of_ahnqiraj(Map* pMap) : ScriptedInstance(pMap) {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* pCreature, bool add)
{
switch (pCreature->GetEntry())
@@ -58,11 +70,13 @@ struct TRINITY_DLL_DECL instance_temple_of_ahnqiraj : public ScriptedInstance
case 15275: VeknilashGUID = pCreature->GetGUID(); break;
}
}
+
bool IsEncounterInProgress() const
{
//not active in AQ40
return false;
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -71,21 +85,26 @@ struct TRINITY_DLL_DECL instance_temple_of_ahnqiraj : public ScriptedInstance
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)
@@ -103,6 +122,7 @@ struct TRINITY_DLL_DECL instance_temple_of_ahnqiraj : public ScriptedInstance
}
return 0;
} // end GetData64
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -110,25 +130,31 @@ struct TRINITY_DLL_DECL instance_temple_of_ahnqiraj : public ScriptedInstance
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* pMap)
{
return new instance_temple_of_ahnqiraj(pMap);
}
+
void AddSC_instance_temple_of_ahnqiraj()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp
index a7814ea3ede..04e31d7eebd 100644
--- a/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/temple_of_ahnqiraj/mob_anubisath_sentinel.cpp
@@ -13,35 +13,46 @@
* along 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
+
struct TRINITY_DLL_DECL aqsentinelAI;
class TRINITY_DLL_DECL SentinelAbilityAura : public Aura
{
@@ -54,10 +65,12 @@ class TRINITY_DLL_DECL SentinelAbilityAura : public Aura
int32 currentBasePoints;
uint32 abilityId;
};
+
struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
{
uint32 ability;
int abselected;
+
void selectAbility(int asel)
{
switch (asel)
@@ -73,16 +86,20 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
case 8: ability = SPELL_STORM_BUFF;break;
}
}
+
aqsentinelAI(Creature *c) : ScriptedAI(c)
{
ClearBuddyList();
abselected = 0; // just initialization of variable
}
+
Creature *nearby[3];
+
void ClearBuddyList()
{
nearby[0] = nearby[1] = nearby[2] = NULL;
}
+
void AddBuddyToList(Creature *c)
{
if (c==m_creature)
@@ -98,6 +115,7 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
}
}
}
+
void GiveBuddyMyList(Creature *c)
{
aqsentinelAI *cai = CAST_AI(aqsentinelAI, (c)->AI());
@@ -106,12 +124,14 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
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)
@@ -128,15 +148,19 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
}
}
}
+
void AddSentinelsNear(Unit *nears)
{
std::list<Creature*> assistList;
m_creature->GetCreatureListWithEntryInGrid(assistList,15264,70.0f);
+
if (assistList.empty())
return;
- for (std::list<Creature*>::iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
+
+ for(std::list<Creature*>::iterator iter = assistList.begin(); iter != assistList.end(); ++iter)
AddBuddyToList((*iter));
}
+
int pickAbilityRandom(bool *chosenAbilities)
{
for (int t = 0; t < 2; ++t)
@@ -152,11 +176,13 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
}
return 0; // should never happen
}
+
void GetOtherSentinels(Unit *who)
{
bool *chosenAbilities = new bool[9];
memset(chosenAbilities, 0, 9*sizeof(bool));
selectAbility(pickAbilityRandom(chosenAbilities));
+
ClearBuddyList();
AddSentinelsNear(m_creature);
int bli;
@@ -172,9 +198,12 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
DoYell("I dont have enough buddies.", LANG_NEUTRAL, 0);*/
SendMyListToBuddies();
CallBuddiesToAttack(who);
+
delete[] chosenAbilities;
}
+
bool gatherOthersWhenAggro;
+
void Reset()
{
if (!m_creature->isDead())
@@ -190,6 +219,7 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
ClearBuddyList();
gatherOthersWhenAggro = true;
}
+
void GainSentinelAbility(uint32 id)
{
const SpellEntry *spell = GetSpellStore()->LookupEntry(id);
@@ -203,13 +233,16 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
SentinelAbilityAura *a = new SentinelAbilityAura(this, (SpellEntry*)spell, id, eff_mask);
m_creature->AddAura(a);
}
+
void EnterCombat(Unit *who)
{
if (gatherOthersWhenAggro)
GetOtherSentinels(who);
+
GainSentinelAbility(ability);
DoZoneInCombat();
}
+
void JustDied(Unit* who)
{
for (int ni=0; ni<3; ++ni)
@@ -226,10 +259,11 @@ struct TRINITY_DLL_DECL aqsentinelAI : public ScriptedAI
CAST_AI(aqsentinelAI, sent->AI())->GainSentinelAbility(ability);
}
}
+
Unit *GetHatedManaUser()
{
std::list<HostilReference*>::iterator i;
- for (i = m_creature->getThreatManager().getThreatList().begin(); i != m_creature->getThreatManager().getThreatList().end(); ++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)
@@ -242,6 +276,7 @@ CreatureAI* GetAI_mob_anubisath_sentinelAI(Creature* pCreature)
{
return new aqsentinelAI (pCreature);
}
+
void AddSC_mob_anubisath_sentinel()
{
Script *newscript;
@@ -250,6 +285,7 @@ void AddSC_mob_anubisath_sentinel()
newscript->GetAI = &GetAI_mob_anubisath_sentinelAI;
newscript->RegisterSelf();
}
+
SentinelAbilityAura::~SentinelAbilityAura() {}
Unit* SentinelAbilityAura::GetTriggerTarget() const
{
@@ -260,8 +296,10 @@ Unit* SentinelAbilityAura::GetTriggerTarget() const
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:
@@ -270,6 +308,7 @@ Unit* SentinelAbilityAura::GetTriggerTarget() const
return aOwner->m_creature;
}
}
+
SentinelAbilityAura::SentinelAbilityAura(aqsentinelAI *abilityOwner, SpellEntry *spell, uint32 ability, uint32 eff)
: Aura(spell, eff, abilityOwner->m_creature, abilityOwner->m_creature, abilityOwner->m_creature, NULL)
{
diff --git a/src/bindings/scripts/scripts/kalimdor/the_barrens.cpp b/src/bindings/scripts/scripts/kalimdor/the_barrens.cpp
index c3e984ab2f5..70e0b3e5e28 100644
--- a/src/bindings/scripts/scripts/kalimdor/the_barrens.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/the_barrens.cpp
@@ -13,12 +13,14 @@
* along 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: 863, 898, 1719, 2458, 4921, 6981,
SDCategory: Barrens
EndScriptData */
+
/* ContentData
npc_beaten_corpse
npc_gilthares
@@ -27,23 +29,30 @@ npc_taskmaster_fizzule
npc_twiggy_flathead
npc_wizzlecrank_shredder
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_beaten_corpse
######*/
+
#define GOSSIP_CORPSE "Examine corpse in detail..."
+
enum eQuests
{
QUEST_LOST_IN_BATTLE = 4921
};
+
bool GossipHello_npc_beaten_corpse(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_LOST_IN_BATTLE) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(QUEST_LOST_IN_BATTLE) == QUEST_STATUS_COMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_CORPSE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(3557, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_beaten_corpse(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF +1)
@@ -53,9 +62,11 @@ bool GossipSelect_npc_beaten_corpse(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*######
# npc_gilthares
######*/
+
enum eGilthares
{
SAY_GIL_START = -1000370,
@@ -69,19 +80,25 @@ enum eGilthares
SAY_GIL_ALMOST = -1000378,
SAY_GIL_SWEET = -1000379,
SAY_GIL_FREED = -1000380,
+
QUEST_FREE_FROM_HOLD = 898,
AREA_MERCHANT_COAST = 391,
FACTION_ESCORTEE = 232 //guessed, possible not needed for this quest
};
+
struct TRINITY_DLL_DECL npc_giltharesAI : public npc_escortAI
{
npc_giltharesAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void Reset() { }
+
void WaypointReached(uint32 uiPointId)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(uiPointId)
{
case 16:
@@ -105,11 +122,13 @@ struct TRINITY_DLL_DECL npc_giltharesAI : public npc_escortAI
break;
}
}
+
void Aggro(Unit* pWho)
{
//not always use
if (rand()%4)
return;
+
//only aggro text if not player and only in this area
if (pWho->GetTypeId() != TYPEID_PLAYER && m_creature->GetAreaId() == AREA_MERCHANT_COAST)
{
@@ -118,35 +137,45 @@ struct TRINITY_DLL_DECL npc_giltharesAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_npc_gilthares(Creature* pCreature)
{
return new npc_giltharesAI(pCreature);
}
+
bool QuestAccept_npc_gilthares(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_FREE_FROM_HOLD)
{
pCreature->setFaction(FACTION_ESCORTEE);
pCreature->SetStandState(UNIT_STAND_STATE_STAND);
+
DoScriptText(SAY_GIL_START, pCreature, pPlayer);
+
if (npc_giltharesAI* pEscortAI = CAST_AI(npc_giltharesAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return true;
}
+
/*######
## npc_sputtervalve
######*/
+
#define GOSSIP_SPUTTERVALVE "Can you tell me about this shard?"
+
bool GossipHello_npc_sputtervalve(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(6981) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SPUTTERVALVE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_sputtervalve(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -156,25 +185,30 @@ bool GossipSelect_npc_sputtervalve(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*######
## npc_taskmaster_fizzule
######*/
+
enum eEnums
{
FACTION_FRIENDLY_F = 35,
SPELL_FLARE = 10113,
SPELL_FOLLY = 10137,
};
+
struct TRINITY_DLL_DECL npc_taskmaster_fizzuleAI : public ScriptedAI
{
npc_taskmaster_fizzuleAI(Creature* c) : ScriptedAI(c)
{
factionNorm = c->getFaction();
}
+
uint32 factionNorm;
bool IsFriend;
uint32 Reset_Timer;
uint8 FlareCount;
+
void Reset()
{
IsFriend = false;
@@ -182,26 +216,33 @@ struct TRINITY_DLL_DECL npc_taskmaster_fizzuleAI : public ScriptedAI
FlareCount = 0;
m_creature->setFaction(factionNorm);
}
+
void DoFriend()
{
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
+
m_creature->StopMoving();
m_creature->GetMotionMaster()->MoveIdle();
+
m_creature->setFaction(FACTION_FRIENDLY_F);
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_SALUTE);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_FLARE || spell->Id == SPELL_FOLLY)
{
++FlareCount;
+
if (FlareCount >= 2)
IsFriend = true;
}
}
+
void EnterCombat(Unit* who) { }
+
void UpdateAI(const uint32 diff)
{
if (IsFriend)
@@ -212,10 +253,13 @@ struct TRINITY_DLL_DECL npc_taskmaster_fizzuleAI : public ScriptedAI
return;
} else Reset_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void ReceiveEmote(Player* pPlayer, uint32 emote)
{
if (emote == TEXTEMOTE_SALUTE)
@@ -224,18 +268,22 @@ struct TRINITY_DLL_DECL npc_taskmaster_fizzuleAI : public ScriptedAI
{
if (m_creature->getFaction() == FACTION_FRIENDLY_F)
return;
+
DoFriend();
}
}
}
};
+
CreatureAI* GetAI_npc_taskmaster_fizzule(Creature* pCreature)
{
return new npc_taskmaster_fizzuleAI(pCreature);
}
+
/*#####
## npc_twiggy_flathead
#####*/
+
#define BIG_WILL 6238
#define AFFRAY_CHALLENGER 6240
#define SAY_BIG_WILL_READY -1000267
@@ -243,6 +291,7 @@ CreatureAI* GetAI_npc_taskmaster_fizzule(Creature* pCreature)
#define SAY_TWIGGY_FLATHEAD_FRAY -1000269
#define SAY_TWIGGY_FLATHEAD_DOWN -1000270
#define SAY_TWIGGY_FLATHEAD_OVER -1000271
+
float AffrayChallengerLoc[6][4]=
{
{-1683, -4326, 2.79, 0},
@@ -252,9 +301,11 @@ float AffrayChallengerLoc[6][4]=
{-1674, -4326, 2.79, 3.49},
{-1677, -4334, 2.79, 1.66}
};
+
struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
{
npc_twiggy_flatheadAI(Creature *c) : ScriptedAI(c) {}
+
bool EventInProgress;
bool EventGrate;
bool EventBigWill;
@@ -265,6 +316,7 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
uint64 PlayerGUID;
uint64 AffrayChallenger[6];
uint64 BigWill;
+
void Reset()
{
EventInProgress = false;
@@ -274,37 +326,47 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
Challenger_checker = 0;
Wave = 0;
PlayerGUID = 0;
- for (uint8 i = 0; i < 6; ++i)
+
+ for(uint8 i = 0; i < 6; ++i)
{
AffrayChallenger[i] = 0;
Challenger_down[i] = false;
}
BigWill = 0;
}
+
void EnterCombat(Unit *who) { }
+
void MoveInLineOfSight(Unit *who)
{
if (!who || (!who->isAlive())) return;
+
if (m_creature->IsWithinDistInMap(who, 10.0f) && (who->GetTypeId() == TYPEID_PLAYER) && CAST_PLR(who)->GetQuestStatus(1719) == QUEST_STATUS_INCOMPLETE && !EventInProgress)
{
PlayerGUID = who->GetGUID();
EventInProgress = true;
}
}
+
void KilledUnit(Unit *victim) { }
+
void UpdateAI(const uint32 diff)
{
if (EventInProgress) {
Player* pWarrior = NULL;
+
if (PlayerGUID)
pWarrior = Unit::GetPlayer(PlayerGUID);
+
if (!pWarrior)
return;
+
if (!pWarrior->isAlive() && pWarrior->GetQuestStatus(1719) == QUEST_STATUS_INCOMPLETE) {
EventInProgress = false;
DoScriptText(SAY_TWIGGY_FLATHEAD_DOWN, m_creature);
pWarrior->FailQuest(1719);
- for (uint8 i = 0; i < 6; ++i)
+
+ for(uint8 i = 0; i < 6; ++i)
{
if (AffrayChallenger[i])
{
@@ -321,6 +383,7 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
AffrayChallenger[i] = 0;
Challenger_down[i] = false;
}
+
if (BigWill)
{
Creature* pCreature = Unit::GetCreature((*m_creature), BigWill);
@@ -334,14 +397,17 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
}
BigWill = 0;
}
+
if (!EventGrate && EventInProgress)
{
float x,y,z;
pWarrior->GetPosition(x, y, z);
+
if (x >= -1684 && x <= -1674 && y >= -4334 && y <= -4324) {
pWarrior->AreaExploredOrEventHappens(1719);
DoScriptText(SAY_TWIGGY_FLATHEAD_BEGIN, m_creature);
- for (uint8 i = 0; i < 6; ++i)
+
+ for(uint8 i = 0; i < 6; ++i)
{
Creature* pCreature = m_creature->SummonCreature(AFFRAY_CHALLENGER, AffrayChallengerLoc[i][0], AffrayChallengerLoc[i][1], AffrayChallengerLoc[i][2], AffrayChallengerLoc[i][3], TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 600000);
if (!pCreature)
@@ -361,7 +427,7 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
{
if (Challenger_checker < diff)
{
- for (uint8 i = 0; i < 6; ++i)
+ for(uint8 i = 0; i < 6; ++i)
{
if (AffrayChallenger[i])
{
@@ -375,6 +441,7 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
}
Challenger_checker = 1000;
} else Challenger_checker -= diff;
+
if (Wave_Timer < diff)
{
if (AffrayChallenger[Wave] && Wave < 6 && !EventBigWill)
@@ -422,13 +489,16 @@ struct TRINITY_DLL_DECL npc_twiggy_flatheadAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_twiggy_flathead(Creature* pCreature)
{
return new npc_twiggy_flatheadAI (pCreature);
}
+
/*#####
## npc_wizzlecrank_shredder
#####*/
+
enum eEnums_Wizzlecrank
{
SAY_START = -1000272,
@@ -439,11 +509,13 @@ enum eEnums_Wizzlecrank
SAY_PROGRESS_2 = -1000277,
SAY_PROGRESS_3 = -1000278,
SAY_END = -1000279,
+
QUEST_ESCAPE = 863,
FACTION_RATCHET = 637,
NPC_PILOT_WIZZ = 3451,
NPC_MERCENARY = 3282,
};
+
struct TRINITY_DLL_DECL npc_wizzlecrank_shredderAI : public npc_escortAI
{
npc_wizzlecrank_shredderAI(Creature* pCreature) : npc_escortAI(pCreature)
@@ -452,25 +524,31 @@ struct TRINITY_DLL_DECL npc_wizzlecrank_shredderAI : public npc_escortAI
m_uiPostEventTimer = 1000;
m_uiPostEventCount = 0;
}
+
bool m_bIsPostEvent;
uint32 m_uiPostEventTimer;
uint32 m_uiPostEventCount;
+
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
{
if (m_creature->getStandState() == UNIT_STAND_STATE_DEAD)
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
m_bIsPostEvent = false;
m_uiPostEventTimer = 1000;
m_uiPostEventCount = 0;
}
}
+
void WaypointReached(uint32 uiPointId)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(uiPointId)
{
case 0:
@@ -491,11 +569,14 @@ struct TRINITY_DLL_DECL npc_wizzlecrank_shredderAI : public npc_escortAI
break;
}
}
+
void WaypointStart(uint32 uiPointId)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(uiPointId)
{
case 9:
@@ -507,13 +588,16 @@ struct TRINITY_DLL_DECL npc_wizzlecrank_shredderAI : public npc_escortAI
break;
}
}
+
void JustSummoned(Creature* pSummoned)
{
if (pSummoned->GetEntry() == NPC_PILOT_WIZZ)
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
+
if (pSummoned->GetEntry() == NPC_MERCENARY)
pSummoned->AI()->AttackStart(m_creature);
}
+
void UpdateEscortAI(const uint32 uiDiff)
{
if (!UpdateVictim())
@@ -541,17 +625,21 @@ struct TRINITY_DLL_DECL npc_wizzlecrank_shredderAI : public npc_escortAI
}
break;
}
+
++m_uiPostEventCount;
m_uiPostEventTimer = 5000;
}
else
m_uiPostEventTimer -= uiDiff;
}
+
return;
}
+
DoMeleeAttackIfReady();
}
};
+
bool QuestAccept_npc_wizzlecrank_shredder(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_ESCAPE)
@@ -562,36 +650,44 @@ bool QuestAccept_npc_wizzlecrank_shredder(Player* pPlayer, Creature* pCreature,
}
return true;
}
+
CreatureAI* GetAI_npc_wizzlecrank_shredderAI(Creature* pCreature)
{
return new npc_wizzlecrank_shredderAI(pCreature);
}
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_gilthares";
newscript->GetAI = &GetAI_npc_gilthares;
newscript->pQuestAccept = &QuestAccept_npc_gilthares;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_sputtervalve";
newscript->pGossipHello = &GossipHello_npc_sputtervalve;
newscript->pGossipSelect = &GossipSelect_npc_sputtervalve;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_taskmaster_fizzule";
newscript->GetAI = &GetAI_npc_taskmaster_fizzule;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_twiggy_flathead";
newscript->GetAI = &GetAI_npc_twiggy_flathead;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_wizzlecrank_shredder";
newscript->GetAI = &GetAI_npc_wizzlecrank_shredderAI;
diff --git a/src/bindings/scripts/scripts/kalimdor/thousand_needles.cpp b/src/bindings/scripts/scripts/kalimdor/thousand_needles.cpp
index c8f19e44edd..9616aa4acf6 100644
--- a/src/bindings/scripts/scripts/kalimdor/thousand_needles.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/thousand_needles.cpp
@@ -13,34 +13,44 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Thousand Needles
SD%Complete: 100
SDComment: Support for Quest: 1950, 4770, 4904, 4966
SDCategory: Thousand Needles
EndScriptData */
+
/* ContentData
npc_kanati
npc_lakota_windsong
npc_swiftmountain
npc_plucky
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*#####
# npc_kanati
######*/
+
enum eKanati
{
SAY_KAN_START = -1000410,
+
QUEST_PROTECT_KANATI = 4966,
NPC_GALAK_ASS = 10720
};
+
const float m_afGalakLoc[]= {-4867.387695, -1357.353760, -48.226 };
+
struct TRINITY_DLL_DECL npc_kanatiAI : public npc_escortAI
{
npc_kanatiAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void Reset() { }
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -55,22 +65,26 @@ struct TRINITY_DLL_DECL npc_kanatiAI : public npc_escortAI
break;
}
}
+
void DoSpawnGalak()
{
- for (int i = 0; i < 3; ++i)
+ for(int i = 0; i < 3; ++i)
m_creature->SummonCreature(NPC_GALAK_ASS,
m_afGalakLoc[0], m_afGalakLoc[1], m_afGalakLoc[2], 0.0f,
TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
}
+
void JustSummoned(Creature* pSummoned)
{
pSummoned->AI()->AttackStart(m_creature);
}
};
+
CreatureAI* GetAI_npc_kanati(Creature* pCreature)
{
return new npc_kanatiAI(pCreature);
}
+
bool QuestAccept_npc_kanati(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_PROTECT_KANATI)
@@ -80,9 +94,11 @@ bool QuestAccept_npc_kanati(Player* pPlayer, Creature* pCreature, const Quest* p
}
return true;
}
+
/*######
# npc_lakota_windsong
######*/
+
enum eLakota
{
SAY_LAKO_START = -1000365,
@@ -90,13 +106,16 @@ enum eLakota
SAY_LAKO_HERE_COME = -1000367,
SAY_LAKO_MORE = -1000368,
SAY_LAKO_END = -1000369,
+
QUEST_FREE_AT_LAST = 4904,
NPC_GRIM_BANDIT = 10758,
FACTION_ESCORTEE_LAKO = 232, //guessed
+
ID_AMBUSH_1 = 0,
ID_AMBUSH_2 = 2,
ID_AMBUSH_3 = 4
};
+
float m_afBanditLoc[6][6]=
{
{-4905.479492, -2062.732666, 84.352},
@@ -106,10 +125,13 @@ float m_afBanditLoc[6][6]=
{-4767.985352, -1873.169189, 90.192},
{-4788.861328, -1888.007813, 89.888}
};
+
struct TRINITY_DLL_DECL npc_lakota_windsongAI : public npc_escortAI
{
npc_lakota_windsongAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void Reset() { }
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -132,51 +154,62 @@ struct TRINITY_DLL_DECL npc_lakota_windsongAI : public npc_escortAI
break;
}
}
+
void DoSpawnBandits(int uiAmbushId)
{
- for (int i = 0; i < 2; ++i)
+ for(int i = 0; i < 2; ++i)
m_creature->SummonCreature(NPC_GRIM_BANDIT,
m_afBanditLoc[i+uiAmbushId][0], m_afBanditLoc[i+uiAmbushId][1], m_afBanditLoc[i+uiAmbushId][2], 0.0f,
TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000);
}
};
+
CreatureAI* GetAI_npc_lakota_windsong(Creature* pCreature)
{
return new npc_lakota_windsongAI(pCreature);
}
+
bool QuestAccept_npc_lakota_windsong(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_FREE_AT_LAST)
{
DoScriptText(SAY_LAKO_START, pCreature, pPlayer);
pCreature->setFaction(FACTION_ESCORTEE_LAKO);
+
if (npc_lakota_windsongAI* pEscortAI = CAST_AI(npc_lakota_windsongAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return true;
}
+
/*######
# npc_paoka_swiftmountain
######*/
+
enum ePacka
{
SAY_START = -1000147,
SAY_WYVERN = -1000148,
SAY_COMPLETE = -1000149,
+
QUEST_HOMEWARD = 4770,
NPC_WYVERN = 4107,
FACTION_ESCORTEE = 232 //guessed
};
+
float m_afWyvernLoc[3][3]=
{
{-4990.606, -906.057, -5.343},
{-4970.241, -927.378, -4.951},
{-4985.364, -952.528, -5.199}
};
+
struct TRINITY_DLL_DECL npc_paoka_swiftmountainAI : public npc_escortAI
{
npc_paoka_swiftmountainAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void Reset() { }
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -194,33 +227,40 @@ struct TRINITY_DLL_DECL npc_paoka_swiftmountainAI : public npc_escortAI
break;
}
}
+
void DoSpawnWyvern()
{
- for (int i = 0; i < 3; ++i)
+ for(int i = 0; i < 3; ++i)
m_creature->SummonCreature(NPC_WYVERN,
m_afWyvernLoc[i][0], m_afWyvernLoc[i][1], m_afWyvernLoc[i][2], 0.0f,
TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000);
}
};
+
CreatureAI* GetAI_npc_paoka_swiftmountain(Creature* pCreature)
{
return new npc_paoka_swiftmountainAI(pCreature);
}
+
bool QuestAccept_npc_paoka_swiftmountain(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_HOMEWARD)
{
DoScriptText(SAY_START, pCreature, pPlayer);
pCreature->setFaction(FACTION_ESCORTEE);
+
if (npc_paoka_swiftmountainAI* pEscortAI = CAST_AI(npc_paoka_swiftmountainAI,pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return true;
}
+
/*#####
# npc_plucky
######*/
+
#define GOSSIP_P "Please tell me the Phrase.."
+
enum ePlucky
{
FACTION_FRIENDLY = 35,
@@ -228,20 +268,27 @@ enum ePlucky
SPELL_PLUCKY_HUMAN = 9192,
SPELL_PLUCKY_CHICKEN = 9220
};
+
struct TRINITY_DLL_DECL npc_pluckyAI : public ScriptedAI
{
npc_pluckyAI(Creature *c) : ScriptedAI(c) { m_uiNormFaction = c->getFaction(); }
+
uint32 m_uiNormFaction;
uint32 m_uiResetTimer;
+
void Reset()
{
m_uiResetTimer = 120000;
+
if (m_creature->getFaction() != m_uiNormFaction)
m_creature->setFaction(m_uiNormFaction);
+
if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+
m_creature->CastSpell(m_creature, SPELL_PLUCKY_CHICKEN, false);
}
+
void ReceiveEmote(Player* pPlayer, uint32 uiTextEmote)
{
if (pPlayer->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE)
@@ -253,6 +300,7 @@ struct TRINITY_DLL_DECL npc_pluckyAI : public ScriptedAI
m_creature->CastSpell(m_creature, SPELL_PLUCKY_HUMAN, false);
}
}
+
if (uiTextEmote == TEXTEMOTE_CHICKEN)
{
if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
@@ -266,6 +314,7 @@ struct TRINITY_DLL_DECL npc_pluckyAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (m_creature->HasFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP))
@@ -276,23 +325,29 @@ struct TRINITY_DLL_DECL npc_pluckyAI : public ScriptedAI
EnterEvadeMode();
else
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
+
return;
}
else
m_uiResetTimer -= uiDiff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
bool GossipHello_npc_plucky(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_SCOOP) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_P, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(738, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_plucky(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -304,31 +359,38 @@ bool GossipSelect_npc_plucky(Player* pPlayer, Creature* pCreature, uint32 uiSend
}
return true;
}
+
CreatureAI* GetAI_npc_plucky(Creature* pCreature)
{
return new npc_pluckyAI(pCreature);
}
+
/*#####
#
######*/
+
void AddSC_thousand_needles()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_kanati";
newscript->GetAI = &GetAI_npc_kanati;
newscript->pQuestAccept = &QuestAccept_npc_kanati;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lakota_windsong";
newscript->GetAI = &GetAI_npc_lakota_windsong;
newscript->pQuestAccept = &QuestAccept_npc_lakota_windsong;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_paoka_swiftmountain";
newscript->GetAI = &GetAI_npc_paoka_swiftmountain;
newscript->pQuestAccept = &QuestAccept_npc_paoka_swiftmountain;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_plucky";
newscript->GetAI = &GetAI_npc_plucky;
diff --git a/src/bindings/scripts/scripts/kalimdor/thunder_bluff.cpp b/src/bindings/scripts/scripts/kalimdor/thunder_bluff.cpp
index f5b5c49ae6c..f94a31a0dc9 100644
--- a/src/bindings/scripts/scripts/kalimdor/thunder_bluff.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/thunder_bluff.cpp
@@ -13,31 +13,38 @@
* along 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
+
#define GOSSIP_HCB "I know this is rather silly but a young ward who is a bit shy would like your hoofprint."
//TODO: verify abilities/timers
struct TRINITY_DLL_DECL npc_cairne_bloodhoofAI : public ScriptedAI
{
npc_cairne_bloodhoofAI(Creature* c) : ScriptedAI(c) {}
+
uint32 BerserkerCharge_Timer;
uint32 Cleave_Timer;
uint32 MortalStrike_Timer;
uint32 Thunderclap_Timer;
uint32 Uppercut_Timer;
+
void Reset()
{
BerserkerCharge_Timer = 30000;
@@ -46,11 +53,14 @@ struct TRINITY_DLL_DECL npc_cairne_bloodhoofAI : public ScriptedAI
Thunderclap_Timer = 15000;
Uppercut_Timer = 10000;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (BerserkerCharge_Timer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
@@ -58,26 +68,31 @@ struct TRINITY_DLL_DECL npc_cairne_bloodhoofAI : public ScriptedAI
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();
}
};
@@ -85,15 +100,20 @@ CreatureAI* GetAI_npc_cairne_bloodhoof(Creature* pCreature)
{
return new npc_cairne_bloodhoofAI (pCreature);
}
+
bool GossipHello_npc_cairne_bloodhoof(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(925) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HCB, GOSSIP_SENDER_MAIN, GOSSIP_SENDER_INFO);
+
pPlayer->SEND_GOSSIP_MENU(7013, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_cairne_bloodhoof(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_SENDER_INFO)
@@ -103,9 +123,11 @@ bool GossipSelect_npc_cairne_bloodhoof(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
void AddSC_thunder_bluff()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_cairne_bloodhoof";
newscript->GetAI = &GetAI_npc_cairne_bloodhoof;
diff --git a/src/bindings/scripts/scripts/kalimdor/ungoro_crater.cpp b/src/bindings/scripts/scripts/kalimdor/ungoro_crater.cpp
index cc20f7c1022..eac657fd727 100644
--- a/src/bindings/scripts/scripts/kalimdor/ungoro_crater.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/ungoro_crater.cpp
@@ -13,42 +13,54 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Ungoro Crater
SD%Complete: 100
SDComment: Support for Quest: 4245, 4491
SDCategory: Ungoro Crater
EndScriptData */
+
/* ContentData
npc_a-me
npc_ringo
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "follower_ai.h"
+
#define SAY_READY -1000200
#define SAY_AGGRO1 -1000201
#define SAY_SEARCH -1000202
#define SAY_AGGRO2 -1000203
#define SAY_AGGRO3 -1000204
#define SAY_FINISH -1000205
+
#define SPELL_DEMORALIZINGSHOUT 13730
+
#define QUEST_CHASING_AME 4245
#define ENTRY_TARLORD 6519
#define ENTRY_TARLORD1 6519
#define ENTRY_STOMPER 6513
+
struct TRINITY_DLL_DECL npc_ameAI : public npc_escortAI
{
npc_ameAI(Creature *c) : npc_escortAI(c) {}
+
uint32 DEMORALIZINGSHOUT_Timer;
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch (i)
{
+
case 19:
m_creature->SummonCreature(ENTRY_STOMPER, -6391.69, -1730.49, -272.83, 4.96, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
DoScriptText(SAY_AGGRO1, m_creature, pPlayer);
@@ -69,33 +81,41 @@ struct TRINITY_DLL_DECL npc_ameAI : public npc_escortAI
if (pPlayer)
pPlayer->GroupEventHappens(QUEST_CHASING_AME,m_creature);
break;
+
}
}
+
void Reset()
{
DEMORALIZINGSHOUT_Timer = 5000;
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void JustDied(Unit* killer)
{
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->FailQuest(QUEST_CHASING_AME);
}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
if (!UpdateVictim())
return;
+
if (DEMORALIZINGSHOUT_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_DEMORALIZINGSHOUT);
DEMORALIZINGSHOUT_Timer = 70000;
}else DEMORALIZINGSHOUT_Timer -= diff;
+
}
};
+
bool QuestAccept_npc_ame(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_CHASING_AME)
@@ -108,25 +128,31 @@ bool QuestAccept_npc_ame(Player* pPlayer, Creature* pCreature, Quest const* ques
}
return true;
}
+
CreatureAI* GetAI_npc_ame(Creature* pCreature)
{
return new npc_ameAI(pCreature);
}
+
/*####
# npc_ringo
####*/
+
enum eRingo
{
SAY_RIN_START_1 = -1000416,
SAY_RIN_START_2 = -1000417,
+
SAY_FAINT_1 = -1000418,
SAY_FAINT_2 = -1000419,
SAY_FAINT_3 = -1000420,
SAY_FAINT_4 = -1000421,
+
SAY_WAKE_1 = -1000422,
SAY_WAKE_2 = -1000423,
SAY_WAKE_3 = -1000424,
SAY_WAKE_4 = -1000425,
+
SAY_RIN_END_1 = -1000426,
SAY_SPR_END_2 = -1000427,
SAY_RIN_END_3 = -1000428,
@@ -135,18 +161,23 @@ enum eRingo
SAY_RIN_END_6 = -1000431, // signed for 6784
SAY_SPR_END_7 = -1000432,
EMOTE_RIN_END_8 = -1000433,
+
SPELL_REVIVE_RINGO = 15591,
QUEST_A_LITTLE_HELP = 4491,
NPC_SPRAGGLE = 9997,
FACTION_ESCORTEE = 113
};
+
struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
{
npc_ringoAI(Creature* pCreature) : FollowerAI(pCreature) { }
+
uint32 m_uiFaintTimer;
uint32 m_uiEndEventProgress;
uint32 m_uiEndEventTimer;
+
Unit* pSpraggle;
+
void Reset()
{
m_uiFaintTimer = urand(30000, 60000);
@@ -154,9 +185,11 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
m_uiEndEventTimer = 1000;
pSpraggle = NULL;
}
+
void MoveInLineOfSight(Unit *pWho)
{
FollowerAI::MoveInLineOfSight(pWho);
+
if (!m_creature->getVictim() && !HasFollowState(STATE_FOLLOW_COMPLETE) && pWho->GetEntry() == NPC_SPRAGGLE)
{
if (m_creature->IsWithinDistInMap(pWho, INTERACTION_DISTANCE))
@@ -166,34 +199,44 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
if (pPlayer->GetQuestStatus(QUEST_A_LITTLE_HELP) == QUEST_STATUS_INCOMPLETE)
pPlayer->GroupEventHappens(QUEST_A_LITTLE_HELP, m_creature);
}
+
pSpraggle = pWho;
SetFollowComplete(true);
}
}
}
+
void SpellHit(Unit* pCaster, const SpellEntry* pSpell)
{
if (HasFollowState(STATE_FOLLOW_INPROGRESS | STATE_FOLLOW_PAUSED) && pSpell->Id == SPELL_REVIVE_RINGO)
ClearFaint();
}
+
void SetFaint()
{
if (!HasFollowState(STATE_FOLLOW_POSTEVENT))
{
SetFollowPaused(true);
+
DoScriptText(RAND(SAY_FAINT_1,SAY_FAINT_2,SAY_FAINT_3,SAY_FAINT_4), m_creature);
}
+
//what does actually happen here? Emote? Aura?
m_creature->SetStandState(UNIT_STAND_STATE_SLEEP);
}
+
void ClearFaint()
{
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
if (HasFollowState(STATE_FOLLOW_POSTEVENT))
return;
+
DoScriptText(RAND(SAY_WAKE_1,SAY_WAKE_2,SAY_WAKE_3,SAY_WAKE_4), m_creature);
+
SetFollowPaused(false);
}
+
void UpdateFollowerAI(const uint32 uiDiff)
{
if (!UpdateVictim())
@@ -207,6 +250,7 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
SetFollowComplete();
return;
}
+
switch(m_uiEndEventProgress)
{
case 1:
@@ -247,6 +291,7 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
SetFollowComplete();
break;
}
+
++m_uiEndEventProgress;
}
else
@@ -265,15 +310,19 @@ struct TRINITY_DLL_DECL npc_ringoAI : public FollowerAI
m_uiFaintTimer -= uiDiff;
}
}
+
return;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_ringo(Creature* pCreature)
{
return new npc_ringoAI(pCreature);
}
+
bool QuestAccept_npc_ringo(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_A_LITTLE_HELP)
@@ -284,16 +333,20 @@ bool QuestAccept_npc_ringo(Player* pPlayer, Creature* pCreature, const Quest* pQ
pRingoAI->StartFollow(pPlayer, FACTION_ESCORTEE, pQuest);
}
}
+
return true;
}
+
void AddSC_ungoro_crater()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_ame";
newscript->GetAI = &GetAI_npc_ame;
newscript->pQuestAccept = &QuestAccept_npc_ame;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_ringo";
newscript->GetAI = &GetAI_npc_ringo;
diff --git a/src/bindings/scripts/scripts/kalimdor/wailing_caverns/def_wailing_caverns.h b/src/bindings/scripts/scripts/kalimdor/wailing_caverns/def_wailing_caverns.h
index 87a93c9a386..f6c7bee8571 100644
--- a/src/bindings/scripts/scripts/kalimdor/wailing_caverns/def_wailing_caverns.h
+++ b/src/bindings/scripts/scripts/kalimdor/wailing_caverns/def_wailing_caverns.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_WAILING_CAVERNS_H
#define DEF_WAILING_CAVERNS_H
+
enum eTypes
{
TYPE_LORD_COBRAHN = 1,
@@ -15,6 +17,8 @@ enum eTypes
TYPE_NARALEX_PART3 = 8,
TYPE_MUTANUS_THE_DEVOURER = 9,
TYPE_NARALEX_YELLED = 10,
+
DATA_NARALEX = 3679,
};
-#endif
+
+#endif \ No newline at end of file
diff --git a/src/bindings/scripts/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp b/src/bindings/scripts/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp
index c446c3beb6b..d955226e1c3 100644
--- a/src/bindings/scripts/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/wailing_caverns/instance_wailing_caverns.cpp
@@ -13,32 +13,42 @@
* along 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: 99
SDComment: Everything seems to work, still need some checking
SDCategory: Wailing Caverns
EndScriptData */
+
#include "precompiled.h"
#include "def_wailing_caverns.h"
+
#define MAX_ENCOUNTER 9
+
struct TRINITY_DLL_DECL instance_wailing_caverns : public ScriptedInstance
{
instance_wailing_caverns(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
bool yelled;
uint64 NaralexGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
yelled = false;
NaralexGUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
if (pCreature->GetEntry() == DATA_NARALEX)
NaralexGUID = pCreature->GetGUID();
}
+
void SetData(uint32 type, uint32 data)
{
switch (type)
@@ -56,6 +66,7 @@ struct TRINITY_DLL_DECL instance_wailing_caverns : public ScriptedInstance
}
if (data == DONE)SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch (type)
@@ -73,21 +84,26 @@ struct TRINITY_DLL_DECL instance_wailing_caverns : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
if (data == DATA_NARALEX)return NaralexGUID;
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " "
<< m_auiEncounter[3] << " " << m_auiEncounter[4] << " " << m_auiEncounter[5] << " "
<< m_auiEncounter[6] << " " << m_auiEncounter[7] << " " << m_auiEncounter[8];
+
OUT_SAVE_INST_DATA_COMPLETE;
return saveStream.str();
}
+
void Load(const char* in)
{
if (!in)
@@ -95,20 +111,27 @@ struct TRINITY_DLL_DECL instance_wailing_caverns : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]
>> m_auiEncounter[4] >> m_auiEncounter[5] >> m_auiEncounter[6] >> m_auiEncounter[7] >> m_auiEncounter[8];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] != DONE)
m_auiEncounter[i] = NOT_STARTED;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
+
};
+
InstanceData* GetInstanceData_instance_wailing_caverns(Map* pMap)
{
return new instance_wailing_caverns(pMap);
}
+
void AddSC_instance_wailing_caverns()
{
Script *newscript;
@@ -116,4 +139,4 @@ void AddSC_instance_wailing_caverns()
newscript->Name = "instance_wailing_caverns";
newscript->GetInstanceData = &GetInstanceData_instance_wailing_caverns;
newscript->RegisterSelf();
-}
+} \ No newline at end of file
diff --git a/src/bindings/scripts/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp b/src/bindings/scripts/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp
index 75c23a4121e..d734e32d13b 100644
--- a/src/bindings/scripts/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/wailing_caverns/wailing_caverns.cpp
@@ -13,20 +13,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Wailing Caverns
SD%Complete: 95
SDComment: Need to add skill usage for Disciple of Naralex
SDCategory: Wailing Caverns
EndScriptData */
+
/* ContentData
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "def_wailing_caverns.h"
+
/*######
## npc_disciple_of_naralex
######*/
+
enum eEnums
{
//say
@@ -62,10 +67,12 @@ enum eEnums
NPC_NIGHTMARE_ECTOPLASM = 5763,
NPC_MUTANUS_THE_DEVOURER = 3654,
};
+
#define GOSSIP_ID_START_1 698 //Naralex sleeps again!
#define GOSSIP_ID_START_2 699 //The fanglords are dead!
#define GOSSIP_ITEM_NARALEX "Let the event begin!"
#define ACHIEVEMENT_WAILING_CAVERNS 630
+
struct TRINITY_DLL_DECL npc_disciple_of_naralexAI : public npc_escortAI
{
npc_disciple_of_naralexAI(Creature *c) : npc_escortAI(c)
@@ -77,14 +84,17 @@ struct TRINITY_DLL_DECL npc_disciple_of_naralexAI : public npc_escortAI
m_creature->setActive(true);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
+
uint32 eventTimer;
uint32 currentEvent;
uint32 eventProgress;
ScriptedInstance *pInstance;
+
void WaypointReached(uint32 i)
{
if (!pInstance)
return;
+
switch (i)
{
case 4:
@@ -111,13 +121,17 @@ struct TRINITY_DLL_DECL npc_disciple_of_naralexAI : public npc_escortAI
break;
}
}
+
void Reset()
{
+
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_ATTACKED, m_creature, who);
}
+
void JustDied(Unit *slayer)
{
if (pInstance)
@@ -128,14 +142,17 @@ struct TRINITY_DLL_DECL npc_disciple_of_naralexAI : public npc_escortAI
pInstance->SetData(TYPE_NARALEX_PART3, FAIL);
}
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (currentEvent != TYPE_NARALEX_PART3)
npc_escortAI::UpdateAI(diff);
+
if (!pInstance)
return;
if (eventTimer <= diff)
@@ -244,7 +261,7 @@ struct TRINITY_DLL_DECL npc_disciple_of_naralexAI : public npc_escortAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievWC);
}
}
@@ -312,13 +329,16 @@ struct TRINITY_DLL_DECL npc_disciple_of_naralexAI : public npc_escortAI
}else eventTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_disciple_of_naralex(Creature* pCreature)
{
return new npc_disciple_of_naralexAI(pCreature);
}
+
bool GossipHello_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature)
{
ScriptedInstance *pInstance = pCreature->GetInstanceData();
+
if (pInstance)
{
pCreature->CastSpell(pPlayer, SPELL_MARK_OF_THE_WILD_RANK_2, true);
@@ -327,6 +347,7 @@ bool GossipHello_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_NARALEX, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_ID_START_2, pCreature->GetGUID());
+
if (!pInstance->GetData(TYPE_NARALEX_YELLED))
{
DoScriptText(SAY_AT_LAST, pCreature);
@@ -340,6 +361,7 @@ bool GossipHello_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
bool GossipSelect_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
ScriptedInstance *pInstance = pCreature->GetInstanceData();
@@ -348,18 +370,23 @@ bool GossipSelect_npc_disciple_of_naralex(Player* pPlayer, Creature* pCreature,
pPlayer->CLOSE_GOSSIP_MENU();
if (pInstance)
pInstance->SetData(TYPE_NARALEX_EVENT, IN_PROGRESS);
+
DoScriptText(SAY_MAKE_PREPARATIONS, pCreature);
+
pCreature->setFaction(250);
pCreature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
+
CAST_AI(npc_escortAI, (pCreature->AI()))->Start(false, false, pPlayer->GetGUID());
CAST_AI(npc_escortAI, (pCreature->AI()))->SetDespawnAtFar(false);
CAST_AI(npc_escortAI, (pCreature->AI()))->SetDespawnAtEnd(false);
}
return true;
}
+
void AddSC_wailing_caverns()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_disciple_of_naralex";
newscript->pGossipHello = &GossipHello_npc_disciple_of_naralex;
diff --git a/src/bindings/scripts/scripts/kalimdor/winterspring.cpp b/src/bindings/scripts/scripts/kalimdor/winterspring.cpp
index ba9e628a270..cf4f019f705 100644
--- a/src/bindings/scripts/scripts/kalimdor/winterspring.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/winterspring.cpp
@@ -13,36 +13,47 @@
* along 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
######*/
+
#define GOSSIP_HL "Talk to me"
+
#define GOSSIP_SL1 "What do you do here?"
#define GOSSIP_SL2 "I can help you"
#define GOSSIP_SL3 "What deal?"
#define GOSSIP_SL4 "Then what happened?"
#define GOSSIP_SL5 "He is not safe, i'll make sure of that."
+
bool GossipHello_npc_lorax(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(5126) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lorax(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -74,40 +85,53 @@ bool GossipSelect_npc_lorax(Player* pPlayer, Creature* pCreature, uint32 uiSende
}
return true;
}
+
/*######
## npc_rivern_frostwind
######*/
+
bool GossipHello_npc_rivern_frostwind(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor() && pPlayer->GetReputationRank(589) == REP_EXALTED)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_rivern_frostwind(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_witch_doctor_mauari
######*/
+
#define GOSSIP_HWDM "I'd like you to make me a new Cache of Mau'ari please."
+
bool GossipHello_npc_witch_doctor_mauari(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestRewardStatus(975))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HWDM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(3377, pCreature->GetGUID());
}else
pPlayer->SEND_GOSSIP_MENU(3375, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_witch_doctor_mauari(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction ==GOSSIP_ACTION_INFO_DEF+1)
@@ -115,21 +139,26 @@ bool GossipSelect_npc_witch_doctor_mauari(Player* pPlayer, Creature* pCreature,
pPlayer->CLOSE_GOSSIP_MENU();
pCreature->CastSpell(pPlayer, 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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_rivern_frostwind";
newscript->pGossipHello = &GossipHello_npc_rivern_frostwind;
newscript->pGossipSelect = &GossipSelect_npc_rivern_frostwind;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_witch_doctor_mauari";
newscript->pGossipHello = &GossipHello_npc_witch_doctor_mauari;
diff --git a/src/bindings/scripts/scripts/kalimdor/zulfarrak/zulfarrak.cpp b/src/bindings/scripts/scripts/kalimdor/zulfarrak/zulfarrak.cpp
index 97c62595708..d0111410bbd 100644
--- a/src/bindings/scripts/scripts/kalimdor/zulfarrak/zulfarrak.cpp
+++ b/src/bindings/scripts/scripts/kalimdor/zulfarrak/zulfarrak.cpp
@@ -13,66 +13,85 @@
* along 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 TRINITY_DLL_DECL npc_sergeant_blyAI : public ScriptedAI
{
npc_sergeant_blyAI(Creature *c) : ScriptedAI(c)
{
//pInstance = c->GetInstanceData();
}
+
//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. Trinity support required.
+
void Reset()
{
ShieldBash_Timer = 5000;
Revenge_Timer = 8000;
+
m_creature->setFaction(FACTION_FRIENDLY);
+
/*if (pInstance)
pInstance->SetData(0, NOT_STARTED);*/
}
+
void EnterCombat(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 (!UpdateVictim())
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();
}
};
@@ -80,6 +99,7 @@ CreatureAI* GetAI_npc_sergeant_bly(Creature* pCreature)
{
return new npc_sergeant_blyAI (pCreature);
}
+
bool GossipHello_npc_sergeant_bly(Player* pPlayer, Creature* pCreature)
{
/*if (pInstance->GetData(0) == DONE)
@@ -91,8 +111,10 @@ bool GossipHello_npc_sergeant_bly(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(1516, pCreature->GetGUID());
else
pPlayer->SEND_GOSSIP_MENU(1515, pCreature->GetGUID());*/
+
return true;
}
+
bool GossipSelect_npc_sergeant_bly(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -103,40 +125,50 @@ bool GossipSelect_npc_sergeant_bly(Player* pPlayer, Creature* pCreature, uint32
}
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 TRINITY_DLL_DECL npc_weegli_blastfuseAI : public ScriptedAI
{
npc_weegli_blastfuseAI(Creature *c) : ScriptedAI(c)
{
//pInstance = c->GetInstanceData();
}
+
//ScriptedInstance* pInstance;
+
void Reset()
{
/*if (pInstance)
pInstance->SetData(0, NOT_STARTED);*/
}
+
void EnterCombat(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 (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
@@ -144,6 +176,7 @@ CreatureAI* GetAI_npc_weegli_blastfuse(Creature* pCreature)
{
return new npc_weegli_blastfuseAI (pCreature);
}
+
bool GossipHello_npc_weegli_blastfuse(Player* pPlayer, Creature* pCreature)
{
//event not implemented yet, this is only placeholder for future developement
@@ -158,6 +191,7 @@ bool GossipHello_npc_weegli_blastfuse(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(1511, pCreature->GetGUID()); //if event not started
return true;
}
+
bool GossipSelect_npc_weegli_blastfuse(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -167,15 +201,18 @@ bool GossipSelect_npc_weegli_blastfuse(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
/*######
## go_shallow_grave
######*/
+
enum {
ZOMBIE = 7286,
DEAD_HERO = 7276,
ZOMBIE_CHANCE = 65,
DEAD_HERO_CHANCE = 10
};
+
bool GOHello_go_shallow_grave(Player* pPlayer, GameObject* pGo)
{
// randomly summon a zombie or dead hero the first time a grave is used
@@ -190,42 +227,53 @@ bool GOHello_go_shallow_grave(Player* pPlayer, GameObject* pGo)
pGo->AddUse();
return false;
}
+
/*######
## at_zumrah
######*/
+
enum {
ZUMRAH_ID = 7271,
ZUMRAH_HOSTILE_FACTION = 37
};
+
bool AreaTrigger_at_zumrah(Player* pPlayer, AreaTriggerEntry *at)
{
Creature* Zumrah = pPlayer->FindNearestCreature(ZUMRAH_ID, 30.0f);
+
if (!Zumrah)
return false;
+
Zumrah->setFaction(ZUMRAH_HOSTILE_FACTION);
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;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_shallow_grave";
newscript->pGOHello = &GOHello_go_shallow_grave;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "at_zumrah";
newscript->pAreaTrigger = &AreaTrigger_at_zumrah;
newscript->RegisterSelf();
+
}
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp
index caad936d0f9..8a77a455da0 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_amanitar.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* Script Data Start
SDName: boss_amanitar
SDAuthor: WarHead
@@ -22,23 +23,31 @@ SD%Complete: 80%
SDComment: Find correct mushrooms spell to make them visible - buffs of the mushrooms not ever applied to the users...
SDCategory: Ahn'kahet
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_amanitar' where entry = '30258';
UPDATE `creature_template` SET `ScriptName`='mob_amanitar_mushrooms' WHERE `entry` IN ('30435','30391');
*** SQL END ***/
+
#include "precompiled.h"
#include "def_ahnkahet.h"
+
#define SPELL_BASH 57094 // Victim
#define SPELL_ENTANGLING_ROOTS 57095 // Random Victim 100Y
#define SPELL_MINI 57055 // Self
#define SPELL_VENOM_BOLT_VOLLEY 57088 // Random Victim 100Y
+
#define HEALTHY_MUSHROOM_SPELL_POTENT_FUNGUS 56648 // Killer 3Y
+
#define POISONOUS_MUSHROOM_SPELL_POISON_CLOUD 57061 // Self - Duration 8 Sec
#define POISONOUS_MUSHROOM_SPELL_VISUAL_AREA 61566 // Self
#define POISONOUS_MUSHROOM_SPELL_VISUAL_AURA 56741 // Self
+
#define SPELL_PUTRID_MUSHROOM 31690 // To make the mushrooms visible
+
#define HealthyMushroom 30391
#define PoisonousMushroom 30435
+
struct MANGOS_DLL_DECL boss_amanitarAI : public ScriptedAI
{
boss_amanitarAI(Creature *c) : ScriptedAI(c)
@@ -46,26 +55,35 @@ struct MANGOS_DLL_DECL boss_amanitarAI : public ScriptedAI
pInstance = c->GetInstanceData();
FirstTime = true;
}
+
ScriptedInstance* pInstance;
+
uint32 roottimer,
bashtimer,
bolttimer,
spawntimer;
+
bool FirstTime;
+
void Reset()
{
roottimer = urand(5000,9000);
bashtimer = urand(10000,14000);
bolttimer = urand(15000,30000);
spawntimer = 0;
+
m_creature->SetMeleeDamageSchool(SPELL_SCHOOL_NATURE);
m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_NATURE, true);
+
if (pInstance && !FirstTime)
pInstance->SetData(DATA_AMANITAR, FAIL);
+
FirstTime = false;
+
if (pInstance)
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI);
}
+
void JustDied(Unit *Killer)
{
if (pInstance)
@@ -74,20 +92,26 @@ struct MANGOS_DLL_DECL boss_amanitarAI : public ScriptedAI
pInstance->DoRemoveAurasDueToSpellOnPlayers(SPELL_MINI);
}
}
+
void EnterCombat(Unit *who)
{
m_creature->SetInCombatWithZone();
+
if (pInstance) pInstance->SetData(DATA_AMANITAR, IN_PROGRESS);
+
m_creature->CastSpell(m_creature, SPELL_MINI, false);
}
+
void SpawnAdds()
{
uint32 DSpwTime = 30000;
float x = 0.0f, y = 0.0f, z = 0.0f;
TempSummonType DSpwType = TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN;
+
for (uint8 i = 0; i < 30; ++i)
{
Unit* victim = SelectUnit(SELECT_TARGET_RANDOM, 0);
+
if (victim)
{
Position pos;
@@ -98,53 +122,66 @@ struct MANGOS_DLL_DECL boss_amanitarAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim()) return;
+
if (spawntimer < diff)
{
SpawnAdds();
spawntimer = urand(35000,40000);
} else spawntimer -= diff;
+
if (roottimer < diff)
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
m_creature->CastSpell(target, SPELL_ENTANGLING_ROOTS, false);
roottimer = urand(15000,30000);
}
+
if (bashtimer < diff)
{
m_creature->CastSpell(m_creature->getVictim(), SPELL_BASH, false);
bashtimer = urand(15000,30000);
} else bashtimer -= diff;
+
if (bolttimer < diff)
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
m_creature->CastSpell(target, SPELL_VENOM_BOLT_VOLLEY, false);
bolttimer = urand(15000,30000);
} else bolttimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct MANGOS_DLL_DECL mob_amanitar_mushroomsAI : public Scripted_NoMovementAI
{
mob_amanitar_mushroomsAI(Creature* c) : Scripted_NoMovementAI(c)
{
c->SetUInt32Value(UNIT_FIELD_DISPLAYID, 26981); // Unsichtbar...
}
+
uint32 auratimer,
deathtimer; // Without not all will despawn after 30000! :-(
+
void Reset()
{
m_creature->CastSpell(m_creature, SPELL_PUTRID_MUSHROOM, true); // Hack, to make the mushrooms visible, can't find orig. spell...
+
if (m_creature->GetEntry() == PoisonousMushroom) m_creature->CastSpell(m_creature, POISONOUS_MUSHROOM_SPELL_VISUAL_AURA, true);
+
auratimer = 0;
deathtimer = 30000;
}
+
void DamageTaken(Unit *killer, uint32 &damage)
{
if (!killer || !damage) return;
+
if (m_creature->GetEntry() == HealthyMushroom && damage >= m_creature->GetHealth())
{
if (killer->GetTypeId() == TYPEID_PLAYER && m_creature->GetDistance(killer) <= 3.0f)
@@ -154,8 +191,10 @@ struct MANGOS_DLL_DECL mob_amanitar_mushroomsAI : public Scripted_NoMovementAI
}
}
}
+
void EnterCombat(Unit *who) { }
void AttackStart(Unit *victim) { }
+
void UpdateAI(const uint32 diff)
{
if (m_creature->GetEntry() == PoisonousMushroom)
@@ -175,21 +214,26 @@ struct MANGOS_DLL_DECL mob_amanitar_mushroomsAI : public Scripted_NoMovementAI
} else deathtimer -= diff;
}
};
+
CreatureAI* GetAI_boss_amanitar(Creature* pCreature)
{
return new boss_amanitarAI (pCreature);
}
+
CreatureAI* GetAI_mob_amanitar_mushrooms(Creature *pCreature)
{
return new mob_amanitar_mushroomsAI (pCreature);
}
+
void AddSC_boss_amanitar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_amanitar";
newscript->GetAI = &GetAI_boss_amanitar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_amanitar_mushrooms";
newscript->GetAI = &GetAI_mob_amanitar_mushrooms;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp
index 654ae06190e..35827c0aac1 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_elder_nadox.cpp
@@ -15,16 +15,21 @@
* along 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_elder_nadox
SD%Complete: 100
SDComment:
SDCategory: Ahn'kahet
EndScriptData */
+
#include "precompiled.h"
#include "def_ahnkahet.h"
+
bool DeadAhnkaharGuardian; // needed for achievement: Respect Your Elders(2038)
+
#define ACHIEVEMENT_RESPECT_YOUR_ELDERS 2038
+
//not in db
#define SAY_AGGRO -1619014
#define SAY_SLAY_1 -1619015
@@ -33,16 +38,21 @@ bool DeadAhnkaharGuardian; // needed for achievement: Respect Your Elders(2038)
#define SAY_DEATH -1619018
#define SAY_EGG_SAC_1 -1619019
#define SAY_EGG_SAC_2 -1619020
+
#define SPELL_BROOD_PLAGUE 56130
#define H_SPELL_BROOD_PLAGUE 59467
#define H_SPELL_BROOD_RAGE 59465
#define SPELL_ENRAGE 26662// Enraged if too far away from home
+
#define MOB_AHNKAHAR_SWARMER 30178
#define SPELL_SUMMON_SWARMERS 56119//2x 30178 -- 2x every 10secs
+
#define MOB_AHNKAHAR_GUARDIAN_ENTRY 30176
#define SPELL_SUMMON_SWARM_GUARD 56120//1x 30176 -- every 25secs
#define SPELL_GUARDIAN_AURA 56151
+
#define EMOTE_HATCHES "An Ahn'kahar Guardian hatches!"
+
struct TRINITY_DLL_DECL boss_elder_nadoxAI : public ScriptedAI
{
boss_elder_nadoxAI(Creature *c) : ScriptedAI(c)
@@ -50,40 +60,53 @@ struct TRINITY_DLL_DECL boss_elder_nadoxAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
uint32 plague_Timer;
uint32 rage_Timer;
+
uint32 swarmer_spawn_Timer;
uint32 guard_spawn_Timer;
uint32 enrage_Timer;
+
ScriptedInstance *pInstance;
+
void Reset()
{
plague_Timer = 13000;
rage_Timer = 20000;
+
swarmer_spawn_Timer = 10000;
guard_spawn_Timer = 25000;
+
enrage_Timer = 5000;
+
DeadAhnkaharGuardian = false;
+
if (pInstance)
pInstance->SetData(DATA_ELDER_NADOX_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_DEATH,m_creature);
+
if (pInstance)
pInstance->SetData(DATA_ELDER_NADOX_EVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_SLAY_3,m_creature);
+
if (HeroicMode && !DeadAhnkaharGuardian)
{
AchievementEntry const *AchievRespectYourElders = GetAchievementStore()->LookupEntry(ACHIEVEMENT_RESPECT_YOUR_ELDERS);
@@ -93,33 +116,39 @@ struct TRINITY_DLL_DECL boss_elder_nadoxAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievRespectYourElders);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_ELDER_NADOX_EVENT, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (plague_Timer < diff)
{
DoCast(m_creature->getVictim(),HEROIC(SPELL_BROOD_PLAGUE, H_SPELL_BROOD_PLAGUE));
plague_Timer = 15000;
}else plague_Timer -= diff;
+
if (HeroicMode)
if (rage_Timer < diff)
{
Unit* Swarmer = m_creature->FindNearestCreature(MOB_AHNKAHAR_SWARMER, 35);
+
if (Swarmer)
{
DoCast(Swarmer,H_SPELL_BROOD_RAGE,true);
rage_Timer = 15000;
}
}else rage_Timer -= diff;
+
if (swarmer_spawn_Timer < diff)
{
DoCast(m_creature,SPELL_SUMMON_SWARMERS,true);
@@ -130,16 +159,19 @@ struct TRINITY_DLL_DECL boss_elder_nadoxAI : public ScriptedAI
}
swarmer_spawn_Timer = 10000;
}else swarmer_spawn_Timer -= diff;
+
if (guard_spawn_Timer < diff)
{
m_creature->MonsterTextEmote(EMOTE_HATCHES,m_creature->GetGUID(),true);
DoCast(m_creature,SPELL_SUMMON_SWARM_GUARD);
guard_spawn_Timer = 25000;
}else guard_spawn_Timer -= diff;
+
if (enrage_Timer < diff)
{
if (m_creature->HasAura(SPELL_ENRAGE,0))
return;
+
float x, y, z, o;
m_creature->GetHomePosition(x, y, z, o);
if (z < 24)
@@ -151,38 +183,48 @@ struct TRINITY_DLL_DECL boss_elder_nadoxAI : public ScriptedAI
}
enrage_Timer = 5000;
}else enrage_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_elder_nadox(Creature* pCreature)
{
return new boss_elder_nadoxAI(pCreature);
}
+
#define SPELL_SPRINT 56354
+
struct TRINITY_DLL_DECL mob_ahnkahar_nerubianAI : public ScriptedAI
{
mob_ahnkahar_nerubianAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 sprint_Timer;
+
void Reset()
{
if (m_creature->GetEntry() == 30176)
DoCast(m_creature,SPELL_GUARDIAN_AURA,true);
sprint_Timer = 10000;
}
+
void JustDied(Unit *killer)
{
if (m_creature->GetEntry() == MOB_AHNKAHAR_GUARDIAN_ENTRY)
DeadAhnkaharGuardian = true;
}
+
void EnterCombat(Unit *who){}
+
void UpdateAI(const uint32 diff)
{
if (m_creature->GetEntry() == 30176)
m_creature->RemoveAurasDueToSpell(SPELL_GUARDIAN_AURA);
+
if (pInstance)
{
if (pInstance->GetData(DATA_ELDER_NADOX_EVENT) != IN_PROGRESS)
@@ -191,27 +233,34 @@ struct TRINITY_DLL_DECL mob_ahnkahar_nerubianAI : public ScriptedAI
m_creature->RemoveCorpse();
}
}
+
if (!UpdateVictim())
return;
+
if (sprint_Timer < diff)
{
DoCast(m_creature,SPELL_SPRINT);
sprint_Timer = 25000;
}else sprint_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_ahnkahar_nerubian(Creature* pCreature)
{
return new mob_ahnkahar_nerubianAI(pCreature);
}
+
void AddSC_boss_elder_nadox()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_elder_nadox";
newscript->GetAI = &GetAI_boss_elder_nadox;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ahnkahar_nerubian";
newscript->GetAI = &GetAI_mob_ahnkahar_nerubian;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp
index 4ec6df3173d..33c3ba42011 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_herald_volazj.cpp
@@ -15,6 +15,7 @@
* along 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_herald_volazj
SDAuthor: Tartalo
@@ -22,8 +23,10 @@ SD%Complete: 20
SDComment: Coded all but Insanity
SDCategory: Ahn'kahet
EndScriptData */
+
#include "precompiled.h"
#include "def_ahnkahet.h"
+
enum Spells
{
SPELL_INSANITY = 57496, //Dummy
@@ -35,6 +38,7 @@ enum Spells
SPELL_SHIVER = 57949,
H_SPELL_SHIVER = 59978
};
+
//not in db
enum Yells
{
@@ -46,88 +50,110 @@ enum Yells
SAY_DEATH_2 = -1619035,
SAY_PHASE = -1619036
};
+
enum Achievements
{
ACHIEVEMENT_QUICK_DEMISE = 1862
};
+
struct TRINITY_DLL_DECL boss_volazjAI : public ScriptedAI
{
boss_volazjAI(Creature* pCreature) : ScriptedAI(pCreature)
{
pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 uiMindFlayTimer;
uint32 uiShadowBoltVolleyTimer;
uint32 uiShiverTimer;
uint32 uiEncounterTimer;
+
void Reset()
{
uiEncounterTimer = 0;
uiMindFlayTimer = 8000;
uiShadowBoltVolleyTimer = 5000;
uiShiverTimer = 15000;
+
if (pInstance)
pInstance->SetData(DATA_HERALD_VOLAZJ, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_HERALD_VOLAZJ, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiMindFlayTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_MIND_FLAY, H_SPELL_MIND_FLAY));
uiMindFlayTimer = 20000;
} else uiMindFlayTimer -= diff;
+
if (uiShadowBoltVolleyTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_SHADOW_BOLT_VOLLEY, H_SPELL_SHADOW_BOLT_VOLLEY));
uiShadowBoltVolleyTimer = 5000;
} else uiShadowBoltVolleyTimer -= diff;
+
if (uiShiverTimer < diff)
{
if (Unit * target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, HEROIC(SPELL_SHIVER, H_SPELL_SHIVER));
uiShiverTimer = 15000;
} else uiShiverTimer -= diff;
+
uiEncounterTimer += diff;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH_1, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_HERALD_VOLAZJ, DONE);
+
AchievementEntry const *AchievQuickDemise = GetAchievementStore()->LookupEntry(ACHIEVEMENT_QUICK_DEMISE);
Map* pMap = m_creature->GetMap();
+
if (HeroicMode && uiEncounterTimer < 120000 && pMap && pMap->IsDungeon() && AchievQuickDemise)
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievQuickDemise);
}
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_volazj(Creature* pCreature)
{
return new boss_volazjAI (pCreature);
}
+
void AddSC_boss_volazj()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_volazj";
newscript->GetAI = &GetAI_boss_volazj;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp
index 4def5f611ef..a239c961bfa 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_jedoga_shadowseeker.cpp
@@ -15,6 +15,7 @@
* along 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_jedoga_shadowseeker
SDAuthor: LordVanMartin
@@ -22,11 +23,14 @@ SD%Complete: 0
SDComment:
SDCategory: Ahn'kahet
EndScriptData */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_jedoga' where entry = '';
*** SQL END ***/
+
#include "precompiled.h"
#include "def_ahnkahet.h"
+
#define SPELL_CYCLONE_STRIKE 56855
#define H_SPELL_CYCLONE_STRIKE 60030//kein unterschied zu 56855 außer das 60030 1,5sec cast ist
#define SPELL_LIGHTNING_BOLT 56891
@@ -34,6 +38,7 @@ update creature_template set scriptname = 'boss_jedoga' where entry = '';
#define SPELL_THUNDERSHOCK 56926//AOE works
#define H_SPELL_THUNDERSHOCK 60029//AOE works
#define SPELL_GIFT_OF_THE_HERALD 56219//triggert if sucessfull sacreficed
+
//not in db
#define SAY_AGGRO -1619000
#define SAY_C_SACRIFICE_1 -1619001
@@ -49,9 +54,11 @@ update creature_template set scriptname = 'boss_jedoga' where entry = '';
#define SAY_PREACHING_3 -1619011
#define SAY_PREACHING_4 -1619012
#define SAY_PREACHING_5 -1619013
+
struct TRINITY_DLL_DECL boss_jedogaAI : public ScriptedAI
{
boss_jedogaAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who)
{
@@ -64,26 +71,33 @@ struct TRINITY_DLL_DECL boss_jedogaAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_jedoga(Creature* pCreature)
{
return new boss_jedogaAI (pCreature);
}
+
void AddSC_boss_jedoga()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_jedoga";
newscript->GetAI = &GetAI_boss_jedoga;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp
index 04e8aa0b498..b6b1ac586eb 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/boss_prince_taldaram.cpp
@@ -15,6 +15,7 @@
* along 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_taldaram
SDAuthor: Tartalo & tlexii
@@ -22,8 +23,10 @@ SD%Complete: 0
SDComment:
SDCategory: Ahn'kahet
EndScriptData */
+
#include "precompiled.h"
#include "def_ahnkahet.h"
+
enum Spells
{
SPELL_BLOODTHIRST = 55968, //Trigger Spell + add aura
@@ -75,6 +78,7 @@ enum CombatPhase
VANISHED,
FEEDING
};
+
struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
{
boss_taldaramAI(Creature *c) : ScriptedAI(c)
@@ -83,6 +87,7 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
uint32 uiBloodthirstTimer;
uint32 uiVanishTimer;
uint32 uiWaitTimer;
@@ -90,12 +95,18 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
uint32 uiEmbraceTakenDamage;
uint32 uiFlamesphereTimer;
uint32 uiPhaseTimer;
+
uint64 uiSphereGuids[2];
+
Unit *pEmbraceTarget;
Unit *pSphereTarget;
+
Creature* pSpheres[3];
+
CombatPhase Phase;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiBloodthirstTimer = 10000;
@@ -109,12 +120,14 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_PRINCE_TALDARAM_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
pInstance->SetData(DATA_PRINCE_TALDARAM_EVENT, IN_PROGRESS);
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -154,6 +167,7 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
pSpheres[2]->GetMotionMaster()->MovePoint(0, x, y, pSpheres[2]->GetPositionZ());
}
}
+
Phase = NORMAL;
uiPhaseTimer = 0;
break;
@@ -183,6 +197,7 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_BLOODTHIRST);
uiBloodthirstTimer = 10000;
} else uiBloodthirstTimer -= diff;
+
if (uiFlamesphereTimer < diff)
{
DoCast(m_creature, SPELL_CONJURE_FLAME_SPHERE);
@@ -190,13 +205,14 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
uiPhaseTimer = 3000 + diff;
uiFlamesphereTimer = 15000;
} else uiFlamesphereTimer -= diff;
+
if (uiVanishTimer < diff )
{
//Count alive players
Unit *target = NULL;
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
std::vector<Unit *> target_list;
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
// exclude pets & totems
@@ -215,11 +231,13 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
}
uiVanishTimer = urand(25000,35000);
} else uiVanishTimer -= diff;
+
DoMeleeAttackIfReady();
break;
}
} else uiPhaseTimer -= diff;
}
+
void DamageTaken(Unit* done_by, uint32 &damage)
{
if (Phase == FEEDING && pEmbraceTarget && pEmbraceTarget->isAlive())
@@ -234,12 +252,15 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(DATA_PRINCE_TALDARAM_EVENT, DONE);
+
//The Party's Over achievement:
AchievementEntry const *AchievThePartyIsOver = GetAchievementStore()->LookupEntry(ACHIEVEMENT_THE_PARTY_IS_OVER);
Map* pMap = m_creature->GetMap();
@@ -247,14 +268,15 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
{
Map::PlayerList const &players = pMap->GetPlayers();
uint8 count = 0;
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
++count;
if (count < 5)
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievThePartyIsOver);
}
}
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
@@ -267,24 +289,27 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
}
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
bool CheckSpheres()
{
if(!pInstance)
- return false;
+ return false;
uiSphereGuids[0] = pInstance->GetData64(DATA_SPHERE1);
uiSphereGuids[1] = pInstance->GetData64(DATA_SPHERE2);
+
GameObject *pSpheres[2];
for (uint8 i=0; i < 2; ++i)
{
- pSpheres[i] = pInstance->instance->GetGameObject(uiSphereGuids[i]);
- if (!pSpheres[i])
- return false;
- if (pSpheres[i]->GetGoState() != GO_STATE_ACTIVE)
- return false;
+ pSpheres[i] = pInstance->instance->GetGameObject(uiSphereGuids[i]);
+ if (!pSpheres[i])
+ return false;
+ if (pSpheres[i]->GetGoState() != GO_STATE_ACTIVE)
+ return false;
}
RemovePrison();
return true;
}
+
void RemovePrison()
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
@@ -296,14 +321,17 @@ struct TRINITY_DLL_DECL boss_taldaramAI : public ScriptedAI
pInstance->HandleGameObject(prison_GUID,true);
}
};
+
struct TRINITY_DLL_DECL mob_taldaram_flamesphereAI : public ScriptedAI
{
mob_taldaram_flamesphereAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiDespawnTimer;
ScriptedInstance* pInstance;
+
void Reset()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -315,12 +343,15 @@ struct TRINITY_DLL_DECL mob_taldaram_flamesphereAI : public ScriptedAI
DoCast(m_creature, HEROIC(SPELL_FLAME_SPHERE_PERIODIC, H_SPELL_FLAME_SPHERE_PERIODIC));
uiDespawnTimer = 10000;
}
+
void EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
+
void JustDied(Unit* slayer)
{
DoCast(m_creature, SPELL_FLAME_SPHERE_DEATH_EFFECT);
}
+
void UpdateAI(const uint32 diff)
{
if (uiDespawnTimer < diff)
@@ -329,43 +360,53 @@ struct TRINITY_DLL_DECL mob_taldaram_flamesphereAI : public ScriptedAI
uiDespawnTimer -= diff;
}
};
+
CreatureAI* GetAI_boss_taldaram(Creature* pCreature)
{
return new boss_taldaramAI (pCreature);
}
+
CreatureAI* GetAI_mob_taldaram_flamesphere(Creature* pCreature)
{
return new mob_taldaram_flamesphereAI (pCreature);
}
+
bool GOHello_prince_taldaram_sphere(Player *pPlayer, GameObject *pGO)
{
ScriptedInstance *pInstance = pGO->GetInstanceData();
+
Creature *pPrinceTaldaram = Unit::GetCreature(*pGO, pInstance ? pInstance->GetData64(DATA_PRINCE_TALDARAM) : 0);
if (pPrinceTaldaram && pPrinceTaldaram->isAlive())
{
// maybe these are hacks :(
pGO->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK1);
pGO->SetGoState(GO_STATE_ACTIVE);
+
switch(pGO->GetEntry())
{
case 193093: pInstance->SetData(DATA_SPHERE1_EVENT,IN_PROGRESS); break;
case 193094: pInstance->SetData(DATA_SPHERE2_EVENT,IN_PROGRESS); break;
}
+
CAST_AI(boss_taldaramAI, pPrinceTaldaram->AI())->CheckSpheres();
}
return true;
}
+
void AddSC_boss_taldaram()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_taldaram";
newscript->GetAI = &GetAI_boss_taldaram;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_taldaram_flamesphere";
newscript->GetAI = &GetAI_mob_taldaram_flamesphere;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "prince_taldaram_sphere";
newscript->pGOHello = &GOHello_prince_taldaram_sphere;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/def_ahnkahet.h b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/def_ahnkahet.h
index c988d44688c..9c17171d019 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/def_ahnkahet.h
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/def_ahnkahet.h
@@ -15,21 +15,26 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DEF_AHNKAHET_H
#define DEF_AHNKAHET_H
+
#define DATA_ELDER_NADOX 1
#define DATA_PRINCE_TALDARAM 2
#define DATA_JEDOGA_SHADOWSEEKER 3
#define DATA_HERALD_VOLAZJ 4
#define DATA_AMANITAR 5
+
#define DATA_ELDER_NADOX_EVENT 6
#define DATA_PRINCE_TALDARAM_EVENT 7
#define DATA_JEDOGA_SHADOWSEEKER_EVENT 8
#define DATA_HERALD_VOLAZJ_EVENT 9
#define DATA_AMANITAR_EVENT 10
+
#define DATA_SPHERE1 11
#define DATA_SPHERE2 12
#define DATA_SPHERE1_EVENT 13
#define DATA_SPHERE2_EVENT 14
#define DATA_PRINCE_TALDARAM_PLATFORM 15
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp
index bb522258266..c8a0fc22c3c 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/ahnkahet/instance_ahnkahet.cpp
@@ -15,15 +15,19 @@
* along 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_Azjol_Nerub
SD%Complete: 0
SDComment:
SDCategory: Azjol Nerub
EndScriptData */
+
#include "precompiled.h"
#include "def_ahnkahet.h"
+
#define MAX_ENCOUNTER 5
+
/* Ahn'kahet encounters:
0 - Elder Nadox
1 - Prince Taldaram
@@ -31,19 +35,24 @@ EndScriptData */
3 - Herald Volazj
4 - Amanitar (Heroic only)
*/
+
struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
{
instance_ahnkahet(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 Elder_Nadox;
uint64 Prince_Taldaram;
uint64 Jedoga_Shadowseeker;
uint64 Herald_Volazj;
uint64 Amanitar;
+
uint64 Prince_TaldaramSpheres[2];
uint64 Prince_TaldaramPlatform;
uint64 Prince_TaldaramGate;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint32 spheres[2];
+
void Initialize()
{
Elder_Nadox =0;
@@ -51,18 +60,22 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
Jedoga_Shadowseeker =0;
Herald_Volazj =0;
Amanitar =0;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
m_auiEncounter[i] = NOT_STARTED;
spheres[0] = NOT_STARTED;
spheres[1] = NOT_STARTED;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -74,6 +87,7 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
case 30258: Amanitar = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -100,6 +114,7 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
if (m_auiEncounter[1] == DONE)HandleGameObject(NULL,true,pGo);break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -115,37 +130,37 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
{
- case DATA_ELDER_NADOX_EVENT:
- m_auiEncounter[0] = data;
- break;
- case DATA_PRINCE_TALDARAM_EVENT:
- if (data == DONE)
- HandleGameObject(Prince_TaldaramGate,true);
- m_auiEncounter[1] = data;
- break;
- case DATA_JEDOGA_SHADOWSEEKER_EVENT:
- m_auiEncounter[2] = data;
- break;
- case DATA_HERALD_VOLAZJ:
- m_auiEncounter[3] = data;
- break;
- case DATA_AMANITAR:
- m_auiEncounter[4] = data;
- break;
- case DATA_SPHERE1_EVENT:
- spheres[0] = data;
- break;
- case DATA_SPHERE2_EVENT:
- spheres[1] = data;
- break;
- }
+ case DATA_ELDER_NADOX_EVENT:
+ m_auiEncounter[0] = data;break;
+ case DATA_PRINCE_TALDARAM_EVENT:
+ if (data == DONE)
+ {
+ HandleGameObject(Prince_TaldaramGate,true);
+ }
+ m_auiEncounter[1] = data; break;
+ case DATA_JEDOGA_SHADOWSEEKER_EVENT:
+ m_auiEncounter[2] = data; break;
+ case DATA_HERALD_VOLAZJ:
+ m_auiEncounter[3] = data; break;
+ case DATA_AMANITAR:
+ m_auiEncounter[4] = data; break;
+ case DATA_SPHERE1_EVENT:
+ spheres[0] = data; break;
+ case DATA_SPHERE2_EVENT:
+ spheres[1] = data; break;
+ }
+
if (data == DONE)
+ {
SaveToDB();
+ }
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -160,18 +175,24 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::string str_data;
+
std::ostringstream saveStream;
saveStream << "A K " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << m_auiEncounter[3] << " " << m_auiEncounter[4] << " "
<< spheres[0] << " " << spheres[1];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -179,11 +200,15 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0,data1,data2,data3,data4, data5, data6;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4 >> data5 >> data6;
+
if (dataHead1 == 'A' && dataHead2 == 'K')
{
m_auiEncounter[0] = data0;
@@ -191,19 +216,25 @@ struct TRINITY_DLL_DECL instance_ahnkahet : public ScriptedInstance
m_auiEncounter[2] = data2;
m_auiEncounter[3] = data3;
m_auiEncounter[4] = data4;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
spheres[0] = data5;
spheres[1] = data6;
+
}else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_ahnkahet(Map* pMap)
{
return new instance_ahnkahet(pMap);
}
+
void AddSC_instance_ahnkahet()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp
index 18b95192c96..8172c9a3f66 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_anubarak.cpp
@@ -15,6 +15,7 @@
* along 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_anubarak
SDAuthor: LordVanMartin
@@ -22,16 +23,20 @@ SD%Complete: 0
SDComment:
SDCategory: Azjol Nerub
EndScriptData */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_anub_arak' where entry = '';
*** SQL END ***/
+
#include "precompiled.h"
#include "def_azjol_nerub.h"
+
//Spells
#define SPELL_CARRION_BEETLES 53520
#define SPELL_LOCUST_SWARM 53467
#define SPELL_IMPALE 53454
#define SPELL_POUND 53472
+
// not in db
//Yell
#define SAY_INTRO -1601010
@@ -45,10 +50,13 @@ update creature_template set scriptname = 'boss_anub_arak' where entry = '';
#define SAY_SUBMERGE_1 -1601008
#define SAY_SUBMERGE_2 -1601009
#define SAY_DEATH -1601004
+
struct TRINITY_DLL_DECL boss_anub_arakAI : public ScriptedAI
{
boss_anub_arakAI(Creature *c) : ScriptedAI(c) {}
+
uint32 phase;
+
void Reset() {}
void EnterCombat(Unit* who)
{
@@ -61,11 +69,14 @@ struct TRINITY_DLL_DECL boss_anub_arakAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
phase =1;
+
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= 33)
phase = 2;
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) == 15)
phase = 3;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -76,16 +87,20 @@ struct TRINITY_DLL_DECL boss_anub_arakAI : public ScriptedAI
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_anub_arak(Creature* pCreature)
{
return new boss_anub_arakAI (pCreature);
}
+
void AddSC_boss_anub_arak()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_anub_arak";
newscript->GetAI = &GetAI_boss_anub_arak;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp
index d27997ee8cd..b9a0a7e670d 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_hadronox.cpp
@@ -15,15 +15,19 @@
* along 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_hadronox
SD%Complete: 0
SDComment: Placeholder
SDCategory: Azjol Nerub
EndScriptData */
+
#include "precompiled.h"
#include "def_azjol_nerub.h"
+
#define SPELL_WEB_FRONT_DOORS 53177//dummy
+
#define SPELL_ACID_CLOUD 53400
#define H_SPELL_ACID_CLOUD 59419
#define SPELL_LEECH_POISON 53030
@@ -32,6 +36,7 @@ EndScriptData */
#define SPELL_PIECE_ARMOR 53418
#define SPELL_WEB_GRAB 53406
#define H_SPELL_WEB_GRAB 59420
+
/* Script Data Start
SDName: Boss hadronox
SDAuthor: LordVanMartin
@@ -39,27 +44,35 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_hadronox' where entry = '';
*** SQL END ***/
+
//Spells
#define SPELL_LEECH_POISON 53030
#define SPELL_ACID_CLOUD 53400
#define SPELL_PIERCE_ARMOR 53418 //Source SUN++
#define SPELL_WEB_GRAB 53406
+
//Phase 0 => Gauntlet Event described below
//Phase 1 => Boss Encounter
+
/*When your group enters the main room (the one after the bridge), you will notice a group of 3 Nerubians.
When you engage them, 2 more groups like this one spawn behind the first one - it is important to pull the first group back,
so you don't aggro all 3. Hadronox will be under you, fighting Nerubians.
+
This is the timed gauntlet - waves of non-elite spiders
will spawn from the 3 doors located a little above the main room, and will then head down to fight Hadronox. After clearing the
main room, it is recommended to just stay in it, kill the occasional non-elites that will attack you instead of the boss, and wait for
Hadronox to make his way to you. When Hadronox enters the main room, she will web the doors, and no more non-elites will spawn.*/
+
struct TRINITY_DLL_DECL boss_hadronoxAI : public ScriptedAI
{
boss_hadronoxAI(Creature *c) : ScriptedAI(c) {}
+
uint32 phase;
+
void Reset() {}
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
@@ -69,24 +82,31 @@ struct TRINITY_DLL_DECL boss_hadronoxAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
phase =1;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer) {}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
//when Hadronox kills any enemy (that includes a party member) she will regain 10% of her HP if the target had Leech Poison on
}
};
+
CreatureAI* GetAI_boss_hadronox(Creature* pCreature)
{
return new boss_hadronoxAI (pCreature);
}
+
void AddSC_boss_hadronox()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_hadronox";
newscript->GetAI = &GetAI_boss_hadronox;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp
index 5fbca03945f..377a9d58c8f 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/boss_krikthir_the_gatewatcher.cpp
@@ -15,17 +15,21 @@
* along 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_krikthir_the_gatewatcher
SD%Complete: 80 %
SDComment: Find in the future best timers and the event is not implemented.
SDCategory: Azjol Nerub
EndScriptData */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_krik_thir' where entry = '';
*** SQL END ***/
+
#include "precompiled.h"
#include "def_azjol_nerub.h"
+
enum Spells
{
SPELL_MIND_FLAY = 52586,
@@ -33,33 +37,44 @@ enum Spells
SPELL_CURSE_OF_FATIGUE = 52592,
H_SPELL_CURSE_OF_FATIGUE = 59368,
SPELL_FRENZY = 28747, //maybe 53361
+
SPELL_SUMMON_SKITTERING_SWARMER = 52438, //AOE Effect 140, maybe 52439
SPELL_SUMMON_SKITTERING_SWARMER_1 = 52439, //Summon 3x 28735
+
H_SPELL_ACID_SPLASH = 59363,
SPELL_ACID_SPLASH = 52446,
+
SPELL_CHARGE = 16979,//maybe is another spell
SPELL_BACKSTAB = 52540,
+
SPELL_SHADOW_BOLT = 52534,
H_SPELL_SHADOW_BOLT = 59357,
SPELL_SHADOW_NOVA = 52535,
H_SPELL_SHADOW_NOVA = 59358,
+
SPELL_STRIKE = 52532,
SPELL_CLEAVE = 49806,
+
SPELL_ENRAGE = 52470,
+
SPELL_INFECTED_BITE = 52469,
H_SPELL_INFECTED_BITE = 59364,
SPELL_WEB_WRAP = 52086,//the spell is not working propperly
+
SPELL_BLINDING_WEBS = 52524,
H_SPELL_BLINDING_WEBS = 59365,
+
SPELL_POSION_SPRAY = 52493,
H_SPELL_POSION_SPRAY = 59366,
};
+
enum Mobs
{
MOB_SKITTERING_SWARMER = 28735,
MOB_SKITTERING_SWARMER_CONTROLLER = 32593,
MOB_SKITTERING_INFECTIOR = 28736,
};
+
enum Yells
{
SAY_AGGRO = -1601000,
@@ -87,26 +102,33 @@ struct TRINITY_DLL_DECL boss_krik_thirAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint32 MindFlayTimer;
uint32 CurseFatigueTimer;
uint32 SummonTimer;
+
void Reset()
{
MindFlayTimer = 15000;
CurseFatigueTimer = 12000;
+
if (pInstance)
pInstance->SetData(DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
Summon();
SummonTimer = 15000;
+
if (pInstance)
pInstance->SetData(DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, IN_PROGRESS);
}
+
void Summon()
{
m_creature->SummonCreature(MOB_SKITTERING_SWARMER,566.164,682.087,769.079,2.21657,TEMPSUMMON_TIMED_DESPAWN,25000);
@@ -126,37 +148,46 @@ struct TRINITY_DLL_DECL boss_krik_thirAI : public ScriptedAI
m_creature->SummonCreature(MOB_SKITTERING_SWARMER,552.625,706.408,777.177,3.4383,TEMPSUMMON_TIMED_DESPAWN,25000);
m_creature->SummonCreature(MOB_SKITTERING_SWARMER,552.625,706.408,777.177,3.4383,TEMPSUMMON_TIMED_DESPAWN,25000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(SummonTimer < diff)
{
Summon();
SummonTimer = 15000;
} else SummonTimer -= diff;
+
if (MindFlayTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_MIND_FLAY, H_SPELL_MIND_FLAY));
MindFlayTimer = 15000;
} else MindFlayTimer -= diff;
+
if (CurseFatigueTimer < diff)
{
//WowWiki say "Curse of Fatigue-Kirk'thir will cast Curse of Fatigue on 2-3 targets periodically. "
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
Unit* target_1 = SelectUnit(SELECT_TARGET_RANDOM, 1);
+
DoCast(target, HEROIC(SPELL_CURSE_OF_FATIGUE, H_SPELL_CURSE_OF_FATIGUE));
DoCast(target_1, HEROIC(SPELL_CURSE_OF_FATIGUE, H_SPELL_CURSE_OF_FATIGUE));
+
CurseFatigueTimer = 10000;
} else CurseFatigueTimer -= diff;
+
if (!m_creature->HasAura(SPELL_FRENZY) && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 10)
m_creature->CastSpell(m_creature,SPELL_FRENZY,true);
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(DATA_KRIKTHIR_THE_GATEWATCHER_EVENT, DONE);
@@ -170,7 +201,7 @@ struct TRINITY_DLL_DECL boss_krik_thirAI : public ScriptedAI
HeroicMode && pMap && pMap->IsDungeon() && AchievWatchHimDie)
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievWatchHimDie);
}
}
@@ -179,36 +210,46 @@ struct TRINITY_DLL_DECL boss_krik_thirAI : public ScriptedAI
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustSummoned(Creature* summoned)
{
summoned->GetMotionMaster()->MovePoint(m_creature->GetEntry(),m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ());
}
};
+
struct TRINITY_DLL_DECL npc_skittering_infectorAI : public ScriptedAI
{
npc_skittering_infectorAI(Creature *c) : ScriptedAI(c) {}
+
void JustDied(Unit* killer)
{
//The spell is not working propperly
DoCast(m_creature->getVictim(),HEROIC(SPELL_ACID_SPLASH, H_SPELL_ACID_SPLASH), true);
}
+
};
+
struct TRINITY_DLL_DECL npc_anub_ar_skirmisherAI : public ScriptedAI
{
npc_anub_ar_skirmisherAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ChargeTimer;
uint32 BackstabTimer;
+
void Reset()
{
ChargeTimer = 11000;
BackstabTimer = 7000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(ChargeTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
@@ -217,238 +258,301 @@ struct TRINITY_DLL_DECL npc_anub_ar_skirmisherAI : public ScriptedAI
m_creature->AddThreat(pTarget,1.0f);
DoCast(pTarget,SPELL_CHARGE,true);
}
+
ChargeTimer = 15000;
} else ChargeTimer -= diff;
+
if(BackstabTimer < diff)
{
DoCast(m_creature->getVictim(),SPELL_BACKSTAB);
BackstabTimer = 12000;
} else BackstabTimer -= diff;
+
DoMeleeAttackIfReady();
+
}
};
+
struct TRINITY_DLL_DECL npc_anub_ar_shadowcasterAI : public ScriptedAI
{
npc_anub_ar_shadowcasterAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowBoltTimer;
uint32 ShadowNovaTimer;
+
void Reset()
{
ShadowBoltTimer = 6000;
ShadowNovaTimer = 15000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(ShadowBoltTimer < diff)
{
if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
DoCast(target, HEROIC(SPELL_SHADOW_BOLT, H_SPELL_SHADOW_BOLT), true);
ShadowBoltTimer = 15000;
} else ShadowBoltTimer -= diff;
+
if(ShadowNovaTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_SHADOW_NOVA, H_SPELL_SHADOW_NOVA), true);
ShadowNovaTimer = 17000;
} else ShadowNovaTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL npc_anub_ar_warriorAI : public ScriptedAI
{
npc_anub_ar_warriorAI(Creature *c) : ScriptedAI(c){}
+
uint32 CleaveTimer;
uint32 StrikeTimer;
+
void Reset()
{
CleaveTimer = 11000;
StrikeTimer = 6000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(StrikeTimer < diff)
{
m_creature->CastSpell(m_creature->getVictim(), SPELL_STRIKE, true);
StrikeTimer = 15000;
} else StrikeTimer -= diff;
+
if(CleaveTimer < diff)
{
m_creature->CastSpell(m_creature->getVictim(), SPELL_CLEAVE, true);
CleaveTimer = 17000;
} else CleaveTimer -= diff;
+
DoMeleeAttackIfReady();
+
}
+
};
+
struct TRINITY_DLL_DECL npc_watcher_gashraAI : public ScriptedAI
{
npc_watcher_gashraAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WebWrapTimer;
uint32 InfectedBiteTimer;
+
void Reset()
{
WebWrapTimer = 11000;
InfectedBiteTimer = 4000;
}
+
void EnterCombat(Unit* who)
{
m_creature->CastSpell(m_creature,SPELL_ENRAGE,true);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(WebWrapTimer < diff)
{
if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
m_creature->CastSpell(target, SPELL_WEB_WRAP,true);
WebWrapTimer = 17000;
} else WebWrapTimer -= diff;
+
if(InfectedBiteTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_INFECTED_BITE, H_SPELL_INFECTED_BITE), true);
InfectedBiteTimer = 15000;
} else InfectedBiteTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL npc_watcher_narjilAI : public ScriptedAI
{
npc_watcher_narjilAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WebWrapTimer;
uint32 InfectedBiteTimer;
uint32 BlindingWebsTimer;
+
void Reset()
{
WebWrapTimer = 11000;
InfectedBiteTimer = 4000;
BlindingWebsTimer = 17000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(WebWrapTimer < diff)
{
if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
DoCast(target, SPELL_WEB_WRAP,true);
WebWrapTimer = 15000;
} else WebWrapTimer -= diff;
+
if(InfectedBiteTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_INFECTED_BITE, H_SPELL_INFECTED_BITE), true);
InfectedBiteTimer = 11000;
} else InfectedBiteTimer -= diff;
+
if(BlindingWebsTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_BLINDING_WEBS, H_SPELL_BLINDING_WEBS), true);
BlindingWebsTimer = 17000;
} else BlindingWebsTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL npc_watcher_silthikAI : public ScriptedAI
{
npc_watcher_silthikAI(Creature *c) : ScriptedAI(c) {}
+
uint32 WebWrapTimer;
uint32 InfectedBiteTimer;
uint32 PosionSprayTimer;
+
void Reset()
{
WebWrapTimer = 11000;
InfectedBiteTimer = 4000;
PosionSprayTimer = 15000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(WebWrapTimer < diff)
{
if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
DoCast(target, SPELL_WEB_WRAP,true);
+
WebWrapTimer = 15000;
} else WebWrapTimer -= diff;
+
if(InfectedBiteTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_INFECTED_BITE, H_SPELL_INFECTED_BITE), true);
InfectedBiteTimer = 15000;
} else InfectedBiteTimer -= diff;
+
if(PosionSprayTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_POSION_SPRAY, H_SPELL_POSION_SPRAY), true);
PosionSprayTimer = 17000;
} else PosionSprayTimer -= diff;
+
DoMeleeAttackIfReady();
+
}
};
+
CreatureAI* GetAI_boss_krik_thir(Creature* pCreature)
{
return new boss_krik_thirAI (pCreature);
}
+
CreatureAI* GetAI_npc_anub_ar_skirmisher (Creature* pCreature)
{
return new npc_anub_ar_skirmisherAI (pCreature);
}
+
CreatureAI* GetAI_npc_skittering_infector (Creature* pCreature)
{
return new npc_skittering_infectorAI (pCreature);
}
+
CreatureAI* GetAI_npc_anub_ar_shadowcaster (Creature* pCreature)
{
return new npc_anub_ar_shadowcasterAI (pCreature);
}
+
CreatureAI* GetAI_npc_anub_ar_warrior (Creature* pCreature)
{
return new npc_anub_ar_warriorAI (pCreature);
}
+
CreatureAI* GetAI_npc_watcher_gashra (Creature* pCreature)
{
return new npc_watcher_gashraAI (pCreature);
}
+
CreatureAI* GetAI_npc_watcher_narjil (Creature* pCreature)
{
return new npc_watcher_narjilAI (pCreature);
}
+
CreatureAI* GetAI_npc_watcher_silthik (Creature* pCreature)
{
return new npc_watcher_silthikAI (pCreature);
}
+
void AddSC_boss_krik_thir()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_krik_thir";
newscript->GetAI = &GetAI_boss_krik_thir;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_skittering_infector";
newscript->GetAI = &GetAI_npc_skittering_infector;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_anub_ar_skirmisher";
newscript->GetAI = &GetAI_npc_anub_ar_skirmisher;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_anub_ar_shadowcaster";
newscript->GetAI = &GetAI_npc_anub_ar_shadowcaster;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_watcher_gashra";
newscript->GetAI = &GetAI_npc_watcher_gashra;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_anub_ar_warrior";
newscript->GetAI = &GetAI_npc_anub_ar_warrior;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_watcher_silthik";
newscript->GetAI = &GetAI_npc_watcher_silthik;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_watcher_narjil";
newscript->GetAI = &GetAI_npc_watcher_narjil;
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/def_azjol_nerub.h b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/def_azjol_nerub.h
index 64bb38d235c..df02f9e13d3 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/def_azjol_nerub.h
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/def_azjol_nerub.h
@@ -15,8 +15,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DEF_AZJOL_NERUB_H
#define DEF_AZJOL_NERUB_H
+
enum Data64
{
DATA_KRIKTHIR_THE_GATEWATCHER,
@@ -32,4 +34,5 @@ enum Data
DATA_HADRONOX_EVENT,
DATA_ANUBARAK_EVENT
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp
index 05b507c9ffa..d06335bdbd3 100644
--- a/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp
+++ b/src/bindings/scripts/scripts/northrend/azjol_nerub/azjol_nerub/instance_azjol_nerub.cpp
@@ -15,34 +15,44 @@
* along 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_Azjol_Nerub
SD%Complete: 0
SDComment: Placeholder
SDCategory: Azjol Nerub
EndScriptData */
+
#include "precompiled.h"
#include "def_azjol_nerub.h"
+
#define MAX_ENCOUNTER 3
+
/* Azjol Nerub encounters:
0 - Krik'thir the Gatewatcher
1 - Hadronox
2 - Anub'arak
*/
+
struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
{
instance_azjol_nerub(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 m_uiKrikthir;
uint64 m_uiHadronox;
uint64 m_uiAnubarak;
uint64 m_uiWatcherGashra;
uint64 m_uiWatcherSilthik;
uint64 m_uiWatcherNarjil;
+
uint64 m_uiKrikthirDoor;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiKrikthir = 0;
m_uiHadronox = 0;
m_uiAnubarak = 0;
@@ -51,13 +61,16 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
m_uiWatcherNarjil = 0;
m_uiKrikthirDoor = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -70,6 +83,7 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
case 28729: m_uiWatcherNarjil = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch (pGo->GetEntry())
@@ -81,6 +95,7 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -92,8 +107,10 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
case DATA_WATCHER_SILTHIK: return m_uiWatcherSilthik;
case DATA_WATCHER_NARJIL: return m_uiWatcherNarjil;
}
+
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -108,11 +125,13 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
case DATA_ANUBARAK_EVENT:
m_auiEncounter[2] = data; break;
}
+
if (data == DONE)
{
SaveToDB();
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -121,19 +140,26 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
case DATA_HADRONOX_EVENT: return m_auiEncounter[1];
case DATA_ANUBARAK_EVENT: return m_auiEncounter[2];
}
+
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::string str_data;
+
std::ostringstream saveStream;
saveStream << "A N " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -141,27 +167,36 @@ struct TRINITY_DLL_DECL instance_azjol_nerub : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0,data1,data2;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2;
+
if (dataHead1 == 'A' && dataHead2 == 'N')
{
m_auiEncounter[0] = data0;
m_auiEncounter[1] = data1;
m_auiEncounter[2] = data2;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
}else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_azjol_nerub(Map* pMap)
{
return new instance_azjol_nerub(pMap);
}
+
void AddSC_instance_azjol_nerub()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/borean_tundra.cpp b/src/bindings/scripts/scripts/northrend/borean_tundra.cpp
index 3bed5fb8e54..85fc89940db 100644
--- a/src/bindings/scripts/scripts/northrend/borean_tundra.cpp
+++ b/src/bindings/scripts/scripts/northrend/borean_tundra.cpp
@@ -13,12 +13,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Borean_Tundra
SD%Complete: 100
SDComment: Quest support: 11708. Taxi vendors.
SDCategory: Borean Tundra
EndScriptData */
+
/* ContentData
npc_fizzcrank_fullthrottle
npc_surristrasz
@@ -26,12 +28,16 @@ npc_tiare
npc_iruk
npc_corastrasza
EndContentData */
+
#include "precompiled.h"
+
/*######
## npc_fizzcrank_fullthrottle
######*/
+
#define GOSSIP_ITEM_GO_ON "Go on."
#define GOSSIP_ITEM_TELL_ME "Tell me what's going on out here, Fizzcrank."
+
enum eFizzcrank
{
GOSSIP_TEXTID_FIZZCRANK1 = 12456,
@@ -43,17 +49,22 @@ enum eFizzcrank
GOSSIP_TEXTID_FIZZCRANK7 = 12462,
GOSSIP_TEXTID_FIZZCRANK8 = 12463,
GOSSIP_TEXTID_FIZZCRANK9 = 12464,
+
QUEST_THE_MECHAGNOMES = 11708
};
+
bool GossipHello_npc_fizzcrank_fullthrottle(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_THE_MECHAGNOMES) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELL_ME, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_fizzcrank_fullthrottle(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -97,53 +108,68 @@ bool GossipSelect_npc_fizzcrank_fullthrottle(Player* pPlayer, Creature* pCreatur
}
return true;
}
+
/*######
## npc_surristrasz
######*/
+
#define GOSSIP_ITEM_FREE_FLIGHT "I'd like passage to the Transitus Shield."
#define GOSSIP_ITEM_FLIGHT "May I use a drake to fly elsewhere?"
+
enum eSurristrasz
{
SPELL_ABMER_TO_COLDARRA = 46064
};
+
bool GossipHello_npc_surristrasz(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isTaxi())
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_FREE_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_GOSSIP);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_ITEM_FLIGHT, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_TAXIVENDOR);
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_surristrasz(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_OPTION_GOSSIP)
{
pPlayer->CLOSE_GOSSIP_MENU();
+
//TaxiPath 795 (amber to coldarra)
pPlayer->CastSpell(pPlayer, SPELL_ABMER_TO_COLDARRA, true);
}
+
if (uiAction == GOSSIP_OPTION_TAXIVENDOR)
pPlayer->GetSession()->SendTaxiMenu(pCreature);
+
return true;
}
+
/*######
## npc_tiare
######*/
+
#define GOSSIP_ITEM_TELEPORT "Teleport me to Amber Ledge, please."
+
enum eTiare
{
SPELL_TELEPORT_COLDARRA = 50135
};
+
bool GossipHello_npc_tiare(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_GOSSIP);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_tiare(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_OPTION_GOSSIP)
@@ -153,9 +179,11 @@ bool GossipSelect_npc_tiare(Player* pPlayer, Creature* pCreature, uint32 uiSende
}
return true;
}
+
/*######
## npc_sinkhole_kill_credit
######*/
+
enum eSinkhole
{
SPELL_SET_CART = 46797,
@@ -163,22 +191,27 @@ enum eSinkhole
SPELL_SUMMON_CART = 46798,
SPELL_SUMMON_WORM = 46800,
};
+
struct TRINITY_DLL_DECL npc_sinkhole_kill_creditAI : public ScriptedAI
{
npc_sinkhole_kill_creditAI(Creature* c) : ScriptedAI(c){}
+
uint32 Phase_Timer;
uint8 Phase;
uint64 casterGuid;
+
void Reset()
{
Phase_Timer = 500;
Phase = 0;
casterGuid = 0;
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (Phase)
return;
+
if (spell->Id == SPELL_SET_CART && caster->GetTypeId() == TYPEID_PLAYER
&& CAST_PLR(caster)->GetQuestStatus(11897) == QUEST_STATUS_INCOMPLETE)
{
@@ -186,11 +219,14 @@ struct TRINITY_DLL_DECL npc_sinkhole_kill_creditAI : public ScriptedAI
casterGuid = caster->GetGUID();
}
}
+
void EnterCombat(Unit* who) { }
+
void UpdateAI(const uint32 diff)
{
if (!Phase)
return;
+
if (Phase_Timer < diff)
{
switch (Phase)
@@ -246,23 +282,31 @@ struct TRINITY_DLL_DECL npc_sinkhole_kill_creditAI : public ScriptedAI
break;
}
} else Phase_Timer -= diff;
+
}
+
};
+
CreatureAI* GetAI_npc_sinkhole_kill_credit(Creature* pCreature)
{
return new npc_sinkhole_kill_creditAI(pCreature);
}
+
/*######
## npc_khunok_the_behemoth
######*/
+
struct TRINITY_DLL_DECL npc_khunok_the_behemothAI : public ScriptedAI
{
npc_khunok_the_behemothAI(Creature *c) : ScriptedAI(c) {}
+
void MoveInLineOfSight(Unit *who)
{
ScriptedAI::MoveInLineOfSight(who);
+
if (who->GetTypeId() != TYPEID_UNIT)
return;
+
if (who->GetEntry() == 25861 && me->IsWithinDistInMap(who, 10.0f))
{
if (Unit *owner = who->GetOwner())
@@ -276,27 +320,36 @@ struct TRINITY_DLL_DECL npc_khunok_the_behemothAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_khunok_the_behemoth(Creature* pCreature)
{
return new npc_khunok_the_behemothAI(pCreature);
}
+
/*######
## npc_keristrasza
######*/
+
enum eKeristrasza
{
SPELL_TELEPORT_TO_SARAGOSA = 46772
};
+
#define GOSSIP_HELLO_KERI "I am prepared to face Saragosa!"
+
bool GossipHello_npc_keristrasza(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(11957) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_KERI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_keristrasza(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -304,57 +357,76 @@ bool GossipSelect_npc_keristrasza(Player* pPlayer, Creature* pCreature, uint32 u
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->CastSpell(pPlayer, SPELL_TELEPORT_TO_SARAGOSA, true);
}
+
return true;
}
+
/*######
## npc_corastrasza
######*/
+
#define GOSSIP_ITEM_C_1 "I... I think so..."
+
enum eCorastrasza
{
SPELL_SUMMON_WYRMREST_SKYTALON = 61240,
SPELL_WYRMREST_SKYTALON_RIDE_PERIODIC = 61244,
+
QUEST_ACES_HIGH_DAILY = 13414,
QUEST_ACES_HIGH = 13413
};
+
bool GossipHello_npc_corastrasza(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_ACES_HIGH) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(QUEST_ACES_HIGH_DAILY) == QUEST_STATUS_INCOMPLETE) //It's the same dragon for both quests.
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_C_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_corastrasza(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
{
pPlayer->CLOSE_GOSSIP_MENU();
+
pPlayer->CastSpell(pPlayer, SPELL_SUMMON_WYRMREST_SKYTALON, true);
pPlayer->CastSpell(pPlayer, SPELL_WYRMREST_SKYTALON_RIDE_PERIODIC, true);
+
}
+
return true;
}
+
/*######
## npc_iruk
######*/
+
#define GOSSIP_ITEM_I "<Search corpse for Issliruk's Totem.>"
+
enum eIruk
{
QUEST_SPIRITS_WATCH_OVER_US = 11961,
SPELL_CREATURE_TOTEM_OF_ISSLIRUK = 46816,
GOSSIP_TEXT_I = 12585
};
+
bool GossipHello_npc_iruk(Player* pPlayer, Creature* pCreature)
{
+
if (pPlayer->GetQuestStatus(QUEST_SPIRITS_WATCH_OVER_US) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_I, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->PlayerTalkClass->SendGossipMenu(GOSSIP_TEXT_I, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_iruk(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -363,13 +435,16 @@ bool GossipSelect_npc_iruk(Player* pPlayer, Creature* pCreature, uint32 uiSender
pPlayer->CastSpell(pPlayer, SPELL_CREATURE_TOTEM_OF_ISSLIRUK, true);
pPlayer->CLOSE_GOSSIP_MENU();
break;
+
}
return true;
}
/*######
## mob_nerubar_victim
######*/
+
#define WARSONG_PEON 25270
+
const uint32 nerubarVictims[3] =
{
45526, 45527, 45514
@@ -377,9 +452,11 @@ const uint32 nerubarVictims[3] =
struct TRINITY_DLL_DECL mob_nerubar_victimAI : public ScriptedAI
{
mob_nerubar_victimAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
+
void JustDied(Unit* Killer)
{
if(Killer->GetTypeId() == TYPEID_PLAYER)
@@ -405,44 +482,53 @@ CreatureAI* GetAI_mob_nerubar_victim(Creature *pCreature)
void AddSC_borean_tundra()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_fizzcrank_fullthrottle";
newscript->pGossipHello = &GossipHello_npc_fizzcrank_fullthrottle;
newscript->pGossipSelect = &GossipSelect_npc_fizzcrank_fullthrottle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_surristrasz";
newscript->pGossipHello = &GossipHello_npc_surristrasz;
newscript->pGossipSelect = &GossipSelect_npc_surristrasz;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tiare";
newscript->pGossipHello = &GossipHello_npc_tiare;
newscript->pGossipSelect = &GossipSelect_npc_tiare;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_sinkhole_kill_credit";
newscript->GetAI = &GetAI_npc_sinkhole_kill_credit;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_khunok_the_behemoth";
newscript->GetAI = &GetAI_npc_khunok_the_behemoth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_keristrasza";
newscript->pGossipHello = &GossipHello_npc_keristrasza;
newscript->pGossipSelect = &GossipSelect_npc_keristrasza;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_corastrasza";
newscript->pGossipHello = &GossipHello_npc_corastrasza;
newscript->pGossipSelect = &GossipSelect_npc_corastrasza;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_iruk";
newscript->pGossipHello = &GossipHello_npc_iruk;
newscript->pGossipSelect = &GossipSelect_npc_iruk;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_nerubar_victim";
newscript->GetAI = &GetAI_mob_nerubar_victim;
diff --git a/src/bindings/scripts/scripts/northrend/dragonblight.cpp b/src/bindings/scripts/scripts/northrend/dragonblight.cpp
index b129285a320..31deb88c069 100644
--- a/src/bindings/scripts/scripts/northrend/dragonblight.cpp
+++ b/src/bindings/scripts/scripts/northrend/dragonblight.cpp
@@ -13,32 +13,41 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Dragonblight
SD%Complete: 100
SDComment:
SDCategory: Dragonblight
EndScriptData */
+
/* ContentData
npc_alexstrasza_wr_gate
EndContentData */
+
#include "precompiled.h"
+
enum eEnums
{
QUEST_RETURN_TO_AG_A = 12499,
QUEST_RETURN_TO_AG_H = 12500,
MOVIE_ID_GATES = 14
};
+
#define GOSSIP_ITEM_WHAT_HAPPENED "Alexstrasza, can you show me what happened here?"
+
bool GossipHello_npc_alexstrasza_wr_gate(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestRewardStatus(QUEST_RETURN_TO_AG_A) || pPlayer->GetQuestRewardStatus(QUEST_RETURN_TO_AG_H))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_WHAT_HAPPENED, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_alexstrasza_wr_gate(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -46,11 +55,14 @@ bool GossipSelect_npc_alexstrasza_wr_gate(Player* pPlayer, Creature* pCreature,
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->SendMovieStart(MOVIE_ID_GATES);
}
+
return true;
}
+
void AddSC_dragonblight()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_alexstrasza_wr_gate";
newscript->pGossipHello = &GossipHello_npc_alexstrasza_wr_gate;
diff --git a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_dred.cpp b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_dred.cpp
index b4b8d22c5fa..eedc97e3e36 100644
--- a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_dred.cpp
+++ b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_dred.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_drak_tharon_keep.h"
+
enum Spells
{
SPELL_BELLOWING_ROAR = 44863, // fears the group, can be resisted/dispelled
@@ -20,43 +22,53 @@ enum Spells
SPELL_PIERCING_SLASH = 48878, //debuff -->Armor reduced by 75%
SPELL_RAPTOR_CALL = 59416 //dummy
};
+
struct TRINITY_DLL_DECL boss_dredAI : public ScriptedAI
{
boss_dredAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_DRED_EVENT,NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
pInstance->SetData(DATA_DRED_EVENT,IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
if (pInstance)
pInstance->SetData(DATA_DRED_EVENT,DONE);
}
};
+
CreatureAI* GetAI_boss_dred(Creature* pCreature)
{
return new boss_dredAI (pCreature);
}
+
void AddSC_boss_dred()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_dred";
newscript->GetAI = &GetAI_boss_dred;
diff --git a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_novos.cpp b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_novos.cpp
index eead65d916f..2f07378c992 100644
--- a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_novos.cpp
+++ b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_novos.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_novos' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_drak_tharon_keep.h"
+
enum Spells
{
SPELL_ARCANE_BLAST = 49198,
@@ -50,12 +52,15 @@ enum Achievement
{
ACHIEVEMENT_OH_NOVOS = 2057
};
+
struct Location
{
float x,y,z;
};
+
static Location AddSpawnPoint = { -379.20, -816.76, 59.70};
static Location AddDestinyPoint = { -282.169, -711.369, 27.375};
+
struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
{
boss_novosAI(Creature *c) : Scripted_NoMovementAI(c)
@@ -63,12 +68,18 @@ struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
pInstance = c->GetInstanceData();
Reset();
}
+
uint32 uiTimer;
uint32 uiCrystalHandlerTimer;
+
bool bAchiev;
+
std::list<uint64> luiCrystals;
+
CombatPhase Phase;
+
ScriptedInstance* pInstance;
+
void Reset()
{
Phase = IDLE;
@@ -88,6 +99,7 @@ struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
}
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
@@ -108,6 +120,7 @@ struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void UpdateAI(const uint32 diff)
{
switch (Phase)
@@ -147,6 +160,7 @@ struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
{
if (pInstance)
pInstance->SetData(DATA_NOVOS_EVENT, DONE);
+
if (HeroicMode && bAchiev)
{
AchievementEntry const *AchievOhNovos = GetAchievementStore()->LookupEntry(ACHIEVEMENT_OH_NOVOS);
@@ -156,18 +170,20 @@ struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievOhNovos);
}
}
}
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
DoScriptText(SAY_KILL, m_creature);
}
+
void RemoveCrystal()
{
if (!luiCrystals.empty())
@@ -188,26 +204,32 @@ struct TRINITY_DLL_DECL boss_novosAI : public Scripted_NoMovementAI
}
}
};
+
struct TRINITY_DLL_DECL mob_crystal_handlerAI : public ScriptedAI
{
mob_crystal_handlerAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
void JustDied(Unit* killer)
{
if (Creature* pNovos = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_NOVOS) : 0))
CAST_AI(boss_novosAI,pNovos->AI())->RemoveCrystal();
}
};
+
struct TRINITY_DLL_DECL mob_novos_minionAI : public ScriptedAI
{
mob_novos_minionAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
void MovementInform(uint32 type, uint32 id)
{
if(type != POINT_MOTION_TYPE)
@@ -216,29 +238,36 @@ struct TRINITY_DLL_DECL mob_novos_minionAI : public ScriptedAI
CAST_AI(boss_novosAI, pNovos->AI())->bAchiev = false;
}
};
+
CreatureAI* GetAI_boss_novos(Creature* pCreature)
{
return new boss_novosAI (pCreature);
}
+
CreatureAI* GetAI_mob_crystal_handler(Creature* pCreature)
{
return new mob_crystal_handlerAI (pCreature);
}
+
CreatureAI* GetAI_mob_novos_minion(Creature* pCreature)
{
return new mob_novos_minionAI (pCreature);
}
+
void AddSC_boss_novos()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_novos";
newscript->GetAI = &GetAI_boss_novos;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_crystal_handler";
newscript->GetAI = &GetAI_mob_crystal_handler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_novos_minion";
newscript->GetAI = &GetAI_mob_novos_minion;
diff --git a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp
index 3d65f24fc89..185d062b11a 100644
--- a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp
+++ b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_tharon_ja.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_tharon_ja' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_drak_tharon_keep.h"
+
enum Spells
{
SPELL_COURSE_OF_LIFE = 49527,
@@ -38,6 +40,7 @@ enum PlayerSkills
};
//Phase 1 all abilities except Eye beam
//Phase 2 turns players to skeletons with new abilities, boss grows skin
+
//not in db
enum Yells
{
@@ -50,13 +53,16 @@ enum Yells
SAY_SKELETON_2 = -1600017,
SAY_DEATH = -1600018
};
+
struct TRINITY_DLL_DECL boss_tharon_jaAI : public ScriptedAI
{
boss_tharon_jaAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
void Reset()
{
if (pInstance)
@@ -65,6 +71,7 @@ struct TRINITY_DLL_DECL boss_tharon_jaAI : public ScriptedAI
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_THARON_JA_EVENT, IN_PROGRESS);
}
@@ -75,26 +82,33 @@ struct TRINITY_DLL_DECL boss_tharon_jaAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2),m_creature);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH,m_creature);
+
if (pInstance)
pInstance->SetData(DATA_THARON_JA_EVENT, DONE);
}
};
+
CreatureAI* GetAI_boss_tharon_ja(Creature* pCreature)
{
return new boss_tharon_jaAI (pCreature);
}
+
void AddSC_boss_tharon_ja()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_tharon_ja";
newscript->GetAI = &GetAI_boss_tharon_ja;
diff --git a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_trollgore.cpp b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_trollgore.cpp
index b2b16a8b234..64c476296cc 100644
--- a/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_trollgore.cpp
+++ b/src/bindings/scripts/scripts/northrend/draktharon_keep/boss_trollgore.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_trollgore' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_drak_tharon_keep.h"
+
enum Spells
{
SPELL_INFECTED_WOUND = 49637,
@@ -29,21 +31,26 @@ enum Yells
SAY_EXPLODE = -1600009,
SAY_DEATH = -1600010
};
+
struct TRINITY_DLL_DECL boss_trollgoreAI : public ScriptedAI
{
boss_trollgoreAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_TROLLGORE_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_TROLLGORE_EVENT, IN_PROGRESS);
}
@@ -54,11 +61,13 @@ struct TRINITY_DLL_DECL boss_trollgoreAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_TROLLGORE_EVENT, DONE);
}
@@ -69,13 +78,16 @@ struct TRINITY_DLL_DECL boss_trollgoreAI : public ScriptedAI
DoScriptText(SAY_KILL, m_creature);
}
};
+
CreatureAI* GetAI_boss_trollgore(Creature* pCreature)
{
return new boss_trollgoreAI (pCreature);
}
+
void AddSC_boss_trollgore()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_trollgore";
newscript->GetAI = &GetAI_boss_trollgore;
diff --git a/src/bindings/scripts/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp b/src/bindings/scripts/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp
index bfbf25b80f7..4b498223838 100644
--- a/src/bindings/scripts/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp
+++ b/src/bindings/scripts/scripts/northrend/draktharon_keep/instance_drak_tharon_keep.cpp
@@ -1,5 +1,6 @@
#include "precompiled.h"
#include "def_drak_tharon_keep.h"
+
#define MAX_ENCOUNTER 4
/* Drak'Tharon Keep encounters:
0 - Trollgore
@@ -7,19 +8,25 @@
2 - King Dred
3 - Tharon Ja
*/
+
struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
{
instance_drak_tharon(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 uiTrollgore;
uint64 uiNovos;
uint64 uiDred;
uint64 uiTharonJa;
+
uint64 uiNovosCrystal1;
uint64 uiNovosCrystal2;
uint64 uiNovosCrystal3;
uint64 uiNovosCrystal4;
+
uint8 m_auiEncounter[MAX_ENCOUNTER];
+
std::string str_data;
+
void Initialize()
{
uiTrollgore = 0;
@@ -31,12 +38,15 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
uiNovosCrystal3 = 0;
uiNovosCrystal4 = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -55,6 +65,7 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -73,6 +84,7 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -86,8 +98,10 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
case DATA_NOVOS_CRYSTAL_3: return uiNovosCrystal3;
case DATA_NOVOS_CRYSTAL_4: return uiNovosCrystal4;
}
+
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -105,11 +119,13 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
m_auiEncounter[3] = data;
break;
}
+
if (data == DONE)
{
SaveToDB();
}
}
+
uint32 GetData(uint32 type)
{
switch (type)
@@ -121,17 +137,23 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::string str_data;
+
std::ostringstream saveStream;
saveStream << "D K " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << m_auiEncounter[3];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -139,28 +161,36 @@ struct TRINITY_DLL_DECL instance_drak_tharon : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0,data1,data2,data3;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3;
+
if (dataHead1 == 'D' && dataHead2 == 'K')
{
m_auiEncounter[0] = data0;
m_auiEncounter[1] = data1;
m_auiEncounter[2] = data2;
m_auiEncounter[3] = data3;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
}else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_drak_tharon(Map* pMap)
{
return new instance_drak_tharon(pMap);
}
+
void AddSC_instance_drak_tharon()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/grizzly_hills.cpp b/src/bindings/scripts/scripts/northrend/grizzly_hills.cpp
index 0e289b2dfbc..2d115bcdeb8 100644
--- a/src/bindings/scripts/scripts/northrend/grizzly_hills.cpp
+++ b/src/bindings/scripts/scripts/northrend/grizzly_hills.cpp
@@ -13,40 +13,51 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Grizzly_Hills
SD%Complete: 80
SDComment: Quest support: 12231, 12247
SDCategory: Grizzly Hills
EndScriptData */
+
/* ContentData
npc_orsonn_and_kodian
EndContentData */
+
#include "precompiled.h"
+
#define GOSSIP_ITEM1 "You're free to go Orsonn, but first tell me what's wrong with the furbolg."
#define GOSSIP_ITEM2 "What happened then?"
#define GOSSIP_ITEM3 "Thank you, Son of Ursoc. I'll see what can be done."
#define GOSSIP_ITEM4 "Who was this stranger?"
#define GOSSIP_ITEM5 "Thank you, Kodian. I'll do what I can."
+
enum eEnums
{
GOSSIP_TEXTID_ORSONN1 = 12793,
GOSSIP_TEXTID_ORSONN2 = 12794,
GOSSIP_TEXTID_ORSONN3 = 12796,
+
GOSSIP_TEXTID_KODIAN1 = 12797,
GOSSIP_TEXTID_KODIAN2 = 12798,
+
NPC_ORSONN = 27274,
NPC_KODIAN = 27275,
+
//trigger creatures
NPC_ORSONN_CREDIT = 27322,
NPC_KODIAN_CREDIT = 27321,
+
QUEST_CHILDREN_OF_URSOC = 12247,
QUEST_THE_BEAR_GODS_OFFSPRING = 12231
};
+
bool GossipHello_npc_orsonn_and_kodian(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_CHILDREN_OF_URSOC) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(QUEST_THE_BEAR_GODS_OFFSPRING) == QUEST_STATUS_INCOMPLETE)
{
switch(pCreature->GetEntry())
@@ -69,9 +80,11 @@ bool GossipHello_npc_orsonn_and_kodian(Player* pPlayer, Creature* pCreature)
break;
}
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_orsonn_and_kodian(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -88,6 +101,7 @@ bool GossipSelect_npc_orsonn_and_kodian(Player* pPlayer, Creature* pCreature, ui
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->TalkedToCreature(NPC_ORSONN_CREDIT, pCreature->GetGUID());
break;
+
case GOSSIP_ACTION_INFO_DEF+4:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_KODIAN2, pCreature->GetGUID());
@@ -97,11 +111,14 @@ bool GossipSelect_npc_orsonn_and_kodian(Player* pPlayer, Creature* pCreature, ui
pPlayer->TalkedToCreature(NPC_KODIAN_CREDIT, pCreature->GetGUID());
break;
}
+
return true;
}
+
void AddSC_grizzly_hills()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "npc_orsonn_and_kodian";
newscript->pGossipHello = &GossipHello_npc_orsonn_and_kodian;
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/boss_drakkari_colossus.cpp b/src/bindings/scripts/scripts/northrend/gundrak/boss_drakkari_colossus.cpp
index 4542cbd124f..3ca1abdd8f1 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/boss_drakkari_colossus.cpp
+++ b/src/bindings/scripts/scripts/northrend/gundrak/boss_drakkari_colossus.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* Script Data Start
SDName: Boss Drakkari Colossus
SDAuthor: Manuel
@@ -22,137 +23,178 @@ SD%Complete: 95 %
SDComment: The event with the Living Mojos is not implemented, just is done that when one of the mojos around the boss take damage will make that the boss enter in combat!
SDCategory:
Script Data End */
+
#include "precompiled.h"
#include "def_gundrak.h"
+
enum Spells
{
SPELL_EMERGE = 54850,
SPELL_EMERGE_2 = 54851,
+
SPELL_MIGHTY_BLOW = 54719,
+
SPELL_MERGE = 54878,
SPELL_SURGE = 54801,
+
SPELL_FREEZE_ANIM = 16245,
+
SPELL_MOJO_PUDDLE = 55627,
H_SPELL_MOJO_PUDDLE = 58994,
+
SPELL_MOJO_WAVE = 55626,
H_SPELL_MOJO_WAVE = 58993
};
+
enum Entries
{
DRAKKARI_COLOSSUS = 29307,
//DRAKKARI_ELEMENTAL = 29573
};
+
struct TRINITY_DLL_DECL boss_drakkari_colossusAI : public ScriptedAI
{
boss_drakkari_colossusAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
bool Summoned;
+
uint32 Summon_Timer;
uint32 MightyBlowTimer;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_DRAKKARI_COLOSSUS_EVENT, NOT_STARTED);
+
m_creature->clearUnitState(UNIT_STAT_STUNNED);
m_creature->SetReactState(REACT_PASSIVE);
+
Summoned = false;
+
MightyBlowTimer = 10000;
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
pInstance->SetData(DATA_DRAKKARI_COLOSSUS_EVENT, IN_PROGRESS);
}
+
void PrepareToSummonElemental()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->addUnitState(UNIT_STAT_STUNNED);
m_creature->CastSpell(m_creature,SPELL_EMERGE,false);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Summoned == false && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50 && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() > 6)
{
PrepareToSummonElemental();
m_creature->CastSpell(m_creature->getVictim(), SPELL_EMERGE_2, true);
Summoned = true;
}
+
if (Summoned == true && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 5)
{
m_creature->RemoveAllAuras();
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->addUnitState(UNIT_STAT_STUNNED);
m_creature->RemoveAllAuras();
+
Summoned = false;
}
+
if (MightyBlowTimer < diff)
{
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED))
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_MIGHTY_BLOW,true);
}
+
MightyBlowTimer = 10000;
} else MightyBlowTimer -= diff;
+
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED))
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
if (pInstance)
pInstance->SetData(DATA_DRAKKARI_COLOSSUS_EVENT, DONE);
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature->getVictim());
}
};
+
struct TRINITY_DLL_DECL boss_drakkari_elementalAI : public ScriptedAI
{
boss_drakkari_elementalAI(Creature *c) : ScriptedAI(c)
{
Colossus = c->FindNearestCreature(DRAKKARI_COLOSSUS, 30.0f, true);
}
+
Creature* Colossus;
+
uint32 SurgeTimer;
+
bool PreparationDone;
bool GoToColossus;
bool Checked;
+
void Reset()
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->clearUnitState(UNIT_STAT_STUNNED);
+
SurgeTimer = 7000;
+
GoToColossus = false;
PreparationDone = false;
Checked = false;
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if(!UpdateVictim())
return;
+
if(GoToColossus == false && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 50)
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->CastSpell(Colossus ,SPELL_MERGE,true);
+
GoToColossus = true;
PreparationDone = true;
}
+
if(GoToColossus == true && PreparationDone == true)
{
m_creature->addUnitState(UNIT_STAT_STUNNED);
m_creature->SetVisibility(VISIBILITY_OFF);
m_creature->CastSpell(Colossus ,SPELL_MERGE,true);
+
PreparationDone = false;
+
Colossus->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
Colossus->clearUnitState(UNIT_STAT_STUNNED);
}
+
if (SurgeTimer < diff)
{
if(m_creature->GetVisibility() == VISIBILITY_ON)
@@ -160,8 +202,10 @@ struct TRINITY_DLL_DECL boss_drakkari_elementalAI : public ScriptedAI
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
m_creature->CastSpell(target,SPELL_SURGE,false);
}
+
SurgeTimer = 7000;
} else SurgeTimer -= diff;
+
if(Checked == false && Colossus->GetHealth()*100 / Colossus->GetMaxHealth() <= 5)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -169,24 +213,30 @@ struct TRINITY_DLL_DECL boss_drakkari_elementalAI : public ScriptedAI
m_creature->SetVisibility(VISIBILITY_ON);
Checked = true;
}
+
//prevent do melee if is invisible
if(m_creature->GetVisibility() == VISIBILITY_ON)
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
Colossus->Kill(Colossus);
}
};
+
struct TRINITY_DLL_DECL npc_living_mojoAI : public ScriptedAI
{
npc_living_mojoAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
uint32 MojoWaveTimer;
uint32 MojoPuddleTimer;
+
void Reset()
{
//Check if the npc is near of Drakkari Colossus.
@@ -199,6 +249,7 @@ struct TRINITY_DLL_DECL npc_living_mojoAI : public ScriptedAI
MojoPuddleTimer = 7000;
}
}
+
void DamageTaken(Unit* pDone_by, uint32& uiDamage)
{
if (m_creature->HasReactState(REACT_PASSIVE))
@@ -213,47 +264,59 @@ struct TRINITY_DLL_DECL npc_living_mojoAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (MojoWaveTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_MOJO_WAVE, H_SPELL_MOJO_WAVE));
MojoWaveTimer = 15000;
} else MojoWaveTimer -= diff;
+
if (MojoPuddleTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_MOJO_PUDDLE, H_SPELL_MOJO_PUDDLE));
MojoPuddleTimer = 18000;
} else MojoPuddleTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
};
+
CreatureAI* GetAI_boss_drakkari_colossus(Creature* pCreature)
{
return new boss_drakkari_colossusAI (pCreature);
}
+
CreatureAI* GetAI_boss_drakkari_elemental(Creature* pCreature)
{
return new boss_drakkari_elementalAI (pCreature);
}
+
CreatureAI* GetAI_npc_living_mojo(Creature* pCreature)
{
return new npc_living_mojoAI (pCreature);
}
+
void AddSC_boss_drakkari_colossus()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_drakkari_colossus";
newscript->GetAI = &GetAI_boss_drakkari_colossus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_drakkari_elemental";
newscript->GetAI = &GetAI_boss_drakkari_elemental;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_living_mojo";
newscript->GetAI = &GetAI_npc_living_mojo;
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/boss_eck.cpp b/src/bindings/scripts/scripts/northrend/gundrak/boss_eck.cpp
index ed428f3730b..af0ebba511b 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/boss_eck.cpp
+++ b/src/bindings/scripts/scripts/northrend/gundrak/boss_eck.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment: Only appears in Heroic mode
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_gundrak.h"
+
enum Spells
{
SPELL_ECK_BERSERK = 55816, //Eck goes berserk, increasing his attack speed by 150% and all damage he deals by 500%.
@@ -24,42 +26,53 @@ struct TRINITY_DLL_DECL boss_eckAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
uint32 uiBerserkTimer;
uint32 uiBiteTimer;
uint32 uiSpitTimer;
uint32 uiSpringTimer;
+
bool bBerserk;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiBerserkTimer = 60000 + rand()%30000; //60-90 secs according to wowwiki
uiBiteTimer = 5000;
uiSpitTimer = 10000;
uiSpringTimer = 8000;
+
bBerserk = false;
+
if (pInstance)
pInstance->SetData(DATA_ECK_THE_FEROCIOUS_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
pInstance->SetData(DATA_ECK_THE_FEROCIOUS_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiBiteTimer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ECK_BITE);
uiBiteTimer = 8000 + rand()%4000;
} else uiBiteTimer -= diff;
+
if (uiSpitTimer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ECK_SPIT);
uiSpitTimer = 6000 + rand()%8000;
} else uiSpitTimer -= diff;
+
if (uiSpringTimer < diff)
{
Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM,1);
@@ -69,6 +82,7 @@ struct TRINITY_DLL_DECL boss_eckAI : public ScriptedAI
uiSpringTimer = 5000 + rand()%10000;
}
} else uiSpringTimer -= diff;
+
//Berserk on timer or 20% of health
if (!bBerserk)
{
@@ -87,21 +101,26 @@ struct TRINITY_DLL_DECL boss_eckAI : public ScriptedAI
}
}
}
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
if (pInstance)
pInstance->SetData(DATA_ECK_THE_FEROCIOUS_EVENT, DONE);
}
};
+
CreatureAI* GetAI_boss_eck(Creature* pCreature)
{
return new boss_eckAI (pCreature);
}
+
void AddSC_boss_eck()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_eck";
newscript->GetAI = &GetAI_boss_eck;
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/boss_gal_darah.cpp b/src/bindings/scripts/scripts/northrend/gundrak/boss_gal_darah.cpp
index 36a08a7394a..b82f937c157 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/boss_gal_darah.cpp
+++ b/src/bindings/scripts/scripts/northrend/gundrak/boss_gal_darah.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_gundrak.h"
+
//Spells
enum Spells
{
@@ -25,6 +27,7 @@ enum Spells
SPELL_WHIRLING_SLASH = 55249,
H_SPELL_WHIRLING_SLASH = 55825
};
+
//Yells
enum Yells
{
@@ -39,26 +42,33 @@ enum Yells
SAY_TRANSFORM_1 = -1604008, //Phase change
SAY_TRANSFORM_2 = -1604009
};
+
enum CombatPhase
{
TROLL,
RHINO
};
+
struct TRINITY_DLL_DECL boss_gal_darahAI : public ScriptedAI
{
boss_gal_darahAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiStampedeTimer;
uint32 uiWhirlingSlashTimer;
uint32 uiPunctureTimer;
uint32 uiEnrageTimer;
uint32 uiImpalingChargeTimer;
uint32 uiStompTimer;
+
CombatPhase Phase;
+
uint8 uiPhaseCounter;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiStampedeTimer = 10000;
@@ -67,21 +77,27 @@ struct TRINITY_DLL_DECL boss_gal_darahAI : public ScriptedAI
uiEnrageTimer = 15000;
uiImpalingChargeTimer = 20000;
uiStompTimer = 25000;
+
Phase = TROLL;
+
if (pInstance)
pInstance->SetData(DATA_GAL_DARAH_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_GAL_DARAH_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
switch (Phase)
{
case TROLL:
@@ -100,6 +116,7 @@ struct TRINITY_DLL_DECL boss_gal_darahAI : public ScriptedAI
DoScriptText(RAND(SAY_SUMMON_RHINO_1,SAY_SUMMON_RHINO_2,SAY_SUMMON_RHINO_3),m_creature);
uiStampedeTimer = 15000;
} else uiStampedeTimer -= diff;
+
if (uiWhirlingSlashTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_WHIRLING_SLASH, H_SPELL_WHIRLING_SLASH));
@@ -123,16 +140,19 @@ struct TRINITY_DLL_DECL boss_gal_darahAI : public ScriptedAI
DoCast(m_creature->getVictim(), HEROIC(SPELL_PUNCTURE, H_SPELL_PUNCTURE));
uiPunctureTimer = 8000;
} else uiPunctureTimer -= diff;
+
if (uiEnrageTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_ENRAGE, H_SPELL_ENRAGE));
uiEnrageTimer = 20000;
} else uiEnrageTimer -= diff;
+
if(uiStompTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_STOMP, H_SPELL_STOMP));
uiStompTimer = 20000;
} else uiStompTimer -= diff;
+
if (uiImpalingChargeTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
@@ -143,28 +163,36 @@ struct TRINITY_DLL_DECL boss_gal_darahAI : public ScriptedAI
}
break;
}
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_GAL_DARAH_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_gal_darah(Creature* pCreature)
{
return new boss_gal_darahAI (pCreature);
}
+
void AddSC_boss_gal_darah()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_gal_darah";
newscript->GetAI = &GetAI_boss_gal_darah;
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/boss_moorabi.cpp b/src/bindings/scripts/scripts/northrend/gundrak/boss_moorabi.cpp
index eaf6a35c34e..a352310a78d 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/boss_moorabi.cpp
+++ b/src/bindings/scripts/scripts/northrend/gundrak/boss_moorabi.cpp
@@ -5,19 +5,26 @@ SD%Complete: 100%
SDComment: Maybe needs better timers.
SDCategory: Gundrak
Script Data End */
+
#include "precompiled.h"
#include "def_gundrak.h"
+
//Spells
+
#define SPELL_DETERMINED_STAB 55104
#define SPELL_GROUND_TREMOR 55142
#define SPELL_NUMBING_SHOUT 55106
+
#define SPELL_DETERMINED_GORE 55102
#define SPELL_DETERMINED_GORE_1 59444
#define SPELL_QUAKE 55101
#define SPELL_NUMBING_ROAR 55100
+
#define SPELL_MOJO_FRENZY 55163
#define SPELL_TRANSFORMATION 55098 //Periodic, The caster transforms into a powerful mammoth, increasing Physical damage done by 25% and granting immunity to Stun effects.
+
#define ACHIEVEMENT_LESS_RABI 2040
+
//Yell
#define SAY_AGGRO -1604010
//#define SAY_SLAY_1 -1604011 // not in db
@@ -27,12 +34,14 @@ Script Data End */
#define SAY_TRANSFORM -1604015
#define SAY_QUAKE -1604016
#define EMOTE_TRANSFORM -1604017
+
struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
{
boss_moorabiAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
bool Phase;
uint32 SPELL_QUAKE_TIMER;
@@ -40,6 +49,7 @@ struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
uint32 SPELL_GROUND_TREMOR_TIMER;
uint32 SPELL_DETERMINED_STAB_TIMER;
uint32 SPELL_TRANSFORMATION_TIMER;
+
void Reset()
{
SPELL_GROUND_TREMOR_TIMER = 18000;
@@ -47,37 +57,46 @@ struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
SPELL_DETERMINED_STAB_TIMER = 20000;
SPELL_TRANSFORMATION_TIMER = 12000;
Phase = false;
+
if (pInstance)
pInstance->SetData(DATA_MOORABI_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
m_creature->CastSpell(m_creature,SPELL_MOJO_FRENZY,true);
+
if (pInstance)
pInstance->SetData(DATA_MOORABI_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if(Phase == false && m_creature->HasAura(SPELL_TRANSFORMATION))
Phase = true;
+
if(Phase == true)
{
m_creature->RemoveAura(SPELL_MOJO_FRENZY);
+
if (SPELL_GROUND_TREMOR_TIMER < diff)
{
DoScriptText(SAY_QUAKE, m_creature);
m_creature->CastSpell(m_creature->getVictim(),SPELL_QUAKE,true);
SPELL_GROUND_TREMOR_TIMER = 10000;
} else SPELL_GROUND_TREMOR_TIMER -= diff;
+
if (SPELL_NUMBING_SHOUT_TIMER < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_NUMBING_ROAR,true);
SPELL_NUMBING_SHOUT_TIMER = 10000;
} else SPELL_NUMBING_SHOUT_TIMER -=diff;
+
if (SPELL_DETERMINED_STAB_TIMER < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_DETERMINED_GORE, SPELL_DETERMINED_GORE_1));
@@ -94,6 +113,7 @@ struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
m_creature->CastSpell(m_creature,SPELL_TRANSFORMATION,false);
SPELL_TRANSFORMATION_TIMER = 10000;
} else SPELL_TRANSFORMATION_TIMER -= diff;
+
//CAST GROUND TERMOR || QUAKE
if (SPELL_GROUND_TREMOR_TIMER < diff)
{
@@ -101,23 +121,27 @@ struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
m_creature->CastSpell(m_creature->getVictim(),SPELL_GROUND_TREMOR,true);
SPELL_GROUND_TREMOR_TIMER = 10000;
} else SPELL_GROUND_TREMOR_TIMER -= diff;
+
//CAST NUMBING SHOUT || DETERMINED_STAB
if (SPELL_NUMBING_SHOUT_TIMER < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_NUMBING_SHOUT,true);
SPELL_NUMBING_SHOUT_TIMER = 10000;
} else SPELL_NUMBING_SHOUT_TIMER -= diff;
+
if (SPELL_DETERMINED_STAB_TIMER < diff)
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_DETERMINED_STAB,true);
SPELL_DETERMINED_STAB_TIMER = 8000;
} else SPELL_DETERMINED_STAB_TIMER -= diff;
}
+
DoMeleeAttackIfReady();
}
void JustDied(Unit *killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (HeroicMode && Phase == false)
{
AchievementEntry const *AchievLessRabi = GetAchievementStore()->LookupEntry(ACHIEVEMENT_LESS_RABI);
@@ -127,11 +151,12 @@ struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievLessRabi);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_MOORABI_EVENT, DONE);
}
@@ -139,16 +164,20 @@ struct TRINITY_DLL_DECL boss_moorabiAI : public ScriptedAI
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI *GetAI_boss_moorabi(Creature *pCreature)
{
return new boss_moorabiAI(pCreature);
}
+
void AddSC_boss_moorabi()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_moorabi";
newscript->GetAI = &GetAI_boss_moorabi;
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/boss_slad_ran.cpp b/src/bindings/scripts/scripts/northrend/gundrak/boss_slad_ran.cpp
index 81767b128d1..95f74af7e35 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/boss_slad_ran.cpp
+++ b/src/bindings/scripts/scripts/northrend/gundrak/boss_slad_ran.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_slad_ran' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_gundrak.h"
+
//Spells
enum Spells
{
@@ -20,6 +22,7 @@ enum Spells
SPELL_VENOM_BOLT = 54970,
H_SPELL_VENOM_BOLT = 59839
};
+
//Yell
enum Yells
{
@@ -31,12 +34,14 @@ enum Yells
SAY_SUMMON_SNAKES = -1604022,
SAY_SUMMON_CONSTRICTORS = -1604023
};
+
//Creatures
enum Creatures
{
CREATURE_SNAKE = 29680,
CREATURE_CONSTRICTORS = 29713
};
+
//Creatures' spells
enum ConstrictorSpells
{
@@ -44,11 +49,13 @@ enum ConstrictorSpells
SPELL_VENOMOUS_BITE = 54987,
H_SPELL_VENOMOUS_BITE = 58996
};
+
// Spawning locations
struct Locations
{
float x, y, z, orientation;
};
+
static Locations SpawnLoc[]=
{
{1783.81, 646.637, 133.948, 3.71755},
@@ -57,18 +64,23 @@ static Locations SpawnLoc[]=
{1765.66, 646.542, 134.02, 5.11381},
{1716.76, 635.159, 129.282, 0.191986}
};
+
struct TRINITY_DLL_DECL boss_slad_ranAI : public ScriptedAI
{
boss_slad_ranAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiPoisonNovaTimer;
uint32 uiPowerfullBiteTimer;
uint32 uiVenomBoltTimer;
uint32 uiSpawnTimer;
+
uint8 uiPhase;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiPoisonNovaTimer = 10000;
@@ -76,48 +88,57 @@ struct TRINITY_DLL_DECL boss_slad_ranAI : public ScriptedAI
uiVenomBoltTimer = 15000;
uiSpawnTimer = 5000;
uiPhase = 0;
+
if (pInstance)
pInstance->SetData(DATA_SLAD_RAN_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_SLAD_RAN_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiPoisonNovaTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_POISON_NOVA, H_SPELL_POISON_NOVA));
uiPoisonNovaTimer = 15000;
} else uiPoisonNovaTimer -= diff;
+
if (uiPowerfullBiteTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_POWERFULL_BITE, H_SPELL_POWERFULL_BITE));
uiPowerfullBiteTimer = 10000;
} else uiPowerfullBiteTimer -= diff;
+
if (uiVenomBoltTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_VENOM_BOLT, H_SPELL_VENOM_BOLT));
uiVenomBoltTimer = 10000;
} else uiVenomBoltTimer -= diff;
+
if (uiPhase)
{
if(uiSpawnTimer < diff)
{
if (uiPhase == 1)
- for (uint8 i = 0; i < (HeroicMode ? 5 : 3); ++i)
+ for (uint8 i = 0;i < (HeroicMode ? 5 : 3); ++i)
m_creature->SummonCreature(CREATURE_SNAKE, SpawnLoc[i].x, SpawnLoc[i].y, SpawnLoc[i].z, SpawnLoc[i].orientation, TEMPSUMMON_CORPSE_TIMED_DESPAWN,20000);
if (uiPhase == 2)
- for (uint8 i = 0; i < (HeroicMode ? 5 : 3); ++i)
+ for (uint8 i = 0;i < (HeroicMode ? 5 : 3); ++i)
m_creature->SummonCreature(CREATURE_CONSTRICTORS, SpawnLoc[i].x, SpawnLoc[i].y, SpawnLoc[i].z, SpawnLoc[i].orientation, TEMPSUMMON_CORPSE_TIMED_DESPAWN,20000);
uiSpawnTimer = 5000;
} else uiSpawnTimer -= diff;
}
+
if ((uiPhase == 0) && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 30)
{
DoScriptText(SAY_SUMMON_SNAKES,m_creature);
@@ -128,31 +149,41 @@ struct TRINITY_DLL_DECL boss_slad_ranAI : public ScriptedAI
DoScriptText(SAY_SUMMON_CONSTRICTORS,m_creature);
uiPhase = 2;
}
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_SLAD_RAN_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void JustSummoned(Creature* summoned)
{
summoned->GetMotionMaster()->MovePoint(0,m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ());
+
}
};
+
struct TRINITY_DLL_DECL mob_slad_ran_constrictorAI : public ScriptedAI
{
mob_slad_ran_constrictorAI(Creature *c) : ScriptedAI(c) {}
+
uint32 uiGripOfSladRanTimer;
+
void Reset()
{
uiGripOfSladRanTimer = 1000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -163,21 +194,28 @@ struct TRINITY_DLL_DECL mob_slad_ran_constrictorAI : public ScriptedAI
uiGripOfSladRanTimer = 5000;
} else uiGripOfSladRanTimer -= diff;;
}
+
ScriptedInstance* pInstance;
};
+
struct TRINITY_DLL_DECL mob_slad_ran_viperAI : public ScriptedAI
{
mob_slad_ran_viperAI(Creature *c) : ScriptedAI(c) {}
+
uint32 uiVenomousBiteTimer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiVenomousBiteTimer = 2000;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (uiVenomousBiteTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_VENOMOUS_BITE, H_SPELL_VENOMOUS_BITE));
@@ -185,29 +223,36 @@ struct TRINITY_DLL_DECL mob_slad_ran_viperAI : public ScriptedAI
} else uiVenomousBiteTimer -= diff;
}
};
+
CreatureAI* GetAI_boss_slad_ran(Creature* pCreature)
{
return new boss_slad_ranAI (pCreature);
}
+
CreatureAI* GetAI_mob_slad_ran_constrictor(Creature* pCreature)
{
return new mob_slad_ran_constrictorAI (pCreature);
}
+
CreatureAI* GetAI_mob_slad_ran_viper(Creature* pCreature)
{
return new mob_slad_ran_viperAI (pCreature);
}
+
void AddSC_boss_slad_ran()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_slad_ran";
newscript->GetAI = &GetAI_boss_slad_ran;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_slad_ran_constrictor";
newscript->GetAI = &GetAI_mob_slad_ran_constrictor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_slad_ran_viper";
newscript->GetAI = &GetAI_mob_slad_ran_viper;
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/def_gundrak.h b/src/bindings/scripts/scripts/northrend/gundrak/def_gundrak.h
index 3428b6dd0e3..957bb9d2367 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/def_gundrak.h
+++ b/src/bindings/scripts/scripts/northrend/gundrak/def_gundrak.h
@@ -1,5 +1,6 @@
#ifndef DEF_GUNDRAK_H
#define DEF_GUNDRAK_H
+
enum Data
{
DATA_SLAD_RAN_EVENT,
@@ -8,6 +9,7 @@ enum Data
DATA_GAL_DARAH_EVENT,
DATA_ECK_THE_FEROCIOUS_EVENT
};
+
enum Data64
{
DATA_SLAD_RAN_ALTAR,
@@ -17,4 +19,5 @@ enum Data64
DATA_MOORABI_STATUE,
DATA_DRAKKARI_COLOSSUS_STATUE
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/gundrak/instance_gundrak.cpp b/src/bindings/scripts/scripts/northrend/gundrak/instance_gundrak.cpp
index e67c3888a61..3b8dd7edea2 100644
--- a/src/bindings/scripts/scripts/northrend/gundrak/instance_gundrak.cpp
+++ b/src/bindings/scripts/scripts/northrend/gundrak/instance_gundrak.cpp
@@ -1,6 +1,8 @@
#include "precompiled.h"
#include "def_gundrak.h"
+
#define MAX_ENCOUNTER 5
+
/* GunDrak encounters:
0 - Slad'Ran
1 - Moorabi
@@ -8,6 +10,7 @@
3 - Gal'Darah
4 - Eck the Ferocious
*/
+
struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
{
instance_gundrak(Map* pMap) : ScriptedInstance(pMap)
@@ -15,12 +18,15 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
bHeroicMode = pMap->IsHeroic();
Initialize();
};
+
bool bHeroicMode;
+
uint64 uiSladRan;
uint64 uiMoorabi;
uint64 uiDrakkariColossus;
uint64 uiGalDarah;
uint64 uiEckTheFerocious;
+
uint64 uiSladRanAltar;
uint64 uiMoorabiAltar;
uint64 uiDrakkariColossusAltar;
@@ -32,8 +38,11 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
uint64 uiGalDarahDoor2;
uint64 uiBridge;
uint64 uiCollision;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
std::string str_data;
+
void Initialize()
{
uiSladRan = 0;
@@ -41,25 +50,33 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
uiDrakkariColossus = 0;
uiGalDarah = 0;
uiEckTheFerocious = 0;
+
uiSladRanAltar = 0;
uiMoorabiAltar = 0;
uiDrakkariColossusAltar = 0;
+
uiSladRanStatue = 0;
uiMoorabiStatue = 0;
uiDrakkariColossusStatue = 0;
+
uiEckTheFerociousDoor = 0;
uiGalDarahDoor1 = 0;
uiGalDarahDoor2 = 0;
+
uiBridge = 0;
uiCollision = 0;
+
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -71,6 +88,7 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
case 29932: uiEckTheFerocious = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -124,6 +142,7 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -169,9 +188,11 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
m_auiEncounter[4] = data;
break;
}
+
if (data == DONE)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -182,8 +203,10 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
case DATA_DRAKKARI_COLOSSUS_EVENT: return m_auiEncounter[3];
case DATA_ECK_THE_FEROCIOUS_EVENT: return m_auiEncounter[4];
}
+
return 0;
}
+
uint64 GetData64(uint32 type)
{
switch(type)
@@ -195,18 +218,24 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
case DATA_MOORABI_STATUE: return uiMoorabiStatue;
case DATA_DRAKKARI_COLOSSUS_STATUE: return uiDrakkariColossusStatue;
}
+
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << "G D " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << m_auiEncounter[3] << " " << m_auiEncounter[4];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -214,11 +243,15 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0, data1, data2, data3, data4;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4;
+
if (dataHead1 == 'G' && dataHead2 == 'D')
{
m_auiEncounter[0] = data0;
@@ -226,17 +259,21 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
m_auiEncounter[2] = data2;
m_auiEncounter[3] = data3;
m_auiEncounter[4] = data4;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
}else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
+
void CheckAltars()
{
GameObject* pSladRanAltar = instance->GetGameObject(uiSladRanAltar);
GameObject* pMoorabiAltar = instance->GetGameObject(uiMoorabiAltar);
GameObject* pDrakkariColossusAltar = instance->GetGameObject(uiDrakkariColossusAltar);
+
if (pSladRanAltar && pSladRanAltar->GetGoState() == GO_STATE_ACTIVE &&
pMoorabiAltar && pMoorabiAltar->GetGoState() == GO_STATE_ACTIVE &&
pDrakkariColossusAltar && pDrakkariColossusAltar->GetGoState() == GO_STATE_ACTIVE)
@@ -246,12 +283,15 @@ struct TRINITY_DLL_DECL instance_gundrak : public ScriptedInstance
}
}
};
+
bool GOHello_altar(Player *pPlayer, GameObject *pGO)
{
ScriptedInstance *pInstance = pGO->GetInstanceData();
uint32 uiStatue;
+
pGO->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK1);
pGO->SetGoState(GO_STATE_ACTIVE);
+
if (pInstance)
{
switch(pGO->GetEntry())
@@ -266,10 +306,12 @@ bool GOHello_altar(Player *pPlayer, GameObject *pGO)
}
return false;
}
+
InstanceData* GetInstanceData_instance_gundrak(Map* pMap)
{
return new instance_gundrak(pMap);
}
+
void AddSC_instance_gundrak()
{
Script *newscript;
@@ -277,6 +319,7 @@ void AddSC_instance_gundrak()
newscript->Name = "instance_gundrak";
newscript->GetInstanceData = &GetInstanceData_instance_gundrak;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_gundrak_altar";
newscript->pGOHello = &GOHello_altar;
diff --git a/src/bindings/scripts/scripts/northrend/howling_fjord.cpp b/src/bindings/scripts/scripts/northrend/howling_fjord.cpp
index e7df7d38ce5..cabb00d748f 100644
--- a/src/bindings/scripts/scripts/northrend/howling_fjord.cpp
+++ b/src/bindings/scripts/scripts/northrend/howling_fjord.cpp
@@ -14,18 +14,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Sholazar_Basin
SD%Complete: 100
SDComment: Quest support: 11253, 11241.
SDCategory: howling_fjord
EndScriptData */
+
/* ContentData
npc_plaguehound_tracker
npc_apothecary_hanes
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_apothecary_hanes
######*/
@@ -38,6 +42,7 @@ enum Entries
QUEST_TRAIL_OF_FIRE = 11241,
SPELL_COSMETIC_LOW_POLY_FIRE = 56274
};
+
bool QuestAccept_npc_apothecary_hanes(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_TRAIL_OF_FIRE)
@@ -55,20 +60,24 @@ bool QuestAccept_npc_apothecary_hanes(Player* pPlayer, Creature* pCreature, Ques
}
return true;
}
+
struct TRINITY_DLL_DECL npc_Apothecary_HanesAI : public npc_escortAI
{
npc_Apothecary_HanesAI(Creature* pCreature) : npc_escortAI(pCreature){}
uint32 PotTimer;
+
void Reset ()
{
SetDespawnAtFar(false);
PotTimer = 10000; //10 sec cooldown on potion
}
+
void JustDied(Unit* killer)
{
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->FailQuest(QUEST_TRAIL_OF_FIRE);
}
+
void UpdateEscortAI(const uint32 diff)
{
if(HealthBelowPct(75))
@@ -82,6 +91,7 @@ struct TRINITY_DLL_DECL npc_Apothecary_HanesAI : public npc_escortAI
if (GetAttack() && UpdateVictim())
DoMeleeAttackIfReady();
}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
@@ -138,18 +148,22 @@ CreatureAI* GetAI_npc_apothecary_hanes(Creature* pCreature)
/*######
## npc_plaguehound_tracker
######*/
+
enum ePlaguehound
{
QUEST_SNIFF_OUT_ENEMY = 11253
};
+
struct TRINITY_DLL_DECL npc_plaguehound_trackerAI : public npc_escortAI
{
npc_plaguehound_trackerAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
void Reset()
{
InitScriptData();
}
+
void InitScriptData()
{
Player* pPlayer = NULL;
@@ -157,11 +171,14 @@ struct TRINITY_DLL_DECL npc_plaguehound_trackerAI : public npc_escortAI
if(Unit* summoner = CAST_SUM(me)->GetSummoner())
if(summoner->GetTypeId() == TYPEID_PLAYER)
pPlayer = CAST_PLR(summoner);
+
if (!pPlayer)
return;
+
me->SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
Start(false, false, pPlayer->GetGUID());
}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = NULL;
@@ -169,8 +186,10 @@ struct TRINITY_DLL_DECL npc_plaguehound_trackerAI : public npc_escortAI
if(Unit* summoner = CAST_SUM(me)->GetSummoner())
if(summoner->GetTypeId() == TYPEID_PLAYER)
pPlayer = CAST_PLR(summoner);
+
if (!pPlayer)
return;
+
switch(i)
{
case 26:
@@ -179,15 +198,19 @@ struct TRINITY_DLL_DECL npc_plaguehound_trackerAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_npc_plaguehound_tracker(Creature* pCreature)
{
return new npc_plaguehound_trackerAI(pCreature);
}
+
/*######
## npc_razael_and_lyana
######*/
+
#define GOSSIP_RAZAEL_REPORT "High Executor Anselm wants a report on the situation."
#define GOSSIP_LYANA_REPORT "High Executor Anselm requests your report."
+
enum eRazael
{
QUEST_REPORTS_FROM_THE_FIELD = 11221,
@@ -198,6 +221,7 @@ enum eRazael
GOSSIP_TEXTID_LYANA1 = 11586,
GOSSIP_TEXTID_LYANA2 = 11588
};
+
bool GossipHello_npc_razael_and_lyana(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_REPORTS_FROM_THE_FIELD) == QUEST_STATUS_INCOMPLETE)
@@ -223,6 +247,7 @@ bool GossipHello_npc_razael_and_lyana(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_razael_and_lyana(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -238,18 +263,22 @@ bool GossipSelect_npc_razael_and_lyana(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
void AddSC_howling_fjord()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_apothecary_hanes";
newscript->GetAI = &GetAI_npc_apothecary_hanes;
newscript->pQuestAccept = &QuestAccept_npc_apothecary_hanes;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_plaguehound_tracker";
newscript->GetAI = &GetAI_npc_plaguehound_tracker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_razael_and_lyana";
newscript->pGossipHello = &GossipHello_npc_razael_and_lyana;
diff --git a/src/bindings/scripts/scripts/northrend/icecrown.cpp b/src/bindings/scripts/scripts/northrend/icecrown.cpp
index 99c251604c5..443b6603943 100644
--- a/src/bindings/scripts/scripts/northrend/icecrown.cpp
+++ b/src/bindings/scripts/scripts/northrend/icecrown.cpp
@@ -13,19 +13,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Icecrown
SD%Complete: 100
SDComment: Quest support: 12807
SDCategory: Icecrown
EndScriptData */
+
/* ContentData
npc_arete
EndContentData */
+
#include "precompiled.h"
+
/*######
## npc_arete
######*/
+
#define GOSSIP_ARETE_ITEM1 "Lord-Commander, I would hear your tale."
#define GOSSIP_ARETE_ITEM2 "<You nod slightly but do not complete the motion as the Lord-Commander narrows his eyes before he continues.>"
#define GOSSIP_ARETE_ITEM3 "I thought that they now called themselves the Scarlet Onslaught?"
@@ -33,6 +38,7 @@ EndContentData */
#define GOSSIP_ARETE_ITEM5 "That's fine. When do I start?"
#define GOSSIP_ARETE_ITEM6 "Let's finish this!"
#define GOSSIP_ARETE_ITEM7 "That's quite a tale, Lord-Commander."
+
enum eArete
{
GOSSIP_TEXTID_ARETE1 = 13525,
@@ -42,21 +48,26 @@ enum eArete
GOSSIP_TEXTID_ARETE5 = 13529,
GOSSIP_TEXTID_ARETE6 = 13530,
GOSSIP_TEXTID_ARETE7 = 13531,
+
QUEST_THE_STORY_THUS_FAR = 12807
};
+
bool GossipHello_npc_arete(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_THE_STORY_THUS_FAR) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ARETE_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_ARETE1, pCreature->GetGUID());
return true;
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_arete(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -90,11 +101,14 @@ bool GossipSelect_npc_arete(Player* pPlayer, Creature* pCreature, uint32 uiSende
pPlayer->AreaExploredOrEventHappens(QUEST_THE_STORY_THUS_FAR);
break;
}
+
return true;
}
+
void AddSC_icecrown()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_arete";
newscript->pGossipHello = &GossipHello_npc_arete;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_anubrekhan.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_anubrekhan.cpp
index f14dd5d4daa..2de7abcf392 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_anubrekhan.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_anubrekhan.cpp
@@ -13,39 +13,53 @@
* 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"
#include "def_naxxramas.h"
+
#define SAY_GREET RAND(-1533000,-1533004,-1533005,-1533006,-1533007)
#define SAY_AGGRO RAND(-1533001,-1533002,-1533003)
#define SAY_SLAY -1533008
+
#define SPELL_IMPALE HEROIC(28783,56090)
#define SPELL_LOCUSTSWARM HEROIC(28785,54021)
+
#define SPELL_SELF_SPAWN_5 29105 //This spawns 5 corpse scarabs ontop of us (most likely the player casts this on death)
+
#define EVENT_IMPALE 1
#define EVENT_LOCUST 2
+
#define MOB_CRYPT_GUARD 16573
+
struct TRINITY_DLL_DECL boss_anubrekhanAI : public BossAI
{
boss_anubrekhanAI(Creature *c) : BossAI(c, BOSS_ANUBREKHAN) { Prepare(); }
+
bool HasTaunted;
+
void Prepare()
{
HasTaunted = false;
+
if (HeroicMode)
{
DoSpawnCreature(MOB_CRYPT_GUARD, 0, -10, 0, me->GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
DoSpawnCreature(MOB_CRYPT_GUARD, 0, 10, 0, me->GetOrientation(), TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60000);
}
}
+
void InitializeAI() { Prepare(); BossAI::InitializeAI(); }
void JustReachedHome() { Prepare(); _JustReachedHome(); }
+
void KilledUnit(Unit* victim)
{
//Force the player to spawn corpse scarabs via spell
victim->CastSpell(victim, SPELL_SELF_SPAWN_5, true, NULL, NULL, me->GetGUID());
+
if (!(rand()%5))
DoScriptText(SAY_SLAY, me);
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -53,6 +67,7 @@ struct TRINITY_DLL_DECL boss_anubrekhanAI : public BossAI
events.ScheduleEvent(EVENT_IMPALE, 15000, 1);
events.ScheduleEvent(EVENT_LOCUST, 80000 + rand()%40000, 1);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!HasTaunted && me->IsWithinDistInMap(who, 60.0f))
@@ -62,11 +77,14 @@ struct TRINITY_DLL_DECL boss_anubrekhanAI : public BossAI
}
ScriptedAI::MoveInLineOfSight(who);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim() || !CheckInRoom())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -87,13 +105,16 @@ struct TRINITY_DLL_DECL boss_anubrekhanAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_anubrekhan(Creature* pCreature)
{
return new boss_anubrekhanAI (pCreature);
}
+
void AddSC_boss_anubrekhan()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_faerlina.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_faerlina.cpp
index 8675a47ed87..e7cd19d3b26 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_faerlina.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_faerlina.cpp
@@ -15,27 +15,35 @@
* 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"
#include "def_naxxramas.h"
+
#define SAY_GREET -1533009
#define SAY_AGGRO RAND(-1533010,-1533011,-1533012,-1533013)
#define SAY_SLAY RAND(-1533014,-1533015)
#define SAY_DEATH -1533016
+
//#define SOUND_RANDOM_AGGRO 8955 //soundId containing the 4 aggro sounds, we not using this
+
#define SPELL_POSION_BOLT_VOLLEY HEROIC(28796,54098)
#define SPELL_RAIN_OF_FIRE HEROIC(28794,54099)
#define SPELL_FRENZY HEROIC(28798,54100)
#define SPELL_WIDOWS_EMBRACE HEROIC(28732,54097)
+
enum Events
{
EVENT_POSION = 1,
EVENT_FIRE,
EVENT_FRENZY,
};
+
struct TRINITY_DLL_DECL boss_faerlinaAI : public BossAI
{
boss_faerlinaAI(Creature *c) : BossAI(c, BOSS_FAERLINA), greet(false) {}
+
bool greet;
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -44,6 +52,7 @@ struct TRINITY_DLL_DECL boss_faerlinaAI : public BossAI
events.ScheduleEvent(EVENT_FIRE, 5000 + rand()%15000);
events.ScheduleEvent(EVENT_FRENZY, 60000 + rand()%20000);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!greet)
@@ -53,21 +62,26 @@ struct TRINITY_DLL_DECL boss_faerlinaAI : public BossAI
}
BossAI::MoveInLineOfSight(who);
}
+
void KilledUnit(Unit* victim)
{
if (!(rand()%3))
DoScriptText(SAY_SLAY, me);
}
+
void JustDied(Unit* Killer)
{
_JustDied();
DoScriptText(SAY_DEATH, me);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -88,13 +102,16 @@ struct TRINITY_DLL_DECL boss_faerlinaAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_faerlina(Creature* pCreature)
{
return new boss_faerlinaAI (pCreature);
}
+
void AddSC_boss_faerlina()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_four_horsemen.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_four_horsemen.cpp
index c841a6877a5..a39e76a24b6 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_four_horsemen.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_four_horsemen.cpp
@@ -13,8 +13,10 @@
* 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"
#include "def_naxxramas.h"
+
enum Horsemen
{
HORSEMEN_THANE,
@@ -22,12 +24,14 @@ enum Horsemen
HORSEMEN_BARON,
HORSEMEN_SIR,
};
+
enum Events
{
EVENT_MARK = 1,
EVENT_CAST,
EVENT_BERSERK,
};
+
const uint32 MOB_HORSEMEN[] = {16064, 16065, 30549, 16063};
const uint32 SPELL_MARK[] = {28832, 28833, 28834, 28835};
#define SPELL_PRIMARY(i) HEROIC(SPELL_PRIMARY_N[i],SPELL_PRIMARY_H[i])
@@ -38,6 +42,7 @@ const uint32 SPELL_SECONDARY_N[]= {0, 57374, 0, 57376};
const uint32 SPELL_SECONDARY_H[]= {0, 57464, 0, 57465};
const uint32 SPELL_PUNISH[] = {0, 57381, 0, 57377};
#define SPELL_BERSERK 26662
+
// used by 16063,16064,16065,30549, but signed for 16063
const int32 SAY_AGGRO[] = {-1533051, -1533044, -1533065, -1533058};
const int32 SAY_TAUNT[3][4] ={ {-1533052, -1533045, -1533071, -1533059},
@@ -46,26 +51,31 @@ const int32 SAY_TAUNT[3][4] ={ {-1533052, -1533045, -1533071, -1533059},
const int32 SAY_SPECIAL[] = {-1533055, -1533048, -1533070, -1533062};
const int32 SAY_SLAY[] = {-1533056, -1533049, -1533068, -1533063};
const int32 SAY_DEATH[] = {-1533057, -1533050, -1533074, -1533064};
+
#define SAY_BARON_AGGRO RAND(-1533065,-1533066,-1533067)
#define SAY_BARON_SLAY RAND(-1533068,-1533069)
+
struct TRINITY_DLL_DECL boss_four_horsemenAI : public BossAI
{
boss_four_horsemenAI(Creature *c) : BossAI(c, BOSS_HORSEMEN)
{
id = Horsemen(0);
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
if (me->GetEntry() == MOB_HORSEMEN[i])
id = Horsemen(i);
caster = (id == HORSEMEN_LADY || id == HORSEMEN_SIR);
}
+
Horsemen id;
bool caster;
+
void MoveInLineOfSight(Unit *who)
{
BossAI::MoveInLineOfSight(who);
if (caster)
SelectNearestTarget(who);
}
+
void AttackStart(Unit *who)
{
if (caster)
@@ -73,6 +83,7 @@ struct TRINITY_DLL_DECL boss_four_horsemenAI : public BossAI
else
BossAI::AttackStart(who);
}
+
void KilledUnit(Unit* victim)
{
if (!(rand()%5))
@@ -83,11 +94,13 @@ struct TRINITY_DLL_DECL boss_four_horsemenAI : public BossAI
DoScriptText(SAY_SLAY[id], me);
}
}
+
void JustDied(Unit* killer)
{
_JustDied();
DoScriptText(SAY_DEATH[id], me);
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -99,13 +112,17 @@ struct TRINITY_DLL_DECL boss_four_horsemenAI : public BossAI
events.ScheduleEvent(EVENT_CAST, 20000+rand()%5000);
events.ScheduleEvent(EVENT_BERSERK, 15*100*1000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim() || !CheckInRoom())
return;
+
events.Update(diff);
+
if (me->hasUnitState(UNIT_STAT_CASTING))
return;
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -128,16 +145,19 @@ struct TRINITY_DLL_DECL boss_four_horsemenAI : public BossAI
return;
}
}
+
if (!caster)
DoMeleeAttackIfReady();
else if (!DoSpellAttackIfReady(SPELL_SECONDARY(id)))
DoCastAOE(SPELL_PUNISH[id]);
}
};
+
CreatureAI* GetAI_four_horsemen(Creature* pCreature)
{
return new boss_four_horsemenAI (pCreature);
}
+
void AddSC_boss_four_horsemen()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_gluth.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_gluth.cpp
index 05a551d76f2..ed38cbe62c4 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_gluth.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_gluth.cpp
@@ -13,20 +13,25 @@
* 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"
#include "def_naxxramas.h"
+
#define SPELL_MORTAL_WOUND 25646
#define SPELL_ENRAGE HEROIC(28371,54427)
#define SPELL_DECIMATE HEROIC(28374,54426)
#define SPELL_BERSERK 26662
#define SPELL_INFECTED_WOUND 29306
+
#define MOB_ZOMBIE 16360
+
const Position PosSummon[3] =
{
{3267.9, -3172.1, 297.42, 0.94},
{3253.2, -3132.3, 297.42, 0},
{3308.3, -3185.8, 297.42, 1.58},
};
+
enum Events
{
EVENT_WOUND = 1,
@@ -35,7 +40,9 @@ enum Events
EVENT_BERSERK,
EVENT_SUMMON,
};
+
#define EMOTE_NEARBY " spots a nearby zombie to devour!"
+
struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
{
boss_gluthAI(Creature *c) : BossAI(c, BOSS_GLUTH)
@@ -43,12 +50,15 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
// Do not let Gluth be affected by zombies' debuff
me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_INFECTED_WOUND, true);
}
+
std::vector<Creature*> triggers;
+
void Reset()
{
triggers.clear();
_Reset();
}
+
void MoveInLineOfSight(Unit *who)
{
if (who->GetEntry() == MOB_ZOMBIE && me->IsWithinDistInMap(who, 15))
@@ -59,9 +69,10 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
else
BossAI::MoveInLineOfSight(who);
}
+
void EnterCombat(Unit *who)
{
- for (uint32 i = 0; i < 3; ++i)
+ for(uint32 i = 0; i < 3; ++i)
if (Creature *trigger = DoSummon(WORLD_TRIGGER, PosSummon[i]))
triggers.push_back(trigger);
if (triggers.size() < 3)
@@ -70,6 +81,7 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
EnterEvadeMode();
return;
}
+
_EnterCombat();
events.ScheduleEvent(EVENT_WOUND, 10000);
events.ScheduleEvent(EVENT_ENRAGE, 30000);
@@ -77,6 +89,7 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
events.ScheduleEvent(EVENT_BERSERK, 8*60000);
events.ScheduleEvent(EVENT_SUMMON, 10000);
}
+
void JustSummoned(Creature *summon)
{
if (summon->GetEntry() == WORLD_TRIGGER)
@@ -85,11 +98,14 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
summon->AI()->AttackStart(me);
summons.Summon(summon);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictimWithGaze() || !CheckInRoom())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -110,12 +126,13 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
DoCast(me, SPELL_BERSERK);
return;
case EVENT_SUMMON:
- for (uint32 i = 0; i < HEROIC(1,2); ++i)
+ for(uint32 i = 0; i < HEROIC(1,2); ++i)
DoSummon(MOB_ZOMBIE, triggers[rand()%3]);
events.ScheduleEvent(EVENT_SUMMON, 10000);
return;
}
}
+
if (me->getVictim()->GetEntry() == MOB_ZOMBIE)
{
if (me->IsWithinMeleeRange(me->getVictim()))
@@ -128,10 +145,12 @@ struct TRINITY_DLL_DECL boss_gluthAI : public BossAI
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_gluth(Creature* pCreature)
{
return new boss_gluthAI (pCreature);
}
+
void AddSC_boss_gluth()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_gothik.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_gothik.cpp
index e9278b33223..647c62a32eb 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_gothik.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_gothik.cpp
@@ -13,21 +13,26 @@
* 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"
#include "def_naxxramas.h"
+
#define SAY_SPEECH -1533040
#define SAY_KILL -1533041
#define SAY_DEATH -1533042
#define SAY_TELEPORT -1533043
+
//Gothik
#define SPELL_HARVEST_SOUL 28679
#define SPELL_SHADOW_BOLT HEROIC(29317,56405)
+
#define SPELL_INFORM_LIVE_TRAINEE 27892
#define SPELL_INFORM_LIVE_KNIGHT 27928
#define SPELL_INFORM_LIVE_RIDER 27935
#define SPELL_INFORM_DEAD_TRAINEE 27915
#define SPELL_INFORM_DEAD_KNIGHT 27931
#define SPELL_INFORM_DEAD_RIDER 27937
+
#define MOB_LIVE_TRAINEE 16124
#define MOB_LIVE_KNIGHT 16125
#define MOB_LIVE_RIDER 16126
@@ -35,6 +40,7 @@
#define MOB_DEAD_KNIGHT 16148
#define MOB_DEAD_RIDER 16150
#define MOB_DEAD_HORSE 16149
+
const struct Waves { uint32 entry, number, time; }
waves[] =
{
@@ -63,21 +69,26 @@ waves[] =
{MOB_LIVE_TRAINEE, 2, 29000},
{0, 0, 0},
};
+
#define POS_Y_GATE -3360.78f
+
enum Events
{
EVENT_SUMMON = 1,
EVENT_HARVEST,
EVENT_BOLT,
};
+
#define POS_LIVE 3
#define POS_DEAD 5
+
const Position PosSummonLive[POS_LIVE] =
{
{2669.7, -3430.9, 268.56, 1.6},
{2692.0, -3430.9, 268.56, 1.6},
{2714.1, -3430.9, 268.56, 1.6},
};
+
const Position PosSummonDead[POS_DEAD] =
{
{2725.1, -3310.0, 268.85, 3.4},
@@ -86,14 +97,18 @@ const Position PosSummonDead[POS_DEAD] =
{2682.8, -3304.2, 268.85, 3.9},
{2664.8, -3340.7, 268.23, 3.7},
};
+
const float PosGround[4] = {2691.2, -3362.7, 267.68, 1.7};
const float PosPlatform[4] = {2640.5, -3360.6, 285.26, 0};
+
struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
{
boss_gothikAI(Creature *c) : BossAI(c, BOSS_GOTHIK) {}
+
uint32 waveCount;
typedef std::vector<Creature*> TriggerVct;
TriggerVct liveTrigger, deadTrigger;
+
void Reset()
{
liveTrigger.clear();
@@ -102,20 +117,23 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
me->SetReactState(REACT_PASSIVE);
_Reset();
}
+
void EnterCombat(Unit *who)
{
- for (uint32 i = 0; i < POS_LIVE; ++i)
+ for(uint32 i = 0; i < POS_LIVE; ++i)
if (Creature *trigger = DoSummon(WORLD_TRIGGER, PosSummonLive[i]))
liveTrigger.push_back(trigger);
- for (uint32 i = 0; i < POS_DEAD; ++i)
+ for(uint32 i = 0; i < POS_DEAD; ++i)
if (Creature *trigger = DoSummon(WORLD_TRIGGER, PosSummonDead[i]))
deadTrigger.push_back(trigger);
+
if (liveTrigger.size() < POS_LIVE || deadTrigger.size() < POS_DEAD)
{
error_log("Script Gothik: cannot summon triggers!");
EnterEvadeMode();
return;
}
+
_EnterCombat();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
waveCount = 0;
@@ -125,6 +143,7 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
if (instance)
instance->SetData(DATA_GOTHIK_GATE, 1);
}
+
void JustSummoned(Creature *summon)
{
if (summon->GetEntry() == WORLD_TRIGGER)
@@ -136,23 +155,27 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
}
summons.Summon(summon);
}
+
void SummonedCreatureDespawn(Creature *summon)
{
if (summon->GetEntry() == WORLD_TRIGGER)
{
- //for (TriggerVct::iterator itr = liveTrigger.begin(); itr != liveTrigger.end(); ++itr)
+ //for(TriggerVct::iterator itr = liveTrigger.begin(); itr != liveTrigger.end(); ++itr)
// if(*itr == summon)
error_log("boss_gothikAI: trigger is despawned!");
EnterEvadeMode();
return;
}
+
summons.Despawn(summon);
}
+
void KilledUnit(Unit* victim)
{
if (!(rand()%5))
DoScriptText(SAY_KILL, me);
}
+
void JustDied(Unit* Killer)
{
liveTrigger.clear();
@@ -160,6 +183,7 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
_JustDied();
DoScriptText(SAY_DEATH, me);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
uint32 spellId = 0;
@@ -175,10 +199,12 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
me->CastSpell(deadTrigger[rand()%POS_DEAD], spellId, true);
}
}
+
void SpellHitTarget(Unit *target, const SpellEntry *spell)
{
if (!me->isInCombat())
return;
+
switch(spell->Id)
{
case SPELL_INFORM_DEAD_TRAINEE: DoSummon(MOB_DEAD_TRAINEE, target, 0); break;
@@ -187,13 +213,17 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
DoSummon(MOB_DEAD_HORSE, target, 1.0f); break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateCombatState() || !CheckInRoom())
return;
+
events.Update(diff);
+
if (me->hasUnitState(UNIT_STAT_CASTING))
return;
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -201,7 +231,7 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
case EVENT_SUMMON:
if (waves[waveCount].entry)
{
- for (uint32 i = 0; i < waves[waveCount].number; ++i)
+ for(uint32 i = 0; i < waves[waveCount].number; ++i)
DoSummon(waves[waveCount].entry, liveTrigger[rand()%POS_LIVE], 1.0f);
events.ScheduleEvent(EVENT_SUMMON, waves[waveCount].time);
++waveCount;
@@ -230,27 +260,34 @@ struct TRINITY_DLL_DECL boss_gothikAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_gothik_minionAI : public CombatAI
{
mob_gothik_minionAI(Creature *c) : CombatAI(c)
{
liveSide = me->GetPositionY() < POS_Y_GATE;
}
+
bool liveSide;
bool gateClose;
+
#define SIDE_CHECK(who) (liveSide == (who->GetPositionY() < POS_Y_GATE))
+
void DoAction(const int32 param)
{
gateClose = param;
}
+
void DamageTaken(Unit *attacker, uint32 &damage)
{
if (gateClose && !SIDE_CHECK(attacker))
damage = 0;
}
+
void JustDied(Unit *killer)
{
if (me->isSummon())
@@ -259,6 +296,7 @@ struct TRINITY_DLL_DECL mob_gothik_minionAI : public CombatAI
CombatAI::JustDied(owner);
}
}
+
void EnterEvadeMode()
{
if (!gateClose)
@@ -266,13 +304,15 @@ struct TRINITY_DLL_DECL mob_gothik_minionAI : public CombatAI
CombatAI::EnterEvadeMode();
return;
}
+
if (!_EnterEvadeMode())
return;
+
Map* pMap = me->GetMap();
if (pMap->IsDungeon())
{
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (i->getSource()->isAlive() && SIDE_CHECK(i->getSource()))
{
@@ -281,9 +321,11 @@ struct TRINITY_DLL_DECL mob_gothik_minionAI : public CombatAI
}
}
}
+
me->GetMotionMaster()->MoveIdle();
Reset();
}
+
void UpdateAI(const uint32 diff)
{
if (gateClose && (!SIDE_CHECK(me) || me->getVictim() && !SIDE_CHECK(me->getVictim())))
@@ -291,17 +333,21 @@ struct TRINITY_DLL_DECL mob_gothik_minionAI : public CombatAI
EnterEvadeMode();
return;
}
+
CombatAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_boss_gothik(Creature* pCreature)
{
return new boss_gothikAI (pCreature);
}
+
CreatureAI* GetAI_mob_gothik_minion(Creature* pCreature)
{
return new mob_gothik_minionAI (pCreature);
}
+
void AddSC_boss_gothik()
{
Script *newscript;
@@ -309,6 +355,7 @@ void AddSC_boss_gothik()
newscript->Name = "boss_gothik";
newscript->GetAI = &GetAI_boss_gothik;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_gothik_minion";
newscript->GetAI = &GetAI_mob_gothik_minion;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_grobbulus.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_grobbulus.cpp
index 3a346798e9d..38edde2c327 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_grobbulus.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_grobbulus.cpp
@@ -13,25 +13,32 @@
* 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"
#include "def_naxxramas.h"
+
#define SPELL_BOMBARD_SLIME 28280
+
#define SPELL_POISON_CLOUD 28240
#define SPELL_MUTATING_INJECTION 28169
#define SPELL_SLIME_SPRAY HEROIC(28157,54364)
#define SPELL_BERSERK 26662
#define SPELL_POISON_CLOUD_ADD 59116
+
#define EVENT_BERSERK 1
#define EVENT_CLOUD 2
#define EVENT_INJECT 3
#define EVENT_SPRAY 4
+
#define MOB_FALLOUT_SLIME 16290
+
struct TRINITY_DLL_DECL boss_grobbulusAI : public BossAI
{
boss_grobbulusAI(Creature *c) : BossAI(c, BOSS_GROBBULUS)
{
me->ApplySpellImmune(0, IMMUNITY_ID, SPELL_POISON_CLOUD_ADD, true);
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -40,6 +47,7 @@ struct TRINITY_DLL_DECL boss_grobbulusAI : public BossAI
events.ScheduleEvent(EVENT_SPRAY, 15000+rand()%15000); //not sure
events.ScheduleEvent(EVENT_BERSERK, 12*60000);
}
+
void SpellHitTarget(Unit *target, const SpellEntry *spell)
{
if (spell->Id == SPELL_SLIME_SPRAY)
@@ -48,11 +56,14 @@ struct TRINITY_DLL_DECL boss_grobbulusAI : public BossAI
DoZoneInCombat(slime);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -76,21 +87,26 @@ struct TRINITY_DLL_DECL boss_grobbulusAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL npc_grobbulus_poison_cloudAI : public Scripted_NoMovementAI
{
npc_grobbulus_poison_cloudAI(Creature* pCreature) : Scripted_NoMovementAI(pCreature)
{
Reset();
}
+
uint32 Cloud_Timer;
+
void Reset()
{
Cloud_Timer = 1000;
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
void UpdateAI(const uint32 diff)
{
if (Cloud_Timer < diff)
@@ -100,14 +116,17 @@ struct TRINITY_DLL_DECL npc_grobbulus_poison_cloudAI : public Scripted_NoMovemen
} else Cloud_Timer -= diff;
}
};
+
CreatureAI* GetAI_boss_grobbulus(Creature* pCreature)
{
return new boss_grobbulusAI (pCreature);
}
+
CreatureAI* GetAI_npc_grobbulus_poison_cloud(Creature* pCreature)
{
return new npc_grobbulus_poison_cloudAI(pCreature);
}
+
void AddSC_boss_grobbulus()
{
Script *newscript;
@@ -115,6 +134,7 @@ void AddSC_boss_grobbulus()
newscript->Name = "boss_grobbulus";
newscript->GetAI = &GetAI_boss_grobbulus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_grobbulus_poison_cloud";
newscript->GetAI = &GetAI_npc_grobbulus_poison_cloud;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_heigan.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_heigan.cpp
index 417dc61b5cf..4cf9eb0ffcb 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_heigan.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_heigan.cpp
@@ -13,15 +13,19 @@
* 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"
#include "def_naxxramas.h"
+
#define SAY_AGGRO RAND(-1533109,-1533110,-1533111)
#define SAY_SLAY -1533112
#define SAY_TAUNT RAND(-1533113,-1533114,-1533115,-1533116,-1533117)
#define SAY_DEATH -1533118
+
#define SPELL_SPELL_DISRUPTION 29310
#define SPELL_DECREPIT_FEVER HEROIC(29998,55011)
#define SPELL_PLAGUE_CLOUD 29350
+
enum Events
{
EVENT_DISRUPT = 1,
@@ -29,35 +33,43 @@ enum Events
EVENT_ERUPT,
EVENT_PHASE,
};
+
enum Phases
{
PHASE_FIGHT = 1,
PHASE_DANCE,
};
+
//Spell by eye stalks
#define SPELL_MIND_FLAY 26143
+
struct TRINITY_DLL_DECL boss_heiganAI : public BossAI
{
boss_heiganAI(Creature *c) : BossAI(c, BOSS_HEIGAN) {}
+
uint32 eruptSection;
bool eruptDirection;
Phases phase;
+
void KilledUnit(Unit* Victim)
{
if (!(rand()%5))
DoScriptText(SAY_SLAY, me);
}
+
void JustDied(Unit* Killer)
{
_JustDied();
DoScriptText(SAY_DEATH, me);
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
DoScriptText(SAY_AGGRO, me);
EnterPhase(PHASE_FIGHT);
}
+
void EnterPhase(Phases newPhase)
{
phase = newPhase;
@@ -80,11 +92,14 @@ struct TRINITY_DLL_DECL boss_heiganAI : public BossAI
events.ScheduleEvent(EVENT_ERUPT, 5000);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim() || !CheckInRoom())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -103,22 +118,28 @@ struct TRINITY_DLL_DECL boss_heiganAI : public BossAI
case EVENT_ERUPT:
instance->SetData(DATA_HEIGAN_ERUPT, eruptSection);
TeleportCheaters();
+
if (eruptSection == 0)
eruptDirection = true;
else if (eruptSection == 3)
eruptDirection = false;
+
eruptDirection ? ++eruptSection : --eruptSection;
+
events.ScheduleEvent(EVENT_ERUPT, phase == PHASE_FIGHT ? 10000 : 3000);
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_heigan(Creature* pCreature)
{
return new boss_heiganAI (pCreature);
}
+
void AddSC_boss_heigan()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_kelthuzad.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_kelthuzad.cpp
index a07c09ff748..2e395ecec03 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_kelthuzad.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_kelthuzad.cpp
@@ -13,36 +13,45 @@
* along 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"
#include "def_naxxramas.h"
+
//when shappiron dies. dialog between kel and lich king (in this order)
#define SAY_SAPP_DIALOG1 -1533084 //not used
#define SAY_SAPP_DIALOG2_LICH -1533085 //not used
#define SAY_SAPP_DIALOG3 -1533086 //not used
#define SAY_SAPP_DIALOG4_LICH -1533087 //not used
#define SAY_SAPP_DIALOG5 -1533088 //not used
+
//when cat dies
#define SAY_CAT_DIED -1533089 //not used
+
//when each of the 4 wing bosses dies
#define SAY_TAUNT1 -1533090 //not used
#define SAY_TAUNT2 -1533091 //not used
#define SAY_TAUNT3 -1533092 //not used
#define SAY_TAUNT4 -1533093 //not used
+
#define SAY_SUMMON_MINIONS -1533105 //start of phase 1 not used
+
#define SAY_AGGRO RAND(-1533094,-1533095,-1533096) //start of phase 2
#define SAY_SLAY RAND(-1533097,-1533098)
#define SAY_DEATH -1533099
#define SAY_CHAIN RAND(-1533100,-1533101)
#define SAY_FROST_BLAST -1533102
#define SAY_SPECIAL RAND(-1533106,-1533107,-1533108)
+
#define SAY_REQUEST_AID -1533103 //start of phase 3
#define SAY_ANSWER_REQUEST -1533104 //lich king answer
+
enum Event
{
EVENT_BOLT = 1,
@@ -51,12 +60,15 @@ enum Event
EVENT_DETONATE,
EVENT_FISSURE,
EVENT_BLAST,
+
EVENT_WASTE,
EVENT_ABOMIN,
EVENT_WEAVER,
EVENT_ICECROWN,
+
EVENT_PHASE,
};
+
#define SPELL_FROST_BOLT HEROIC(28478,55802)
#define SPELL_FROST_BOLT_AOE HEROIC(28479,55807)
#define SPELL_SHADOW_FISURE 27810
@@ -65,10 +77,12 @@ enum Event
#define SPELL_FROST_BLAST 27808
#define SPELL_CHAINS_OF_KELTHUZAD 28410 //28408 script effect
#define SPELL_BERSERK 28498
+
#define MOB_WASTE 16427 // Soldiers of the Frozen Wastes
#define MOB_ABOMINATION 16428 // Unstoppable Abominations
#define MOB_WEAVER 16429 // Soul Weavers
#define MOB_ICECROWN 16441 // Guardians of Icecrown
+
const Position Pos[12] =
{
{3783.272705, -5062.697266, 143.711203,3.617599},//LEFT_FAR
@@ -84,29 +98,38 @@ const Position Pos[12] =
{3707.990733,-5151.450195,142.032562,1.376855},//RIGHT_MIDDLE
{3739.500000,-5141.883989,142.0141130, 2.121412}//RIGHT_NEAR
};
+
struct TRINITY_DLL_DECL boss_kelthuzadAI : public BossAI
{
boss_kelthuzadAI(Creature* c) : BossAI(c, BOSS_KELTHUZAD) {}
+
uint32 GuardiansOfIcecrown_Count;
+
uint32 Phase;
uint32 GuardiansOfIcecrown_Timer;
+
void Reset()
{
_Reset();
me->SetReactState(REACT_AGGRESSIVE);
GuardiansOfIcecrown_Count = 0;
+
GuardiansOfIcecrown_Timer = 5000; //5 seconds for summoning each Guardian of Icecrown in phase 3
+
Phase=0;
}
+
void KilledUnit()
{
DoScriptText(SAY_SLAY, m_creature);
}
+
void JustDied(Unit* Killer)
{
_JustDied();
DoScriptText(SAY_DEATH, m_creature);
}
+
void EnterCombat(Unit* who)
{
_EnterCombat();
@@ -119,11 +142,14 @@ struct TRINITY_DLL_DECL boss_kelthuzadAI : public BossAI
events.ScheduleEvent(EVENT_WEAVER, 20000);
events.ScheduleEvent(EVENT_PHASE, 228000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateCombatState())
return;
+
events.Update(diff);
+
if (Phase == 1)
{
while(uint32 eventId = events.GetEvent())
@@ -184,8 +210,10 @@ struct TRINITY_DLL_DECL boss_kelthuzadAI : public BossAI
}
else GuardiansOfIcecrown_Timer -= diff;
}
+
if (me->hasUnitState(UNIT_STAT_CASTING))
return;
+
if (uint32 eventId = events.GetEvent())
{
switch(eventId)
@@ -208,13 +236,14 @@ struct TRINITY_DLL_DECL boss_kelthuzadAI : public BossAI
{
std::vector<Unit*> unitList;
std::list<HostilReference*> *threatList = &me->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::const_iterator itr = threatList->begin(); itr != threatList->end(); ++itr)
+ for(std::list<HostilReference*>::const_iterator itr = threatList->begin(); itr != threatList->end(); ++itr)
{
if ((*itr)->getTarget()->GetTypeId() == TYPEID_PLAYER
&& (*itr)->getTarget()->getPowerType() == POWER_MANA
&& (*itr)->getTarget()->GetPower(POWER_MANA))
unitList.push_back((*itr)->getTarget());
}
+
if (!unitList.empty())
{
std::vector<Unit*>::iterator itr = unitList.begin();
@@ -222,6 +251,7 @@ struct TRINITY_DLL_DECL boss_kelthuzadAI : public BossAI
DoCast(*itr, SPELL_MANA_DETONATION);
DoScriptText(SAY_SPECIAL, me);
}
+
events.RepeatEvent(20000);
return;
}
@@ -242,14 +272,17 @@ struct TRINITY_DLL_DECL boss_kelthuzadAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
}
};
+
CreatureAI* GetAI_boss_kelthuzadAI(Creature* pCreature)
{
return new boss_kelthuzadAI (pCreature);
}
+
void AddSC_boss_kelthuzad()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_loatheb.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_loatheb.cpp
index 9790f40fda3..c87caf2beae 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_loatheb.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_loatheb.cpp
@@ -15,21 +15,26 @@
* 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"
#include "def_naxxramas.h"
+
#define SPELL_NECROTIC_AURA 55593
#define SPELL_SUMMON_SPORE 29234
#define SPELL_DEATHBLOOM HEROIC(29865,55053)
#define SPELL_INEVITABLE_DOOM HEROIC(29204,55052)
+
enum Events
{
EVENT_AURA = 1,
EVENT_BLOOM,
EVENT_DOOM,
};
+
struct TRINITY_DLL_DECL boss_loathebAI : public BossAI
{
boss_loathebAI(Creature *c) : BossAI(c, BOSS_LOATHEB) {}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -37,11 +42,14 @@ struct TRINITY_DLL_DECL boss_loathebAI : public BossAI
events.ScheduleEvent(EVENT_BLOOM, 30000);
events.ScheduleEvent(EVENT_DOOM, 120000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -61,13 +69,16 @@ struct TRINITY_DLL_DECL boss_loathebAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_loatheb(Creature* pCreature)
{
return new boss_loathebAI (pCreature);
}
+
void AddSC_boss_loatheb()
{
Script *newscript;
@@ -75,6 +86,7 @@ void AddSC_boss_loatheb()
newscript->Name = "boss_loatheb";
newscript->GetAI = &GetAI_boss_loatheb;
newscript->RegisterSelf();
+
// Fungal Creep
GetAISpellInfo(29232)->condition = AICOND_DIE;
}
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_maexxna.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_maexxna.cpp
index b9b0085e230..ff84d1077e4 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_maexxna.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_maexxna.cpp
@@ -15,15 +15,19 @@
* 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"
#include "def_naxxramas.h"
+
#define SPELL_WEB_WRAP 28622
#define SPELL_WEB_SPRAY HEROIC(29484,54125)
#define SPELL_POISON_SHOCK HEROIC(28741,54122)
#define SPELL_NECROTIC_POISON HEROIC(54121,28776)
#define SPELL_FRENZY HEROIC(54123,54124)
+
#define MOB_WEB_WRAP 16486
#define MOB_SPIDERLING 17055
+
#define MAX_POS_WRAP 3
const float PosWrap[MAX_POS_WRAP][3] =
{
@@ -31,6 +35,7 @@ const float PosWrap[MAX_POS_WRAP][3] =
{3531.271, -3847.424, 299.450+20},
{3497.067, -3843.384, 302.384+20},
};
+
enum Events
{
EVENT_SPRAY = 1,
@@ -39,10 +44,13 @@ enum Events
EVENT_WRAP,
EVENT_SUMMON,
};
+
struct TRINITY_DLL_DECL boss_maexxnaAI : public BossAI
{
boss_maexxnaAI(Creature *c) : BossAI(c, BOSS_MAEXXNA) {}
+
bool enraged;
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -53,17 +61,20 @@ struct TRINITY_DLL_DECL boss_maexxnaAI : public BossAI
events.ScheduleEvent(EVENT_POISON, 5000);
events.ScheduleEvent(EVENT_SUMMON, 40000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim() || !CheckInRoom())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
{
case EVENT_WRAP:
- for (uint32 i = 0; i < HEROIC(1,2); ++i)
+ for(uint32 i = 0; i < HEROIC(1,2); ++i)
{
if (Unit *target = SelectTarget(SELECT_TARGET_RANDOM, 1, 0, true, -SPELL_WEB_WRAP))
{
@@ -94,13 +105,14 @@ struct TRINITY_DLL_DECL boss_maexxnaAI : public BossAI
case EVENT_SUMMON:
{
uint32 amount = 8+rand()%2;
- for (uint32 i = 0; i < amount; ++i)
+ for(uint32 i = 0; i < amount; ++i)
DoSummon(MOB_SPIDERLING, me);
events.ScheduleEvent(EVENT_SUMMON, 40000);
break;
}
}
}
+
if (!enraged && HealthBelowPct(30))
{
DoCast(me, SPELL_FRENZY);
@@ -110,13 +122,16 @@ struct TRINITY_DLL_DECL boss_maexxnaAI : public BossAI
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_maexxna(Creature* pCreature)
{
return new boss_maexxnaAI (pCreature);
}
+
void AddSC_boss_maexxna()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_maexxna";
newscript->GetAI = &GetAI_boss_maexxna;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_noth.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_noth.cpp
index 594fe83620f..c4ba86a91d6 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_noth.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_noth.cpp
@@ -13,26 +13,34 @@
* 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"
#include "def_naxxramas.h"
+
#define SAY_AGGRO RAND(-1533075,-1533076,-1533077)
#define SAY_SUMMON -1533078
#define SAY_SLAY RAND(-1533079,-1533080)
#define SAY_DEATH -1533081
+
#define SOUND_DEATH 8848
+
#define SPELL_CURSE_PLAGUEBRINGER HEROIC(29213,54835)
#define SPELL_BLINK RAND(29208,29209,29210,29211)
#define SPELL_CRIPPLE HEROIC(29212,54814)
#define SPELL_TELEPORT 29216
+
#define MOB_WARRIOR 16984
#define MOB_CHAMPION 16983
#define MOB_GUARDIAN 16981
+
// 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 MAX_SUMMON_POS 5
+
const float SummonPos[MAX_SUMMON_POS][4] =
{
{2728.12, -3544.43, 261.91, 6.04},
@@ -41,6 +49,7 @@ const float SummonPos[MAX_SUMMON_POS][4] =
{2704.11, -3456.81, 265.53, 4.51},
{2663.56, -3464.43, 262.66, 5.20},
};
+
enum Events
{
EVENT_BERSERK = 1,
@@ -51,16 +60,20 @@ enum Events
EVENT_WAVE,
EVENT_GROUND,
};
+
struct TRINITY_DLL_DECL boss_nothAI : public BossAI
{
boss_nothAI(Creature *c) : BossAI(c, BOSS_NOTH) {}
+
uint32 waveCount, balconyCount;
+
void Reset()
{
me->SetReactState(REACT_AGGRESSIVE);
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
_Reset();
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -68,6 +81,7 @@ struct TRINITY_DLL_DECL boss_nothAI : public BossAI
balconyCount = 0;
EnterPhaseGround();
}
+
void EnterPhaseGround()
{
me->SetReactState(REACT_AGGRESSIVE);
@@ -84,36 +98,43 @@ struct TRINITY_DLL_DECL boss_nothAI : public BossAI
events.ScheduleEvent(EVENT_BLINK, 20000+rand()%10000);
}
}
+
void KilledUnit(Unit* victim)
{
if (!(rand()%5))
DoScriptText(SAY_SLAY, me);
}
+
void JustSummoned(Creature *summon)
{
summons.Summon(summon);
summon->setActive(true);
summon->AI()->DoZoneInCombat();
}
+
void JustDied(Unit* Killer)
{
_JustDied();
DoScriptText(SAY_DEATH, me);
}
+
void SummonUndead(uint32 entry, uint32 num)
{
- for (uint32 i = 0; i < num; ++i)
+ for(uint32 i = 0; i < num; ++i)
{
uint32 pos = rand()%MAX_SUMMON_POS;
me->SummonCreature(entry, SummonPos[pos][0], SummonPos[pos][1], SummonPos[pos][2],
SummonPos[pos][3], TEMPSUMMON_CORPSE_DESPAWN, 60000);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateCombatState() || !CheckInRoom())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -168,14 +189,17 @@ struct TRINITY_DLL_DECL boss_nothAI : public BossAI
}
}
}
+
if (me->HasReactState(REACT_AGGRESSIVE))
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_noth(Creature* pCreature)
{
return new boss_nothAI (pCreature);
}
+
void AddSC_boss_noth()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_patchwerk.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_patchwerk.cpp
index 34ebdfe3e1c..5077f0267ed 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_patchwerk.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_patchwerk.cpp
@@ -13,36 +13,48 @@
* 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"
#include "def_naxxramas.h"
+
#define SAY_AGGRO RAND(-1533017,-1533018)
#define SAY_SLAY -1533019
#define SAY_DEATH -1533020
+
#define EMOTE_BERSERK -1533021
#define EMOTE_ENRAGE -1533022
+
#define SPELL_HATEFULSTRIKE HEROIC(41926,59192)
#define SPELL_FRENZY 28131
#define SPELL_BERSERK 26662
#define SPELL_SLIMEBOLT 32309
+
#define EVENT_BERSERK 1
#define EVENT_HATEFUL 2
#define EVENT_SLIME 3
+
#define ACHIEVEMENT_MAKE_QUICK_WERK_OF_HIM HEROIC(1856, 1857)
#define MAX_ENCOUNTER_TIME 3 * 60 * 1000
+
struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
{
boss_patchwerkAI(Creature *c) : BossAI(c, BOSS_PATCHWERK) {}
+
bool Enraged;
+
uint32 EncounterTime;
+
void KilledUnit(Unit* Victim)
{
if (!(rand()%5))
DoScriptText(SAY_SLAY, me);
}
+
void JustDied(Unit* Killer)
{
_JustDied();
DoScriptText(SAY_DEATH, me);
+
if(EncounterTime <= MAX_ENCOUNTER_TIME)
{
AchievementEntry const *AchievMakeQuickWerkOfHim = GetAchievementStore()->LookupEntry(ACHIEVEMENT_MAKE_QUICK_WERK_OF_HIM);
@@ -52,12 +64,13 @@ struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
if(pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievMakeQuickWerkOfHim);
}
}
}
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -67,12 +80,16 @@ struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
events.ScheduleEvent(EVENT_HATEFUL, 1200);
events.ScheduleEvent(EVENT_BERSERK, 360000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
EncounterTime += diff;
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -84,7 +101,7 @@ struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
uint32 MostHP = 0;
Unit* pMostHPTarget = NULL;
std::list<HostilReference*>::iterator i = me->getThreatManager().getThreatList().begin();
- for (; i != me->getThreatManager().getThreatList().end(); ++i)
+ for(; i != me->getThreatManager().getThreatList().end(); ++i)
{
Unit* target = (*i)->getTarget();
if (target->isAlive() && target->GetHealth() > MostHP && me->IsWithinMeleeRange(target))
@@ -93,8 +110,10 @@ struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
pMostHPTarget = target;
}
}
+
if (pMostHPTarget)
DoCast(pMostHPTarget, SPELL_HATEFULSTRIKE, true);
+
events.ScheduleEvent(EVENT_HATEFUL, 1200);
return;
}
@@ -109,6 +128,7 @@ struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
return;
}
}
+
if (!Enraged && HealthBelowPct(5))
{
DoCast(m_creature, SPELL_FRENZY);
@@ -116,13 +136,16 @@ struct TRINITY_DLL_DECL boss_patchwerkAI : public BossAI
Enraged = true;
return;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_patchwerk(Creature* pCreature)
{
return new boss_patchwerkAI (pCreature);
}
+
void AddSC_boss_patchwerk()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_razuvious.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_razuvious.cpp
index 71698e1c25c..7f92978e8b1 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_razuvious.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_razuvious.cpp
@@ -15,8 +15,10 @@
* 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"
#include "def_naxxramas.h"
+
//Razuvious - NO TEXT sound only
//8852 aggro01 - Hah hah, I'm just getting warmed up!
//8853 aggro02 Stand and fight!
@@ -30,15 +32,18 @@
//8861 Sweep the leg! Do you have a problem with that?
//8860 death - An honorable... death...
//8947 - Aggro Mixed? - ?
+
#define SOUND_AGGRO RAND(8852,8853,8854)
#define SOUND_SLAY RAND(8861,8863)
#define SOUND_COMMND RAND(8855,8856,8858,8859,8861)
#define SOUND_DEATH 8860
#define SOUND_AGGROMIX 8847
+
#define SPELL_UNBALANCING_STRIKE 26613
#define SPELL_DISRUPTING_SHOUT HEROIC(29107,55543)
#define SPELL_JAGGED_KNIFE 55550
#define SPELL_HOPELESS 29125
+
enum Events
{
EVENT_STRIKE,
@@ -46,14 +51,17 @@ enum Events
EVENT_KNIFE,
EVENT_COMMAND,
};
+
struct TRINITY_DLL_DECL boss_razuviousAI : public BossAI
{
boss_razuviousAI(Creature *c) : BossAI(c, BOSS_RAZUVIOUS) {}
+
void KilledUnit(Unit* victim)
{
if (!(rand()%3))
DoPlaySoundToSet(me, SOUND_SLAY);
}
+
void DamageTaken(Unit* pDone_by, uint32& uiDamage)
{
// Damage done by the controlled Death Knight understudies should also count toward damage done by players
@@ -62,12 +70,14 @@ struct TRINITY_DLL_DECL boss_razuviousAI : public BossAI
me->LowerPlayerDamageReq(uiDamage);
}
}
+
void JustDied(Unit* killer)
{
_JustDied();
DoPlaySoundToSet(me, SOUND_DEATH);
me->CastSpell(me, SPELL_HOPELESS, true); // TODO: this may affect other creatures
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -76,11 +86,14 @@ struct TRINITY_DLL_DECL boss_razuviousAI : public BossAI
events.ScheduleEvent(EVENT_SHOUT, 25000);
events.ScheduleEvent(EVENT_COMMAND, 40000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -104,13 +117,16 @@ struct TRINITY_DLL_DECL boss_razuviousAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_razuvious(Creature* pCreature)
{
return new boss_razuviousAI (pCreature);
}
+
void AddSC_boss_razuvious()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_sapphiron.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_sapphiron.cpp
index 4ec38e2a446..e5fb1c6463a 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_sapphiron.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_sapphiron.cpp
@@ -15,10 +15,13 @@
* 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"
#include "def_naxxramas.h"
+
#define EMOTE_BREATH -1533082
#define EMOTE_ENRAGE -1533083
+
#define SPELL_FROST_AURA HEROIC(28531,55799)
#define SPELL_CLEAVE 19983
#define SPELL_TAIL_SWEEP HEROIC(55697,55696)
@@ -30,11 +33,15 @@
#define SPELL_FROST_MISSILE 30101
#define SPELL_BERSERK 26662
#define SPELL_DIES 29357
+
#define SPELL_CHILL HEROIC(28547,55699)
+
#define MOB_BLIZZARD 16474
#define GO_ICEBLOCK 181247
+
#define ACHIEVEMENT_THE_HUNDRED_CLUB HEROIC(2146, 2147)
#define MAX_FROST_RESISTANCE 100
+
enum Phases
{
PHASE_NULL = 0,
@@ -42,6 +49,7 @@ enum Phases
PHASE_GROUND,
PHASE_FLIGHT,
};
+
enum Events
{
EVENT_BERSERK = 1,
@@ -58,7 +66,9 @@ enum Events
EVENT_GROUND,
EVENT_BIRTH,
};
+
typedef std::map<uint64, uint64> IceBlockMap;
+
struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
{
boss_sapphironAI(Creature* c) : BossAI(c, BOSS_SAPPHIRON)
@@ -66,12 +76,15 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
{
pMap = m_creature->GetMap();
}
+
Phases phase;
uint32 iceboltCount;
IceBlockMap iceblocks;
+
bool CanTheHundredClub; // needed for achievement: The Hundred Club(2146, 2147)
uint32 CheckFrostResistTimer;
Map* pMap;
+
void InitializeAI()
{
float x, y, z;
@@ -80,25 +93,35 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
me->SetVisibility(VISIBILITY_OFF);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->SetReactState(REACT_PASSIVE);
+
ScriptedAI::InitializeAI();
}
+
void Reset()
{
_Reset();
+
if (phase = PHASE_FLIGHT)
ClearIceBlock();
+
phase = PHASE_NULL;
+
CanTheHundredClub = true;
CheckFrostResistTimer = 5000;
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
+
me->CastSpell(me, SPELL_FROST_AURA, true);
+
events.ScheduleEvent(EVENT_BERSERK, 15*60000);
EnterPhaseGround();
+
CheckPlayersFrostResist();
}
+
void SpellHitTarget(Unit *target, const SpellEntry *spell)
{
if (spell->Id == SPELL_ICEBOLT)
@@ -111,10 +134,12 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
}
}
}
+
void JustDied(Unit* who)
{
_JustDied();
me->CastSpell(me, SPELL_DIES, true);
+
CheckPlayersFrostResist();
if(CanTheHundredClub)
{
@@ -124,17 +149,19 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
if(pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievTheHundredClub);
}
}
}
}
+
void MovementInform(uint32, uint32 id)
{
if (id == 1)
events.ScheduleEvent(EVENT_LIFTOFF, 0);
}
+
void DoAction(const int32 param)
{
if (param == DATA_SAPPHIRON_BIRTH)
@@ -143,12 +170,13 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
events.ScheduleEvent(EVENT_BIRTH, 23000);
}
}
+
void CheckPlayersFrostResist()
{
if(CanTheHundredClub && pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if(itr->getSource()->GetResistance(SPELL_SCHOOL_FROST) > MAX_FROST_RESISTANCE)
{
@@ -158,6 +186,7 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
}
}
}
+
void EnterPhaseGround()
{
phase = PHASE_GROUND;
@@ -169,9 +198,10 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
events.ScheduleEvent(EVENT_BLIZZARD, 5000+rand()%5000, 0, PHASE_GROUND);
events.ScheduleEvent(EVENT_FLIGHT, 45000);
}
+
void ClearIceBlock()
{
- for (IceBlockMap::iterator itr = iceblocks.begin(); itr != iceblocks.end(); ++itr)
+ for(IceBlockMap::iterator itr = iceblocks.begin(); itr != iceblocks.end(); ++itr)
{
if (Player* pPlayer = Unit::GetPlayer(itr->first))
pPlayer->RemoveAura(SPELL_ICEBOLT);
@@ -180,13 +210,17 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
}
iceblocks.clear();
}
+
void UpdateAI(const uint32 diff)
{
if (!phase)
return;
+
events.Update(diff);
+
if (phase != PHASE_BIRTH && !UpdateCombatState() || !CheckInRoom())
return;
+
if(CanTheHundredClub)
{
if(CheckFrostResistTimer < diff)
@@ -195,6 +229,7 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
CheckFrostResistTimer = (rand() % 5 + 5) * 1000;
}else CheckFrostResistTimer -= diff;
}
+
if (phase == PHASE_GROUND)
{
while(uint32 eventId = events.ExecuteEvent())
@@ -236,6 +271,7 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
return;
}
}
+
DoMeleeAttackIfReady();
}
else
@@ -255,9 +291,10 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
{
std::vector<Unit*> targets;
std::list<HostilReference*>::iterator i = me->getThreatManager().getThreatList().begin();
- for (; i != me->getThreatManager().getThreatList().end(); ++i)
+ for(; i != me->getThreatManager().getThreatList().end(); ++i)
if ((*i)->getTarget()->GetTypeId() == TYPEID_PLAYER && !(*i)->getTarget()->HasAura(SPELL_ICEBOLT))
targets.push_back((*i)->getTarget());
+
if (targets.empty())
iceboltCount = 0;
else
@@ -268,6 +305,7 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
DoCast(*itr, SPELL_ICEBOLT);
--iceboltCount;
}
+
if (iceboltCount)
events.ScheduleEvent(EVENT_ICEBOLT, 1000);
else
@@ -304,23 +342,26 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
}//if (uint32 eventId = events.ExecuteEvent())
}//if (phase == PHASE_GROUND)
}
+
void CastExplosion()
{
DoZoneInCombat(); // make sure everyone is in threatlist
std::vector<Unit*> targets;
std::list<HostilReference*>::iterator i = me->getThreatManager().getThreatList().begin();
- for (; i != me->getThreatManager().getThreatList().end(); ++i)
+ for(; i != me->getThreatManager().getThreatList().end(); ++i)
{
Unit *target = (*i)->getTarget();
if (target->GetTypeId() != TYPEID_PLAYER)
continue;
+
if (target->HasAura(SPELL_ICEBOLT))
{
target->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, true);
targets.push_back(target);
continue;
}
- for (IceBlockMap::iterator itr = iceblocks.begin(); itr != iceblocks.end(); ++itr)
+
+ for(IceBlockMap::iterator itr = iceblocks.begin(); itr != iceblocks.end(); ++itr)
{
if (GameObject* pGo = GameObject::GetGameObject(*me, itr->second))
{
@@ -334,15 +375,19 @@ struct TRINITY_DLL_DECL boss_sapphironAI : public BossAI
}
}
}
+
me->CastSpell(me, SPELL_FROST_EXPLOSION, true);
- for (std::vector<Unit*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+
+ for(std::vector<Unit*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
(*itr)->ApplySpellImmune(0, IMMUNITY_ID, SPELL_FROST_EXPLOSION, false);
}
};
+
CreatureAI* GetAI_boss_sapphiron(Creature* pCreature)
{
return new boss_sapphironAI (pCreature);
}
+
void AddSC_boss_sapphiron()
{
Script *newscript;
@@ -350,6 +395,7 @@ void AddSC_boss_sapphiron()
newscript->Name = "boss_sapphiron";
newscript->GetAI = &GetAI_boss_sapphiron;
newscript->RegisterSelf();
+
// Chill
GetAISpellInfo(28547)->cooldown = 1000;
GetAISpellInfo(55699)->cooldown = 1000;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/boss_thaddius.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/boss_thaddius.cpp
index 477ac69df87..173bb84dba7 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/boss_thaddius.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/boss_thaddius.cpp
@@ -15,24 +15,32 @@
* 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"
#include "def_naxxramas.h"
+
//Stalagg
#define SAY_STAL_AGGRO -1533023 //not used
#define SAY_STAL_SLAY -1533024 //not used
#define SAY_STAL_DEATH -1533025 //not used
+
#define SPELL_POWERSURGE 28134
+
//Feugen
#define SAY_FEUG_AGGRO -1533026 //not used
#define SAY_FEUG_SLAY -1533027 //not used
#define SAY_FEUG_DEATH -1533028 //not used
+
#define SPELL_MANABURN 28135
+
//both
#define SPELL_WARSTOMP 28125
+
//generic
#define C_TESLA_COIL 16218 //the coils (emotes "Tesla Coil overloads!")
+
//Thaddus
#define SAY_GREET -1533029 //not used
#define SAY_AGGRO RAND(-1533030,-1533031,-1533032)
@@ -43,16 +51,19 @@
#define SAY_SCREAM2 -1533037 //not used
#define SAY_SCREAM3 -1533038 //not used
#define SAY_SCREAM4 -1533039 //not used
+
#define SPELL_POLARITY_SHIFT 28089
#define SPELL_BALL_LIGHTNING 28299
#define SPELL_CHAIN_LIGHTNING HEROIC(28167,54531)
#define SPELL_BERSERK 27680
+
enum Events
{
EVENT_SHIFT = 1,
EVENT_CHAIN,
EVENT_BERSERK,
};
+
struct TRINITY_DLL_DECL boss_thaddiusAI : public BossAI
{
boss_thaddiusAI(Creature *c) : BossAI(c, BOSS_THADDIUS)
@@ -60,16 +71,19 @@ struct TRINITY_DLL_DECL boss_thaddiusAI : public BossAI
// temp
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_STUNNED);
}
+
void KilledUnit(Unit* victim)
{
if (!(rand()%5))
DoScriptText(SAY_SLAY, me);
}
+
void JustDied(Unit* Killer)
{
_JustDied();
DoScriptText(SAY_DEATH, me);
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -78,13 +92,17 @@ struct TRINITY_DLL_DECL boss_thaddiusAI : public BossAI
events.ScheduleEvent(EVENT_CHAIN, 10000+rand()%10000);
events.ScheduleEvent(EVENT_BERSERK, 6*60000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
if (me->hasUnitState(UNIT_STAT_CASTING))
return;
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -102,16 +120,19 @@ struct TRINITY_DLL_DECL boss_thaddiusAI : public BossAI
return;
}
}
+
if (events.GetTimer() > 15000 && !me->IsWithinMeleeRange(me->getVictim()))
DoCast(me->getVictim(), SPELL_BALL_LIGHTNING);
else
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_thaddius(Creature* pCreature)
{
return new boss_thaddiusAI (pCreature);
}
+
void AddSC_boss_thaddius()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/def_naxxramas.h b/src/bindings/scripts/scripts/northrend/naxxramas/def_naxxramas.h
index 06692a009d4..383200d4600 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/def_naxxramas.h
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/def_naxxramas.h
@@ -15,8 +15,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DEF_NAXXRAMAS_H
#define DEF_NAXXRAMAS_H
+
enum Encounter
{
BOSS_ANUBREKHAN,
@@ -36,12 +38,15 @@ enum Encounter
BOSS_KELTHUZAD,
MAX_BOSS_NUMBER
};
+
enum Data
{
DATA_HEIGAN_ERUPT,
DATA_GOTHIK_GATE,
DATA_SAPPHIRON_BIRTH,
};
+
#define GO_BIRTH 181356
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/naxxramas/instance_naxxramas.cpp b/src/bindings/scripts/scripts/northrend/naxxramas/instance_naxxramas.cpp
index f6ea3d6bca9..189eac62ca8 100644
--- a/src/bindings/scripts/scripts/northrend/naxxramas/instance_naxxramas.cpp
+++ b/src/bindings/scripts/scripts/northrend/naxxramas/instance_naxxramas.cpp
@@ -13,8 +13,10 @@
* 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"
#include "def_naxxramas.h"
+
const DoorData doorData[] =
{
{181126, BOSS_ANUBREKHAN,DOOR_TYPE_ROOM, BOUNDARY_S},
@@ -42,6 +44,7 @@ const DoorData doorData[] =
{181225, BOSS_SAPPHIRON, DOOR_TYPE_PASSAGE, BOUNDARY_W},
{0, 0, DOOR_TYPE_ROOM, 0}, // EOF
};
+
const MinionData minionData[] =
{
//{16573, BOSS_ANUBREKHAN}, there is no spawn point in db, so we do not add them here
@@ -53,13 +56,16 @@ const MinionData minionData[] =
{30549, BOSS_HORSEMEN},
{0, 0,}
};
+
enum eEnums
{
GO_HORSEMEN_CHEST_HERO = 193426,
GO_HORSEMEN_CHEST = 181366, //four horsemen event, DoRespawnGameObject() when event == DONE
GO_GOTHIK_GATE = 181170,
+
SPELL_ERUPTION = 29371
};
+
const float HeiganPos[2] = {2796, -3707};
const float HeiganEruptionSlope[3] =
{
@@ -67,6 +73,7 @@ const float HeiganEruptionSlope[3] =
(-3647 - HeiganPos[1]) /(2749 - HeiganPos[0]),
(-3637 - HeiganPos[1]) /(2771 - HeiganPos[0]),
};
+
// 0 H x
// 1 ^
// 2 |
@@ -76,15 +83,18 @@ inline uint32 GetEruptionSection(float x, float y)
y -= HeiganPos[1];
if (y < 1.0f)
return 0;
+
x -= HeiganPos[0];
if (x > -1.0f)
return 3;
+
float slope = y/x;
- for (uint32 i = 0; i < 3; ++i)
+ for(uint32 i = 0; i < 3; ++i)
if (slope > HeiganEruptionSlope[i])
return i;
return 3;
}
+
struct TRINITY_DLL_DECL instance_naxxramas : public InstanceData
{
instance_naxxramas(Map* pMap) : InstanceData(pMap)
@@ -94,18 +104,22 @@ struct TRINITY_DLL_DECL instance_naxxramas : public InstanceData
LoadDoorData(doorData);
LoadMinionData(minionData);
}
+
std::set<GameObject*> HeiganEruption[4];
GameObject* pGothikGate, *HorsemenChest;
Creature* Sapphiron;
uint32 HorsemenNum;
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
{
case 15989: Sapphiron = add ? pCreature : NULL; return;
}
+
AddMinion(pCreature, add);
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
if (pGo->GetGOInfo()->displayId == 6785 || pGo->GetGOInfo()->displayId == 1287)
@@ -117,6 +131,7 @@ struct TRINITY_DLL_DECL instance_naxxramas : public InstanceData
HeiganEruption[section].erase(pGo);
return;
}
+
switch(pGo->GetEntry())
{
case GO_BIRTH: if (!add && Sapphiron) Sapphiron->AI()->DoAction(DATA_SAPPHIRON_BIRTH); return;
@@ -124,8 +139,10 @@ struct TRINITY_DLL_DECL instance_naxxramas : public InstanceData
case GO_HORSEMEN_CHEST: HorsemenChest = add ? pGo : NULL; break;
case GO_HORSEMEN_CHEST_HERO: HorsemenChest = add ? pGo : NULL; break;
}
+
AddDoor(pGo, add);
}
+
void SetData(uint32 id, uint32 value)
{
switch(id)
@@ -139,21 +156,26 @@ struct TRINITY_DLL_DECL instance_naxxramas : public InstanceData
break;
}
}
+
bool SetBossState(uint32 id, EncounterState state)
{
if (!InstanceData::SetBossState(id, state))
return false;
+
if (id == BOSS_HORSEMEN && state == DONE && HorsemenChest)
HorsemenChest->SetRespawnTime(HorsemenChest->GetRespawnDelay());
+
return true;
}
+
void HeiganErupt(uint32 section)
{
- for (uint32 i = 0; i < 4; ++i)
+ for(uint32 i = 0; i < 4; ++i)
{
if (i == section)
continue;
- for (std::set<GameObject*>::iterator itr = HeiganEruption[i].begin(); itr != HeiganEruption[i].end(); ++itr)
+
+ for(std::set<GameObject*>::iterator itr = HeiganEruption[i].begin(); itr != HeiganEruption[i].end(); ++itr)
{
(*itr)->SendCustomAnim();
(*itr)->CastSpell(NULL, SPELL_ERUPTION);
@@ -161,21 +183,26 @@ struct TRINITY_DLL_DECL instance_naxxramas : public InstanceData
}
}
};
+
bool AreaTrigger_at_naxxramas_frostwyrm_wing(Player* pPlayer, AreaTriggerEntry *at)
{
if (pPlayer->isGameMaster())
return false;
+
InstanceData *data = pPlayer->GetInstanceData();
if (data)
- for (uint32 i = BOSS_ANUBREKHAN; i < BOSS_SAPPHIRON; ++i)
+ for(uint32 i = BOSS_ANUBREKHAN; i < BOSS_SAPPHIRON; ++i)
if (data->GetBossState(i) != DONE)
return true;
+
return false;
}
+
InstanceData* GetInstanceData_instance_naxxramas(Map* pMap)
{
return new instance_naxxramas(pMap);
}
+
void AddSC_instance_naxxramas()
{
Script *newscript;
@@ -183,6 +210,7 @@ void AddSC_instance_naxxramas()
newscript->Name = "instance_naxxramas";
newscript->GetInstanceData = &GetInstanceData_instance_naxxramas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "at_naxxramas_frostwyrm_wing";
newscript->pAreaTrigger = &AreaTrigger_at_naxxramas_frostwyrm_wing;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp b/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp
index a8e261a80a5..e96d6893380 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/boss_malygos.cpp
@@ -5,10 +5,12 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_ARCANE_BREATH_N 56272
#define SPELL_ARCANE_BREATH_H 60072
@@ -21,6 +23,7 @@ update creature_template set scriptname = '' where entry = '';
#define SPELL_SURGE_OF_POWER_2 57407
#define SPELL_SURGE_OF_POWER_3 60936
#define SPELL_VORTEX 56105
+
//Dragon "mounts" spells in Phase3
//they use Rugelike energy
#define SPELL_DMOUNT_FLAME_SPIKE 56091 //maybe not accurate
@@ -29,6 +32,7 @@ update creature_template set scriptname = '' where entry = '';
#define SPELL_DMOUNT_LIFE_BURST 57143
#define SPELL_DMOUNT_FLAME_SHIELD 57108
//#define SPELL_DMOUNT_UNKNOWN XYZ //Increases your drake's flight speed by 500%.
+
//not in db
//Yell
//-->Other
@@ -53,7 +57,9 @@ update creature_template set scriptname = '' where entry = '';
#define SAY_PHASE1_SLAY_1 -1616015
#define SAY_PHASE1_SLAY_2 -1616016
#define SAY_PHASE1_SLAY_3 -1616017
+
//--> Phase2 at 50% HP,
+
/*Malygos himself is not targetable during this phase, it will end when the adds he spawns are all killed. However, he does continue to play a part in the encounter.
During this phase he drops anti-magic zones onto the ground the raid MUST stand inside of, it reduces magical damage taken by 50%. They shrink over time, so it's important that your raid moves to each new one he drops.
Throughout the phase, he will deep breath doing ~4k damage per second, unless you are standing inside of the anti-magic zone.
@@ -62,6 +68,7 @@ The Lords will move down onto the group, and need to be tanked (They will one-sh
It is recommended to let melee take the first disks, then ranged. As those mobs die, they also drop disks, which allows the rest of your dps to get onto them.
The Scions will continually cast Arcane Blast on random targets on the floor, which is mitigated by the anti-magic zones. While mounted on a disk, you will not take damage.
After all of the NPCs riding on the disks die, the players on the disks need to dismount as Phase 3 is about to begin.*/
+
//not in db
#define SAY_PHASE2_AGGRO -1616018
#define SAY_PHASE2_END -1616019
@@ -75,11 +82,14 @@ After all of the NPCs riding on the disks die, the players on the disks need to
#define SAY_PHASE3_SLAY_2 -1616026
#define SAY_PHASE3_SLAY_3 -1616027
#define SAY_PHASE3_BIG_ATTACK -1616028
+
struct TRINITY_DLL_DECL boss_malygosAI : public ScriptedAI
{
boss_malygosAI(Creature *c) : ScriptedAI(c) {}
+
uint32 phase,
enrage;
+
void Reset()
{
//Source Deadly Boss Mod
@@ -102,12 +112,14 @@ struct TRINITY_DLL_DECL boss_malygosAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= 50){
phase = 2;
//spawn adds
//set malygos unatackable untill all adds spawned dead
//start phase3
}
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -118,6 +130,7 @@ struct TRINITY_DLL_DECL boss_malygosAI : public ScriptedAI
{
if (victim == m_creature)
return;
+
if (phase ==1)
DoScriptText(RAND(SAY_PHASE1_SLAY_1,SAY_PHASE1_SLAY_2,SAY_PHASE1_SLAY_3), m_creature);
if (phase ==2)
@@ -126,13 +139,16 @@ struct TRINITY_DLL_DECL boss_malygosAI : public ScriptedAI
DoScriptText(RAND(SAY_PHASE3_SLAY_1,SAY_PHASE3_SLAY_2,SAY_PHASE3_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_malygos(Creature* pCreature)
{
return new boss_malygosAI (pCreature);
}
+
void AddSC_boss_malygos()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_malygos";
newscript->GetAI = &GetAI_boss_malygos;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/def_eye_of_eternity.h b/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/def_eye_of_eternity.h
index b2624ea1796..caa82a92e95 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/def_eye_of_eternity.h
+++ b/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/def_eye_of_eternity.h
@@ -1,3 +1,4 @@
#ifndef DEF_EYE_OF_ETERNITY_H
#define DEF_EYE_OF_ETERNITY_H
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp b/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp
index aa2c7107ad4..7c89a117d92 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/eye_of_eternity/instance_eye_of_eternity.cpp
@@ -1,13 +1,16 @@
#include "precompiled.h"
#include "def_eye_of_eternity.h"
+
struct TRINITY_DLL_DECL instance_eye_of_eternity : public ScriptedInstance
{
instance_eye_of_eternity(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
};
+
InstanceData* GetInstanceData_instance_eye_of_eternity(Map* pMap)
{
return new instance_eye_of_eternity(pMap);
}
+
void AddSC_instance_eye_of_eternity()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_anomalus.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_anomalus.cpp
index 4a1dc380925..f0759804235 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_anomalus.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_anomalus.cpp
@@ -13,18 +13,23 @@
* along 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_Anomalus
SD%Complete:
SDComment:
SDCategory: The Nexus, The Nexus
EndScriptData */
+
#include "precompiled.h"
#include "def_nexus.h"
+
bool DeadChaoticRift; // needed for achievement: Chaos Theory(2037)
+
enum eEnums
{
ACHIEVEMENT_CHAOS_THEORY = 2037,
+
//Spells
SPELL_SPARK_N = 47751,
SPELL_SPARK_H = 57062,
@@ -32,17 +37,20 @@ enum eEnums
SPELL_CHARGE_RIFT = 47747, //Works wrong (affect players, not rifts)
SPELL_CREATE_RIFT = 47743, //Don't work, using WA
SPELL_ARCANE_ATTRACTION = 57063, //No idea, when it's used
+
MOB_CRAZED_MANA_WRAITH = 26746,
MOB_CHAOTIC_RIFT = 26918,
SPELL_CHAOTIC_ENERGY_BURST = 47688,
SPELL_CHARGED_CHAOTIC_ENERGY_BURST = 47737,
SPELL_ARCANEFORM = 48019, //Chaotic Rift visual
+
//Yell
SAY_AGGRO = -1576010,
SAY_DEATH = -1576011,
SAY_RIFT = -1576012,
SAY_SHIELD = -1576013
};
+
float RiftLocation[6][3]=
{
{652.64, -273.70, -8.75},
@@ -52,6 +60,7 @@ float RiftLocation[6][3]=
{639.87, -314.11, -9.49},
{651.72, -297.44, -9.37}
};
+
struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
{
boss_anomalusAI(Creature *c) : ScriptedAI(c)
@@ -59,31 +68,40 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint8 Phase;
uint32 SPELL_SPARK_Timer;
uint32 SPELL_CREATE_RIFT_Timer;
uint64 ChaoticRiftGUID;
+
void Reset()
{
Phase = 0;
SPELL_SPARK_Timer = 5000;
SPELL_CREATE_RIFT_Timer = 25000;
ChaoticRiftGUID = 0;
+
DeadChaoticRift = false;
+
if (pInstance)
pInstance->SetData(DATA_ANOMALUS_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_ANOMALUS_EVENT, IN_PROGRESS);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (HeroicMode && !DeadChaoticRift)
{
AchievementEntry const *AchievChaosTheory = GetAchievementStore()->LookupEntry(ACHIEVEMENT_CHAOS_THEORY);
@@ -93,18 +111,21 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievChaosTheory);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_ANOMALUS_EVENT, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (m_creature->HasAura(SPELL_RIFT_SHIELD))
{
if (ChaoticRiftGUID)
@@ -119,11 +140,13 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
}
} else
ChaoticRiftGUID = 0;
+
if ((Phase == 0) && (m_creature->GetHealth() < m_creature->GetMaxHealth() * 0.75))
{
Phase = 1;
DoScriptText(SAY_SHIELD, m_creature);
DoCast(m_creature, SPELL_RIFT_SHIELD);
+
int tmp = rand()%(2);
Creature* Rift = m_creature->SummonCreature(MOB_CHAOTIC_RIFT, RiftLocation[tmp][0], RiftLocation[tmp][1], RiftLocation[tmp][2], 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
if (Rift)
@@ -135,11 +158,13 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
DoScriptText(SAY_RIFT , m_creature);
}
}
+
if ((Phase == 1) && (m_creature->GetHealth() < m_creature->GetMaxHealth() * 0.50))
{
Phase = 2;
DoScriptText(SAY_SHIELD , m_creature);
DoCast(m_creature,SPELL_RIFT_SHIELD);
+
int tmp = rand()%(2);
Creature* Rift = m_creature->SummonCreature(MOB_CHAOTIC_RIFT, RiftLocation[tmp][0], RiftLocation[tmp][1], RiftLocation[tmp][2], 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
if (Rift)
@@ -151,11 +176,13 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
DoScriptText(SAY_RIFT , m_creature);
}
}
+
if ((Phase == 2) && (m_creature->GetHealth() < m_creature->GetMaxHealth() * 0.25))
{
Phase = 3;
DoScriptText(SAY_SHIELD , m_creature);
DoCast(m_creature,SPELL_RIFT_SHIELD);
+
int tmp = rand()%(2);
Creature* Rift = m_creature->SummonCreature(MOB_CHAOTIC_RIFT, RiftLocation[tmp][0], RiftLocation[tmp][1], RiftLocation[tmp][2], 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
if (Rift)
@@ -167,15 +194,18 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
DoScriptText(SAY_RIFT , m_creature);
}
}
+
if (SPELL_SPARK_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, HEROIC(SPELL_SPARK_N, SPELL_SPARK_H));
SPELL_SPARK_Timer = 5000;
}else SPELL_SPARK_Timer -=diff;
+
if (SPELL_CREATE_RIFT_Timer < diff)
{
DoScriptText(SAY_RIFT , m_creature);
+
int tmp = rand()%(2);
Creature* Rift = m_creature->SummonCreature(MOB_CHAOTIC_RIFT, RiftLocation[tmp][0], RiftLocation[tmp][1], RiftLocation[tmp][2], 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
if (Rift)
@@ -183,22 +213,28 @@ struct TRINITY_DLL_DECL boss_anomalusAI : public ScriptedAI
Rift->AI()->AttackStart(target);
SPELL_CREATE_RIFT_Timer = 25000;
}else SPELL_CREATE_RIFT_Timer -=diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_anomalus(Creature* pCreature)
{
return new boss_anomalusAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_chaotic_riftAI : public Scripted_NoMovementAI
{
mob_chaotic_riftAI(Creature *c) : Scripted_NoMovementAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 SPELL_CHAOTIC_ENERGY_BURST_Timer;
uint32 SUMMON_CRAZED_MANA_WRAITH_Timer;
+
void Reset()
{
SPELL_CHAOTIC_ENERGY_BURST_Timer = 1000;
@@ -208,14 +244,17 @@ struct TRINITY_DLL_DECL mob_chaotic_riftAI : public Scripted_NoMovementAI
//Set model to horde number
DoCast(m_creature, SPELL_ARCANEFORM, false);
}
+
void JustDied(Unit *killer)
{
DeadChaoticRift = true;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (SPELL_CHAOTIC_ENERGY_BURST_Timer < diff)
{
Unit* Anomalus = Unit::GetUnit(*m_creature, pInstance ? pInstance->GetData64(DATA_ANOMALUS) : 0);
@@ -226,6 +265,7 @@ struct TRINITY_DLL_DECL mob_chaotic_riftAI : public Scripted_NoMovementAI
DoCast(target, SPELL_CHAOTIC_ENERGY_BURST);
SPELL_CHAOTIC_ENERGY_BURST_Timer = 1000;
}else SPELL_CHAOTIC_ENERGY_BURST_Timer -=diff;
+
if (SUMMON_CRAZED_MANA_WRAITH_Timer < diff)
{
Creature* Wraith = m_creature->SummonCreature(MOB_CRAZED_MANA_WRAITH, m_creature->GetPositionX()+1, m_creature->GetPositionY()+1, m_creature->GetPositionZ(), 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
@@ -240,17 +280,21 @@ struct TRINITY_DLL_DECL mob_chaotic_riftAI : public Scripted_NoMovementAI
}else SUMMON_CRAZED_MANA_WRAITH_Timer -=diff;
}
};
+
CreatureAI* GetAI_mob_chaotic_rift(Creature* pCreature)
{
return new mob_chaotic_riftAI (pCreature);
}
+
void AddSC_boss_anomalus()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_anomalus";
newscript->GetAI = &GetAI_boss_anomalus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_chaotic_rift";
newscript->GetAI = &GetAI_mob_chaotic_rift;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_keristrasza.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_keristrasza.cpp
index 6274bf3d1f8..4845119a89e 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_keristrasza.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_keristrasza.cpp
@@ -13,18 +13,23 @@
* along 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_Keristrasza
SD%Complete:
SDComment:
SDCategory: The Nexus, The Nexus
EndScriptData */
+
#include "precompiled.h"
#include "def_nexus.h"
+
enum eEnums
{
CONTAINMENT_SPHERES = 3,
+
ACHIEVEMENT_INTENSE_COLD = 2036,
+
//Spells
SPELL_FROZEN_PRISON = 47854,
SPELL_TAIL_SWEEP = 50155,
@@ -35,6 +40,7 @@ enum eEnums
SPELL_CRYSTALIZE = 48179,
SPELL_INTENSE_COLD = 48094,
SPELL_INTENSE_COLD_TRIGGERED = 48095,
+
//Yell
SAY_AGGRO = -1576040,
SAY_SLAY = -1576041,
@@ -42,6 +48,7 @@ enum eEnums
SAY_DEATH = -1576043,
SAY_CRYSTAL_NOVA = -1576044
};
+
struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
{
boss_keristraszaAI(Creature *c) : ScriptedAI(c)
@@ -49,38 +56,51 @@ struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint32 CRYSTALFIRE_BREATH_Timer;
uint32 CRYSTAL_CHAINS_CRYSTALIZE_Timer;
uint32 TAIL_SWEEP_Timer;
bool Enrage;
+
uint64 ContainmentSphereGUIDs[CONTAINMENT_SPHERES];
+
uint32 CheckIntenseColdTimer;
bool MoreThanTwoIntenseCold; // needed for achievement: Intense Cold(2036)
+
void Reset()
{
CRYSTALFIRE_BREATH_Timer = 14000;
CRYSTAL_CHAINS_CRYSTALIZE_Timer = HEROIC(30000,11000);
TAIL_SWEEP_Timer = 5000;
Enrage = false;
+
CheckIntenseColdTimer = 2000;
MoreThanTwoIntenseCold = false;
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
+
RemovePrison(CheckContainmentSpheres());
+
if (pInstance)
pInstance->SetData(DATA_KERISTRASZA_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoCastAOE(SPELL_INTENSE_COLD);
+
if (pInstance)
pInstance->SetData(DATA_KERISTRASZA_EVENT, IN_PROGRESS);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (HeroicMode && !MoreThanTwoIntenseCold)
{
AchievementEntry const *AchievIntenseCold = GetAchievementStore()->LookupEntry(ACHIEVEMENT_INTENSE_COLD);
@@ -90,27 +110,33 @@ struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievIntenseCold);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_KERISTRASZA_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_SLAY, m_creature);
}
+
bool CheckContainmentSpheres(bool remove_prison = false)
{
if(!pInstance)
return false;
+
ContainmentSphereGUIDs[0] = pInstance->GetData64(ANOMALUS_CONTAINMET_SPHERE);
ContainmentSphereGUIDs[1] = pInstance->GetData64(ORMOROKS_CONTAINMET_SPHERE);
ContainmentSphereGUIDs[2] = pInstance->GetData64(TELESTRAS_CONTAINMET_SPHERE);
+
GameObject *ContainmentSpheres[CONTAINMENT_SPHERES];
- for (uint8 i = 0; i < CONTAINMENT_SPHERES; ++i)
+
+ for(uint8 i = 0; i < CONTAINMENT_SPHERES; ++i)
{
ContainmentSpheres[i] = pInstance->instance->GetGameObject(ContainmentSphereGUIDs[i]);
if (!ContainmentSpheres[i])
@@ -122,6 +148,7 @@ struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
RemovePrison(true);
return true;
}
+
void RemovePrison(bool remove)
{
if (remove)
@@ -138,18 +165,21 @@ struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
m_creature->CastSpell(m_creature, SPELL_FROZEN_PRISON, false);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (CheckIntenseColdTimer < diff && !MoreThanTwoIntenseCold)
{
std::list<HostilReference*> ThreatList = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::const_iterator itr = ThreatList.begin(); itr != ThreatList.end(); itr++)
+ for(std::list<HostilReference*>::const_iterator itr = ThreatList.begin(); itr != ThreatList.end(); itr++)
{
Unit *target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
if (!target || target->GetTypeId() != TYPEID_PLAYER)
continue;
+
Aura *AuraIntenseCold = target->GetAura(SPELL_INTENSE_COLD_TRIGGERED);
if (AuraIntenseCold && AuraIntenseCold->GetStackAmount() > 2)
{
@@ -159,22 +189,26 @@ struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
}
CheckIntenseColdTimer = 2000;
} else CheckIntenseColdTimer -= diff;
+
if (!Enrage && (m_creature->GetHealth() < m_creature->GetMaxHealth() * 0.25))
{
DoScriptText(SAY_ENRAGE, m_creature);
DoCast(m_creature, SPELL_ENRAGE);
Enrage = true;
}
+
if (CRYSTALFIRE_BREATH_Timer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_CRYSTALFIRE_BREATH_N, SPELL_CRYSTALFIRE_BREATH_H));
CRYSTALFIRE_BREATH_Timer = 14000;
} else CRYSTALFIRE_BREATH_Timer -=diff;
+
if (TAIL_SWEEP_Timer < diff)
{
DoCast(m_creature, SPELL_TAIL_SWEEP);
TAIL_SWEEP_Timer = 5000;
} else TAIL_SWEEP_Timer -=diff;
+
if (CRYSTAL_CHAINS_CRYSTALIZE_Timer < diff)
{
DoScriptText(SAY_CRYSTAL_NOVA, m_creature);
@@ -184,33 +218,41 @@ struct TRINITY_DLL_DECL boss_keristraszaAI : public ScriptedAI
DoCast(target, SPELL_CRYSTAL_CHAINS);
CRYSTAL_CHAINS_CRYSTALIZE_Timer = HEROIC(30000,11000);
} else CRYSTAL_CHAINS_CRYSTALIZE_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_keristrasza(Creature* pCreature)
{
return new boss_keristraszaAI (pCreature);
}
+
bool GOHello_containment_sphere(Player *pPlayer, GameObject *pGO)
{
ScriptedInstance *pInstance = pGO->GetInstanceData();
+
Creature *Keristrasza = Unit::GetCreature(*pGO, pInstance ? pInstance->GetData64(DATA_KERISTRASZA) : 0);
if (Keristrasza && Keristrasza->isAlive())
{
// maybe these are hacks :(
pGO->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK1);
pGO->SetGoState(GO_STATE_ACTIVE);
+
CAST_AI(boss_keristraszaAI, Keristrasza->AI())->CheckContainmentSpheres(true);
}
return true;
}
+
void AddSC_boss_keristrasza()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_keristrasza";
newscript->GetAI = &GetAI_boss_keristrasza;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "containment_sphere";
newscript->pGOHello = &GOHello_containment_sphere;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp
index 5b7d2000c6c..d043705ab54 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_magus_telestra.cpp
@@ -13,14 +13,17 @@
* along 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_Magus_Telestra
SD%Complete:
SDComment:
SDCategory: The Nexus, The Nexus
EndScriptData */
+
#include "precompiled.h"
#include "def_nexus.h"
+
enum eEnums
{
//Spells
@@ -30,13 +33,16 @@ enum eEnums
SPELL_FIREBOMB_H = 56934,
SPELL_GRAVITY_WELL = 47756,
SPELL_TELESTRA_BACK = 47714,
+
//At 50% HP - 3 clones, Frost, Fire, Arcane (and in 10% HP in Heroic)
MOB_FIRE_MAGUS = 26928,
MOB_FROST_MAGUS = 26930,
MOB_ARCANE_MAGUS = 26929,
+
SPELL_FIRE_MAGUS_VISUAL = 47705,
SPELL_FROST_MAGUS_VISUAL = 47706,
SPELL_ARCANE_MAGUS_VISUAL = 47704,
+
//Yell
SAY_AGGRO = -1576000,
SAY_KILL = -1576001,
@@ -44,14 +50,17 @@ enum eEnums
SAY_MERGE = -1576003,
SAY_SPLIT_1 = -1576004,
SAY_SPLIT_2 = -1576005,
+
//Achievement
ACHIEV_SPLIT_PERSONALITY = 2150,
ACHIEV_TIMER = 5 * 1000
};
+
float CenterOfRoom[1][4] =
{
{504.80, 89.07, -16.12, 6.27}
};
+
struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
{
boss_magus_telestraAI(Creature* c) : ScriptedAI(c)
@@ -59,24 +68,31 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint64 FireMagusGUID;
uint64 FrostMagusGUID;
uint64 ArcaneMagusGUID;
bool FireMagusDead;
bool FrostMagusDead;
bool ArcaneMagusDead;
+
uint32 AppearDelay_Timer;
bool AppearDelay;
+
uint8 Phase;
+
uint32 SPELL_ICE_NOVA_Timer;
uint32 SPELL_FIREBOMB_Timer;
uint32 SPELL_GRAVITY_WELL_Timer;
uint32 Cooldown;
+
bool AchievementTimerRunning;
uint8 AchievementProgress;
uint32 AchievementTimer;
+
void Reset()
{
Phase = 0;
@@ -85,27 +101,36 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
SPELL_FIREBOMB_Timer = 0;
SPELL_GRAVITY_WELL_Timer = 15000;
Cooldown = 0;
+
FireMagusGUID = 0;
FrostMagusGUID = 0;
ArcaneMagusGUID = 0;
+
AchievementProgress = 0;
AchievementTimer = 0;
AchievementTimerRunning = false;
+
AppearDelay = false;
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetVisibility(VISIBILITY_ON);
+
if (pInstance)
pInstance->SetData(DATA_MAGUS_TELESTRA_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_MAGUS_TELESTRA_EVENT, IN_PROGRESS);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (HeroicMode && AchievementProgress == 2)
{
AchievementEntry const *AchievSplitPersonality = GetAchievementStore()->LookupEntry(ACHIEV_SPLIT_PERSONALITY);
@@ -115,18 +140,21 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievSplitPersonality);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_MAGUS_TELESTRA_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
uint64 SplitPersonality(uint32 entry)
{
Creature* Summoned = m_creature->SummonCreature(entry, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
@@ -156,6 +184,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
}
return 0;
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
@@ -163,6 +192,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
{
return;
}
+
if (AppearDelay)
{
m_creature->StopMoving();
@@ -175,6 +205,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
return;
}
+
if ((Phase == 1)||(Phase == 3))
{
Unit* FireMagus;
@@ -229,6 +260,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
}else
return;
}
+
if ((Phase == 0) && (m_creature->GetHealth() <= (m_creature->GetMaxHealth() * 0.5)))
{
Phase = 1;
@@ -245,6 +277,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
DoScriptText(RAND(SAY_SPLIT_1,SAY_SPLIT_2), m_creature);
return;
}
+
if (HeroicMode && (Phase == 2) && (m_creature->GetHealth() <= (m_creature->GetMaxHealth() * 0.1)))
{
Phase = 3;
@@ -261,6 +294,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
DoScriptText(RAND(SAY_SPLIT_1,SAY_SPLIT_2), m_creature);
return;
}
+
if (Cooldown)
{
if (Cooldown < diff)
@@ -271,6 +305,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
return;
}
}
+
if (SPELL_ICE_NOVA_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -280,6 +315,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
}
SPELL_ICE_NOVA_Timer = 15000;
}else SPELL_ICE_NOVA_Timer -=diff;
+
if (SPELL_GRAVITY_WELL_Timer < diff)
{
if (Unit* target = m_creature->getVictim())
@@ -289,6 +325,7 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
}
SPELL_GRAVITY_WELL_Timer = 15000;
}else SPELL_GRAVITY_WELL_Timer -=diff;
+
if (SPELL_FIREBOMB_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -298,16 +335,20 @@ struct TRINITY_DLL_DECL boss_magus_telestraAI : public ScriptedAI
}
SPELL_FIREBOMB_Timer = 2000;
}else SPELL_FIREBOMB_Timer -=diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_magus_telestra(Creature* pCreature)
{
return new boss_magus_telestraAI (pCreature);
}
+
void AddSC_boss_magus_telestra()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_magus_telestra";
newscript->GetAI = &GetAI_boss_magus_telestra;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_ormorok.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_ormorok.cpp
index 62d4ae008b3..44c4a16401d 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_ormorok.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/boss_ormorok.cpp
@@ -13,14 +13,17 @@
* along 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_Ormorok
SD%Complete:
SDComment:
SDCategory: The Nexus, The Nexus
EndScriptData */
+
#include "precompiled.h"
#include "def_nexus.h"
+
enum eEnums
{
//Spells
@@ -39,6 +42,7 @@ enum eEnums
SPELL_SUMMON_CRYSTALLINE_TANGLER = 61564, //summons npc 32665
MOB_CRYSTALLINE_TANGLER = 32665,
SPELL_ROOTS = 28858, //proper spell id is unknown
+
//Yell
SAY_AGGRO = -1576020,
SAY_DEATH = -1576021,
@@ -46,7 +50,9 @@ enum eEnums
SAY_CRYSTAL_SPIKES = -1576023,
SAY_KILL = -1576024
};
+
#define SPIKE_DISTANCE 5.0f
+
struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
{
boss_ormorokAI(Creature *c) : ScriptedAI(c)
@@ -54,6 +60,7 @@ struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
bool Frenzy;
@@ -64,12 +71,14 @@ struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
float BaseZ;
float BaseO;
float SpikeXY[4][2];
+
uint32 SPELL_CRYSTAL_SPIKES_Timer;
uint32 CRYSTAL_SPIKES_Timer;
uint32 SPELL_TRAMPLE_Timer;
uint32 SPELL_FRENZY_Timer;
uint32 SPELL_SPELL_REFLECTION_Timer;
uint32 SPELL_SUMMON_CRYSTALLINE_TANGLER_Timer;
+
void Reset()
{
SPELL_CRYSTAL_SPIKES_Timer = 12000;
@@ -78,25 +87,32 @@ struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
SPELL_SUMMON_CRYSTALLINE_TANGLER_Timer = 17000;
Frenzy = false;
CrystalSpikes = false;
+
if (pInstance)
pInstance->SetData(DATA_ORMOROK_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_ORMOROK_EVENT, IN_PROGRESS);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_ORMOROK_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -120,22 +136,26 @@ struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
CrystalSpikes = false;
CRYSTAL_SPIKES_Timer = 200;
}else CRYSTAL_SPIKES_Timer -= diff;
+
if (!Frenzy && (m_creature->GetHealth() < m_creature->GetMaxHealth() * 0.25))
{
DoCast(m_creature, SPELL_FRENZY);
Frenzy = true;
}
+
if (SPELL_TRAMPLE_Timer < diff)
{
DoCast(m_creature, HEROIC(SPELL_TRAMPLE_N, SPELL_TRAMPLE_H));
SPELL_TRAMPLE_Timer = 10000;
}else SPELL_TRAMPLE_Timer -= diff;
+
if (SPELL_SPELL_REFLECTION_Timer < diff)
{
DoScriptText(SAY_REFLECT, m_creature);
DoCast(m_creature, SPELL_SPELL_REFLECTION);
SPELL_SPELL_REFLECTION_Timer = 30000;
}else SPELL_SPELL_REFLECTION_Timer -= diff;
+
if (SPELL_CRYSTAL_SPIKES_Timer < diff)
{
DoScriptText(SAY_CRYSTAL_SPIKES, m_creature);
@@ -148,6 +168,7 @@ struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
BaseO = m_creature->GetOrientation();
SPELL_CRYSTAL_SPIKES_Timer = 20000;
}else SPELL_CRYSTAL_SPIKES_Timer -=diff;
+
if (HeroicMode && (SPELL_SUMMON_CRYSTALLINE_TANGLER_Timer < diff))
{
Creature* Crystalline_Tangler = m_creature->SummonCreature(MOB_CRYSTALLINE_TANGLER, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 1000);
@@ -187,18 +208,23 @@ struct TRINITY_DLL_DECL boss_ormorokAI : public ScriptedAI
}
SPELL_SUMMON_CRYSTALLINE_TANGLER_Timer = 17000;
}else SPELL_SUMMON_CRYSTALLINE_TANGLER_Timer -=diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_crystal_spikeAI : public Scripted_NoMovementAI
{
mob_crystal_spikeAI(Creature *c) : Scripted_NoMovementAI(c)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
uint32 SPELL_CRYSTALL_SPIKE_DAMAGE_Timer;
uint32 SPELL_CRYSTAL_SPIKE_PREVISUAL_Timer;
+
void Reset()
{
SPELL_CRYSTALL_SPIKE_DAMAGE_Timer = 3700;
@@ -208,6 +234,7 @@ struct TRINITY_DLL_DECL mob_crystal_spikeAI : public Scripted_NoMovementAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); //
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); //
}
+
void UpdateAI(const uint32 diff)
{
if (SPELL_CRYSTAL_SPIKE_PREVISUAL_Timer < diff)
@@ -215,6 +242,7 @@ struct TRINITY_DLL_DECL mob_crystal_spikeAI : public Scripted_NoMovementAI
DoCast(m_creature, SPELL_CRYSTAL_SPIKE_PREVISUAL);
SPELL_CRYSTAL_SPIKE_PREVISUAL_Timer = 10000;
}else SPELL_CRYSTAL_SPIKE_PREVISUAL_Timer -=diff;
+
if (SPELL_CRYSTALL_SPIKE_DAMAGE_Timer < diff)
{
DoCast(m_creature, HEROIC(SPELL_CRYSTALL_SPIKE_DAMAGE_N, SPELL_CRYSTALL_SPIKE_DAMAGE_H));
@@ -222,14 +250,18 @@ struct TRINITY_DLL_DECL mob_crystal_spikeAI : public Scripted_NoMovementAI
}else SPELL_CRYSTALL_SPIKE_DAMAGE_Timer -=diff;
}
};
+
struct TRINITY_DLL_DECL mob_crystalline_tanglerAI : public ScriptedAI
{
mob_crystalline_tanglerAI(Creature *c) : ScriptedAI(c) {}
+
uint32 SPELL_ROOTS_Timer;
+
void Reset()
{
SPELL_ROOTS_Timer = 1000;
}
+
void UpdateAI(const uint32 diff)
{
if (SPELL_ROOTS_Timer < diff)
@@ -242,29 +274,36 @@ struct TRINITY_DLL_DECL mob_crystalline_tanglerAI : public ScriptedAI
}else SPELL_ROOTS_Timer -=diff;
}
};
+
CreatureAI* GetAI_mob_crystal_spike(Creature* pCreature)
{
return new mob_crystal_spikeAI (pCreature);
}
+
CreatureAI* GetAI_mob_crystalline_tangler(Creature* pCreature)
{
return new mob_crystalline_tanglerAI (pCreature);
}
+
CreatureAI* GetAI_boss_ormorok(Creature* pCreature)
{
return new boss_ormorokAI (pCreature);
}
+
void AddSC_boss_ormorok()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ormorok";
newscript->GetAI = &GetAI_boss_ormorok;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_crystal_spike";
newscript->GetAI = &GetAI_mob_crystal_spike;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_crystalline_tangler";
newscript->GetAI = &GetAI_mob_crystalline_tangler;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_kolurg.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_kolurg.cpp
index aaee8e8bd55..6fd8a7d4fe2 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_kolurg.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_kolurg.cpp
@@ -5,23 +5,28 @@ SD%Complete:
SDComment: Only Alliance Heroic
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_commander_kolurg' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
#define SPELL_BATTLE_SHOUT 31403
#define SPELL_CHARGE 60067
#define SPELL_FRIGHTENING_SHOUT 19134
#define SPELL_WHIRLWIND_1 38619
#define SPELL_WHIRLWIND_2 38618
+
//not used
//Yell
#define SAY_AGGRO -1576024
#define SAY_KILL -1576025
#define SAY_DEATH -1576026
+
struct TRINITY_DLL_DECL boss_commander_kolurgAI : public ScriptedAI
{
boss_commander_kolurgAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
@@ -31,17 +36,21 @@ struct TRINITY_DLL_DECL boss_commander_kolurgAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer) {}
};
+
CreatureAI* GetAI_boss_commander_kolurg(Creature* pCreature)
{
return new boss_commander_kolurgAI (pCreature);
}
+
void AddSC_boss_commander_kolurg()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_commander_kolurg";
newscript->GetAI = &GetAI_boss_commander_kolurg;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp
index 2a64bc3915b..def5fc92999 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/commander_stoutbeard.cpp
@@ -5,23 +5,28 @@ SD%Complete:
SDComment: Only Horde Heroic
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_commander_stoutbeard' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
#define SPELL_BATTLE_SHOUT 31403
#define SPELL_CHARGE 60067
#define SPELL_FRIGHTENING_SHOUT 19134
#define SPELL_WHIRLWIND_1 38619
#define SPELL_WHIRLWIND_2 38618
+
//not used
//Yell
#define SAY_AGGRO -1576021
#define SAY_KILL -1576022
#define SAY_DEATH -1576023
+
struct TRINITY_DLL_DECL boss_commander_stoutbeardAI : public ScriptedAI
{
boss_commander_stoutbeardAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who)
{
@@ -34,6 +39,7 @@ struct TRINITY_DLL_DECL boss_commander_stoutbeardAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -41,13 +47,16 @@ struct TRINITY_DLL_DECL boss_commander_stoutbeardAI : public ScriptedAI
DoScriptText(SAY_DEATH, m_creature);
}
};
+
CreatureAI* GetAI_boss_commander_stoutbeard(Creature* pCreature)
{
return new boss_commander_stoutbeardAI (pCreature);
}
+
void AddSC_boss_commander_stoutbeard()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_commander_stoutbeard";
newscript->GetAI = &GetAI_boss_commander_stoutbeard;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/def_nexus.h b/src/bindings/scripts/scripts/northrend/nexus/nexus/def_nexus.h
index 2fd3a37bb16..de36fea4b93 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/def_nexus.h
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/def_nexus.h
@@ -1,15 +1,19 @@
#ifndef DEF_NEXUS_H
#define DEF_NEXUS_H
+
enum eTypes
{
DATA_MAGUS_TELESTRA_EVENT,
DATA_ANOMALUS_EVENT,
DATA_ORMOROK_EVENT,
DATA_KERISTRASZA_EVENT,
+
DATA_ANOMALUS,
DATA_KERISTRASZA,
+
ANOMALUS_CONTAINMET_SPHERE,
ORMOROKS_CONTAINMET_SPHERE,
TELESTRAS_CONTAINMET_SPHERE
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/nexus/nexus/instance_nexus.cpp b/src/bindings/scripts/scripts/northrend/nexus/nexus/instance_nexus.cpp
index 6249d035c49..c420cb1920d 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/nexus/instance_nexus.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/nexus/instance_nexus.cpp
@@ -13,34 +13,46 @@
* along 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_Nexus
SD%Complete:
SDComment:
SDCategory: The Nexus, The Nexus
EndScriptData */
+
#include "precompiled.h"
#include "def_nexus.h"
+
#define NUMBER_OF_ENCOUNTERS 4
+
struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
{
instance_nexus(Map* pMap) : ScriptedInstance(pMap) { Initialize(); }
+
uint32 m_auiEncounter[NUMBER_OF_ENCOUNTERS];
+
uint64 Anomalus;
uint64 Keristrasza;
+
uint64 AnomalusContainmentSphere;
uint64 OrmoroksContainmentSphere;
uint64 TelestrasContainmentSphere;
+
std::string strInstData;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
Anomalus = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
Map::PlayerList const& players = instance->GetPlayers();
uint32 TeamInInstance = 0;
+
if (!players.isEmpty())
{
if (Player* pPlayer = players.begin()->getSource())
@@ -94,6 +106,7 @@ struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
}
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -121,6 +134,7 @@ struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
}
}
}
+
uint32 GetData(uint32 identifier)
{
switch(identifier)
@@ -132,6 +146,7 @@ struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 identifier, uint32 data)
{
switch(identifier)
@@ -171,17 +186,22 @@ struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
}
case DATA_KERISTRASZA_EVENT: m_auiEncounter[3] = data; break;
}
+
if (data == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " "
<< m_auiEncounter[3];
+
strInstData = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint64 GetData64(uint32 uiIdentifier)
{
switch(uiIdentifier)
@@ -194,10 +214,12 @@ struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
return strInstData;
}
+
void Load(const char* chrIn)
{
if (!chrIn)
@@ -205,21 +227,27 @@ struct TRINITY_DLL_DECL instance_nexus : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(chrIn);
+
std::istringstream loadStream(chrIn);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3];
- for (uint8 i = 0; i < NUMBER_OF_ENCOUNTERS; ++i)
+
+ for(uint8 i = 0; i < NUMBER_OF_ENCOUNTERS; ++i)
{
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
}
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_nexus(Map* pMap)
{
return new instance_nexus(pMap);
}
+
void AddSC_instance_nexus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_drakos.cpp b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_drakos.cpp
index 107753764e4..3ef2fa93fa3 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_drakos.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_drakos.cpp
@@ -5,14 +5,17 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_MAGIC_PULL 51336
#define SPELL_THUNDERING_STOMP 50774
#define SPELL_THUNDERING_STOMP_2 59370
+
//not in db
//Yell
#define SAY_AGGRO -1578000
@@ -27,9 +30,11 @@ update creature_template set scriptname = '' where entry = '';
#define SAY_STOMP_1 -1578009
#define SAY_STOMP_2 -1578010
#define SAY_STOMP_3 -1578011
+
struct TRINITY_DLL_DECL boss_drakosAI : public ScriptedAI
{
boss_drakosAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who)
{
@@ -42,6 +47,7 @@ struct TRINITY_DLL_DECL boss_drakosAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -55,13 +61,16 @@ struct TRINITY_DLL_DECL boss_drakosAI : public ScriptedAI
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2,SAY_KILL_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_drakos(Creature* pCreature)
{
return new boss_drakosAI (pCreature);
}
+
void AddSC_boss_drakos()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_drakos";
newscript->GetAI = &GetAI_boss_drakos;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_eregos.cpp b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_eregos.cpp
index e1640628cae..a0837ac0634 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_eregos.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_eregos.cpp
@@ -5,21 +5,26 @@ SD%Complete:
SDComment: Encounter is done entirely on drake vehicles
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Types of drake mounts: Ruby(Tank), Amber(DPS), Emerald(Healer)
//Two Repeating phases
+
//Spells
#define SPELL_ARCANE_BARRAGE 50804
#define SPELL_ARCANE_VOLLEY 51153
#define SPELL_ENRAGED_ASSAULT 51170
#define SPELL_PLANAR_ANOMALIES 57959
#define SPELL_PLANAR_SHIFT 51162
+
//Heroic
#define SPELL_ARCANE_BARRAGE_H 59381
#define SPELL_ARCANE_VOLLEY_H 59382
+
/*Ruby Drake ,
(npc 27756) (item 37860)
(summoned by spell Ruby Essence = 37860 ---> Call Amber Drake == 49462 ---> Summon 27756)
@@ -27,11 +32,13 @@ update creature_template set scriptname = '' where entry = '';
#define NPC_RUBY_DRAKE_VEHICLE 27756
#define SPELL_RIDE_RUBY_DRAKE_QUE 49463 //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49464
#define SPELL_RUBY_DRAKE_SADDLE 49464 //Allows you to ride on the back of an Amber Drake. ---> Dummy
+
#define SPELL_RUBY_SEARING_WRATH 50232 //(60 yds) - Instant - Breathes a stream of fire at an enemy dragon, dealing 6800 to 9200 Fire damage and then jumping to additional dragons within 30 yards. Each jump increases the damage by 50%. Affects up to 5 total targets
#define SPELL_RUBY_EVASIVE_AURA 50248 //Instant - Allows the Ruby Drake to generate Evasive Charges when hit by hostile attacks and spells.
#define SPELL_RUBY_EVASIVE_MANEUVERS 50240 //Instant - 5 sec. cooldown - Allows your drake to dodge all incoming attacks and spells. Requires Evasive Charges to use. Each attack or spell dodged while this ability is active burns one Evasive Charge. Lasts 30 sec. or until all charges are exhausted.
//you do not have acces to until you kill Mage-Lord Urom
#define SPELL_RUBY_MARTYR 50253 //Instant - 10 sec. cooldown - Redirect all harmful spells cast at friendly drakes to yourself for 10 sec.
+
/*Amber Drake,
(npc 27755) (item 37859)
(summoned by spell Amber Essence = 37859 ---> Call Amber Drake == 49461 ---> Summon 27755)
@@ -39,10 +46,12 @@ update creature_template set scriptname = '' where entry = '';
#define NPC_AMBER_DRAKE_VEHICLE 27755
#define SPELL_RIDE_AMBER_DRAKE_QUE 49459 //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49460
#define SPELL_AMBER_DRAKE_SADDLE 49460 //Allows you to ride on the back of an Amber Drake. ---> Dummy
+
#define SPELL_AMBER_SHOCK_LANCE 49840 //(60 yds) - Instant - Deals 4822 to 5602 Arcane damage and detonates all Shock Charges on an enemy dragon. Damage is increased by 6525 for each detonated.
#define SPELL_AMBER_STOP_TIME //Instant - 1 min cooldown - Halts the passage of time, freezing all enemy dragons in place for 10 sec. This attack applies 5 Shock Charges to each affected target.
//you do not have access to until you kill the Mage-Lord Urom.
#define SPELL_AMBER_TEMPORAL_RIFT 49592 //(60 yds) - Channeled - Channels a temporal rift on an enemy dragon for 10 sec. While trapped in the rift, all damage done to the target is increased by 100%. In addition, for every 15,000 damage done to a target affected by Temporal Rift, 1 Shock Charge is generated.
+
/*Emerald Drake,
(npc 27692) (item 37815),
(summoned by spell Emerald Essence = 37815 ---> Call Emerald Drake == 49345 ---> Summon 27692)
@@ -50,14 +59,18 @@ update creature_template set scriptname = '' where entry = '';
#define NPC_EMERALD_DRAKE_VEHICLE 27692
#define SPELL_RIDE_EMERALD_DRAKE_QUE 49427 //Apply Aura: Periodic Trigger, Interval: 3 seconds ---> 49346
#define SPELL_EMERALD_DRAKE_SADDLE 49346 //Allows you to ride on the back of an Amber Drake. ---> Dummy
+
#define SPELL_EMERALD_LEECHING_POISON 50328 //(60 yds) - Instant - Poisons the enemy dragon, leeching 1300 to the caster every 2 sec. for 12 sec. Stacks up to 3 times.
#define SPELL_EMERALD_TOUCH_THE_NIGHTMARE 50341 //(60 yds) - Instant - Consumes 30% of the caster's max health to inflict 25,000 nature damage to an enemy dragon and reduce the damage it deals by 25% for 30 sec.
// you do not have access to until you kill the Mage-Lord Urom
#define SPELL_EMERALD_DREAM_FUNNEL 50344 //(60 yds) - Channeled - Transfers 5% of the caster's max health to a friendly drake every second for 10 seconds as long as the caster channels.
+
struct TRINITY_DLL_DECL boss_eregosAI : public ScriptedAI
{
boss_eregosAI(Creature *c) : ScriptedAI(c) {}
+
uint32 phase;
+
void Reset() {}
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
@@ -67,18 +80,23 @@ struct TRINITY_DLL_DECL boss_eregosAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
phase =1;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer) {}
};
+
CreatureAI* GetAI_boss_eregos(Creature* pCreature)
{
return new boss_eregosAI (pCreature);
}
+
void AddSC_boss_eregos()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_eregos";
newscript->GetAI = &GetAI_boss_eregos;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_urom.cpp b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_urom.cpp
index 52670dc9bc9..0693b74b9f1 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_urom.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_urom.cpp
@@ -5,10 +5,12 @@ SD%Complete: 1%
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_ARCANE_SHIELD 53813 //Dummy --> Channeled, shields the caster from damage.
#define SPELL_EMPOWERED_ARCANE_EXPLOSION 51110
@@ -20,6 +22,7 @@ update creature_template set scriptname = '' where entry = '';
#define SPELL_TELEPORT 51112 //Teleports to the center of Oculus
#define SPELL_TIME_BOMB 51121 //Deals arcane damage to a random player, and after 6 seconds, deals zone damage to nearby equal to the health missing of the target afflicted by the debuff.
#define SPELL_TIME_BOMB_2 59376
+
//not in db
//Yell
#define SAY_AGGRO -1578012
@@ -32,9 +35,11 @@ update creature_template set scriptname = '' where entry = '';
#define SAY_SUMMON_1 -1578019
#define SAY_SUMMON_2 -1578020
#define SAY_SUMMON_3 -1578021
+
struct TRINITY_DLL_DECL boss_uromAI : public ScriptedAI
{
boss_uromAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who)
{
@@ -47,6 +52,7 @@ struct TRINITY_DLL_DECL boss_uromAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -60,13 +66,16 @@ struct TRINITY_DLL_DECL boss_uromAI : public ScriptedAI
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2,SAY_KILL_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_urom(Creature* pCreature)
{
return new boss_uromAI (pCreature);
}
+
void AddSC_boss_urom()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_urom";
newscript->GetAI = &GetAI_boss_urom;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_varos.cpp b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_varos.cpp
index b2d4c201eba..0ccd453e4d2 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_varos.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/oculus/boss_varos.cpp
@@ -5,10 +5,12 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
+
//Spells
#define SPELL_ENERGIZE_CORES 50785 //Damage 5938 to 6562, effec2 Triggers 54069, effect3 Triggers 56251
#define SPELL_ENERGIZE_CORES_TRIGGER_1 54069
@@ -20,6 +22,7 @@ update creature_template set scriptname = '' where entry = '';
#define SPELL_CALL_AZURE_RING_CAPTAIN_4 51008 //Effect Send Event (18455)
#define SPELL_CALL_AMPLIFY_MAGIC 51054
#define SPELL_CALL_AMPLIFY_MAGIC_2 59371
+
//not in db
//Yell
#define SAY_AGGRO -1578022
@@ -31,9 +34,11 @@ update creature_template set scriptname = '' where entry = '';
#define SAY_STRIKE_3 -1578028
#define SAY_SPAWN -1578029
+
struct TRINITY_DLL_DECL boss_varosAI : public ScriptedAI
{
boss_varosAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit* who)
{
@@ -46,6 +51,7 @@ struct TRINITY_DLL_DECL boss_varosAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -59,13 +65,16 @@ struct TRINITY_DLL_DECL boss_varosAI : public ScriptedAI
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
};
+
CreatureAI* GetAI_boss_varos(Creature* pCreature)
{
return new boss_varosAI (pCreature);
}
+
void AddSC_boss_varos()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_varos";
newscript->GetAI = &GetAI_boss_varos;
diff --git a/src/bindings/scripts/scripts/northrend/nexus/oculus/def_oculus.h b/src/bindings/scripts/scripts/northrend/nexus/oculus/def_oculus.h
index d1924db70bc..0b63a52d866 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/oculus/def_oculus.h
+++ b/src/bindings/scripts/scripts/northrend/nexus/oculus/def_oculus.h
@@ -1,3 +1,4 @@
#ifndef DEF_OCULUS_H
#define DEF_OCULUS_H
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/nexus/oculus/instance_oculus.cpp b/src/bindings/scripts/scripts/northrend/nexus/oculus/instance_oculus.cpp
index 834c222c895..b5ceee9e5ed 100644
--- a/src/bindings/scripts/scripts/northrend/nexus/oculus/instance_oculus.cpp
+++ b/src/bindings/scripts/scripts/northrend/nexus/oculus/instance_oculus.cpp
@@ -1,13 +1,16 @@
#include "precompiled.h"
#include "def_oculus.h"
+
struct TRINITY_DLL_DECL instance_oculus : public ScriptedInstance
{
instance_oculus(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
};
+
InstanceData* GetInstanceData_instance_oculus(Map* pMap)
{
return new instance_oculus(pMap);
}
+
void AddSC_instance_oculus()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp b/src/bindings/scripts/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp
index b32ca88b337..f13372af22d 100644
--- a/src/bindings/scripts/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp
+++ b/src/bindings/scripts/scripts/northrend/obsidian_sanctum/boss_sartharion.cpp
@@ -13,14 +13,17 @@
* along 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 Sartharion
SD%Complete: 70%
SDComment: Flame wave, achievement and portal events need to be implemented
SDCategory: Obsidian Sanctum
EndScriptData */
+
#include "precompiled.h"
#include "def_obsidian_sanctum.h"
+
enum eEnums
{
//Sartharion Yell
@@ -38,11 +41,14 @@ enum eEnums
SAY_SARTHARION_SLAY_1 = -1615029,
SAY_SARTHARION_SLAY_2 = -1615030,
SAY_SARTHARION_SLAY_3 = -1615031,
+
WHISPER_LAVA_CHURN = -1615032,
+
WHISPER_SHADRON_DICIPLE = -1615008,
WHISPER_VESPERON_DICIPLE = -1615041,
WHISPER_HATCH_EGGS = -1615017,
WHISPER_OPEN_PORTAL = -1615042, // whisper, shared by two dragons
+
//Sartharion Spells
SPELL_BERSERK = 61632, // Increases the caster's attack speed by 150% and all damage it deals by 500% for 5 min.
SPELL_CLEAVE = 56909, // Inflicts 35% weapon damage to an enemy and its nearest allies, affecting up to 10 targets.
@@ -53,74 +59,92 @@ enum eEnums
SPELL_WILL_OF_SARTHARION = 61254, // Sartharion's presence bolsters the resolve of the Twilight Drakes, increasing their total health by 25%. This effect also increases Sartharion's health by 25%.
SPELL_LAVA_STRIKE = 57571, // (Real spell casted should be 57578) 57571 then trigger visual missile, then summon Lava Blaze on impact(spell 57572)
SPELL_TWILIGHT_REVENGE = 60639,
+
SPELL_PYROBUFFET = 56916, // currently used for hard enrage after 15 minutes
SPELL_PYROBUFFET_RANGE = 58907, // possibly used when player get too far away from dummy creatures (2x Creature entry 30494)
+
SPELL_TWILIGHT_SHIFT_ENTER = 57620, // enter phase. Player get this when click GO
SPELL_TWILIGHT_SHIFT_REMOVAL = 61187, // leave phase
SPELL_TWILIGHT_SHIFT_REMOVAL_ALL = 61190, // leave phase (probably version to make all leave)
+
//Mini bosses common spells
SPELL_TWILIGHT_RESIDUE = 61885, // makes immune to shadow damage, applied when leave phase
+
//Miniboses (Vesperon, Shadron, Tenebron)
SPELL_SHADOW_BREATH_H = 59126, // Inflicts 8788 to 10212 Fire damage to enemies in a cone in front of the caster.
SPELL_SHADOW_BREATH = 57570, // Inflicts 6938 to 8062 Fire damage to enemies in a cone in front of the caster.
+
SPELL_SHADOW_FISSURE_H = 59127, // Deals 9488 to 13512 Shadow damage to any enemy within the Shadow fissure after 5 sec.
SPELL_SHADOW_FISSURE = 57579, // Deals 6188 to 8812 Shadow damage to any enemy within the Shadow fissure after 5 sec.
+
//Vesperon
//In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times
NPC_ACOLYTE_OF_VESPERON = 31219, // Acolyte of Vesperon
SPELL_POWER_OF_VESPERON = 61251, // Vesperon's presence decreases the maximum health of all enemies by 25%.
SPELL_TWILIGHT_TORMENT_VESP = 57948, // (Shadow only) trigger 57935 then 57988
SPELL_TWILIGHT_TORMENT_VESP_ACO = 58853, // (Fire and Shadow) trigger 58835 then 57988
+
//Shadron
//In portal is a disciple, when disciple killed remove Power_of_vesperon, portal open multiple times
NPC_ACOLYTE_OF_SHADRON = 31218, // Acolyte of Shadron
SPELL_POWER_OF_SHADRON = 58105, // Shadron's presence increases Fire damage taken by all enemies by 100%.
SPELL_GIFT_OF_TWILIGTH_SHA = 57835, // TARGET_SCRIPT shadron
SPELL_GIFT_OF_TWILIGTH_SAR = 58766, // TARGET_SCRIPT sartharion
+
//Tenebron
//in the portal spawns 6 eggs, if not killed in time (approx. 20s) they will hatch, whelps can cast 60708
SPELL_POWER_OF_TENEBRON = 61248, // Tenebron's presence increases Shadow damage taken by all enemies by 100%.
//Tenebron, dummy spell
SPELL_SUMMON_TWILIGHT_WHELP = 58035, // doesn't work, will spawn NPC_TWILIGHT_WHELP
SPELL_SUMMON_SARTHARION_TWILIGHT_WHELP = 58826, // doesn't work, will spawn NPC_SHARTHARION_TWILIGHT_WHELP
+
SPELL_HATCH_EGGS_H = 59189,
SPELL_HATCH_EGGS = 58542,
SPELL_HATCH_EGGS_EFFECT_H = 59190,
SPELL_HATCH_EGGS_EFFECT = 58685,
+
//Whelps
NPC_TWILIGHT_WHELP = 30890,
NPC_SHARTHARION_TWILIGHT_WHELP = 31214,
SPELL_FADE_ARMOR = 60708, // Reduces the armor of an enemy by 1500 for 15s
+
//flame tsunami
SPELL_FLAME_TSUNAMI = 57494, // the visual dummy
SPELL_FLAME_TSUNAMI_LEAP = 60241, // SPELL_EFFECT_138 some leap effect, causing caster to move in direction
SPELL_FLAME_TSUNAMI_DMG_AURA = 57492, // periodic damage, npc has this aura
+
NPC_FLAME_TSUNAMI = 30616, // for the flame waves
NPC_LAVA_BLAZE = 30643, // adds spawning from flame strike
+
//using these custom points for dragons start and end
POINT_ID_INIT = 100,
POINT_ID_LAND = 200
};
+
struct Waypoint
{
float m_fX, m_fY, m_fZ;
};
+
//each dragons special points. First where fly to before connect to connon, second where land point is.
Waypoint m_aTene[]=
{
{3212.854, 575.597, 109.856}, //init
{3246.425, 565.367, 61.249} //end
};
+
Waypoint m_aShad[]=
{
{3293.238, 472.223, 106.968},
{3271.669, 526.907, 61.931}
};
+
Waypoint m_aVesp[]=
{
{3193.310, 472.861, 102.697},
{3227.268, 533.238, 59.995}
};
+
#define MAX_WAYPOINT 6
//points around raid "isle", counter clockwise. should probably be adjusted to be more alike
Waypoint m_aDragonCommon[MAX_WAYPOINT]=
@@ -132,9 +156,11 @@ Waypoint m_aDragonCommon[MAX_WAYPOINT]=
{3250.479, 585.827, 98.652},
{3209.969, 566.523, 98.652}
};
+
/*######
## Boss Sartharion
######*/
+
struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
{
boss_sartharionAI(Creature* pCreature) : ScriptedAI(pCreature)
@@ -142,111 +168,143 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
bool m_bIsHeroic;
+
bool m_bIsBerserk;
bool m_bIsSoftEnraged;
+
uint32 m_uiEnrageTimer;
bool m_bIsHardEnraged;
+
uint32 m_uiTenebronTimer;
uint32 m_uiShadronTimer;
uint32 m_uiVesperonTimer;
+
uint32 m_uiFlameTsunamiTimer;
uint32 m_uiFlameBreathTimer;
uint32 m_uiTailSweepTimer;
uint32 m_uiCleaveTimer;
uint32 m_uiLavaStrikeTimer;
+
bool m_bHasCalledTenebron;
bool m_bHasCalledShadron;
bool m_bHasCalledVesperon;
+
void Reset()
{
m_bIsBerserk = false;
m_bIsSoftEnraged = false;
+
m_uiEnrageTimer = 15*MINUTE*IN_MILISECONDS;
m_bIsHardEnraged = false;
+
m_uiTenebronTimer = 30000;
m_uiShadronTimer = 75000;
m_uiVesperonTimer = 120000;
+
m_uiFlameTsunamiTimer = 30000;
m_uiFlameBreathTimer = 20000;
m_uiTailSweepTimer = 20000;
m_uiCleaveTimer = 7000;
m_uiLavaStrikeTimer = 5000;
+
m_bHasCalledTenebron = false;
m_bHasCalledShadron = false;
m_bHasCalledVesperon = false;
+
if (m_creature->HasAura(SPELL_TWILIGHT_REVENGE))
m_creature->RemoveAurasDueToSpell(SPELL_TWILIGHT_REVENGE);
}
+
void JustReachedHome()
{
if (m_pInstance)
m_pInstance->SetData(TYPE_SARTHARION_EVENT, NOT_STARTED);
}
+
void Aggro(Unit* pWho)
{
DoScriptText(SAY_SARTHARION_AGGRO,m_creature);
DoZoneInCombat();
+
if (m_pInstance)
{
m_pInstance->SetData(TYPE_SARTHARION_EVENT, IN_PROGRESS);
FetchDragons();
}
}
+
void JustDied(Unit* pKiller)
{
DoScriptText(SAY_SARTHARION_DEATH,m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_SARTHARION_EVENT, DONE);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_SARTHARION_SLAY_1,SAY_SARTHARION_SLAY_2,SAY_SARTHARION_SLAY_3), m_creature);
}
+
void FetchDragons()
{
Unit* pTene = Unit::GetUnit(*m_creature, m_pInstance->GetData64(DATA_TENEBRON));
Unit* pShad = Unit::GetUnit(*m_creature, m_pInstance->GetData64(DATA_SHADRON));
Unit* pVesp = Unit::GetUnit(*m_creature, m_pInstance->GetData64(DATA_VESPERON));
+
//if at least one of the dragons are alive and are being called
bool bCanUseWill = false;
+
if (pTene && pTene->isAlive() && !pTene->getVictim())
{
bCanUseWill = true;
pTene->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aTene[0].m_fX, m_aTene[0].m_fY, m_aTene[0].m_fZ);
+
if (!pTene->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
pTene->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
if (pShad && pShad->isAlive() && !pShad->getVictim())
{
bCanUseWill = true;
pShad->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aShad[0].m_fX, m_aShad[0].m_fY, m_aShad[0].m_fZ);
+
if (!pShad->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
pShad->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
if (pVesp && pVesp->isAlive() && !pVesp->getVictim())
{
bCanUseWill = true;
pVesp->GetMotionMaster()->MovePoint(POINT_ID_INIT, m_aVesp[0].m_fX, m_aVesp[0].m_fY, m_aVesp[0].m_fZ);
+
if (!pVesp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
pVesp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
if (bCanUseWill)
DoCast(m_creature, SPELL_WILL_OF_SARTHARION);
}
+
void CallDragon(uint32 uiDataId)
{
if (m_pInstance)
{
Creature* pTemp = Unit::GetCreature((*m_creature),m_pInstance->GetData64(uiDataId));
+
if (pTemp && pTemp->isAlive() && !pTemp->getVictim())
{
if (pTemp->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
pTemp->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
if (pTemp->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
pTemp->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
int32 iTextId = 0;
+
switch(pTemp->GetEntry())
{
case NPC_TENEBRON:
@@ -262,16 +320,20 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
pTemp->GetMotionMaster()->MovePoint(POINT_ID_LAND, m_aVesp[1].m_fX, m_aVesp[1].m_fY, m_aVesp[1].m_fZ);
break;
}
+
DoScriptText(iTextId, m_creature);
}
}
}
+
void SendFlameTsunami()
{
Map* pMap = m_creature->GetMap();
+
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &PlayerList = pMap->GetPlayers();
+
if (!PlayerList.isEmpty())
{
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
@@ -282,14 +344,17 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
Unit* pTene = Unit::GetUnit(*m_creature, m_pInstance ? m_pInstance->GetData64(DATA_TENEBRON) : 0);
Unit* pShad = Unit::GetUnit(*m_creature, m_pInstance ? m_pInstance->GetData64(DATA_SHADRON) : 0);
Unit* pVesp = Unit::GetUnit(*m_creature, m_pInstance ? m_pInstance->GetData64(DATA_VESPERON) : 0);
+
//spell will target dragons, if they are still alive at 35%
if (!m_bIsBerserk && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= 35
&& ((pTene && pTene->isAlive()) || (pShad && pShad->isAlive()) || (pVesp && pVesp->isAlive())))
@@ -298,12 +363,14 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
DoCast(m_creature, SPELL_BERSERK);
m_bIsBerserk = true;
}
+
//soft enrage
if (!m_bIsSoftEnraged && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= 10)
{
// TODO
m_bIsSoftEnraged = true;
}
+
// hard enrage
if (!m_bIsHardEnraged)
{
@@ -315,6 +382,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
else
m_uiEnrageTimer -= uiDiff;
}
+
// flame tsunami
if (m_uiFlameTsunamiTimer < uiDiff)
{
@@ -323,6 +391,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiFlameTsunamiTimer -= uiDiff;
+
// flame breath
if (m_uiFlameBreathTimer < uiDiff)
{
@@ -332,6 +401,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiFlameBreathTimer -= uiDiff;
+
// Tail Sweep
if (m_uiTailSweepTimer < uiDiff)
{
@@ -340,6 +410,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiTailSweepTimer -= uiDiff;
+
// Cleave
if (m_uiCleaveTimer < uiDiff)
{
@@ -348,12 +419,14 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiCleaveTimer -= uiDiff;
+
// Lavas Strike
if (m_uiLavaStrikeTimer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
{
DoCast(pTarget, SPELL_LAVA_STRIKE);
+
if(urand(0,4) == 4)
DoScriptText(RAND(SAY_SARTHARION_SPECIAL_1,SAY_SARTHARION_SPECIAL_2,SAY_SARTHARION_SPECIAL_3), m_creature);
}
@@ -361,6 +434,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiLavaStrikeTimer -= uiDiff;
+
// call tenebron
if (!m_bHasCalledTenebron && m_uiTenebronTimer < uiDiff)
{
@@ -369,6 +443,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiTenebronTimer -= uiDiff;
+
// call shadron
if (!m_bHasCalledShadron && m_uiShadronTimer < uiDiff)
{
@@ -377,6 +452,7 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiShadronTimer -= uiDiff;
+
// call vesperon
if (!m_bHasCalledVesperon && m_uiVesperonTimer < uiDiff)
{
@@ -385,14 +461,18 @@ struct TRINITY_DLL_DECL boss_sartharionAI : public ScriptedAI
}
else
m_uiVesperonTimer -= uiDiff;
+
DoMeleeAttackIfReady();
+
EnterEvadeIfOutOfCombatArea(uiDiff);
}
};
+
CreatureAI* GetAI_boss_sartharion(Creature* pCreature)
{
return new boss_sartharionAI(pCreature);
}
+
enum TeneText
{
SAY_TENEBRON_AGGRO = -1615009,
@@ -404,6 +484,7 @@ enum TeneText
SAY_TENEBRON_SPECIAL_1 = -1615015,
SAY_TENEBRON_SPECIAL_2 = -1615016
};
+
enum ShadText
{
SAY_SHADRON_AGGRO = -1615000,
@@ -415,6 +496,7 @@ enum ShadText
SAY_SHADRON_SPECIAL_1 = -1615006,
SAY_SHADRON_SPECIAL_2 = -1615007
};
+
enum VespText
{
SAY_VESPERON_AGGRO = -1615033,
@@ -426,6 +508,7 @@ enum VespText
SAY_VESPERON_SPECIAL_1 = -1615039,
SAY_VESPERON_SPECIAL_2 = -1615040
};
+
//to control each dragons common abilities
struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
{
@@ -434,32 +517,40 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
bool m_bIsHeroic;
+
uint32 m_uiWaypointId;
uint32 m_uiMoveNextTimer;
int32 m_iPortalRespawnTime;
bool m_bCanMoveFree;
+
void Reset()
{
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
m_uiWaypointId = 0;
m_uiMoveNextTimer = 500;
m_iPortalRespawnTime = 30000;
m_bCanMoveFree = false;
}
+
void MovementInform(uint32 uiType, uint32 uiPointId)
{
if (!m_pInstance || uiType != POINT_MOTION_TYPE)
return;
+
debug_log("dummy_dragonAI: %s reached point %u", m_creature->GetName(), uiPointId);
+
//if healers messed up the raid and we was already initialized
if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS)
{
EnterEvadeMode();
return;
}
+
//this is end, if we reach this, don't do much
if (uiPointId == POINT_ID_LAND)
{
@@ -467,26 +558,34 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
m_bCanMoveFree = false;
return;
}
+
//get amount of common points
uint32 uiCommonWPCount = sizeof(m_aDragonCommon)/sizeof(Waypoint);
+
//increase
m_uiWaypointId = uiPointId+1;
+
//if we have reached a point bigger or equal to count, it mean we must reset to point 0
if (m_uiWaypointId >= uiCommonWPCount)
{
if (!m_bCanMoveFree)
m_bCanMoveFree = true;
+
m_uiWaypointId = 0;
}
+
m_uiMoveNextTimer = 500;
}
+
//used when open portal and spawn mobs in phase
void DoRaidWhisper(int32 iTextId)
{
Map* pMap = m_creature->GetMap();
+
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &PlayerList = pMap->GetPlayers();
+
if (!PlayerList.isEmpty())
{
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
@@ -494,14 +593,18 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
}
}
}
+
//"opens" the portal and does the "opening" whisper
void OpenPortal()
{
int32 iTextId = 0;
+
//there are 4 portal spawn locations, each are expected to be spawned with negative spawntimesecs in database
+
//using a grid search here seem to be more efficient than caching all four guids
//in instance script and calculate range to each.
GameObject* pPortal = m_creature->FindNearestGameObject(GO_TWILIGHT_PORTAL,50.0f);
+
switch(m_creature->GetEntry())
{
case NPC_TENEBRON:
@@ -512,23 +615,30 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
iTextId = WHISPER_OPEN_PORTAL;
break;
}
+
DoRaidWhisper(iTextId);
+
//By using SetRespawnTime() we will actually "spawn" the object with our defined time.
//Once time is up, portal will disappear again.
if (pPortal && !pPortal->isSpawned())
pPortal->SetRespawnTime(m_iPortalRespawnTime);
+
//Unclear what are expected to happen if one drake has a portal open already
//Refresh respawnTime so time again are set to 30secs?
}
+
//Removes each drakes unique debuff from players
void RemoveDebuff(uint32 uiSpellId)
{
Map* pMap = m_creature->GetMap();
+
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &PlayerList = pMap->GetPlayers();
+
if (PlayerList.isEmpty())
return;
+
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
{
if (i->getSource()->isAlive() && i->getSource()->HasAura(uiSpellId))
@@ -536,10 +646,12 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* pKiller)
{
int32 iTextId = 0;
uint32 uiSpellId = 0;
+
switch(m_creature->GetEntry())
{
case NPC_TENEBRON:
@@ -555,19 +667,24 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
uiSpellId = SPELL_POWER_OF_VESPERON;
break;
}
+
DoScriptText(iTextId, m_creature);
+
RemoveDebuff(uiSpellId);
+
if (m_pInstance)
{
// not if solo mini-boss fight
if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) != IN_PROGRESS)
return;
+
// Twilight Revenge to main boss
if (Unit* pSartharion = Unit::GetUnit((*m_creature), m_pInstance->GetData64(DATA_SARTHARION)))
if (pSartharion->isAlive())
m_creature->CastSpell(pSartharion,SPELL_TWILIGHT_REVENGE,true);
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (m_bCanMoveFree && m_uiMoveNextTimer)
@@ -577,6 +694,7 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
if(m_uiWaypointId < MAX_WAYPOINT)
m_creature->GetMotionMaster()->MovePoint(m_uiWaypointId,
m_aDragonCommon[m_uiWaypointId].m_fX, m_aDragonCommon[m_uiWaypointId].m_fY, m_aDragonCommon[m_uiWaypointId].m_fZ);
+
debug_log("dummy_dragonAI: %s moving to point %u", m_creature->GetName(), m_uiWaypointId);
m_uiMoveNextTimer = 0;
}
@@ -585,31 +703,38 @@ struct TRINITY_DLL_DECL dummy_dragonAI : public ScriptedAI
}
}
};
+
/*######
## Mob Tenebron
######*/
+
struct TRINITY_DLL_DECL mob_tenebronAI : public dummy_dragonAI
{
mob_tenebronAI(Creature* pCreature) : dummy_dragonAI(pCreature) { }
+
uint32 m_uiShadowBreathTimer;
uint32 m_uiShadowFissureTimer;
uint32 m_uiHatchEggTimer;
+
void Reset()
{
m_uiShadowBreathTimer = 20000;
m_uiShadowFissureTimer = 5000;
m_uiHatchEggTimer = 30000;
}
+
void Aggro(Unit* pWho)
{
DoScriptText(SAY_TENEBRON_AGGRO, m_creature);
DoZoneInCombat();
DoCast(m_creature, SPELL_POWER_OF_TENEBRON);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_TENEBRON_SLAY_1,SAY_TENEBRON_SLAY_2), m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
//if no target, update dummy and return
@@ -618,15 +743,18 @@ struct TRINITY_DLL_DECL mob_tenebronAI : public dummy_dragonAI
dummy_dragonAI::UpdateAI(uiDiff);
return;
}
+
// shadow fissure
if (m_uiShadowFissureTimer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, m_bIsHeroic ? SPELL_SHADOW_FISSURE_H : SPELL_SHADOW_FISSURE);
+
m_uiShadowFissureTimer = urand(15000,20000);
}
else
m_uiShadowFissureTimer -= uiDiff;
+
// shadow breath
if (m_uiShadowBreathTimer < uiDiff)
{
@@ -636,42 +764,53 @@ struct TRINITY_DLL_DECL mob_tenebronAI : public dummy_dragonAI
}
else
m_uiShadowBreathTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_tenebron(Creature* pCreature)
{
return new mob_tenebronAI(pCreature);
}
+
/*######
## Mob Shadron
######*/
+
struct TRINITY_DLL_DECL mob_shadronAI : public dummy_dragonAI
{
mob_shadronAI(Creature* pCreature) : dummy_dragonAI(pCreature) { }
+
uint32 m_uiShadowBreathTimer;
uint32 m_uiShadowFissureTimer;
uint32 m_uiAcolyteShadronTimer;
+
void Reset()
{
m_uiShadowBreathTimer = 20000;
m_uiShadowFissureTimer = 5000;
m_uiAcolyteShadronTimer = 60000;
+
if (m_creature->HasAura(SPELL_TWILIGHT_TORMENT_VESP))
m_creature->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP);
+
if (m_creature->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA))
m_creature->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA);
}
+
void Aggro(Unit* pWho)
{
DoScriptText(SAY_SHADRON_AGGRO,m_creature);
DoZoneInCombat();
DoCast(m_creature, SPELL_POWER_OF_SHADRON);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_SHADRON_SLAY_1,SAY_SHADRON_SLAY_2), m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
//if no target, update dummy and return
@@ -680,15 +819,18 @@ struct TRINITY_DLL_DECL mob_shadronAI : public dummy_dragonAI
dummy_dragonAI::UpdateAI(uiDiff);
return;
}
+
// shadow fissure
if (m_uiShadowFissureTimer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, m_bIsHeroic ? SPELL_SHADOW_FISSURE_H : SPELL_SHADOW_FISSURE);
+
m_uiShadowFissureTimer = urand(15000,20000);
}
else
m_uiShadowFissureTimer -= uiDiff;
+
// shadow breath
if (m_uiShadowBreathTimer < uiDiff)
{
@@ -698,38 +840,47 @@ struct TRINITY_DLL_DECL mob_shadronAI : public dummy_dragonAI
}
else
m_uiShadowBreathTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_shadron(Creature* pCreature)
{
return new mob_shadronAI(pCreature);
}
+
/*######
## Mob Vesperon
######*/
+
struct TRINITY_DLL_DECL mob_vesperonAI : public dummy_dragonAI
{
mob_vesperonAI(Creature* pCreature) : dummy_dragonAI(pCreature) { }
+
uint32 m_uiShadowBreathTimer;
uint32 m_uiShadowFissureTimer;
uint32 m_uiAcolyteVesperonTimer;
+
void Reset()
{
m_uiShadowBreathTimer = 20000;
m_uiShadowFissureTimer = 5000;
m_uiAcolyteVesperonTimer = 60000;
}
+
void Aggro(Unit* pWho)
{
DoScriptText(SAY_VESPERON_AGGRO,m_creature);
DoZoneInCombat();
DoCast(m_creature, SPELL_POWER_OF_VESPERON);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_VESPERON_SLAY_1,SAY_VESPERON_SLAY_2), m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
//if no target, update dummy and return
@@ -738,15 +889,18 @@ struct TRINITY_DLL_DECL mob_vesperonAI : public dummy_dragonAI
dummy_dragonAI::UpdateAI(uiDiff);
return;
}
+
// shadow fissure
if (m_uiShadowFissureTimer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, m_bIsHeroic ? SPELL_SHADOW_FISSURE_H : SPELL_SHADOW_FISSURE);
+
m_uiShadowFissureTimer = urand(15000,20000);
}
else
m_uiShadowFissureTimer -= uiDiff;
+
// shadow breath
if (m_uiShadowBreathTimer < uiDiff)
{
@@ -756,23 +910,29 @@ struct TRINITY_DLL_DECL mob_vesperonAI : public dummy_dragonAI
}
else
m_uiShadowBreathTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_vesperon(Creature* pCreature)
{
return new mob_vesperonAI(pCreature);
}
+
/*######
## Mob Acolyte of Shadron
######*/
+
struct TRINITY_DLL_DECL mob_acolyte_of_shadronAI : public ScriptedAI
{
mob_acolyte_of_shadronAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
void Reset()
{
if (m_pInstance)
@@ -784,15 +944,18 @@ struct TRINITY_DLL_DECL mob_acolyte_of_shadronAI : public ScriptedAI
DoCast(m_creature, SPELL_GIFT_OF_TWILIGTH_SHA);
}
}
+
void JustDied(Unit* killer)
{
if (m_pInstance)
{
Creature* pDebuffTarget = NULL;
+
if (m_pInstance->GetData(TYPE_SARTHARION_EVENT) == IN_PROGRESS)
{
//not solo fight, so main boss has deduff
pDebuffTarget = m_pInstance->instance->GetCreature(m_pInstance->GetData64(DATA_SARTHARION));
+
if (pDebuffTarget && pDebuffTarget->isAlive() && pDebuffTarget->HasAura(SPELL_GIFT_OF_TWILIGTH_SAR))
pDebuffTarget->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SAR);
}
@@ -800,91 +963,116 @@ struct TRINITY_DLL_DECL mob_acolyte_of_shadronAI : public ScriptedAI
{
//event not in progress, then solo fight and must remove debuff mini-boss
pDebuffTarget = m_pInstance->instance->GetCreature(m_pInstance->GetData64(DATA_SHADRON));
+
if (pDebuffTarget && pDebuffTarget->isAlive() && pDebuffTarget->HasAura(SPELL_GIFT_OF_TWILIGTH_SHA))
pDebuffTarget->RemoveAurasDueToSpell(SPELL_GIFT_OF_TWILIGTH_SHA);
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_acolyte_of_shadron(Creature* pCreature)
{
return new mob_acolyte_of_shadronAI(pCreature);
}
+
/*######
## Mob Acolyte of Vesperon
######*/
+
struct TRINITY_DLL_DECL mob_acolyte_of_vesperonAI : public ScriptedAI
{
mob_acolyte_of_vesperonAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
void Reset()
{
DoCast(m_creature, SPELL_TWILIGHT_TORMENT_VESP_ACO);
}
+
void JustDied(Unit* pKiller)
{
// remove twilight torment on Vesperon
if (m_pInstance)
{
Creature* pVesperon = m_pInstance->instance->GetCreature(m_pInstance->GetData64(DATA_VESPERON));
+
if (pVesperon && pVesperon->isAlive() && pVesperon->HasAura(SPELL_TWILIGHT_TORMENT_VESP))
pVesperon->RemoveAurasDueToSpell(SPELL_TWILIGHT_TORMENT_VESP);
}
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_acolyte_of_vesperon(Creature* pCreature)
{
return new mob_acolyte_of_vesperonAI(pCreature);
}
+
/*######
## Mob Twilight Eggs
######*/
+
struct TRINITY_DLL_DECL mob_twilight_eggsAI : public ScriptedAI
{
mob_twilight_eggsAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
uint32 m_uiFadeArmorTimer;
+
void Reset()
{
m_uiFadeArmorTimer = 1000;
}
+
void AttackStart(Unit* pWho) { }
void MoveInLineOfSight(Unit* pWho) { }
};
+
CreatureAI* GetAI_mob_twilight_eggs(Creature* pCreature)
{
return new mob_twilight_eggsAI(pCreature);
}
+
/*######
## Mob Twilight Whelps
######*/
+
struct TRINITY_DLL_DECL mob_twilight_whelpAI : public ScriptedAI
{
mob_twilight_whelpAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
uint32 m_uiFadeArmorTimer;
+
void Reset()
{
m_uiFadeArmorTimer = 1000;
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
// twilight torment
if (m_uiFadeArmorTimer < uiDiff)
{
@@ -893,44 +1081,55 @@ struct TRINITY_DLL_DECL mob_twilight_whelpAI : public ScriptedAI
}
else
m_uiFadeArmorTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_twilight_whelp(Creature* pCreature)
{
return new mob_twilight_whelpAI(pCreature);
}
+
void AddSC_boss_sartharion()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_sartharion";
newscript->GetAI = &GetAI_boss_sartharion;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_vesperon";
newscript->GetAI = &GetAI_mob_vesperon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shadron";
newscript->GetAI = &GetAI_mob_shadron;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_tenebron";
newscript->GetAI = &GetAI_mob_tenebron;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_acolyte_of_shadron";
newscript->GetAI = &GetAI_mob_acolyte_of_shadron;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_acolyte_of_vesperon";
newscript->GetAI = &GetAI_mob_acolyte_of_vesperon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_twilight_eggs";
newscript->GetAI = &GetAI_mob_twilight_eggs;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_twilight_whelp";
newscript->GetAI = &GetAI_mob_twilight_whelp;
diff --git a/src/bindings/scripts/scripts/northrend/obsidian_sanctum/def_obsidian_sanctum.h b/src/bindings/scripts/scripts/northrend/obsidian_sanctum/def_obsidian_sanctum.h
index 053ca56c8cb..59013174795 100644
--- a/src/bindings/scripts/scripts/northrend/obsidian_sanctum/def_obsidian_sanctum.h
+++ b/src/bindings/scripts/scripts/northrend/obsidian_sanctum/def_obsidian_sanctum.h
@@ -1,16 +1,20 @@
#ifndef DEF_OBSIDIAN_SANCTUM_H
#define DEF_OBSIDIAN_SANCTUM_H
+
enum eTypes
{
TYPE_SARTHARION_EVENT = 1,
+
DATA_SARTHARION = 10,
DATA_TENEBRON = 11,
DATA_SHADRON = 12,
DATA_VESPERON = 13,
+
NPC_SARTHARION = 28860,
NPC_TENEBRON = 30452,
NPC_SHADRON = 30451,
NPC_VESPERON = 30449,
GO_TWILIGHT_PORTAL = 193988
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp b/src/bindings/scripts/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp
index 95ec22db002..5dd3e06e585 100644
--- a/src/bindings/scripts/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp
+++ b/src/bindings/scripts/scripts/northrend/obsidian_sanctum/instance_obsidian_sanctum.cpp
@@ -1,25 +1,32 @@
#include "precompiled.h"
#include "def_obsidian_sanctum.h"
+
#define MAX_ENCOUNTER 1
+
/* Obsidian Sanctum encounters:
0 - Sartharion
*/
+
struct TRINITY_DLL_DECL instance_obsidian_sanctum : public ScriptedInstance
{
instance_obsidian_sanctum(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint64 m_uiSartharionGUID;
uint64 m_uiTenebronGUID;
uint64 m_uiShadronGUID;
uint64 m_uiVesperonGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiSartharionGUID = 0;
m_uiTenebronGUID = 0;
m_uiShadronGUID = 0;
m_uiVesperonGUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -43,17 +50,21 @@ struct TRINITY_DLL_DECL instance_obsidian_sanctum : public ScriptedInstance
break;
}
}
+
void SetData(uint32 uiType, uint32 uiData)
{
if (uiType == TYPE_SARTHARION_EVENT)
m_auiEncounter[0] = uiData;
}
+
uint32 GetData(uint32 uiType)
{
if (uiType == TYPE_SARTHARION_EVENT)
return m_auiEncounter[0];
+
return 0;
}
+
uint64 GetData64(uint32 uiData)
{
switch(uiData)
@@ -70,10 +81,12 @@ struct TRINITY_DLL_DECL instance_obsidian_sanctum : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_obsidian_sanctum(Map* pMap)
{
return new instance_obsidian_sanctum(pMap);
}
+
void AddSC_instance_obsidian_sanctum()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/sholazar_basin.cpp b/src/bindings/scripts/scripts/northrend/sholazar_basin.cpp
index c715f2e4a11..0a4421683a3 100644
--- a/src/bindings/scripts/scripts/northrend/sholazar_basin.cpp
+++ b/src/bindings/scripts/scripts/northrend/sholazar_basin.cpp
@@ -14,36 +14,46 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Sholazar_Basin
SD%Complete: 100
SDComment: Quest support: 12570, 12573, 12621.
SDCategory: Sholazar_Basin
EndScriptData */
+
/* ContentData
npc_injured_rainspeaker_oracle
npc_vekjik
avatar_of_freya
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_injured_rainspeaker_oracle
######*/
+
#define GOSSIP_ITEM1 "I am ready to travel to your village now."
+
enum eRainspeaker
{
SAY_START_IRO = -1571000,
SAY_QUEST_ACCEPT_IRO = -1571001,
SAY_END_IRO = -1571002,
+
QUEST_FORTUNATE_MISUNDERSTANDINGS = 12570,
FACTION_ESCORTEE_A = 774,
FACTION_ESCORTEE_H = 775
};
+
struct TRINITY_DLL_DECL npc_injured_rainspeaker_oracleAI : public npc_escortAI
{
npc_injured_rainspeaker_oracleAI(Creature* c) : npc_escortAI(c) { c_guid = c->GetGUID(); }
+
uint64 c_guid;
+
void Reset()
{
me->RestoreFaction();
@@ -54,11 +64,14 @@ struct TRINITY_DLL_DECL npc_injured_rainspeaker_oracleAI : public npc_escortAI
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
}
}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 1: SetRun(); break;
@@ -88,10 +101,12 @@ struct TRINITY_DLL_DECL npc_injured_rainspeaker_oracleAI : public npc_escortAI
break;
}
}
+
void JustDied(Unit* killer)
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (Player* pPlayer = GetPlayerForEscort())
{
if (pPlayer->GetQuestStatus(QUEST_FORTUNATE_MISUNDERSTANDINGS) != QUEST_STATUS_COMPLETE)
@@ -100,15 +115,20 @@ struct TRINITY_DLL_DECL npc_injured_rainspeaker_oracleAI : public npc_escortAI
}
};
+
bool GossipHello_npc_injured_rainspeaker_oracle(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_FORTUNATE_MISUNDERSTANDINGS) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_injured_rainspeaker_oracle(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -117,6 +137,7 @@ bool GossipSelect_npc_injured_rainspeaker_oracle(Player* pPlayer, Creature* pCre
CAST_AI(npc_escortAI, (pCreature->AI()))->SetMaxPlayerDistance(35.0f);
pCreature->SetUnitMovementFlags(MOVEMENTFLAG_JUMPING);
DoScriptText(SAY_START_IRO, pCreature);
+
switch (pPlayer->GetTeam()){
case ALLIANCE:
pCreature->setFaction(FACTION_ESCORTEE_A);
@@ -128,41 +149,53 @@ bool GossipSelect_npc_injured_rainspeaker_oracle(Player* pPlayer, Creature* pCre
}
return true;
}
+
bool QuestAccept_npc_injured_rainspeaker_oracle(Player* pPlayer, Creature* pCreature, Quest const *_Quest)
{
DoScriptText(SAY_QUEST_ACCEPT_IRO, pCreature);
return false;
}
+
CreatureAI* GetAI_npc_injured_rainspeaker_oracle(Creature* pCreature)
{
return new npc_injured_rainspeaker_oracleAI(pCreature);
}
+
/*######
## npc_vekjik
######*/
+
#define GOSSIP_VEKJIK_ITEM1 "Shaman Vekjik, I have spoken with the big-tongues and they desire peace. I have brought this offering on their behalf."
#define GOSSIP_VEKJIK_ITEM2 "No no... I had no intentions of betraying your people. I was only defending myself. it was all a misunderstanding."
+
enum eVekjik
{
GOSSIP_TEXTID_VEKJIK1 = 13137,
GOSSIP_TEXTID_VEKJIK2 = 13138,
+
SAY_TEXTID_VEKJIK1 = -1000208,
+
SPELL_FREANZYHEARTS_FURY = 51469,
+
QUEST_MAKING_PEACE = 12573
};
+
bool GossipHello_npc_vekjik(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_MAKING_PEACE) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_VEKJIK_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_VEKJIK1, pCreature->GetGUID());
return true;
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_vekjik(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -178,33 +211,43 @@ bool GossipSelect_npc_vekjik(Player* pPlayer, Creature* pCreature, uint32 uiSend
pCreature->CastSpell(pPlayer, SPELL_FREANZYHEARTS_FURY, false);
break;
}
+
return true;
}
+
/*######
## avatar_of_freya
######*/
+
#define GOSSIP_ITEM_AOF1 "I want to stop the Scourge as much as you do. How can I help?"
#define GOSSIP_ITEM_AOF2 "You can trust me. I am no friend of the Lich King."
#define GOSSIP_ITEM_AOF3 "I will not fail."
+
enum eFreya
{
QUEST_FREYA_PACT = 12621,
+
SPELL_FREYA_CONVERSATION = 52045,
+
GOSSIP_TEXTID_AVATAR1 = 13303,
GOSSIP_TEXTID_AVATAR2 = 13304,
GOSSIP_TEXTID_AVATAR3 = 13305
};
+
bool GossipHello_npc_avatar_of_freya(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_FREYA_PACT) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_AOF1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->PlayerTalkClass->SendGossipMenu(GOSSIP_TEXTID_AVATAR1, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_avatar_of_freya(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -224,6 +267,7 @@ bool GossipSelect_npc_avatar_of_freya(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
void AddSC_sholazar_basin()
{
Script *newscript;
@@ -234,11 +278,13 @@ void AddSC_sholazar_basin()
newscript->pGossipSelect = &GossipSelect_npc_injured_rainspeaker_oracle;
newscript->pQuestAccept = &QuestAccept_npc_injured_rainspeaker_oracle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_vekjik";
newscript->pGossipHello = &GossipHello_npc_vekjik;
newscript->pGossipSelect = &GossipSelect_npc_vekjik;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_avatar_of_freya";
newscript->pGossipHello = &GossipHello_npc_avatar_of_freya;
diff --git a/src/bindings/scripts/scripts/northrend/storm_peaks.cpp b/src/bindings/scripts/scripts/northrend/storm_peaks.cpp
index 39b9a599d83..473fca5aa71 100644
--- a/src/bindings/scripts/scripts/northrend/storm_peaks.cpp
+++ b/src/bindings/scripts/scripts/northrend/storm_peaks.cpp
@@ -14,37 +14,47 @@
* 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"
+
/*######
## npc_agnetta_tyrsdottar
######*/
+
#define SAY_AGGRO -1571003
#define GOSSIP_AGNETTA "Skip the warmup, sister... or are you too scared to face soemeone your own size?"
+
enum eAgnetta
{
QUEST_ITS_THAT_YOUR_GOBLIN = 12969,
FACTION_HOSTILE_AT1 = 45
};
+
struct TRINITY_DLL_DECL npc_agnetta_tyrsdottarAI : public ScriptedAI
{
npc_agnetta_tyrsdottarAI(Creature* pCreature) : ScriptedAI(pCreature) { }
+
void Reset()
{
me->RestoreFaction();
}
};
+
CreatureAI* GetAI_npc_agnetta_tyrsdottar(Creature* pCreature)
{
return new npc_agnetta_tyrsdottarAI(pCreature);
}
+
bool GossipHello_npc_agnetta_tyrsdottar(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_ITS_THAT_YOUR_GOBLIN) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_AGNETTA, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(13691, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_agnetta_tyrsdottar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction )
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -54,28 +64,36 @@ bool GossipSelect_npc_agnetta_tyrsdottar(Player* pPlayer, Creature* pCreature, u
pCreature->setFaction(FACTION_HOSTILE_AT1);
pCreature->AI()->AttackStart(pPlayer);
}
+
return true;
}
+
/*######
## npc_frostborn_scout
######*/
+
#define GOSSIP_ITEM1 "Are you okay? I've come to take you back to Frosthold if you can stand."
#define GOSSIP_ITEM2 "I'm sorry that I didn't get here sooner. What happened?"
#define GOSSIP_ITEM3 "I'll go get some help. Hang in there."
+
enum eFrostbornScout
{
QUEST_MISSING_SCOUTS = 12864
};
+
bool GossipHello_npc_frostborn_scout(Player* pPlayer, Creature* pCreature)
{
+
if (pPlayer->GetQuestStatus(QUEST_MISSING_SCOUTS) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->PlayerTalkClass->SendGossipMenu(13611, pCreature->GetGUID());
}
+
return true;
}
+
bool GossipSelect_npc_frostborn_scout(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -93,15 +111,19 @@ bool GossipSelect_npc_frostborn_scout(Player* pPlayer, Creature* pCreature, uint
pPlayer->AreaExploredOrEventHappens(QUEST_MISSING_SCOUTS);
break;
}
+
return true;
}
+
/*######
## npc_thorim
######*/
+
#define GOSSIP_HN "Thorim?"
#define GOSSIP_SN1 "Can you tell me what became of Sif?"
#define GOSSIP_SN2 "He did more than that, Thorim. He controls Ulduar now."
#define GOSSIP_SN3 "It needn't end this way."
+
enum eThorim
{
QUEST_SIBLING_RIVALRY = 13064,
@@ -111,10 +133,12 @@ enum eThorim
GOSSIP_TEXTID_THORIM3 = 13802,
GOSSIP_TEXTID_THORIM4 = 13803
};
+
bool GossipHello_npc_thorim(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_SIBLING_RIVALRY) == QUEST_STATUS_INCOMPLETE) {
pPlayer->ADD_GOSSIP_ITEM(0, GOSSIP_HN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_THORIM1, pCreature->GetGUID());
@@ -122,6 +146,7 @@ bool GossipHello_npc_thorim(Player* pPlayer, Creature* pCreature)
}
return false;
}
+
bool GossipSelect_npc_thorim(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -145,20 +170,24 @@ bool GossipSelect_npc_thorim(Player* pPlayer, Creature* pCreature, uint32 uiSend
}
return true;
}
+
void AddSC_storm_peaks()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "npc_agnetta_tyrsdottar";
newscript->GetAI = &GetAI_npc_agnetta_tyrsdottar;
newscript->pGossipHello = &GossipHello_npc_agnetta_tyrsdottar;
newscript->pGossipSelect = &GossipSelect_npc_agnetta_tyrsdottar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_frostborn_scout";
newscript->pGossipHello = &GossipHello_npc_frostborn_scout;
newscript->pGossipSelect = &GossipSelect_npc_frostborn_scout;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_thorim";
newscript->pGossipHello = &GossipHello_npc_thorim;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp
index 10c636d62ea..d2630dc2175 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_bjarngrim.cpp
@@ -13,14 +13,17 @@
* along 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 Bjarngrim
SD%Complete: 70%
SDComment: Waypoint needed, we expect boss to always have 2x Stormforged Lieutenant following
SDCategory: Halls of Lightning
EndScriptData */
+
#include "precompiled.h"
#include "def_halls_of_lightning.h"
+
enum eEnums
{
//Yell
@@ -35,38 +38,47 @@ enum eEnums
EMOTE_BERSEKER_STANCE = -1602008,
SAY_DEFENSIVE_STANCE = -1602009,
EMOTE_DEFENSIVE_STANCE = -1602010,
+
SPELL_DEFENSIVE_STANCE = 53790,
//SPELL_DEFENSIVE_AURA = 41105,
SPELL_SPELL_REFLECTION = 36096,
SPELL_PUMMEL = 12555,
SPELL_KNOCK_AWAY = 52029,
SPELL_IRONFORM = 52022,
+
SPELL_BERSEKER_STANCE = 53791,
//SPELL_BERSEKER_AURA = 41107,
SPELL_INTERCEPT = 58769,
SPELL_WHIRLWIND = 52027,
SPELL_CLEAVE = 15284,
+
SPELL_BATTLE_STANCE = 53792,
//SPELL_BATTLE_AURA = 41106,
SPELL_MORTAL_STRIKE = 16856,
SPELL_SLAM = 52026,
+
//OTHER SPELLS
//SPELL_CHARGE_UP = 52098, // only used when starting walk from one platform to the other
//SPELL_TEMPORARY_ELECTRICAL_CHARGE = 52092, // triggered part of above
+
NPC_STORMFORGED_LIEUTENANT = 29240,
SPELL_ARC_WELD = 59085,
SPELL_RENEW_STEEL_N = 52774,
SPELL_RENEW_STEEL_H = 59160,
+
EQUIP_SWORD = 37871,
EQUIP_SHIELD = 35642,
EQUIP_MACE = 43623,
+
STANCE_DEFENSIVE = 0,
STANCE_BERSERKER = 1,
STANCE_BATTLE = 2
};
+
/*######
## boss_bjarngrim
######*/
+
struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
{
boss_bjarngrimAI(Creature *pCreature) : ScriptedAI(pCreature)
@@ -76,39 +88,54 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
m_uiStance = STANCE_DEFENSIVE;
memset(&m_auiStormforgedLieutenantGUID, 0, sizeof(m_auiStormforgedLieutenantGUID));
}
+
ScriptedInstance* m_pInstance;
+
bool m_bIsHeroic;
bool m_bIsChangingStance;
+
uint8 m_uiChargingStatus;
uint8 m_uiStance;
+
uint32 m_uiCharge_Timer;
uint32 m_uiChangeStance_Timer;
+
uint32 m_uiReflection_Timer;
uint32 m_uiKnockAway_Timer;
uint32 m_uiPummel_Timer;
uint32 m_uiIronform_Timer;
+
uint32 m_uiIntercept_Timer;
uint32 m_uiWhirlwind_Timer;
uint32 m_uiCleave_Timer;
+
uint32 m_uiMortalStrike_Timer;
uint32 m_uiSlam_Timer;
+
uint64 m_auiStormforgedLieutenantGUID[2];
+
void Reset()
{
m_bIsChangingStance = false;
+
m_uiChargingStatus = 0;
m_uiCharge_Timer = 1000;
+
m_uiChangeStance_Timer = 20000 + rand()%5000;
+
m_uiReflection_Timer = 8000;
m_uiKnockAway_Timer = 20000;
m_uiPummel_Timer = 10000;
m_uiIronform_Timer = 25000;
+
m_uiIntercept_Timer = 5000;
m_uiWhirlwind_Timer = 10000;
m_uiCleave_Timer = 8000;
+
m_uiMortalStrike_Timer = 8000;
m_uiSlam_Timer = 10000;
- for (uint8 i = 0; i < 2; ++i)
+
+ for(uint8 i = 0; i < 2; ++i)
{
if (Creature* pStormforgedLieutenant = (Unit::GetCreature((*m_creature), m_auiStormforgedLieutenantGUID[i])))
{
@@ -116,34 +143,44 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
pStormforgedLieutenant->Respawn();
}
}
+
if (m_uiStance != STANCE_DEFENSIVE)
{
DoRemoveStanceAura(m_uiStance);
DoCast(m_creature, SPELL_DEFENSIVE_STANCE);
m_uiStance = STANCE_DEFENSIVE;
}
+
SetEquipmentSlots(false, EQUIP_SWORD, EQUIP_SHIELD, EQUIP_NO_CHANGE);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_BJARNGRIM, NOT_STARTED);
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
+
//must get both lieutenants here and make sure they are with him
m_creature->CallForHelp(30.0f);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_BJARNGRIM, IN_PROGRESS);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void JustDied(Unit* pKiller)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_BJARNGRIM, DONE);
}
+
//TODO: remove when removal is done by mangos
void DoRemoveStanceAura(uint8 uiStance)
{
@@ -160,22 +197,29 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
break;
}
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
// Change stance
if (m_uiChangeStance_Timer < uiDiff)
{
//wait for current spell to finish before change stance
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
DoRemoveStanceAura(m_uiStance);
+
int uiTempStance = rand()%(3-1);
+
if (uiTempStance >= m_uiStance)
++uiTempStance;
+
m_uiStance = uiTempStance;
+
switch(m_uiStance)
{
case STANCE_DEFENSIVE:
@@ -197,11 +241,13 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
SetEquipmentSlots(false, EQUIP_MACE, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
break;
}
+
m_uiChangeStance_Timer = 20000 + rand()%5000;
return;
}
else
m_uiChangeStance_Timer -= uiDiff;
+
switch(m_uiStance)
{
case STANCE_DEFENSIVE:
@@ -213,6 +259,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiReflection_Timer -= uiDiff;
+
if (m_uiKnockAway_Timer < uiDiff)
{
DoCast(m_creature, SPELL_KNOCK_AWAY);
@@ -220,6 +267,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiKnockAway_Timer -= uiDiff;
+
if (m_uiPummel_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_PUMMEL);
@@ -227,6 +275,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiPummel_Timer -= uiDiff;
+
if (m_uiIronform_Timer < uiDiff)
{
DoCast(m_creature, SPELL_IRONFORM);
@@ -234,6 +283,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiIronform_Timer -= uiDiff;
+
break;
}
case STANCE_BERSERKER:
@@ -246,6 +296,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiIntercept_Timer -= uiDiff;
+
if (m_uiWhirlwind_Timer < uiDiff)
{
DoCast(m_creature, SPELL_WHIRLWIND);
@@ -253,6 +304,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiWhirlwind_Timer -= uiDiff;
+
if (m_uiCleave_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_CLEAVE);
@@ -260,6 +312,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiCleave_Timer -= uiDiff;
+
break;
}
case STANCE_BATTLE:
@@ -271,6 +324,7 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiMortalStrike_Timer -= uiDiff;
+
if (m_uiSlam_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_SLAM);
@@ -278,15 +332,19 @@ struct TRINITY_DLL_DECL boss_bjarngrimAI : public ScriptedAI
}
else
m_uiSlam_Timer -= uiDiff;
+
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
/*######
## mob_stormforged_lieutenant
######*/
+
struct TRINITY_DLL_DECL mob_stormforged_lieutenantAI : public ScriptedAI
{
mob_stormforged_lieutenantAI(Creature *pCreature) : ScriptedAI(pCreature)
@@ -294,15 +352,19 @@ struct TRINITY_DLL_DECL mob_stormforged_lieutenantAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
bool m_bIsHeroic;
+
uint32 m_uiArcWeld_Timer;
uint32 m_uiRenewSteel_Timer;
+
void Reset()
{
m_uiArcWeld_Timer = 20000 + rand()%1000;
m_uiRenewSteel_Timer = 10000 + rand()%1000;
}
+
void EnterCombat(Unit* pWho)
{
if (m_pInstance)
@@ -314,11 +376,13 @@ struct TRINITY_DLL_DECL mob_stormforged_lieutenantAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (m_uiArcWeld_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_ARC_WELD);
@@ -326,6 +390,7 @@ struct TRINITY_DLL_DECL mob_stormforged_lieutenantAI : public ScriptedAI
}
else
m_uiArcWeld_Timer -= uiDiff;
+
if (m_uiRenewSteel_Timer < uiDiff)
{
if (m_pInstance)
@@ -340,24 +405,30 @@ struct TRINITY_DLL_DECL mob_stormforged_lieutenantAI : public ScriptedAI
}
else
m_uiRenewSteel_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_bjarngrim(Creature* pCreature)
{
return new boss_bjarngrimAI(pCreature);
}
+
CreatureAI* GetAI_mob_stormforged_lieutenant(Creature* pCreature)
{
return new mob_stormforged_lieutenantAI(pCreature);
}
+
void AddSC_boss_bjarngrim()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_bjarngrim";
newscript->GetAI = &GetAI_boss_bjarngrim;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_stormforged_lieutenant";
newscript->GetAI = &GetAI_mob_stormforged_lieutenant;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp
index ccaf46303c7..96e49343b39 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_ionar.cpp
@@ -13,14 +13,17 @@
* along 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 Ionar
SD%Complete: 80%
SDComment: Timer check
SDCategory: Halls of Lightning
EndScriptData */
+
#include "precompiled.h"
#include "def_halls_of_lightning.h"
+
enum eEnums
{
SAY_AGGRO = -1602011,
@@ -30,23 +33,30 @@ enum eEnums
SAY_DEATH = -1602015,
SAY_SPLIT_1 = -1602016,
SAY_SPLIT_2 = -1602017,
+
SPELL_BALL_LIGHTNING_N = 52780,
SPELL_BALL_LIGHTNING_H = 59800,
SPELL_STATIC_OVERLOAD_N = 52658,
SPELL_STATIC_OVERLOAD_H = 59795,
+
SPELL_DISPERSE = 52770,
SPELL_SUMMON_SPARK = 52746,
SPELL_SPARK_DESPAWN = 52776,
+
//Spark of Ionar
SPELL_SPARK_VISUAL_TRIGGER_N = 52667,
SPELL_SPARK_VISUAL_TRIGGER_H = 59833,
+
NPC_SPARK_OF_IONAR = 28926,
+
MAX_SPARKS = 5,
POINT_CALLBACK = 0
};
+
/*######
## Boss Ionar
######*/
+
struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
{
boss_ionarAI(Creature *pCreature) : ScriptedAI(pCreature)
@@ -54,46 +64,64 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
+
std::list<uint64> m_lSparkGUIDList;
+
bool m_bIsHeroic;
+
bool m_bIsSplitPhase;
uint32 m_uiSplit_Timer;
uint32 m_uiSparkAtHomeCount;
+
uint32 m_uiStaticOverload_Timer;
uint32 m_uiBallLightning_Timer;
+
uint32 m_uiHealthAmountModifier;
+
void Reset()
{
m_lSparkGUIDList.clear();
+
m_bIsSplitPhase = true;
m_uiSplit_Timer = 25000;
m_uiSparkAtHomeCount = 0;
+
m_uiStaticOverload_Timer = 5000 + rand()%1000;
m_uiBallLightning_Timer = 10000 + rand()%1000;
+
m_uiHealthAmountModifier = 1;
+
if (m_creature->GetVisibility() == VISIBILITY_OFF)
m_creature->SetVisibility(VISIBILITY_ON);
}
+
void AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim())
return;
+
if (m_creature->GetVisibility() == VISIBILITY_OFF)
return;
+
AttackStart(pAttacker);
}
+
void Aggro(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_IONAR, IN_PROGRESS);
}
+
void JustReachedHome()
{
if (m_pInstance)
m_pInstance->SetData(TYPE_IONAR, NOT_STARTED);
}
+
void AttackStart(Unit* pWho)
{
if (m_creature->Attack(pWho, true))
@@ -101,26 +129,32 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
if (m_creature->GetVisibility() != VISIBILITY_OFF)
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
DespawnSpark();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_IONAR, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void DespawnSpark()
{
if (m_lSparkGUIDList.empty())
return;
- for (std::list<uint64>::iterator itr = m_lSparkGUIDList.begin(); itr != m_lSparkGUIDList.end(); ++itr)
+
+ for(std::list<uint64>::iterator itr = m_lSparkGUIDList.begin(); itr != m_lSparkGUIDList.end(); ++itr)
{
if (Creature* pTemp = Unit::GetCreature(*m_creature, *itr))
{
@@ -128,15 +162,18 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
pTemp->ForcedDespawn();
}
}
+
m_lSparkGUIDList.clear();
}
+
//make sparks come back
void CallBackSparks()
{
//should never be empty here, but check
if (m_lSparkGUIDList.empty())
return;
- for (std::list<uint64>::iterator itr = m_lSparkGUIDList.begin(); itr != m_lSparkGUIDList.end(); ++itr)
+
+ for(std::list<uint64>::iterator itr = m_lSparkGUIDList.begin(); itr != m_lSparkGUIDList.end(); ++itr)
{
if (Creature* pSpark = Unit::GetCreature(*m_creature, *itr))
{
@@ -144,27 +181,34 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
{
if (pSpark->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
pSpark->GetMotionMaster()->MovementExpired();
+
pSpark->SetSpeed(MOVE_RUN, pSpark->GetCreatureInfo()->speed * 2);
pSpark->GetMotionMaster()->MovePoint(POINT_CALLBACK, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ());
}
}
}
}
+
void RegisterSparkAtHome()
{
++m_uiSparkAtHomeCount;
}
+
void JustSummoned(Creature* pSummoned)
{
if (pSummoned->GetEntry() == NPC_SPARK_OF_IONAR)
{
pSummoned->CastSpell(pSummoned, m_bIsHeroic ? SPELL_SPARK_VISUAL_TRIGGER_H : SPELL_SPARK_VISUAL_TRIGGER_N, true);
+
Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0);
+
if (m_creature->getVictim())
pSummoned->AI()->AttackStart(pTarget ? pTarget : m_creature->getVictim());
+
m_lSparkGUIDList.push_back(pSummoned->GetGUID());
}
}
+
void UpdateAI(const uint32 uiDiff)
{
// Splitted
@@ -175,9 +219,11 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
Reset();
return;
}*/
+
if (m_uiSplit_Timer < uiDiff)
{
m_uiSplit_Timer = 2500;
+
// Return sparks to where Ionar splitted
if (m_bIsSplitPhase)
{
@@ -189,10 +235,13 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
{
m_creature->SetVisibility(VISIBILITY_ON);
m_creature->CastSpell(m_creature, SPELL_SPARK_DESPAWN, false);
+
DespawnSpark();
+
m_uiSparkAtHomeCount = 0;
m_uiSplit_Timer = 25000;
m_bIsSplitPhase = true;
+
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE)
{
if (m_creature->getVictim())
@@ -202,19 +251,24 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
}
else
m_uiSplit_Timer -= uiDiff;
+
return;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (m_uiStaticOverload_Timer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, m_bIsHeroic ? SPELL_STATIC_OVERLOAD_H : SPELL_STATIC_OVERLOAD_N);
+
m_uiStaticOverload_Timer = 5000 + rand()%1000;
}
else
m_uiStaticOverload_Timer -= uiDiff;
+
if (m_uiBallLightning_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), m_bIsHeroic ? SPELL_BALL_LIGHTNING_H : SPELL_BALL_LIGHTNING_N);
@@ -222,22 +276,29 @@ struct TRINITY_DLL_DECL boss_ionarAI : public ScriptedAI
}
else
m_uiBallLightning_Timer -= uiDiff;
+
// Health check
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < (100-(20*m_uiHealthAmountModifier)))
{
++m_uiHealthAmountModifier;
+
DoScriptText(RAND(SAY_SPLIT_1,SAY_SPLIT_2), m_creature);
+
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
DoCast(m_creature, SPELL_DISPERSE);
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_ionar(Creature* pCreature)
{
return new boss_ionarAI(pCreature);
}
+
bool EffectDummyCreature_boss_ionar(Unit* pCaster, uint32 uiSpellId, uint32 uiEffIndex, Creature* pCreatureTarget)
{
//always check spellid and effectindex
@@ -245,32 +306,42 @@ bool EffectDummyCreature_boss_ionar(Unit* pCaster, uint32 uiSpellId, uint32 uiEf
{
if (pCreatureTarget->GetEntry() != NPC_IONAR)
return true;
+
for (uint8 i = 0; i < MAX_SPARKS; ++i)
pCreatureTarget->CastSpell(pCreatureTarget, SPELL_SUMMON_SPARK, true);
+
pCreatureTarget->AttackStop();
pCreatureTarget->SetVisibility(VISIBILITY_OFF);
+
if (pCreatureTarget->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
pCreatureTarget->GetMotionMaster()->MovementExpired();
+
//always return true when we are handling this spell and effect
return true;
}
return false;
}
+
/*######
## mob_spark_of_ionar
######*/
+
struct TRINITY_DLL_DECL mob_spark_of_ionarAI : public ScriptedAI
{
mob_spark_of_ionarAI(Creature *pCreature) : ScriptedAI(pCreature)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
void Reset() { }
+
void MovementInform(uint32 uiType, uint32 uiPointId)
{
if (uiType != POINT_MOTION_TYPE || !m_pInstance)
return;
+
if (uiPointId == POINT_CALLBACK)
{
if (Creature* pIonar = m_pInstance->instance->GetCreature(m_pInstance->GetData64(DATA_IONAR)))
@@ -280,6 +351,7 @@ struct TRINITY_DLL_DECL mob_spark_of_ionarAI : public ScriptedAI
m_creature->ForcedDespawn();
return;
}
+
if (boss_ionarAI* pIonarAI = dynamic_cast<boss_ionarAI*>(pIonar->AI()))
pIonarAI->RegisterSparkAtHome();
}
@@ -288,18 +360,22 @@ struct TRINITY_DLL_DECL mob_spark_of_ionarAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mob_spark_of_ionar(Creature* pCreature)
{
return new mob_spark_of_ionarAI(pCreature);
}
+
void AddSC_boss_ionar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ionar";
newscript->GetAI = &GetAI_boss_ionar;
newscript->pEffectDummyCreature = &EffectDummyCreature_boss_ionar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_spark_of_ionar";
newscript->GetAI = &GetAI_mob_spark_of_ionar;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp
index 227b297e087..19d34eb383c 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_loken.cpp
@@ -13,18 +13,23 @@
* along 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 Loken
SD%Complete: 60%
SDComment: Missing intro. Remove hack of Pulsing Shockwave when core supports. Aura is not working (59414)
SDCategory: Halls of Lightning
EndScriptData */
+
#include "precompiled.h"
#include "def_halls_of_lightning.h"
+
#define MAX_ENCOUNTER_TIME 2 * 60 * 1000
+
enum eEnums
{
ACHIEVEMENT_TIMELY_DEATH = 1867,
+
SAY_AGGRO = -1602018,
SAY_INTRO_1 = -1602019,
SAY_INTRO_2 = -1602020,
@@ -39,16 +44,20 @@ enum eEnums
SAY_50HEALTH = -1602029,
SAY_25HEALTH = -1602030,
EMOTE_NOVA = -1602031,
+
SPELL_ARC_LIGHTNING = 52921,
SPELL_LIGHTNING_NOVA_N = 52960,
SPELL_LIGHTNING_NOVA_H = 59835,
+
SPELL_PULSING_SHOCKWAVE_N = 52961,
SPELL_PULSING_SHOCKWAVE_H = 59836,
SPELL_PULSING_SHOCKWAVE_AURA = 59414
};
+
/*######
## Boss Loken
######*/
+
struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
{
boss_lokenAI(Creature* pCreature) : ScriptedAI(pCreature)
@@ -56,36 +65,50 @@ struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
+
bool m_bIsHeroic;
bool m_bIsAura;
+
uint32 m_uiArcLightning_Timer;
uint32 m_uiLightningNova_Timer;
uint32 m_uiPulsingShockwave_Timer;
uint32 m_uiResumePulsingShockwave_Timer;
+
uint32 m_uiHealthAmountModifier;
+
uint32 EncounterTime;
+
void Reset()
{
m_bIsAura = false;
+
m_uiArcLightning_Timer = 15000;
m_uiLightningNova_Timer = 20000;
m_uiPulsingShockwave_Timer = 2000;
m_uiResumePulsingShockwave_Timer = 15000;
+
m_uiHealthAmountModifier = 1;
+
if (m_pInstance)
m_pInstance->SetData(TYPE_LOKEN, NOT_STARTED);
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
+
EncounterTime = 0;
+
if (m_pInstance)
m_pInstance->SetData(TYPE_LOKEN, IN_PROGRESS);
}
+
void JustDied(Unit* pKiller)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (m_bIsHeroic && EncounterTime <= MAX_ENCOUNTER_TIME)
{
AchievementEntry const *AchievTimelyDeath = GetAchievementStore()->LookupEntry(ACHIEVEMENT_TIMELY_DEATH);
@@ -95,24 +118,29 @@ struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievTimelyDeath);
}
}
}
+
if (m_pInstance)
m_pInstance->SetData(TYPE_LOKEN, DONE);
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
EncounterTime += uiDiff;
+
if (m_bIsAura)
{
// workaround for PULSING_SHOCKWAVE
@@ -122,17 +150,21 @@ struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
if (pMap->IsDungeon())
{
Map::PlayerList const &PlayerList = pMap->GetPlayers();
+
if (PlayerList.isEmpty())
return;
+
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (i->getSource()->isAlive() && i->getSource()->isTargetableForAttack())
{
int32 dmg;
float m_fDist = (m_creature->GetDistance(i->getSource()) - 7.8f);
+
if (m_fDist <= 1.0f) // Less than 1 yard
dmg = (m_bIsHeroic ? 150 : 100); // need to correct damage
else // Further from 1 yard
dmg = ((m_bIsHeroic ? 150 : 100) * m_fDist) + (m_bIsHeroic ? 150 : 100); // need to correct damage
+
m_creature->CastCustomSpell(i->getSource(), (m_bIsHeroic ? 59837 : 52942), &dmg, 0, 0, false);
}
}
@@ -145,6 +177,7 @@ struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
{
//breaks at movement, can we assume when it's time, this spell is casted and also must stop movement?
m_creature->CastSpell(m_creature, SPELL_PULSING_SHOCKWAVE_AURA, true);
+
DoCast(m_creature, m_bIsHeroic ? SPELL_PULSING_SHOCKWAVE_H : SPELL_PULSING_SHOCKWAVE_N); // need core support
m_bIsAura = true;
m_uiResumePulsingShockwave_Timer = 0;
@@ -152,25 +185,30 @@ struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
else
m_uiResumePulsingShockwave_Timer -= uiDiff;
}
+
if (m_uiArcLightning_Timer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_ARC_LIGHTNING);
+
m_uiArcLightning_Timer = 15000 + rand()%1000;
}
else
m_uiArcLightning_Timer -= uiDiff;
+
if (m_uiLightningNova_Timer < uiDiff)
{
DoScriptText(RAND(SAY_NOVA_1,SAY_NOVA_2,SAY_NOVA_3), m_creature);
DoScriptText(EMOTE_NOVA, m_creature);
DoCast(m_creature, m_bIsHeroic ? SPELL_LIGHTNING_NOVA_H : SPELL_LIGHTNING_NOVA_N);
+
m_bIsAura = false;
m_uiResumePulsingShockwave_Timer = (m_bIsHeroic ? 4000 : 5000); // Pause Pulsing Shockwave aura
m_uiLightningNova_Timer = 20000 + rand()%1000;
}
else
m_uiLightningNova_Timer -= uiDiff;
+
// Health check
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < (100-(25*m_uiHealthAmountModifier)))
{
@@ -180,18 +218,23 @@ struct TRINITY_DLL_DECL boss_lokenAI : public ScriptedAI
case 2: DoScriptText(SAY_50HEALTH, m_creature); break;
case 3: DoScriptText(SAY_25HEALTH, m_creature); break;
}
+
++m_uiHealthAmountModifier;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_loken(Creature* pCreature)
{
return new boss_lokenAI(pCreature);
}
+
void AddSC_boss_loken()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_loken";
newscript->GetAI = &GetAI_boss_loken;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp
index 0446e653b2d..1804d316f92 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/boss_volkhan.cpp
@@ -13,14 +13,17 @@
* along 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 Volkhan
SD%Complete: 60%
SDComment: Not considered complete. Some events may fail and need further development
SDCategory: Halls of Lightning
EndScriptData */
+
#include "precompiled.h"
#include "def_halls_of_lightning.h"
+
enum eEnums
{
SAY_AGGRO = -1602032,
@@ -34,31 +37,41 @@ enum eEnums
SAY_FORGE_2 = -1602040,
EMOTE_TO_ANVIL = -1602041,
EMOTE_SHATTER = -1602042,
+
SPELL_HEAT_N = 52387,
SPELL_HEAT_H = 59528,
SPELL_SHATTERING_STOMP_N = 52237,
SPELL_SHATTERING_STOMP_H = 59529,
+
//unclear how "directions" of spells must be. Last, summoning GO, what is it for? Script depend on:
SPELL_TEMPER = 52238, //TARGET_SCRIPT boss->anvil
SPELL_TEMPER_DUMMY = 52654, //TARGET_SCRIPT anvil->boss
+
//SPELL_TEMPER_VISUAL = 52661, //summons GO
+
SPELL_SUMMON_MOLTEN_GOLEM = 52405,
+
//Molten Golem
SPELL_BLAST_WAVE = 23113,
SPELL_IMMOLATION_STRIKE_N = 52433,
SPELL_IMMOLATION_STRIKE_H = 59530,
SPELL_SHATTER_N = 52429,
SPELL_SHATTER_H = 59527,
+
NPC_VOLKHAN_ANVIL = 28823,
NPC_MOLTEN_GOLEM = 28695,
NPC_BRITTLE_GOLEM = 28681,
+
POINT_ID_ANVIL = 0,
MAX_GOLEM = 2,
+
ACHIEVEMENT_SHATTER_RESISTANT = 2042
};
+
/*######
## Boss Volkhan
######*/
+
struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
{
boss_volkhanAI(Creature *pCreature) : ScriptedAI(pCreature)
@@ -66,38 +79,51 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
+
std::list<uint64> m_lGolemGUIDList;
+
bool m_bIsHeroic;
bool m_bHasTemper;
bool m_bIsStriking;
bool m_bCanShatterGolem;
+
uint8 GolemsShattered;
uint32 m_uiPause_Timer;
uint32 m_uiShatteringStomp_Timer;
uint32 m_uiShatter_Timer;
+
uint32 m_uiHealthAmountModifier;
+
void Reset()
{
m_bIsStriking = false;
m_bHasTemper = false;
m_bCanShatterGolem = false;
+
m_uiPause_Timer = 3500;
m_uiShatteringStomp_Timer = 0;
m_uiShatter_Timer = 5000;
GolemsShattered = 0;
+
m_uiHealthAmountModifier = 1;
+
DespawnGolem();
m_lGolemGUIDList.clear();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_VOLKHAN, NOT_STARTED);
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_VOLKHAN, IN_PROGRESS);
}
+
void AttackStart(Unit* pWho)
{
if (m_creature->Attack(pWho, true))
@@ -105,16 +131,20 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
if (!m_bHasTemper)
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
+
void JustDied(Unit* pKiller)
{
DoScriptText(SAY_DEATH, m_creature);
DespawnGolem();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_VOLKHAN, DONE);
+
if (HeroicMode && GolemsShattered < 5)
{
AchievementEntry const *AchievShatterResistant = GetAchievementStore()->LookupEntry(ACHIEVEMENT_SHATTER_RESISTANT);
@@ -124,21 +154,24 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievShatterResistant);
}
}
}
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void DespawnGolem()
{
if (m_lGolemGUIDList.empty())
return;
- for (std::list<uint64>::iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr)
+
+ for(std::list<uint64>::iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr)
{
if (Creature* pTemp = Unit::GetCreature(*m_creature, *itr))
{
@@ -146,13 +179,16 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
pTemp->ForcedDespawn();
}
}
+
m_lGolemGUIDList.clear();
}
+
void ShatterGolem()
{
if (m_lGolemGUIDList.empty())
return;
- for (std::list<uint64>::iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr)
+
+ for(std::list<uint64>::iterator itr = m_lGolemGUIDList.begin(); itr != m_lGolemGUIDList.end(); ++itr)
{
if (Creature* pTemp = Unit::GetCreature(*m_creature, *itr))
{
@@ -165,27 +201,33 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
}
}
}
+
void SpellHit(Unit* pCaster, const SpellEntry* pSpell)
{
if (pSpell->Id == SPELL_TEMPER_DUMMY)
m_bIsStriking = true;
}
+
void JustSummoned(Creature* pSummoned)
{
if (pSummoned->GetEntry() == NPC_MOLTEN_GOLEM)
{
m_lGolemGUIDList.push_back(pSummoned->GetGUID());
+
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
pSummoned->AI()->AttackStart(pTarget);
+
//why healing when just summoned?
pSummoned->CastSpell(pSummoned, m_bIsHeroic ? SPELL_HEAT_H : SPELL_HEAT_N, false, NULL, NULL, m_creature->GetGUID());
}
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (m_bIsStriking)
{
if (m_uiPause_Timer < uiDiff)
@@ -195,29 +237,37 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
if (m_creature->getVictim())
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
}
+
m_bHasTemper = false;
m_bIsStriking = false;
m_uiPause_Timer = 3500;
}
else
m_uiPause_Timer -= uiDiff;
+
return;
}
+
// When to start shatter? After 60, 40 or 20% hp?
if (!m_bHasTemper && m_uiHealthAmountModifier >= 3)
{
if (m_uiShatteringStomp_Timer < uiDiff)
{
//should he stomp even if he has no brittle golem to shatter?
+
DoScriptText(RAND(SAY_STOMP_1,SAY_STOMP_2), m_creature);
+
DoCast(m_creature, m_bIsHeroic ? SPELL_SHATTERING_STOMP_H : SPELL_SHATTERING_STOMP_N);
+
DoScriptText(EMOTE_SHATTER, m_creature);
+
m_uiShatteringStomp_Timer = 30000;
m_bCanShatterGolem = true;
}
else
m_uiShatteringStomp_Timer -= uiDiff;
}
+
// Shatter Golems 3 seconds after Shattering Stomp
if (m_bCanShatterGolem)
{
@@ -230,23 +280,31 @@ struct TRINITY_DLL_DECL boss_volkhanAI : public ScriptedAI
else
m_uiShatter_Timer -= uiDiff;
}
+
// Health check
if (!m_bCanShatterGolem && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < (100-(20*m_uiHealthAmountModifier)))
{
++m_uiHealthAmountModifier;
+
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
DoScriptText(RAND(SAY_FORGE_1,SAY_FORGE_2), m_creature);
+
m_bHasTemper = true;
+
m_creature->CastSpell(m_creature, SPELL_TEMPER, false);
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_volkhan(Creature* pCreature)
{
return new boss_volkhanAI(pCreature);
}
+
bool EffectDummyCreature_boss_volkhan(Unit* pCaster, uint32 uiSpellId, uint32 uiEffIndex, Creature* pCreatureTarget)
{
//always check spellid and effectindex
@@ -254,18 +312,23 @@ bool EffectDummyCreature_boss_volkhan(Unit* pCaster, uint32 uiSpellId, uint32 ui
{
if (pCaster->GetEntry() != NPC_VOLKHAN_ANVIL || pCreatureTarget->GetEntry() != NPC_VOLKHAN)
return true;
- for (uint8 i = 0; i < MAX_GOLEM; ++i)
+
+ for(uint8 i = 0; i < MAX_GOLEM; ++i)
{
pCreatureTarget->CastSpell(pCaster, SPELL_SUMMON_MOLTEN_GOLEM, true);
}
+
//always return true when we are handling this spell and effect
return true;
}
+
return false;
}
+
/*######
## npc_volkhan_anvil
######*/
+
bool EffectDummyCreature_npc_volkhan_anvil(Unit* pCaster, uint32 uiSpellId, uint32 uiEffIndex, Creature* pCreatureTarget)
{
//always check spellid and effectindex
@@ -273,42 +336,58 @@ bool EffectDummyCreature_npc_volkhan_anvil(Unit* pCaster, uint32 uiSpellId, uint
{
if (pCaster->GetEntry() != NPC_VOLKHAN || pCreatureTarget->GetEntry() != NPC_VOLKHAN_ANVIL)
return true;
+
Creature *cre = CAST_CRE(pCaster);
+
DoScriptText(EMOTE_TO_ANVIL, pCaster);
+
float fX, fY, fZ;
pCreatureTarget->GetContactPoint(pCaster, fX, fY, fZ, INTERACTION_DISTANCE);
+
pCaster->AttackStop();
+
if (pCaster->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
pCaster->GetMotionMaster()->MovementExpired();
+
cre->GetMap()->CreatureRelocation(cre, fX, fY, fZ, pCreatureTarget->GetOrientation());
cre->SendMonsterMove(fX, fY, fZ, 0, cre->GetUnitMovementFlags(), 1);
+
pCreatureTarget->CastSpell(pCaster, SPELL_TEMPER_DUMMY, false);
+
//always return true when we are handling this spell and effect
return true;
}
+
return false;
}
+
/*######
## mob_molten_golem
######*/
+
struct TRINITY_DLL_DECL mob_molten_golemAI : public ScriptedAI
{
mob_molten_golemAI(Creature *pCreature) : ScriptedAI(pCreature)
{
m_bIsHeroic = pCreature->GetMap()->IsHeroic();
}
+
bool m_bIsHeroic;
bool m_bIsFrozen;
+
uint32 m_uiBlast_Timer;
uint32 m_uiDeathDelay_Timer;
uint32 m_uiImmolation_Timer;
+
void Reset()
{
m_bIsFrozen = false;
+
m_uiBlast_Timer = 20000;
m_uiDeathDelay_Timer = 0;
m_uiImmolation_Timer = 5000;
}
+
void AttackStart(Unit* pWho)
{
if (m_creature->Attack(pWho, true))
@@ -316,10 +395,12 @@ struct TRINITY_DLL_DECL mob_molten_golemAI : public ScriptedAI
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
if (!m_bIsFrozen)
m_creature->GetMotionMaster()->MoveChase(pWho);
}
}
+
void DamageTaken(Unit* pDoneBy, uint32 &uiDamage)
{
if (uiDamage > m_creature->GetHealth())
@@ -337,6 +418,7 @@ struct TRINITY_DLL_DECL mob_molten_golemAI : public ScriptedAI
m_bIsFrozen = true;
}
}
+
void SpellHit(Unit* pCaster, const SpellEntry* pSpell)
{
//this is the dummy effect of the spells
@@ -346,11 +428,13 @@ struct TRINITY_DLL_DECL mob_molten_golemAI : public ScriptedAI
m_creature->ForcedDespawn();
}
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target or if we are frozen
if (!UpdateVictim() || m_bIsFrozen)
return;
+
if (m_uiBlast_Timer < uiDiff)
{
DoCast(m_creature, SPELL_BLAST_WAVE);
@@ -358,6 +442,7 @@ struct TRINITY_DLL_DECL mob_molten_golemAI : public ScriptedAI
}
else
m_uiBlast_Timer -= uiDiff;
+
if (m_uiImmolation_Timer < uiDiff)
{
DoCast(m_creature->getVictim(), m_bIsHeroic ? SPELL_IMMOLATION_STRIKE_H : SPELL_IMMOLATION_STRIKE_N);
@@ -365,25 +450,31 @@ struct TRINITY_DLL_DECL mob_molten_golemAI : public ScriptedAI
}
else
m_uiImmolation_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_molten_golem(Creature* pCreature)
{
return new mob_molten_golemAI(pCreature);
}
+
void AddSC_boss_volkhan()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_volkhan";
newscript->GetAI = &GetAI_boss_volkhan;
newscript->pEffectDummyCreature = &EffectDummyCreature_boss_volkhan;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_volkhan_anvil";
newscript->pEffectDummyCreature = &EffectDummyCreature_npc_volkhan_anvil;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_molten_golem";
newscript->GetAI = &GetAI_mob_molten_golem;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/def_halls_of_lightning.h b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/def_halls_of_lightning.h
index 0eba059d387..d9739fdf888 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/def_halls_of_lightning.h
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/def_halls_of_lightning.h
@@ -1,27 +1,34 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_HALLS_OF_LIGHTNING_H
#define DEF_HALLS_OF_LIGHTNING_H
+
enum eTypes
{
MAX_ENCOUNTER = 4,
+
DATA_BJARNGRIM = 1,
DATA_IONAR = 2,
DATA_LOKEN = 3,
DATA_VOLKHAN = 4,
+
TYPE_BJARNGRIM = 10,
TYPE_IONAR = 11,
TYPE_LOKEN = 12,
TYPE_VOLKHAN = 13,
+
NPC_BJARNGRIM = 28586,
NPC_VOLKHAN = 28587,
NPC_IONAR = 28546,
NPC_LOKEN = 28923,
+
GO_BJARNGRIM_DOOR = 191416, //_doors10
GO_VOLKHAN_DOOR = 191325, //_doors07
GO_IONAR_DOOR = 191326, //_doors05
GO_LOKEN_DOOR = 191324, //_doors02
GO_LOKEN_THRONE = 192654
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp
index b22aa409f43..e9d75fb8b6f 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_lightning/instance_halls_of_lightning.cpp
@@ -13,46 +13,57 @@
* along 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_Halls_of_Lightning
SD%Complete: 90%
SDComment: All ready.
SDCategory: Halls of Lightning
EndScriptData */
+
#include "precompiled.h"
#include "def_halls_of_lightning.h"
+
/* Halls of Lightning encounters:
0 - General Bjarngrim
1 - Volkhan
2 - Ionar
3 - Loken
*/
+
struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
{
instance_halls_of_lightning(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
uint64 m_uiGeneralBjarngrimGUID;
uint64 m_uiIonarGUID;
uint64 m_uiLokenGUID;
uint64 m_uiVolkhanGUID;
+
uint64 m_uiBjarngrimDoorGUID;
uint64 m_uiVolkhanDoorGUID;
uint64 m_uiIonarDoorGUID;
uint64 m_uiLokenDoorGUID;
uint64 m_uiLokenGlobeGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiGeneralBjarngrimGUID = 0;
m_uiVolkhanGUID = 0;
m_uiIonarGUID = 0;
m_uiLokenGUID = 0;
+
m_uiBjarngrimDoorGUID = 0;
m_uiVolkhanDoorGUID = 0;
m_uiIonarDoorGUID = 0;
m_uiLokenDoorGUID = 0;
m_uiLokenGlobeGUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -71,6 +82,7 @@ struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -100,6 +112,7 @@ struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
break;
}
}
+
void SetData(uint32 uiType, uint32 uiData)
{
switch(uiType)
@@ -123,6 +136,7 @@ struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
if (uiData == DONE)
{
DoUseDoorOrButton(m_uiLokenDoorGUID);
+
//Appears to be type 5 GO with animation. Need to figure out how this work, code below only placeholder
if (GameObject* pGlobe = instance->GetGameObject(m_uiLokenGlobeGUID))
pGlobe->SetGoState(GO_STATE_ACTIVE);
@@ -131,6 +145,7 @@ struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 uiType)
{
switch(uiType)
@@ -146,6 +161,7 @@ struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 uiData)
{
switch(uiData)
@@ -162,10 +178,12 @@ struct TRINITY_DLL_DECL instance_halls_of_lightning : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_halls_of_lightning(Map* pMap)
{
return new instance_halls_of_lightning(pMap);
}
+
void AddSC_instance_halls_of_lightning()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp
index 4d6aef7c122..ed5291a6887 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_krystallus.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_krystallus' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_halls_of_stone.h"
+
enum Spells
{
SPELL_BOULDER_TOSS = 50843,
@@ -21,6 +23,7 @@ enum Spells
SPELL_STOMP = 48131,
H_SPELL_STOMP = 59744
};
+
enum Yells
{
SAY_AGGRO = -1603007,
@@ -28,58 +31,71 @@ enum Yells
SAY_DEATH = -1603009,
SAY_SHATTER = -1603010
};
+
struct TRINITY_DLL_DECL boss_krystallusAI : public ScriptedAI
{
boss_krystallusAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiBoulderTossTimer;
uint32 uiGroundSpikeTimer;
uint32 uiGroundSlamTimer;
uint32 uiShatterTimer;
uint32 uiStompTimer;
+
bool bIsSlam;
+
ScriptedInstance* pInstance;
+
void Reset()
{
bIsSlam = false;
+
uiBoulderTossTimer = 3000 + rand()%6000;
uiGroundSpikeTimer = 9000 + rand()%5000;
uiGroundSlamTimer = 15000 + rand()%3000;
uiStompTimer = 20000 + rand()%9000;
uiShatterTimer = 0;
+
if (pInstance)
pInstance->SetData(DATA_KRYSTALLUS_EVENT, NOT_STARTED);
}
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_KRYSTALLUS_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiBoulderTossTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, HEROIC(SPELL_BOULDER_TOSS, H_SPELL_BOULDER_TOSS));
uiBoulderTossTimer = 9000 + rand()%6000;
} else uiBoulderTossTimer -= diff;
+
if (uiGroundSpikeTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, SPELL_GROUND_SPIKE);
uiGroundSpikeTimer = 12000 + rand()%5000;
} else uiGroundSpikeTimer -= diff;
+
if (uiStompTimer < diff)
{
DoCast(m_creature, HEROIC(SPELL_STOMP, H_SPELL_STOMP));
uiStompTimer = 20000 + rand()%9000;
} else uiStompTimer -= diff;
+
if (uiGroundSlamTimer < diff)
{
DoCast(m_creature, SPELL_GROUND_SLAM);
@@ -87,6 +103,7 @@ struct TRINITY_DLL_DECL boss_krystallusAI : public ScriptedAI
uiShatterTimer = 10000;
uiGroundSlamTimer = 15000 + rand()%3000;
} else uiGroundSlamTimer -= diff;
+
if (bIsSlam)
{
if(uiShatterTimer < diff)
@@ -95,14 +112,18 @@ struct TRINITY_DLL_DECL boss_krystallusAI : public ScriptedAI
bIsSlam = false;
} else uiShatterTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_KRYSTALLUS_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
@@ -110,13 +131,16 @@ struct TRINITY_DLL_DECL boss_krystallusAI : public ScriptedAI
DoScriptText(SAY_KILL, m_creature);
}
};
+
CreatureAI* GetAI_boss_krystallus(Creature* pCreature)
{
return new boss_krystallusAI (pCreature);
}
+
void AddSC_boss_krystallus()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_krystallus";
newscript->GetAI = &GetAI_boss_krystallus;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp
index 2b8cceb14e8..f8fcd720dfa 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_maiden_of_grief.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_maiden_of_grief' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_halls_of_stone.h"
+
enum Spells
{
SPELL_PARTING_SORROW = 59723,
@@ -20,6 +22,7 @@ enum Spells
SPELL_PILLAR_OF_WOE_N = 50761,
SPELL_PILLAR_OF_WOE_H = 59727
};
+
enum Yells
{
SAY_AGGRO = -1603000,
@@ -30,10 +33,12 @@ enum Yells
SAY_DEATH = -1603005,
SAY_STUN = -1603006
};
+
enum Achievements
{
ACHIEVEMENT_GOOD_GRIEF = 1866
};
+
struct TRINITY_DLL_DECL boss_maiden_of_griefAI : public ScriptedAI
{
boss_maiden_of_griefAI(Creature *c) : ScriptedAI(c)
@@ -41,13 +46,16 @@ struct TRINITY_DLL_DECL boss_maiden_of_griefAI : public ScriptedAI
pInstance = m_creature->GetInstanceData();
IsHeroic = m_creature->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool IsHeroic;
+
uint32 PartingSorrowTimer;
uint32 StormOfGriefTimer;
uint32 ShockOfSorrowTimer;
uint32 PillarOfWoeTimer;
uint32 AchievTimer;
+
void Reset()
{
PartingSorrowTimer = 25000 + rand()%5000;
@@ -55,39 +63,49 @@ struct TRINITY_DLL_DECL boss_maiden_of_griefAI : public ScriptedAI
ShockOfSorrowTimer = 20000+rand()%5000;
PillarOfWoeTimer = 5000 + rand()%10000;
AchievTimer = 0;
+
if (pInstance)
pInstance->SetData(DATA_MAIDEN_OF_GRIEF_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_MAIDEN_OF_GRIEF_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Achievement counter
if (pInstance)
if (pInstance->GetData(DATA_MAIDEN_OF_GRIEF_EVENT) == IN_PROGRESS)
AchievTimer += diff;
+
if(IsHeroic)
{
if (PartingSorrowTimer < diff)
{
Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0);
+
if(target)
DoCast(target,SPELL_PARTING_SORROW);
+
PartingSorrowTimer = 30000 + rand()%10000;
}else PartingSorrowTimer -= diff;
}
+
if (StormOfGriefTimer < diff)
{
DoCast(m_creature->getVictim(),IsHeroic ? SPELL_STORM_OF_GRIEF_H : SPELL_STORM_OF_GRIEF_N, true);
StormOfGriefTimer = 15000 + rand()%5000;
}else StormOfGriefTimer -= diff;
+
if (ShockOfSorrowTimer < diff)
{
DoResetThreat();
@@ -95,28 +113,35 @@ struct TRINITY_DLL_DECL boss_maiden_of_griefAI : public ScriptedAI
DoCast(m_creature,IsHeroic ? SPELL_SHOCK_OF_SORROW_H : SPELL_SHOCK_OF_SORROW_N);
ShockOfSorrowTimer = 20000 + rand()%10000;
}else ShockOfSorrowTimer -= diff;
+
if (PillarOfWoeTimer < diff)
{
Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 1);
+
if(target)
DoCast(target,IsHeroic ? SPELL_PILLAR_OF_WOE_H : SPELL_PILLAR_OF_WOE_N);
else
DoCast(m_creature->getVictim(),IsHeroic ? SPELL_PILLAR_OF_WOE_H : SPELL_PILLAR_OF_WOE_N);
+
PillarOfWoeTimer = 5000 + rand()%20000;
}else PillarOfWoeTimer -= diff;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_MAIDEN_OF_GRIEF_EVENT, DONE);
+
AchievementEntry const *AchievGoodGrief = GetAchievementStore()->LookupEntry(ACHIEVEMENT_GOOD_GRIEF);
Map* pMap = m_creature->GetMap();
+
if (HeroicMode && AchievTimer < 60000 && pMap && pMap->IsDungeon() && AchievGoodGrief)
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievGoodGrief);
}
}
@@ -124,16 +149,20 @@ struct TRINITY_DLL_DECL boss_maiden_of_griefAI : public ScriptedAI
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3,SAY_SLAY_4), m_creature);
}
};
+
CreatureAI* GetAI_boss_maiden_of_grief(Creature* pCreature)
{
return new boss_maiden_of_griefAI (pCreature);
}
+
void AddSC_boss_maiden_of_grief()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_maiden_of_grief";
newscript->GetAI = &GetAI_boss_maiden_of_grief;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp
index c093da581c6..69061ad1d1f 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/boss_sjonnir.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_sjonnir' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_halls_of_stone.h"
+
enum Spells
{
SPELL_LIGHTING_RING = 51849, //Periodic Trigger (interval 2s) spell = 50841
@@ -24,6 +26,7 @@ enum Spells
H_SPELL_LIGHTING_SHIELD = 59845,
SPELL_FRENZY = 28747
};
+
enum Yells
{
SAY_AGGRO = -1603011,
@@ -32,7 +35,9 @@ enum Yells
SAY_SLAY_3 = -1603014,
SAY_DEATH = -1603015
};
+
#define EMOTE_GENERIC_FRENZY -1000002
+
enum Creatures
{
CREATURE_FORGED_IRON_TROGG = 27979,
@@ -40,28 +45,35 @@ enum Creatures
CREATURE_FORGED_IRON_DWARF = 27982,
CREATURE_IRON_SLUDGE = 28165
};
+
enum Misc
{
DATA_TIME_BEFORE_OOZE = 150000, //2min 30 secs
ACHIEVEMENT_ABUSE_THE_OOZE = 2155
};
+
struct Locations
{
float x, y, z;
};
+
static Locations PipeLocations[] =
{
{1295.44, 734.07, 200.3}, //left
{1297.7, 595.6, 199.9} //right
};
+
static Locations CenterPoint = {1295.21, 667.157, 189.691};
+
struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
{
boss_sjonnirAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
bool bIsFrenzy;
+
uint32 uiChainLightningTimer;
uint32 uiLightningShieldTimer;
uint32 uiStaticChargeTimer;
@@ -70,10 +82,13 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
uint32 uiFrenzyTimer;
uint32 uiEncounterTimer;
uint32 uiKilledIronSludges;
+
ScriptedInstance* pInstance;
+
void Reset()
{
bIsFrenzy = false;
+
uiEncounterTimer = 0;
uiChainLightningTimer = 3000 + rand()%5000;
uiLightningShieldTimer = 20000 + rand()%5000;
@@ -82,37 +97,46 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
uiSummonTimer = 5000;
uiFrenzyTimer = 300000; //5 minutes
uiKilledIronSludges = 0;
+
if (pInstance)
pInstance->SetData(DATA_SJONNIR_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
uiEncounterTimer = 0;
+
if (pInstance)
pInstance->SetData(DATA_SJONNIR_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiChainLightningTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, HEROIC(SPELL_CHAIN_LIGHTING, H_SPELL_CHAIN_LIGHTING));
uiChainLightningTimer = 10000 + rand()%5000;
} else uiChainLightningTimer -= diff;
+
if (uiLightningShieldTimer < diff)
{
DoCast(m_creature, HEROIC(SPELL_LIGHTING_SHIELD, H_SPELL_LIGHTING_SHIELD));
uiLightningShieldTimer -= diff;
}
+
if (uiStaticChargeTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_STATIC_CHARGE, H_SPELL_STATIC_CHARGE));
uiStaticChargeTimer = 20000 + rand()%5000;
} uiStaticChargeTimer -= diff;
+
if (uiLightningRingTimer < diff)
{
if (m_creature->IsNonMeleeSpellCasted(false))
@@ -120,6 +144,7 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
DoCast(m_creature, HEROIC(SPELL_LIGHTING_RING, H_SPELL_LIGHTING_RING));
uiLightningRingTimer = 30000 + rand()%5000;
} else uiLightningRingTimer -= diff;
+
if (uiSummonTimer < diff)
{
uint32 uiSummonPipe = rand()%2;
@@ -129,6 +154,7 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000);
uiSummonTimer = 20000;
} else uiSummonTimer -= diff;
+
if (!bIsFrenzy)
{
if (uiFrenzyTimer < diff)
@@ -138,18 +164,23 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
}
else uiFrenzyTimer -= diff;
}
+
uiEncounterTimer +=diff;
+
DoMeleeAttackIfReady();
}
+
void JustSummoned(Creature* summon)
{
summon->GetMotionMaster()->MovePoint(0, CenterPoint.x, CenterPoint.y, CenterPoint.z);
/*if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
summon->AI()->AttackStart(pTarget);*/
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (HeroicMode && uiKilledIronSludges > 4)
{
AchievementEntry const *AchievAbuseTheOoze = GetAchievementStore()->LookupEntry(ACHIEVEMENT_ABUSE_THE_OOZE);
@@ -159,11 +190,12 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievAbuseTheOoze);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_SJONNIR_EVENT, DONE);
}
@@ -173,25 +205,31 @@ struct TRINITY_DLL_DECL boss_sjonnirAI : public ScriptedAI
return;
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void KilledIronSludge()
{
++uiKilledIronSludges;
}
};
+
CreatureAI* GetAI_boss_sjonnir(Creature* pCreature)
{
return new boss_sjonnirAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_malformed_oozeAI : public ScriptedAI
{
mob_malformed_oozeAI(Creature *c) : ScriptedAI(c) {}
+
uint32 uiMergeTimer;
bool bIsMerging;
+
void Reset()
{
uiMergeTimer = 5000;
bIsMerging = false;
}
+
void UpdateAI(const uint32 diff)
{
if (bIsMerging)
@@ -206,10 +244,13 @@ struct TRINITY_DLL_DECL mob_malformed_oozeAI : public ScriptedAI
} else bIsMerging = false;
} else uiMergeTimer -= diff;
}
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void MovementInform(uint32 type, uint32 id)
{
if(type != POINT_MOTION_TYPE)
@@ -217,17 +258,21 @@ struct TRINITY_DLL_DECL mob_malformed_oozeAI : public ScriptedAI
bIsMerging = true;
}
};
+
CreatureAI* GetAI_mob_malformed_ooze(Creature* pCreature)
{
return new mob_malformed_oozeAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_iron_sludgeAI : public ScriptedAI
{
mob_iron_sludgeAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
void JustDied(Unit* pKiller)
{
if (pInstance)
@@ -235,21 +280,26 @@ struct TRINITY_DLL_DECL mob_iron_sludgeAI : public ScriptedAI
CAST_AI(boss_sjonnirAI, pSjonnir->AI())->KilledIronSludge();
}
};
+
CreatureAI* GetAI_mob_iron_sludge(Creature* pCreature)
{
return new mob_iron_sludgeAI(pCreature);
}
+
void AddSC_boss_sjonnir()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_sjonnir";
newscript->GetAI = &GetAI_boss_sjonnir;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_malformed_ooze";
newscript->GetAI = &GetAI_mob_malformed_ooze;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_iron_sludge";
newscript->GetAI = &GetAI_mob_iron_sludge;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp
index 3501bc56e3b..0681b376f4e 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/halls_of_stone.cpp
@@ -1,6 +1,7 @@
#include "precompiled.h"
#include "escort_ai.h"
#include "def_halls_of_stone.h"
+
enum Texts
{
SAY_KILL_1 = -1603016,
@@ -12,26 +13,33 @@ enum Texts
SAY_PLAYER_DEATH_2 = -1603022,
SAY_PLAYER_DEATH_3 = -1603023,
SAY_ESCORT_START = -1603024,
+
SAY_SPAWN_DWARF = -1603025,
SAY_SPAWN_TROGG = -1603026,
SAY_SPAWN_OOZE = -1603027,
SAY_SPAWN_EARTHEN = -1603028,
+
SAY_EVENT_INTRO_1 = -1603029,
SAY_EVENT_INTRO_2 = -1603030,
SAY_EVENT_INTRO_3_ABED = -1603031,
+
SAY_EVENT_A_1 = -1603032,
SAY_EVENT_A_2_KADD = -1603033,
SAY_EVENT_A_3 = -1603034,
+
SAY_EVENT_B_1 = -1603035,
SAY_EVENT_B_2_MARN = -1603036,
SAY_EVENT_B_3 = -1603037,
+
SAY_EVENT_C_1 = -1603038,
SAY_EVENT_C_2_ABED = -1603039,
SAY_EVENT_C_3 = -1603040,
+
SAY_EVENT_D_1 = -1603041,
SAY_EVENT_D_2_ABED = -1603042,
SAY_EVENT_D_3 = -1603043,
SAY_EVENT_D_4_ABED = -1603044,
+
SAY_EVENT_END_01 = -1603045,
SAY_EVENT_END_02 = -1603046,
SAY_EVENT_END_03_ABED = -1603047,
@@ -53,12 +61,16 @@ enum Texts
SAY_EVENT_END_19_MARN = -1603063,
SAY_EVENT_END_20 = -1603064,
SAY_EVENT_END_21_ABED = -1603065,
+
SAY_VICTORY_SJONNIR_1 = -1603066,
SAY_VICTORY_SJONNIR_2 = -1603067,
+
SAY_ENTRANCE_MEET = -1603068,
+
TEXT_ID_START = 13100,
TEXT_ID_PROGRESS = 13101
};
+
enum Creatures
{
CREATURE_TRIBUNAL_OF_THE_AGES = 28234,
@@ -70,6 +82,7 @@ enum Creatures
CREATURE_IRON_GOLEM_CUSTODIAN = 27985,
CREATURE_KADDRAK = 30898
};
+
enum Spells
{
SPELL_STEALTH = 58506,
@@ -83,21 +96,26 @@ enum Spells
SPELL_SEARING_GAZE = 51136,
H_SPELL_SEARING_GAZE = 59867
};
+
enum Quests
{
QUEST_HALLS_OF_STONE = 13207
};
+
#define GOSSIP_ITEM_START "Brann, it would be our honor!"
#define GOSSIP_ITEM_PROGRESS "Let's move Brann, enough of the history lessons!"
+
struct Locations
{
float x, y, z;
};
+
static Locations SpawnLocations[]=
{
{946.992, 397.016, 208.374},
{960.748, 382.944, 208.374},
};
+
struct TRINITY_DLL_DECL mob_tribuna_controllerAI : public ScriptedAI
{
mob_tribuna_controllerAI(Creature *c) : ScriptedAI(c)
@@ -105,31 +123,39 @@ struct TRINITY_DLL_DECL mob_tribuna_controllerAI : public ScriptedAI
pInstance = c->GetInstanceData();
SetCombatMovement(false);
}
+
ScriptedInstance* pInstance;
+
uint32 uiKaddrakEncounterTimer;
uint32 uiMarnakEncounterTimer;
uint32 uiAbedneumEncounterTimer;
+
bool bKaddrakActivated;
bool bMarnakActivated;
bool bAbedneumActivated;
+
std::list<Creature*> lKaddrakGUIDList;
+
void Reset()
{
uiKaddrakEncounterTimer = 1500;
uiMarnakEncounterTimer = 10000;
uiAbedneumEncounterTimer = 10000;
+
bKaddrakActivated = false;
bMarnakActivated = false;
bAbedneumActivated = false;
+
lKaddrakGUIDList.clear();
}
+
void UpdateFacesList()
{
/*GetCreatureListWithEntryInGrid(lKaddrakGUIDList, m_creature, CREATURE_KADDRAK, 50.0f);
if (!lKaddrakGUIDList.empty())
{
uint32 uiPositionCounter = 0;
- for (std::list<Creature*>::iterator itr = lKaddrakGUIDList.begin(); itr != lKaddrakGUIDList.end(); ++itr)
+ for(std::list<Creature*>::iterator itr = lKaddrakGUIDList.begin(); itr != lKaddrakGUIDList.end(); ++itr)
{
if ((*itr)->isAlive())
{
@@ -148,6 +174,7 @@ struct TRINITY_DLL_DECL mob_tribuna_controllerAI : public ScriptedAI
}
}*/
}
+
void UpdateAI(const uint32 diff)
{
if(bKaddrakActivated)
@@ -196,19 +223,25 @@ struct TRINITY_DLL_DECL mob_tribuna_controllerAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
{
npc_brann_hosAI(Creature *c) : npc_escortAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiStep;
uint32 uiPhaseTimer;
+
uint64 uiControllerGUID;
std::list<uint64> lDwarfGUIDList;
+
ScriptedInstance* pInstance;
+
bool bIsBattle;
bool bIsLowHP;
+
void Reset()
{
if (!HasEscortState(STATE_ESCORT_ESCORTING))
@@ -218,11 +251,14 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
uiStep = 0;
uiPhaseTimer = 0;
uiControllerGUID = 0;
+
DespawnDwarf();
+
if (pInstance)
pInstance->SetData(DATA_BRANN_EVENT, NOT_STARTED);
}
}
+
void DespawnDwarf()
{
if (lDwarfGUIDList.empty())
@@ -235,6 +271,7 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
}
lDwarfGUIDList.clear();
}
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -267,6 +304,7 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
break;
}
}
+
void SpawnDwarf(uint32 uiType)
{
switch(uiType)
@@ -288,17 +326,20 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
break;
}
}
+
void JustSummoned(Creature* pSummoned)
{
lDwarfGUIDList.push_back(pSummoned->GetGUID());
pSummoned->AddThreat(m_creature, 0.0f);
pSummoned->AI()->AttackStart(m_creature);
}
+
void JumpToNextStep(uint32 uiTimer)
{
uiPhaseTimer = uiTimer;
uiStep++;
}
+
void StartWP()
{
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
@@ -306,6 +347,7 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
uiStep = 1;
Start();
}
+
void UpdateEscortAI(const uint32 uiDiff)
{
if (uiPhaseTimer < uiDiff)
@@ -584,6 +626,7 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
break;
}
} else uiPhaseTimer -= uiDiff;
+
if (!bIsLowHP && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= 30)
{
DoScriptText(SAY_LOW_HEALTH, m_creature);
@@ -592,14 +635,18 @@ struct TRINITY_DLL_DECL npc_brann_hosAI : public npc_escortAI
bIsLowHP = false;
}
};
+
bool GossipHello_npc_brann_hos(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_START, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(TEXT_ID_START, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_brann_hos(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1 || uiAction == GOSSIP_ACTION_INFO_DEF+2)
@@ -607,25 +654,31 @@ bool GossipSelect_npc_brann_hos(Player* pPlayer, Creature* pCreature, uint32 uiS
pPlayer->CLOSE_GOSSIP_MENU();
((npc_brann_hosAI*)pCreature->AI())->StartWP();
}
+
return true;
}
+
CreatureAI* GetAI_mob_tribuna_controller(Creature* pCreature)
{
return new mob_tribuna_controllerAI(pCreature);
}
+
CreatureAI* GetAI_npc_brann_hos(Creature* pCreature)
{
return new npc_brann_hosAI(pCreature);
}
+
void AddSC_halls_of_stone()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_brann_hos";
newscript->GetAI = &GetAI_npc_brann_hos;
newscript->pGossipHello = &GossipHello_npc_brann_hos;
newscript->pGossipSelect = &GossipSelect_npc_brann_hos;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_tribuna_controller";
newscript->GetAI = &GetAI_mob_tribuna_controller;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp
index 8e7bf896803..85d9367ae1d 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/halls_of_stone/instance_halls_of_stone.cpp
@@ -1,22 +1,28 @@
#include "precompiled.h"
#include "def_halls_of_stone.h"
+
#define MAX_ENCOUNTER 4
+
/* Halls of Stone encounters:
0- Krystallus
1- Maiden of Grief
2- Escort Event
3- Sjonnir The Ironshaper
*/
+
struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
{
instance_halls_of_stone(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 uiMaidenOfGrief;
uint64 uiKrystallus;
uint64 uiSjonnir;
+
uint64 uiKaddrak;
uint64 uiAbedneum;
uint64 uiMarnak;
uint64 uiBrann;
+
uint64 uiMaidenOfGriefDoor;
uint64 uiSjonnirDoor;
uint64 uiBrannDoor;
@@ -26,17 +32,22 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
uint64 uiKaddrakGo;
uint64 uiAbedneumGo;
uint64 uiMarnakGo;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
std::string str_data;
+
void Initialize()
{
uiMaidenOfGrief = 0;
uiKrystallus = 0;
uiSjonnir = 0;
+
uiKaddrak = 0;
uiMarnak = 0;
uiAbedneum = 0;
uiBrann = 0;
+
uiMaidenOfGriefDoor = 0;
uiSjonnirDoor = 0;
uiBrannDoor = 0;
@@ -46,9 +57,11 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
uiTribunalConsole = 0;
uiTribunalChest = 0;
uiTribunalSkyFloor = 0;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
m_auiEncounter[i] = NOT_STARTED;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -62,6 +75,7 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
case 28070: uiBrann = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -107,6 +121,7 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -130,9 +145,11 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
HandleGameObject(uiSjonnirDoor,true);
break;
}
+
if (data == DONE)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -142,8 +159,10 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
case DATA_SJONNIR_EVENT: return m_auiEncounter[2];
case DATA_BRANN_EVENT: return m_auiEncounter[3];
}
+
return 0;
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -160,17 +179,23 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
case DATA_GO_MARNAK: return uiMarnakGo;
case DATA_GO_SKY_FLOOR: return uiTribunalSkyFloor;
}
+
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << "H S " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2] << " " << m_auiEncounter[3];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -178,28 +203,37 @@ struct TRINITY_DLL_DECL instance_halls_of_stone : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0, data1, data2, data3;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3;
+
if (dataHead1 == 'H' && dataHead2 == 'S')
{
m_auiEncounter[0] = data0;
m_auiEncounter[1] = data1;
m_auiEncounter[2] = data2;
m_auiEncounter[3] = data3;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
} else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_halls_of_stone(Map* pMap)
{
return new instance_halls_of_stone(pMap);
}
+
void AddSC_instance_halls_of_stone()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_algalon.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_algalon.cpp
index b28c71f645d..a736b2db12a 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_algalon.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_algalon.cpp
@@ -15,5 +15,6 @@
* 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"
#include "def_ulduar.h"
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp
index 9f529b04ebb..df03ff9eff7 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_assembly_of_iron.cpp
@@ -15,17 +15,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
SDName: Assembly of Iron encounter
SD%Complete: 60%
SDComment: Runes need DB support, chain lightning won't cast, supercharge won't cast (target error?) - it worked before during debugging.
SDCategory: Ulduar - Ulduar
EndScriptData */
+
#include "precompiled.h"
#include "def_ulduar.h"
+
// Any boss
#define SPELL_SUPERCHARGE 61920
#define SPELL_BERSERK 47008 // Hard enrage, don't know the correct ID.
+
// Steelbreaker
#define SPELL_HIGH_VOLTAGE 61890
#define SPELL_HIGH_VOLTAGE_H 63498
@@ -36,6 +40,7 @@ EndScriptData */
#define SPELL_OVERWHELMING_POWER_H 61888
#define SPELL_OVERWHELMING_POWER 64637
#define SPELL_ELECTRICAL_CHARGE 61902
+
// Runemaster Molgeim
#define SPELL_SHIELD_OF_RUNES 62274
#define SPELL_SHIELD_OF_RUNES_H 63489
@@ -45,6 +50,7 @@ EndScriptData */
#define SPELL_LIGHTNING_BLAST 62054
#define SPELL_LIGHTNING_BLAST_H 63491
#define CREATURE_RUNE_OF_SUMMONING 33051
+
// Stormcaller Brundir
#define SPELL_CHAIN_LIGHTNING_N 61879
#define SPELL_CHAIN_LIGHTNING_H 63479
@@ -56,6 +62,8 @@ EndScriptData */
#define SPELL_LIGHTNING_TENDRILS_H 63486
#define SPELL_STORMSHIELD 64187
+
+
enum eEnums
{
EVENT_ENRAGE,
@@ -76,21 +84,27 @@ enum eEnums
EVENT_LIGHTNING_TENDRILS,
EVENT_STORMSHIELD,
MAX_EVENT
+
};
+
+
bool IsEncounterComplete(ScriptedInstance* pInstance, Creature* m_creature)
{
if (!pInstance || !m_creature)
return false;
- for (uint8 i = 0; i < 3; ++i)
+
+ for(uint8 i = 0; i < 3; ++i)
{
uint64 guid = pInstance->GetData64(DATA_STEELBREAKER+i);
if(!guid)
return false;
+
if(Creature *boss = (Unit::GetCreature((*m_creature), guid)))
{
if(boss->isAlive())
return false;
+
continue;
}
else
@@ -98,12 +112,14 @@ bool IsEncounterComplete(ScriptedInstance* pInstance, Creature* m_creature)
}
return true;
}
+
struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
{
boss_steelbreakerAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
void Reset()
{
events.Reset();
@@ -112,9 +128,11 @@ struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
if(pInstance)
pInstance->SetData(TYPE_ASSEMBLY, NOT_STARTED);
}
+
EventMap events;
ScriptedInstance* pInstance;
uint32 phase;
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
@@ -122,6 +140,7 @@ struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
events.ScheduleEvent(EVENT_ENRAGE, 900000);
UpdatePhase();
}
+
void UpdatePhase()
{
++phase;
@@ -132,6 +151,7 @@ struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
if(phase >= 3)
events.RescheduleEvent(EVENT_OVERWHELMING_POWER, rand()%5000);
}
+
void DamageTaken(Unit* pKiller, uint32 &damage)
{
if(damage >= m_creature->GetHealth())
@@ -141,6 +161,7 @@ struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
{
Brundir->SetHealth(Brundir->GetMaxHealth());
}
+
if(Creature* Molgeim = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_RUNEMASTER_MOLGEIM) : 0))
if(Molgeim->isAlive())
{
@@ -149,26 +170,32 @@ struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
DoCast(SPELL_SUPERCHARGE);
}
}
+
void JustDied(Unit* Killer)
{
if(IsEncounterComplete(pInstance, m_creature) && pInstance)
pInstance->SetData(TYPE_ASSEMBLY, DONE);
}
+
void KilledUnit(Unit *who)
{
if(phase == 3)
DoCast(m_creature, SPELL_ELECTRICAL_CHARGE);
}
+
void SpellHit(Unit *from, const SpellEntry *spell)
{
if(spell->Id == SPELL_SUPERCHARGE)
UpdatePhase();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -193,15 +220,18 @@ struct TRINITY_DLL_DECL boss_steelbreakerAI : public ScriptedAI
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_runemaster_molgeimAI : public ScriptedAI
{
boss_runemaster_molgeimAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
void Reset()
{
if(pInstance)
@@ -210,15 +240,18 @@ struct TRINITY_DLL_DECL boss_runemaster_molgeimAI : public ScriptedAI
m_creature->RemoveAllAuras();
phase = 0;
}
+
ScriptedInstance* pInstance;
EventMap events;
uint32 phase;
+
void EnterCombat(Unit* who)
{
DoZoneInCombat();
events.ScheduleEvent(EVENT_ENRAGE, 900000);
UpdatePhase();
}
+
void UpdatePhase()
{
++phase;
@@ -230,6 +263,7 @@ struct TRINITY_DLL_DECL boss_runemaster_molgeimAI : public ScriptedAI
if(phase >= 3)
events.RescheduleEvent(EVENT_RUNE_OF_SUMMONING, 20000+(rand()%10)*1000);
}
+
void DamageTaken(Unit* pKiller, uint32 &damage)
{
if(damage >= m_creature->GetHealth())
@@ -239,6 +273,7 @@ struct TRINITY_DLL_DECL boss_runemaster_molgeimAI : public ScriptedAI
{
Steelbreaker->SetHealth(Steelbreaker->GetMaxHealth());
}
+
if(Creature* Brundir = Unit::GetCreature((*m_creature), pInstance ? pInstance->GetData64(DATA_STORMCALLER_BRUNDIR) : 0))
if(Brundir->isAlive())
{
@@ -247,21 +282,26 @@ struct TRINITY_DLL_DECL boss_runemaster_molgeimAI : public ScriptedAI
DoCast(m_creature, SPELL_SUPERCHARGE);
}
}
+
void JustDied(Unit* Killer)
{
if(IsEncounterComplete(pInstance, m_creature) && pInstance)
pInstance->SetData(TYPE_ASSEMBLY, DONE);
}
+
void SpellHit(Unit *from, const SpellEntry *spell)
{
if(spell->Id == SPELL_SUPERCHARGE)
UpdatePhase();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -298,52 +338,64 @@ struct TRINITY_DLL_DECL boss_runemaster_molgeimAI : public ScriptedAI
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_lightning_elementalAI : public ScriptedAI
{
mob_lightning_elementalAI(Creature *c) : ScriptedAI(c)
{
Charge();
}
+
Unit* Target;
+
void Charge()
{
Target = m_creature->SelectNearestTarget();
m_creature->AddThreat(Target, 5000000.0f);
AttackStart(Target);
}
+
void UpdateAI(const uint32 diff)
{
if(!m_creature->isInCombat())
return;
+
if(m_creature->IsWithinMeleeRange(Target))
{
DoCast(Target, HEROIC(SPELL_LIGHTNING_BLAST, SPELL_LIGHTNING_BLAST_H));
m_creature->Kill(m_creature); // hack until spell works
}
+
m_creature->GetMotionMaster()->MoveChase(Target); // needed at every update?
}
+
};
+
struct TRINITY_DLL_DECL mob_rune_of_summoningAI : public ScriptedAI
{
mob_rune_of_summoningAI(Creature *c) : ScriptedAI(c)
{
SummonLightningElemental();
}
+
void SummonLightningElemental()
{
m_creature->SummonCreature(CREATURE_RUNE_OF_SUMMONING, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), 0, TEMPSUMMON_CORPSE_DESPAWN );
m_creature->DealDamage(m_creature, m_creature->GetHealth());
}
};
+
struct TRINITY_DLL_DECL boss_stormcaller_brundirAI : public ScriptedAI
{
boss_stormcaller_brundirAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
void Reset()
{
if(pInstance)
@@ -352,15 +404,18 @@ struct TRINITY_DLL_DECL boss_stormcaller_brundirAI : public ScriptedAI
events.Reset();
phase = 0;
}
+
EventMap events;
ScriptedInstance* pInstance;
uint32 phase;
+
void EnterCombat(Unit* who)
{
DoZoneInCombat();
events.ScheduleEvent(EVENT_ENRAGE, 900000);
UpdatePhase();
}
+
void UpdatePhase()
{
++phase;
@@ -374,7 +429,9 @@ struct TRINITY_DLL_DECL boss_stormcaller_brundirAI : public ScriptedAI
DoCast(m_creature, SPELL_STORMSHIELD);
events.RescheduleEvent(EVENT_LIGHTNING_TENDRILS, 40000+ (rand()%40)*1000);
}
+
}
+
void DamageTaken(Unit* pKiller, uint32 &damage)
{
if(damage >= m_creature->GetHealth())
@@ -384,19 +441,23 @@ struct TRINITY_DLL_DECL boss_stormcaller_brundirAI : public ScriptedAI
{
Steelbreaker->SetHealth(Steelbreaker->GetMaxHealth());
}
+
if(Creature* Molgeim = Unit::GetCreature(*m_creature, pInstance ? pInstance->GetData64(DATA_RUNEMASTER_MOLGEIM) : 0))
if(Molgeim->isAlive())
{
Molgeim->SetHealth(Molgeim->GetMaxHealth());
}
+
DoCast(SPELL_SUPERCHARGE);
}
}
+
void JustDied(Unit* Killer)
{
if(IsEncounterComplete(pInstance, m_creature) && pInstance)
pInstance->SetData(TYPE_ASSEMBLY, DONE);
}
+
void SpellHit(Unit *from, const SpellEntry *spell)
{
if(spell->Id == SPELL_SUPERCHARGE)
@@ -404,11 +465,14 @@ struct TRINITY_DLL_DECL boss_stormcaller_brundirAI : public ScriptedAI
UpdatePhase();
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -438,50 +502,63 @@ struct TRINITY_DLL_DECL boss_stormcaller_brundirAI : public ScriptedAI
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_steelbreaker(Creature* pCreature)
{
return new boss_steelbreakerAI (pCreature);
}
+
CreatureAI* GetAI_boss_runemaster_molgeim(Creature* pCreature)
{
return new boss_runemaster_molgeimAI (pCreature);
}
+
CreatureAI* GetAI_boss_stormcaller_brundir(Creature* pCreature)
{
return new boss_stormcaller_brundirAI (pCreature);
}
+
CreatureAI* GetAI_mob_lightning_elemental(Creature* pCreature)
{
return new mob_lightning_elementalAI (pCreature);
}
+
CreatureAI* GetAI_mob_rune_of_summoning(Creature* pCreature)
{
return new mob_rune_of_summoningAI (pCreature);
}
+
void AddSC_boss_assembly_of_iron()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_steelbreaker";
newscript->GetAI = &GetAI_boss_steelbreaker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_runemaster_molgeim";
newscript->GetAI = &GetAI_boss_runemaster_molgeim;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_stormcaller_brundir";
newscript->GetAI = &GetAI_boss_stormcaller_brundir;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_lightning_elemental";
newscript->GetAI = &GetAI_mob_lightning_elemental;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_rune_of_summoning";
newscript->GetAI = &GetAI_mob_rune_of_summoning;
newscript->RegisterSelf();
-}
+
+} \ No newline at end of file
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp
index 8199a914e2f..e4dedac54ac 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_auriaya.cpp
@@ -15,8 +15,10 @@
* 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"
#include "def_ulduar.h"
+
//boss_auriaya
#define SPELL_TERRIFYING_SCREECH 64386
#define SPELL_SONIC_BOOM 38897
@@ -24,20 +26,25 @@
//beetwen -2000000 and -2999999 are custom texts so wtf?
#define SAY_AGGRO -2615016
#define SAY_SLAY_1 -2615017
+
struct TRINITY_DLL_DECL boss_auriaya_AI : public BossAI
{
boss_auriaya_AI(Creature *pCreature) : BossAI(pCreature, TYPE_AURIAYA)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 TERRIFYING_SCREECH_Timer;
uint32 SONIC_BOOM_Timer;
+
void Reset()
{
TERRIFYING_SCREECH_Timer = 0;
SONIC_BOOM_Timer = 2000;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO,m_creature);
@@ -46,31 +53,39 @@ struct TRINITY_DLL_DECL boss_auriaya_AI : public BossAI
{
DoScriptText(SAY_SLAY_1, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_SLAY_1, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_AURIAYA, DONE);
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (TERRIFYING_SCREECH_Timer < diff)
{
DoCast(m_creature,SPELL_TERRIFYING_SCREECH);
DoScriptText(SAY_SLAY_1, m_creature);
TERRIFYING_SCREECH_Timer = 360000;
} else TERRIFYING_SCREECH_Timer -= diff;
+
if (SONIC_BOOM_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SONIC_BOOM);
SONIC_BOOM_Timer = 20000;
} else SONIC_BOOM_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_auriaya(Creature* pCreature)
{
return new boss_auriaya_AI (pCreature);
@@ -78,6 +93,7 @@ CreatureAI* GetAI_boss_auriaya(Creature* pCreature)
void AddSC_boss_auriaya()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_auriaya";
newscript->GetAI = &GetAI_boss_auriaya;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp
index e16045c1877..1aa70290b69 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_flame_leviathan.cpp
@@ -15,31 +15,43 @@
* 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"
#include "def_ulduar.h"
#include "Vehicle.h"
+
#define SPELL_PURSUED 62374
#define SPELL_GATHERING_SPEED 62375
#define SPELL_BATTERING_RAM 62376
#define SPELL_FLAME_VENTS 62396
#define SPELL_MISSILE_BARRAGE 62400
#define SPELL_SYSTEMS_SHUTDOWN 62475
+
#define SPELL_FLAME_CANNON 62395
//#define SPELL_FLAME_CANNON 64692 trigger the same spell
+
#define SPELL_OVERLOAD_CIRCUIT 62399
+
#define SPELL_SEARING_FLAME 62402
+
#define SPELL_BLAZE 62292
+
#define SPELL_SMOKE_TRAIL 63575
+
#define SPELL_MIMIRON_INFERNO 62910
+
#define SPELL_HODIR_FURY 62297
+
#define SPELL_ELECTROSHOCK 62522
+
enum Mobs
{
MOB_MECHANOLIFT = 33214,
MOB_LIQUID = 33189,
MOB_CONTAINER = 33218,
};
+
enum Events
{
EVENT_PURSUE = 1,
@@ -50,12 +62,14 @@ enum Events
EVENT_MIMIRON_INFERNO, // Not Blizzlike
EVENT_HODIR_FURY, // Not Blizzlike
};
+
enum Seats
{
SEAT_PLAYER = 0,
SEAT_TURRET = 1,
SEAT_DEVICE = 2,
};
+
struct TRINITY_DLL_DECL boss_flame_leviathanAI : public BossAI
{
boss_flame_leviathanAI(Creature *pCreature) : BossAI(pCreature, TYPE_LEVIATHAN), vehicle(me->GetVehicleKit())
@@ -63,13 +77,17 @@ struct TRINITY_DLL_DECL boss_flame_leviathanAI : public BossAI
m_pInstance = pCreature->GetInstanceData();
assert(vehicle);
}
+
ScriptedInstance* m_pInstance;
+
Vehicle *vehicle;
+
void Reset()
{
_Reset();
me->SetReactState(REACT_AGGRESSIVE);
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -84,17 +102,20 @@ struct TRINITY_DLL_DECL boss_flame_leviathanAI : public BossAI
if (Creature *turret = CAST_CRE(vehicle->GetPassenger(7)))
turret->AI()->DoZoneInCombat();
}
+
// TODO: effect 0 and effect 1 may be on different target
void SpellHitTarget(Unit *target, const SpellEntry *spell)
{
if (spell->Id == SPELL_PURSUED)
AttackStart(target);
}
+
void JustDied(Unit *victim)
{
if (m_pInstance)
m_pInstance->SetData(TYPE_LEVIATHAN, DONE);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if(spell->Id == 62472)
@@ -102,21 +123,27 @@ struct TRINITY_DLL_DECL boss_flame_leviathanAI : public BossAI
else if(spell->Id == SPELL_ELECTROSHOCK)
me->InterruptSpell(CURRENT_CHANNELED_SPELL);
}
+
void UpdateAI(const uint32 diff)
{
if (!me->isInCombat())
return;
+
if (me->getThreatManager().isThreatListEmpty())
{
EnterEvadeMode();
return;
}
+
events.Update(diff);
+
if (me->hasUnitState(UNIT_STAT_CASTING))
return;
+
uint32 eventId = events.GetEvent();
if (!me->getVictim())
eventId = EVENT_PURSUE;
+
switch(eventId)
{
case 0: break; // this is a must
@@ -158,10 +185,13 @@ struct TRINITY_DLL_DECL boss_flame_leviathanAI : public BossAI
events.PopEvent();
break;
}
+
DoSpellAttackIfReady(SPELL_BATTERING_RAM);
}
};
+
//#define BOSS_DEBUG
+
struct TRINITY_DLL_DECL boss_flame_leviathan_seatAI : public PassiveAI
{
boss_flame_leviathan_seatAI(Creature *c) : PassiveAI(c), vehicle(c->GetVehicleKit())
@@ -171,7 +201,9 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_seatAI : public PassiveAI
me->SetReactState(REACT_AGGRESSIVE);
#endif
}
+
Vehicle *vehicle;
+
#ifdef BOSS_DEBUG
void MoveInLineOfSight(Unit *who)
{
@@ -180,14 +212,17 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_seatAI : public PassiveAI
who->EnterVehicle(vehicle, SEAT_PLAYER);
}
#endif
+
void PassengerBoarded(Unit *who, int8 seatId, bool apply)
{
if(!me->GetVehicle())
return;
+
if(seatId == SEAT_PLAYER)
{
if(!apply)
return;
+
if(Creature *turret = CAST_CRE(vehicle->GetPassenger(SEAT_TURRET)))
{
turret->setFaction(me->GetVehicleBase()->getFaction());
@@ -204,6 +239,7 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_seatAI : public PassiveAI
{
if(apply)
return;
+
if(Unit *device = vehicle->GetPassenger(SEAT_DEVICE))
{
device->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
@@ -212,14 +248,17 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_seatAI : public PassiveAI
}
}
};
+
struct TRINITY_DLL_DECL boss_flame_leviathan_defense_turretAI : public TurretAI
{
boss_flame_leviathan_defense_turretAI(Creature *c) : TurretAI(c) {}
+
void DamageTaken(Unit *who, uint32 &damage)
{
if(!CanAIAttack(who))
damage = 0;
}
+
bool CanAIAttack(const Unit *who) const
{
if (who->GetTypeId() != TYPEID_PLAYER || !who->GetVehicle() || who->GetVehicleBase()->GetEntry() != 33114)
@@ -227,9 +266,11 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_defense_turretAI : public TurretAI
return true;
}
};
+
struct TRINITY_DLL_DECL boss_flame_leviathan_overload_deviceAI : public PassiveAI
{
boss_flame_leviathan_overload_deviceAI(Creature *c) : PassiveAI(c) {}
+
void DoAction(const int32 param)
{
if(param == EVENT_SPELLCLICK)
@@ -249,9 +290,11 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_overload_deviceAI : public PassiveA
}
}
};
+
struct TRINITY_DLL_DECL boss_flame_leviathan_safety_containerAI : public PassiveAI
{
boss_flame_leviathan_safety_containerAI(Creature *c) : PassiveAI(c) {}
+
void MovementInform(uint32 type, uint32 id)
{
if(id == me->GetEntry())
@@ -261,52 +304,63 @@ struct TRINITY_DLL_DECL boss_flame_leviathan_safety_containerAI : public Passive
me->DisappearAndDie(); // this will relocate creature to sky
}
}
+
void UpdateAI(const uint32 diff)
{
if(!me->GetVehicle() && me->isSummon() && me->GetMotionMaster()->GetCurrentMovementGeneratorType() != POINT_MOTION_TYPE)
me->GetMotionMaster()->MoveFall(409.8f, me->GetEntry());
}
};
+
struct TRINITY_DLL_DECL spell_pool_of_tarAI : public TriggerAI
{
spell_pool_of_tarAI(Creature *c) : TriggerAI(c)
{
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void DamageTaken(Unit *who, uint32 &damage)
{
damage = 0;
}
+
void SpellHit(Unit* caster, const SpellEntry *spell)
{
if(spell->SchoolMask & SPELL_SCHOOL_MASK_FIRE && !me->HasAura(SPELL_BLAZE))
me->CastSpell(me, SPELL_BLAZE, true);
}
};
+
CreatureAI* GetAI_boss_flame_leviathan(Creature* pCreature)
{
return new boss_flame_leviathanAI (pCreature);
}
+
CreatureAI* GetAI_boss_flame_leviathan_seat(Creature* pCreature)
{
return new boss_flame_leviathan_seatAI (pCreature);
}
+
CreatureAI* GetAI_boss_flame_leviathan_defense_turret(Creature* pCreature)
{
return new boss_flame_leviathan_defense_turretAI (pCreature);
}
+
CreatureAI* GetAI_boss_flame_leviathan_overload_device(Creature* pCreature)
{
return new boss_flame_leviathan_overload_deviceAI (pCreature);
}
+
CreatureAI* GetAI_boss_flame_leviathan_safety_containerAI(Creature* pCreature)
{
return new boss_flame_leviathan_safety_containerAI(pCreature);
}
+
CreatureAI* GetAI_spell_pool_of_tar(Creature* pCreature)
{
return new spell_pool_of_tarAI (pCreature);
}
+
void AddSC_boss_flame_leviathan()
{
Script *newscript;
@@ -314,22 +368,27 @@ void AddSC_boss_flame_leviathan()
newscript->Name = "boss_flame_leviathan";
newscript->GetAI = &GetAI_boss_flame_leviathan;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_flame_leviathan_seat";
newscript->GetAI = &GetAI_boss_flame_leviathan_seat;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_flame_leviathan_defense_turret";
newscript->GetAI = &GetAI_boss_flame_leviathan_defense_turret;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_flame_leviathan_overload_device";
newscript->GetAI = &GetAI_boss_flame_leviathan_overload_device;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_flame_leviathan_safety_container";
newscript->GetAI = &GetAI_boss_flame_leviathan_safety_containerAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "spell_pool_of_tar";
newscript->GetAI = &GetAI_spell_pool_of_tar;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_freya.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_freya.cpp
index 447a26ae7b3..ac0b23c1b7a 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_freya.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_freya.cpp
@@ -15,12 +15,15 @@
* 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"
#include "def_ulduar.h"
+
/*
#define SAY_AGGRO -1
#define SAY_SLAY -1
*/
+
struct TRINITY_DLL_DECL boss_freyaAI : public BossAI
{
boss_freyaAI(Creature* pCreature) : BossAI(pCreature, TYPE_FREYA)
@@ -28,39 +31,52 @@ struct TRINITY_DLL_DECL boss_freyaAI : public BossAI
m_pInstance = pCreature->GetInstanceData();
Reset();
}
+
ScriptedInstance* m_pInstance;
+
void Reset()
{
}
+
void KilledUnit(Unit *victim)
{
}
+
void JustDied(Unit *victim)
{
if (m_pInstance)
m_pInstance->SetData(TYPE_FREYA, DONE);
}
+
void Aggro(Unit* pWho)
{
// DoScriptText(SAY_AGGRO, m_creature);
m_creature->SetInCombatWithZone();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_FREYA, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
//SPELLS TODO:
+
//
DoMeleeAttackIfReady();
+
EnterEvadeIfOutOfCombatArea(diff);
+
}
+
};
+
CreatureAI* GetAI_boss_freya(Creature* pCreature)
{
return new boss_freyaAI(pCreature);
}
+
void AddSC_boss_freya()
{
Script *newscript;
@@ -68,4 +84,5 @@ void AddSC_boss_freya()
newscript->Name = "boss_freya";
newscript->GetAI = &GetAI_boss_freya;
newscript->RegisterSelf();
+
}
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp
index b28c71f645d..a736b2db12a 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_general_vezax.cpp
@@ -15,5 +15,6 @@
* 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"
#include "def_ulduar.h"
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_hodir.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_hodir.cpp
index 1b84b927184..c05d30d83ba 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_hodir.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_hodir.cpp
@@ -15,12 +15,15 @@
* 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"
#include "def_ulduar.h"
+
/*
#define SAY_AGGRO -1
#define SAY_SLAY -1
*/
+
struct TRINITY_DLL_DECL boss_hodirAI : public BossAI
{
boss_hodirAI(Creature *pCreature) : BossAI(pCreature, TYPE_HODIR)
@@ -28,39 +31,52 @@ struct TRINITY_DLL_DECL boss_hodirAI : public BossAI
m_pInstance = pCreature->GetInstanceData();
Reset();
}
+
ScriptedInstance* m_pInstance;
+
void Reset()
{
}
+
void KilledUnit(Unit *victim)
{
}
+
void JustDied(Unit *victim)
{
if (m_pInstance)
m_pInstance->SetData(TYPE_HODIR, DONE);
}
+
void Aggro(Unit* pWho)
{
// DoScriptText(SAY_AGGRO, m_creature);
m_creature->SetInCombatWithZone();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_HODIR, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
//SPELLS TODO:
+
//
DoMeleeAttackIfReady();
+
EnterEvadeIfOutOfCombatArea(diff);
+
}
+
};
+
CreatureAI* GetAI_boss_hodir(Creature* pCreature)
{
return new boss_hodirAI(pCreature);
}
+
void AddSC_boss_hodir()
{
Script *newscript;
@@ -68,5 +84,6 @@ void AddSC_boss_hodir()
newscript->Name = "boss_hodir";
newscript->GetAI = &GetAI_boss_hodir;
newscript->RegisterSelf();
+
}
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_ignis.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_ignis.cpp
index e4697b60412..8dfda95a987 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_ignis.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_ignis.cpp
@@ -15,26 +15,33 @@
* 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"
#include "def_ulduar.h"
+
#define SPELL_FLAME_JETS 62680
#define SPELL_SCORCH 62546
#define SPELL_SLAG_POT 62717
+
//wrong ids. not in db
#define SAY_AGGRO -10000002
#define SAY_SLAY -1000003
+
struct TRINITY_DLL_DECL boss_ignis_AI : public BossAI
{
boss_ignis_AI(Creature *pCreature) : BossAI(pCreature, TYPE_IGNIS) {}
+
uint32 FLAME_JETS_Timer;
uint32 SCORCH_Timer;
uint32 SLAG_POT_Timer;
+
void Reset()
{
FLAME_JETS_Timer = 32000;
SCORCH_Timer = 100;
SLAG_POT_Timer = 100;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO,m_creature);
@@ -43,15 +50,19 @@ struct TRINITY_DLL_DECL boss_ignis_AI : public BossAI
{
DoScriptText(SAY_SLAY, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_SLAY, m_creature);
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(m_creature->GetPositionY() < 150 || m_creature->GetPositionX() < 450) // Not Blizzlike, anti-exploit to prevent players from pulling bosses to vehicles.
{
m_creature->RemoveAllAuras();
@@ -59,17 +70,20 @@ struct TRINITY_DLL_DECL boss_ignis_AI : public BossAI
m_creature->CombatStop(false);
m_creature->GetMotionMaster()->MoveTargetedHome();
}
+
if (FLAME_JETS_Timer < diff)
{
DoCast(m_creature,SPELL_FLAME_JETS);
DoScriptText(SAY_SLAY, m_creature);
FLAME_JETS_Timer = 25000;
} else FLAME_JETS_Timer -= diff;
+
if (SCORCH_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SCORCH);
SCORCH_Timer = 20000;
} else SCORCH_Timer -= diff;
+
if (SLAG_POT_Timer < diff)
{
DoCast(m_creature,SPELL_SLAG_POT);
@@ -77,9 +91,11 @@ struct TRINITY_DLL_DECL boss_ignis_AI : public BossAI
SLAG_POT_Timer = 30000;
} else SLAG_POT_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_ignis(Creature* pCreature)
{
return new boss_ignis_AI (pCreature);
@@ -87,6 +103,7 @@ CreatureAI* GetAI_boss_ignis(Creature* pCreature)
void AddSC_boss_ignis()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ignis";
newscript->GetAI = &GetAI_boss_ignis;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp
index 6e47e787ab1..b2dfbc08263 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_kologarn.cpp
@@ -15,16 +15,20 @@
* 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"
#include "def_ulduar.h"
#include "Vehicle.h"
+
#define SPELL_ARM_DEAD_DAMAGE HEROIC(63629,63979)
#define SPELL_TWO_ARM_SMASH HEROIC(63356,64003)
#define SPELL_ONE_ARM_SMASH HEROIC(63573,64006)
#define SPELL_STONE_SHOUT HEROIC(63716,64005)
#define SPELL_PETRIFY_BREATH HEROIC(62030,63980)
+
#define SPELL_STONE_GRIP HEROIC(62166,63981)
#define SPELL_ARM_SWEEP HEROIC(63766,63983)
+
enum Events
{
EVENT_NONE = 0,
@@ -32,6 +36,7 @@ enum Events
EVENT_GRIP,
EVENT_SWEEP,
};
+
struct TRINITY_DLL_DECL boss_kologarnAI : public BossAI
{
boss_kologarnAI(Creature *pCreature) : BossAI(pCreature, TYPE_KOLOGARN), vehicle(me->GetVehicleKit()),
@@ -42,18 +47,23 @@ struct TRINITY_DLL_DECL boss_kologarnAI : public BossAI
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED); // i think this is a hack, but there is no other way to disable his rotation
}
+
ScriptedInstance* m_pInstance;
+
Vehicle *vehicle;
bool left, right;
+
void AttackStart(Unit *who)
{
me->Attack(who, true);
}
+
void JustDied(Unit *victim)
{
if (m_pInstance)
m_pInstance->SetData(TYPE_KOLOGARN, DONE);
}
+
void PassengerBoarded(Unit *who, int8 seatId, bool apply)
{
if(who->GetTypeId() == TYPEID_UNIT)
@@ -66,6 +76,7 @@ struct TRINITY_DLL_DECL boss_kologarnAI : public BossAI
CAST_CRE(who)->SetReactState(REACT_PASSIVE);
}
}
+
void EnterCombat(Unit *who)
{
_EnterCombat();
@@ -73,13 +84,17 @@ struct TRINITY_DLL_DECL boss_kologarnAI : public BossAI
events.ScheduleEvent(EVENT_SWEEP, 10000);
events.ScheduleEvent(EVENT_GRIP, 15000);
}
+
void UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
return;
+
events.Update(diff);
+
if(me->hasUnitState(UNIT_STAT_CASTING))
return;
+
// TODO: because we are using hack, he is stunned and cannot cast, so we use triggered for every spell
switch(events.GetEvent())
{
@@ -105,13 +120,16 @@ struct TRINITY_DLL_DECL boss_kologarnAI : public BossAI
events.PopEvent();
break;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_kologarn(Creature* pCreature)
{
return new boss_kologarnAI (pCreature);
}
+
void AddSC_boss_kologarn()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp
index b28c71f645d..a736b2db12a 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_mimiron.cpp
@@ -15,5 +15,6 @@
* 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"
#include "def_ulduar.h"
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp
index 87df084972a..92dd6ecacab 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_razorscale.cpp
@@ -15,14 +15,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
Name: razorscale
%Complete: 90
Comment: Made by Epsik from WoW Arthas wow.dsl.net.pk
Category:
EndScriptData */
+
#include "precompiled.h"
#include "def_ulduar.h"
+
//wrong text ids. correct are beetwen -1000000 AND -1999999
//beetwen -2000000 and -2999999 are custom texts so wtf?
#define SAY_AGGRO -2000000
@@ -30,17 +33,23 @@ EndScriptData */
#define SAY_PHASE_2_TRANS -2000002
#define SAY_PHASE_3_TRANS -2000003
#define EMOTE_BREATH -2000004
+
#define SPELL_FLAMEBUFFET 64016
#define SPELL_FIREBALL 62796
+
#define SPELL_WINGBUFFET 62666
#define SPELL_FLAMEBREATH 63317
#define SPELL_FUSEARMOR 64771
#define SPELL_DEVOURINGFLAME 63014
#define SPELL_KNOCK_AWAY 19633
+
#define SPELL_BELLOWINGROAR 18431
#define SPELL_HEATED_GROUND 22191
+
#define SPELL_SUMMONADDS 17646
+
#define CREATURE_ADDS 33846
+
static float MovementLocations[4][3]=
{
{607.7, -281.9, 408.6},
@@ -48,6 +57,7 @@ static float MovementLocations[4][3]=
{612.3, -230.8, 409.1},
{624.1, -251.8, 426.1}
};
+
static float SpawnLocations[4][3]=
{
{602.0, -248.1, 391.2},
@@ -55,10 +65,13 @@ static float SpawnLocations[4][3]=
{643.3, -256.4, 391.4},
{626.6, -271.5, 391.4},
};
+
struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
{
boss_razorscaleAI(Creature *pCreature) : BossAI(pCreature, TYPE_RAZORSCALE) {}
+
uint32 Phase;
+
uint32 FlameBreathTimer;
uint32 FUSEARMORTimer;
uint32 DEVOURINGFLAMETimer;
@@ -68,10 +81,13 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
uint32 WingBuffetTimer;
uint32 KnockAwayTimer;
uint32 FireballTimer;
+
bool InitialSpawn;
+
void Reset()
{
Phase = 1;
+
FlameBreathTimer = 20000;
DEVOURINGFLAMETimer = 2000;
FUSEARMORTimer = 15000;
@@ -81,26 +97,33 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
WingBuffetTimer = 17000;
KnockAwayTimer = 15000;
FireballTimer = 18000;
+
InitialSpawn = true;
+
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
m_creature->ApplySpellImmune(1, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
}
+
void JustDied(Unit* Killer)
{
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_KILL, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(m_creature->GetPositionY() > -60 || m_creature->GetPositionX() < 450) // Not Blizzlike, anti-exploit to prevent players from pulling bosses to vehicles.
{
m_creature->RemoveAllAuras();
@@ -108,6 +131,7 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
m_creature->CombatStop(false);
m_creature->GetMotionMaster()->MoveTargetedHome();
}
+
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 99) && (Phase == 1))
{
Phase = 2;
@@ -117,6 +141,7 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
m_creature->GetMotionMaster()->MoveIdle();
DoScriptText(SAY_PHASE_2_TRANS, m_creature);
}
+
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50) && (Phase == 2))
{
Phase = 3;
@@ -128,6 +153,7 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
DoScriptText(SAY_PHASE_3_TRANS, m_creature);
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
}
+
if (Phase == 1 || Phase == 3)
{
if (FlameBreathTimer < diff)
@@ -135,23 +161,28 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
DoCast(m_creature->getVictim(), SPELL_FLAMEBREATH);
FlameBreathTimer = 15000;
} else FlameBreathTimer -= diff;
+
if (DEVOURINGFLAMETimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
if (target && !m_creature->HasInArc(M_PI, target))
DoCast(target, SPELL_DEVOURINGFLAME);
+
DEVOURINGFLAMETimer = 10000;
} else DEVOURINGFLAMETimer -= diff;
+
if (FUSEARMORTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FUSEARMOR);
FUSEARMORTimer = 10000;
} else FUSEARMORTimer -= diff;
+
if (WingBuffetTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_WINGBUFFET);
WingBuffetTimer = 7000 + ((rand()%8)*1000);
} else WingBuffetTimer -= diff;
+
if (KnockAwayTimer < diff)
{
if (rand()%100 <= 30)
@@ -160,27 +191,34 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
}
KnockAwayTimer = 15000;
} else KnockAwayTimer -= diff;
+
if (Phase == 3)
{
if (BellowingRoarTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_BELLOWINGROAR);
+
BellowingRoarTimer = 30000;
} else BellowingRoarTimer -= diff;
+
if (SummonAddsTimer < diff)
{
SummonAdds(Phase);
+
SummonAddsTimer = 45000;
} else SummonAddsTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
if (Phase == 2)
{
if (InitialSpawn)
{
InitialSpawn = false;
- for (uint32 i = 0; i < 4; ++i)
+
+ for(uint32 i = 0; i < 4; ++i)
{
uint32 random = rand()%4;
Creature* Add = m_creature->SummonCreature(CREATURE_ADDS, SpawnLocations[random][0], SpawnLocations[random][1], SpawnLocations[random][2], 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
@@ -189,11 +227,14 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
}
}
+
if (FireballTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
+
FireballTimer = 18000;
} else FireballTimer -= diff;
+
if (MovementTimer < diff)
{
if (rand()%100 <= 30)
@@ -202,15 +243,19 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
DoCast(m_creature->getVictim(), SPELL_FLAMEBUFFET);
}
else ChangePosition();
+
MovementTimer = 25000;
} else MovementTimer -= diff;
+
if (SummonAddsTimer < diff)
{
SummonAdds(Phase);
+
SummonAddsTimer = 45000;
} else SummonAddsTimer -= diff;
}
}
+
void ChangePosition()
{
/* Malfunctioning, Razorscale is flying around randomly, attacking players from hundreds of yards away.
@@ -218,12 +263,13 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
if (random<4){
m_creature->GetMotionMaster()->MovePoint(0, MovementLocations[random][0], MovementLocations[random][1], MovementLocations[random][2]);} */
}
+
void SummonAdds(uint32 Phase)
{
if (Phase == 2)
{
uint32 max = rand()%10;
- for (uint32 i = 0; i < 4; ++i)
+ for(uint32 i = 0; i < 4; ++i)
{
uint32 random = rand()%3;
Creature* Add = m_creature->SummonCreature(CREATURE_ADDS, SpawnLocations[random][0], SpawnLocations[random][1], SpawnLocations[random][2], 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
@@ -231,12 +277,13 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
Add->AI()->AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0));
}
}
+
if (Phase == 3)
{
uint32 max = rand() % 10 +1;
if (max < 1)
{
- for (uint32 i = 0; i < max; ++i)
+ for(uint32 i = 0; i < max; ++i)
{
uint32 random = rand()%4;
Creature* Add = m_creature->SummonCreature(CREATURE_ADDS, SpawnLocations[random][0], SpawnLocations[random][1], SpawnLocations[random][2], 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
@@ -247,10 +294,12 @@ struct TRINITY_DLL_DECL boss_razorscaleAI : public BossAI
}
}
};
+
CreatureAI* GetAI_boss_razorscale(Creature* pCreature)
{
return new boss_razorscaleAI (pCreature);
}
+
void AddSC_boss_razorscale()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_thorim.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_thorim.cpp
index 66cb95be254..d0bd4c87c04 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_thorim.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_thorim.cpp
@@ -15,12 +15,15 @@
* 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"
#include "def_ulduar.h"
+
/*
#define SAY_AGGRO -1
#define SAY_SLAY -1
*/
+
struct TRINITY_DLL_DECL boss_thorimAI : public BossAI
{
boss_thorimAI(Creature* pCreature) : BossAI(pCreature, TYPE_THORIM)
@@ -28,39 +31,52 @@ struct TRINITY_DLL_DECL boss_thorimAI : public BossAI
m_pInstance = pCreature->GetInstanceData();
Reset();
}
+
ScriptedInstance* m_pInstance;
+
void Reset()
{
}
+
void KilledUnit(Unit *victim)
{
}
+
void JustDied(Unit *victim)
{
if (m_pInstance)
m_pInstance->SetData(TYPE_THORIM, DONE);
}
+
void Aggro(Unit* pWho)
{
// DoScriptText(SAY_AGGRO, m_creature);
m_creature->SetInCombatWithZone();
+
if (m_pInstance)
m_pInstance->SetData(TYPE_THORIM, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
//SPELLS TODO:
+
//
DoMeleeAttackIfReady();
+
EnterEvadeIfOutOfCombatArea(diff);
+
}
+
};
+
CreatureAI* GetAI_boss_thorim(Creature* pCreature)
{
return new boss_thorimAI(pCreature);
}
+
void AddSC_boss_thorim()
{
Script *newscript;
@@ -68,4 +84,5 @@ void AddSC_boss_thorim()
newscript->Name = "boss_thorim";
newscript->GetAI = &GetAI_boss_thorim;
newscript->RegisterSelf();
+
}
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_xt002.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_xt002.cpp
index 9b497333e6d..e5889969716 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_xt002.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_xt002.cpp
@@ -15,26 +15,34 @@
* 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"
#include "def_ulduar.h"
+
#define SPELL_SEARING_LIGHT 63018
#define SPELL_SONIC_BOOM 38897
+
#define SAY_AGGRO -1000000
#define SAY_SLAY -1000001
+
struct TRINITY_DLL_DECL boss_xt002_AI : public BossAI
{
boss_xt002_AI(Creature *pCreature) : BossAI(pCreature, TYPE_XT002)
{
m_pInstance = pCreature->GetInstanceData();
}
+
ScriptedInstance* m_pInstance;
+
uint32 SEARING_LIGHT_Timer;
uint32 SONIC_BOOM_Timer;
+
void Reset()
{
SEARING_LIGHT_Timer = 100;
SONIC_BOOM_Timer = 20;
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO,m_creature);
@@ -43,17 +51,22 @@ struct TRINITY_DLL_DECL boss_xt002_AI : public BossAI
{
DoScriptText(SAY_SLAY, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_SLAY, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_XT002, DONE);
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if(m_creature->GetPositionX() < 700) // Not Blizzlike, anti-exploit to prevent players from pulling bosses to vehicles.
{
m_creature->RemoveAllAuras();
@@ -61,20 +74,24 @@ struct TRINITY_DLL_DECL boss_xt002_AI : public BossAI
m_creature->CombatStop(false);
m_creature->GetMotionMaster()->MoveTargetedHome();
}
+
if (SEARING_LIGHT_Timer < diff)
{
DoCast(m_creature,SPELL_SEARING_LIGHT);
DoScriptText(SAY_SLAY, m_creature);
SEARING_LIGHT_Timer = 50000;
} else SEARING_LIGHT_Timer -= diff;
+
if (SONIC_BOOM_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SONIC_BOOM);
SONIC_BOOM_Timer = 20000;
} else SONIC_BOOM_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_xt002(Creature* pCreature)
{
return new boss_xt002_AI (pCreature);
@@ -82,6 +99,7 @@ CreatureAI* GetAI_boss_xt002(Creature* pCreature)
void AddSC_boss_xt002()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_xt002";
newscript->GetAI = &GetAI_boss_xt002;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp
index b28c71f645d..a736b2db12a 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/boss_yoggsaron.cpp
@@ -15,5 +15,6 @@
* 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"
#include "def_ulduar.h"
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/def_ulduar.h b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/def_ulduar.h
index 5f1667dbfee..ed7686343fb 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/def_ulduar.h
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/def_ulduar.h
@@ -15,11 +15,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DEF_ULDUAR_H
#define DEF_ULDUAR_H
+
enum eTypes
{
MAX_ENCOUNTER = 14,
+
TYPE_LEVIATHAN = 0,
TYPE_IGNIS = 1,
TYPE_RAZORSCALE = 2,
@@ -34,11 +37,13 @@ enum eTypes
TYPE_VEZAX = 11,
TYPE_YOGGSARON = 12,
TYPE_ALGALON = 13,
+
DATA_STEELBREAKER = 20,
DATA_MOLGEIM = 21,
DATA_BRUNDIR = 22,
DATA_RUNEMASTER_MOLGEIM = 23,
DATA_STORMCALLER_BRUNDIR = 24,
+
NPC_LEVIATHAN = 33113,
NPC_IGNIS = 33118,
NPC_RAZORSCALE = 33186,
@@ -56,4 +61,5 @@ enum eTypes
NPC_YOGGSARON = 33288,
NPC_ALGALON = 32871
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp
index 23a1eba2d16..4a4c441fffc 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/instance_ulduar.cpp
@@ -15,8 +15,10 @@
* 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"
#include "def_ulduar.h"
+
enum eGameObjects
{
GO_Kologarn_CHEST_HERO = 195047,
@@ -28,11 +30,14 @@ enum eGameObjects
GO_Freya_CHEST_HERO = 194325,
GO_Freya_CHEST = 194324,
};
+
struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
{
instance_ulduar(Map* pMap) : ScriptedInstance(pMap), KologarnChest(NULL), ThorimChest(NULL), HodirChest(NULL), FreyaChest(NULL) { Initialize(); };
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string m_strInstData;
+
uint64 m_uiLeviathanGUID;
uint64 m_uiIgnisGUID;
uint64 m_uiRazorscaleGUID;
@@ -47,7 +52,9 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
uint64 m_uiVezaxGUID;
uint64 m_uiYoggSaronGUID;
uint64 m_uiAlgalonGUID;
+
GameObject* KologarnChest, *ThorimChest, *HodirChest, *FreyaChest;
+
void Initialize()
{
m_uiLeviathanGUID = 0;
@@ -67,18 +74,22 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
ThorimChest = 0;
HodirChest = 0;
FreyaChest = 0;
+
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
memset(&m_auiAssemblyGUIDs, 0, sizeof(m_auiAssemblyGUIDs));
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
{
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
}
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -95,6 +106,7 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
case NPC_XT002:
m_uiXT002GUID = pCreature->GetGUID();
break;
+
// Assembly of Iron
case NPC_STEELBREAKER:
m_auiAssemblyGUIDs[0] = pCreature->GetGUID();
@@ -105,6 +117,7 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
case NPC_BRUNDIR:
m_auiAssemblyGUIDs[2] = pCreature->GetGUID();
break;
+
case NPC_KOLOGARN:
m_uiKologarnGUID = pCreature->GetGUID();
break;
@@ -133,7 +146,9 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
m_uiAlgalonGUID = pCreature->GetGUID();
break;
}
+
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -148,6 +163,7 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
case GO_Freya_CHEST: FreyaChest = add ? pGo : NULL; break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -181,17 +197,23 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
m_auiEncounter[type] = data;
break;
}
+
if (data == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
saveStream << m_auiEncounter[i] << " ";
+
m_strInstData = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -222,6 +244,7 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
return m_uiYoggSaronGUID;
case TYPE_ALGALON:
return m_uiAlgalonGUID;
+
// Assembly of Iron
case DATA_STEELBREAKER:
return m_auiAssemblyGUIDs[0];
@@ -230,8 +253,10 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
case DATA_BRUNDIR:
return m_auiAssemblyGUIDs[2];
}
+
return 0;
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -252,12 +277,15 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
case TYPE_ALGALON:
return m_auiEncounter[type];
}
+
return 0;
}
+
const char* Save()
{
return m_strInstData.c_str();
}
+
void Load(const char* strIn)
{
if (!strIn)
@@ -265,21 +293,28 @@ struct TRINITY_DLL_DECL instance_ulduar : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(strIn);
+
std::istringstream loadStream(strIn);
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
{
loadStream >> m_auiEncounter[i];
+
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
}
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_ulduar(Map* pMap)
{
return new instance_ulduar(pMap);
}
+
void AddSC_instance_ulduar()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp
index fb0d149eb09..3ea183059da 100644
--- a/src/bindings/scripts/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp
+++ b/src/bindings/scripts/scripts/northrend/ulduar/ulduar/ulduar_teleporter.cpp
@@ -1,7 +1,9 @@
#include "precompiled.h"
#include "def_ulduar.h"
+
/*
The teleporter appears to be active and stable.
+
- Expedition Base Camp
- Formation Grounds
- Colossal Forge
@@ -10,6 +12,7 @@ The teleporter appears to be active and stable.
- Shattered Walkway
- Conservatory of Life
*/
+
#define BASE_CAMP 200
#define GROUNDS 201
#define FORGE 202
@@ -17,10 +20,12 @@ The teleporter appears to be active and stable.
#define ANTECHAMBER 204
#define WALKWAY 205
#define CONSERVATORY 206
+
bool GoHello_ulduar_teleporter( Player *pPlayer, GameObject *pGO )
{
ScriptedInstance *pInstance = (ScriptedInstance *) pGO->GetInstanceData();
if(!pInstance) return true;
+
pPlayer->ADD_GOSSIP_ITEM(0, "Teleport to the Expedition Base Camp", GOSSIP_SENDER_MAIN, BASE_CAMP);
pPlayer->ADD_GOSSIP_ITEM(0, "Teleport to the Formation Grounds", GOSSIP_SENDER_MAIN, GROUNDS);
if(pInstance->GetData(TYPE_LEVIATHAN) == DONE)
@@ -39,12 +44,15 @@ bool GoHello_ulduar_teleporter( Player *pPlayer, GameObject *pGO )
}
}
pPlayer->SEND_GOSSIP_MENU(DEFAULT_GOSSIP_MESSAGE, pGO->GetGUID());
+
return true;
}
+
bool GOSelect_ulduar_teleporter( Player *pPlayer, GameObject *pGO, uint32 sender, uint32 action )
{
if(sender != GOSSIP_SENDER_MAIN) return true;
if(!pPlayer->getAttackers().empty()) return true;
+
switch(action)
{
case BASE_CAMP:
@@ -69,8 +77,10 @@ bool GOSelect_ulduar_teleporter( Player *pPlayer, GameObject *pGO, uint32 sender
pPlayer->TeleportTo(603, 2086.27, -24.3134, 421.239, 0);
pPlayer->CLOSE_GOSSIP_MENU(); break;
}
+
return true;
}
+
void AddSC_ulduar_teleporter()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp
index 4cc6163c83b..86523ef0bf6 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_ingvar_the_plunderer.cpp
@@ -15,23 +15,29 @@
* along 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_Ingvar_The_Plunderer
SD%Complete: 95
SDComment: Some Problems with Annhylde Movement, Blizzlike Timers
SDCategory: Udgarde Keep
EndScriptData */
+
#include "precompiled.h"
#include "def_utgarde_keep.h"
+
enum eEnums
{
//Yells Ingvar
YELL_AGGRO_1 = -1574005,
YELL_AGGRO_2 = -1574006,
+
YELL_DEAD_1 = -1574007,
YELL_DEAD_2 = -1574008,
+
YELL_KILL_1 = -1574009,
YELL_KILL_2 = -1574010,
+
//Ingvar Spells human form
MOB_INGVAR_HUMAN = 23954,
SPELL_CLEAVE = 42724,
@@ -41,12 +47,15 @@ enum eEnums
H_SPELL_STAGGERING_ROAR = 59708,
SPELL_ENRAGE = 42705,
H_SPELL_ENRAGE = 59707,
+
MOB_ANNHYLDE_THE_CALLER = 24068,
SPELL_INGVAR_FEIGN_DEATH = 42795,
SPELL_SUMMON_BANSHEE = 42912,
SPELL_SCOURG_RESURRECTION_EFFEKTSPAWN = 42863, //Spawn resurrecteffekt around Ingvar
+
MODEL_INGVAR_UNDEAD = 26351,
MODEL_INGVAR_HUMAN = 21953,
+
//Ingvar Spells undead form
MOB_INGVAR_UNDEAD = 23980,
SPELL_DARK_SMASH = 42723,
@@ -54,9 +63,11 @@ enum eEnums
H_SPELL_DREADFUL_ROAR = 59734,
SPELL_WOE_STRIKE = 42730,
H_SPELL_WOE_STRIKE = 59735,
+
ENTRY_THROW_TARGET = 23996,
SPELL_SHADOW_AXE_SUMMON = 42749
};
+
struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
{
boss_ingvar_the_plundererAI(Creature *c) : ScriptedAI(c)
@@ -64,33 +75,44 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
+
bool HeroicMode;
bool undead;
bool event_inProgress;
+
uint32 Cleave_Timer;
uint32 Smash_Timer;
uint32 Enrage_Timer;
uint32 Roar_Timer;
uint32 SpawnResTimer;
uint32 wait_Timer;
+
void Reset()
{
if (undead) // Visual Hack
m_creature->SetDisplayId(MODEL_INGVAR_HUMAN);
+
undead = false;
event_inProgress = false;
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
Cleave_Timer = 2000;
Smash_Timer = 5000;
Enrage_Timer = 10000;
Roar_Timer = 15000;
+
SpawnResTimer = 3000;
+
wait_Timer = 0;
+
if (pInstance)
pInstance->SetData(DATA_INGVAR_EVENT, NOT_STARTED);
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= m_creature->GetHealth() && !undead)
@@ -106,15 +128,19 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
m_creature->GetMotionMaster()->MoveIdle();
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
// visuel hack end
+
event_inProgress = true;
undead = true;
+
DoScriptText(YELL_DEAD_1,m_creature);
}
+
if (event_inProgress)
{
damage = 0;
}
}
+
void StartZombiePhase()
{
undead = true;
@@ -122,29 +148,37 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
m_creature->AI()->AttackStart(m_creature->getVictim());
+
DoScriptText(YELL_AGGRO_2,m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(YELL_AGGRO_1,m_creature);
+
if (pInstance)
pInstance->SetData(DATA_INGVAR_EVENT, IN_PROGRESS);
}
+
void JustDied(Unit* killer)
{
DoScriptText(YELL_DEAD_2,m_creature);
+
if (pInstance)
pInstance->SetData(DATA_INGVAR_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
if (undead) { DoScriptText(YELL_KILL_1,m_creature); }
else { DoScriptText(YELL_KILL_2,m_creature); }
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (event_inProgress)
{
if (SpawnResTimer)
@@ -154,14 +188,17 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
//DoCast(m_creature,SPELL_SCOURG_RESURRECTION_EFFEKTSPAWN); // Dont needet ?
SpawnResTimer = 0;
}else SpawnResTimer -= diff;
+
return;
}
+
// This is used for a spell queue ... the spells should not castet if one spell is already casting
if (wait_Timer)
if (wait_Timer < diff)
{
wait_Timer = 0;
}else wait_Timer -= diff;
+
if (Cleave_Timer < diff)
{
if (!wait_Timer)
@@ -171,9 +208,11 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
else
DoCast(m_creature->getVictim(),SPELL_CLEAVE);
Cleave_Timer = rand()%5000 + 2000;
+
wait_Timer = 1000;
}
}else Cleave_Timer -= diff;
+
if (Smash_Timer < diff)
{
if (!wait_Timer)
@@ -183,9 +222,11 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
else
DoCast(m_creature->getVictim(),HEROIC(SPELL_SMASH, H_SPELL_SMASH));
Smash_Timer = 10000;
+
wait_Timer = 5000;
}
}else Smash_Timer -= diff;
+
if (!undead)
{
if (Enrage_Timer < diff)
@@ -204,6 +245,7 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
if (target)
{
Creature* temp = m_creature->SummonCreature(ENTRY_THROW_TARGET,target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN,2000);
+
DoCast(m_creature,SPELL_SHADOW_AXE_SUMMON);
}
Enrage_Timer = 30000;
@@ -211,6 +253,7 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
}else Enrage_Timer -= diff;
}
+
if (Roar_Timer < diff)
{
if (!wait_Timer)
@@ -220,36 +263,44 @@ struct TRINITY_DLL_DECL boss_ingvar_the_plundererAI : public ScriptedAI
else
DoCast(m_creature,HEROIC(SPELL_STAGGERING_ROAR, H_SPELL_STAGGERING_ROAR));
Roar_Timer = 10000;
+
wait_Timer = 5000;
}
}else Roar_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_ingvar_the_plunderer(Creature* pCreature)
{
return new boss_ingvar_the_plundererAI (pCreature);
}
+
enum eSpells
{
//we don't have that text in db so comment it until we get this text
// YELL_RESSURECT = -1574025,
+
//Spells for Annhylde
SPELL_SCOURG_RESURRECTION_HEAL = 42704, //Heal Max + DummyAura
SPELL_SCOURG_RESURRECTION_BEAM = 42857, //Channeling Beam of Annhylde
SPELL_SCOURG_RESURRECTION_DUMMY = 42862, //Some Emote Dummy?
SPELL_INGVAR_TRANSFORM = 42796
};
+
struct TRINITY_DLL_DECL mob_annhylde_the_callerAI : public ScriptedAI
{
mob_annhylde_the_callerAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
float x,y,z;
ScriptedInstance* pInstance;
uint32 Resurect_Timer;
uint32 Resurect_Phase;
+
void Reset()
{
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_FLYING + MOVEMENTFLAG_HOVER);
@@ -257,15 +308,19 @@ struct TRINITY_DLL_DECL mob_annhylde_the_callerAI : public ScriptedAI
m_creature->SetSpeed(MOVE_RUN , 1.0f);
m_creature->SetSpeed(MOVE_WALK , 1.0f);
//m_creature->SetSpeed(MOVE_FLIGHT , 1.0f);
+
m_creature->GetPosition(x,y,z);
DoTeleportTo(x+1,y,z+30);
+
Unit* ingvar = Unit::GetUnit(*m_creature, pInstance ? pInstance->GetData64(DATA_INGVAR) : 0);
if (ingvar)
{
m_creature->GetMotionMaster()->MovePoint(1,x,y,z+15);
+
// DoScriptText(YELL_RESSURECT,m_creature);
}
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
@@ -289,6 +344,7 @@ struct TRINITY_DLL_DECL mob_annhylde_the_callerAI : public ScriptedAI
}
}
}
+
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void EnterCombat(Unit *who) {}
@@ -316,31 +372,39 @@ struct TRINITY_DLL_DECL mob_annhylde_the_callerAI : public ScriptedAI
//ingvar->CastSpell(ingvar,SPELL_INGVAR_TRANSFORM,false);
//ingvar->SetDisplayId(MODEL_INGVAR_UNDEAD); // Visual Hack - when he dies he becomes human model -> wrong
Creature* c_ingvar = ingvar;
+
CAST_AI(boss_ingvar_the_plundererAI, (c_ingvar->AI()))->StartZombiePhase();
+
m_creature->GetMotionMaster()->MovePoint(2,x+1,y,z+30);
Resurect_Phase++;
}
}
+
}else Resurect_Timer -= diff;
}
};
+
CreatureAI* GetAI_mob_annhylde_the_caller(Creature* pCreature)
{
return new mob_annhylde_the_callerAI (pCreature);
}
+
enum eShadowAxe
{
SPELL_SHADOW_AXE_DAMAGE = 42750,
H_SPELL_SHADOW_AXE_DAMAGE = 59719
};
+
struct TRINITY_DLL_DECL mob_ingvar_throw_dummyAI : public ScriptedAI
{
mob_ingvar_throw_dummyAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
uint32 Despawn_Timer;
+
void Reset()
{
Unit* target = m_creature->FindNearestCreature(ENTRY_THROW_TARGET,50);
@@ -366,21 +430,26 @@ struct TRINITY_DLL_DECL mob_ingvar_throw_dummyAI : public ScriptedAI
}else Despawn_Timer -= diff;
}
};
+
CreatureAI* GetAI_mob_ingvar_throw_dummy(Creature* pCreature)
{
return new mob_ingvar_throw_dummyAI (pCreature);
}
+
void AddSC_boss_ingvar_the_plunderer()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ingvar_the_plunderer";
newscript->GetAI = &GetAI_boss_ingvar_the_plunderer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_annhylde_the_caller";
newscript->GetAI = &GetAI_mob_annhylde_the_caller;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ingvar_throw_dummy";
newscript->GetAI = &GetAI_mob_ingvar_throw_dummy;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp
index b3075e26688..bff13c8b4cc 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_keleseth.cpp
@@ -15,17 +15,21 @@
* along 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_Keleseth
SD%Complete: 90
SDComment: Needs Prince Movements, Needs adjustments to blizzlike timers, Needs Shadowbolt castbar, Needs right Ressurect Visual, Needs Some Heroic Spells
SDCategory: Utgarde Keep
EndScriptData */
+
#include "precompiled.h"
#include "def_utgarde_keep.h"
+
enum eEnums
{
ACHIEVEMENT_ON_THE_ROCKS = 1919,
+
SPELL_SHADOWBOLT = 43667,
SPELL_SHADOWBOLT_HEROIC = 59389,
SPELL_FROST_TOMB = 48400,
@@ -34,13 +38,16 @@ enum eEnums
SPELL_SCOURGE_RESSURRECTION = 42704,
CREATURE_FROSTTOMB = 23965,
CREATURE_SKELETON = 23970,
+
SAY_AGGRO = -1574000,
SAY_FROST_TOMB = -1574001,
SAY_SKELETONS = -1574002,
SAY_KILL = -1574003,
SAY_DEATH = -1574004
};
+
#define SKELETONSPAWN_Z 42.8668
+
float SkeletonSpawnPoint[5][5]=
{
{156.2559, 259.2093},
@@ -49,27 +56,35 @@ float SkeletonSpawnPoint[5][5]=
{156.2559, 259.2093},
{156.2559, 259.2093},
};
+
float AttackLoc[3]={197.636, 194.046, 40.8164};
+
bool ShatterFrostTomb; // needed for achievement: On The Rocks(1919)
+
struct TRINITY_DLL_DECL mob_frost_tombAI : public ScriptedAI
{
mob_frost_tombAI(Creature *c) : ScriptedAI(c)
{
FrostTombGUID = 0;
}
+
uint64 FrostTombGUID;
+
void SetPrisoner(Unit* uPrisoner)
{
FrostTombGUID = uPrisoner->GetGUID();
}
+
void Reset(){ FrostTombGUID = 0; }
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
+
void JustDied(Unit *killer)
{
if (killer->GetGUID() != m_creature->GetGUID())
ShatterFrostTomb = true;
+
if (FrostTombGUID)
{
Unit* FrostTomb = Unit::GetUnit((*m_creature),FrostTombGUID);
@@ -77,6 +92,7 @@ struct TRINITY_DLL_DECL mob_frost_tombAI : public ScriptedAI
FrostTomb->RemoveAurasDueToSpell(SPELL_FROST_TOMB);
}
}
+
void UpdateAI(const uint32 diff)
{
Unit* temp = Unit::GetUnit((*m_creature),FrostTombGUID);
@@ -84,6 +100,7 @@ struct TRINITY_DLL_DECL mob_frost_tombAI : public ScriptedAI
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
};
+
struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
{
boss_kelesethAI(Creature *c) : ScriptedAI(c)
@@ -91,7 +108,9 @@ struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
pInstance = c->GetInstanceData();
Heroic = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
+
uint32 FrostTombTimer;
uint32 SummonSkeletonsTimer;
uint32 RespawnSkeletonsTimer;
@@ -100,24 +119,32 @@ struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
bool Skeletons;
bool Heroic;
bool RespawnSkeletons;
+
void Reset()
{
ShadowboltTimer = 0;
Skeletons = false;
+
ShatterFrostTomb = false;
+
ResetTimer();
+
if (pInstance)
pInstance->SetData(DATA_PRINCEKELESETH_EVENT, NOT_STARTED);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(SAY_KILL, m_creature);
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (Heroic && !ShatterFrostTomb)
{
AchievementEntry const *AchievOnTheRocks = GetAchievementStore()->LookupEntry(ACHIEVEMENT_ON_THE_ROCKS);
@@ -127,30 +154,36 @@ struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
if (pMap && pMap->IsDungeon())
{
Map::PlayerList const &players = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
itr->getSource()->CompletedAchievement(AchievOnTheRocks);
}
}
}
+
if (pInstance)
pInstance->SetData(DATA_PRINCEKELESETH_EVENT, DONE);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
+
if (pInstance)
pInstance->SetData(DATA_PRINCEKELESETH_EVENT, IN_PROGRESS);
}
+
void ResetTimer(uint32 inc = 0)
{
SummonSkeletonsTimer = 5000 + inc;
FrostTombTimer = 28000 + inc;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (ShadowboltTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO, 0);
@@ -158,12 +191,13 @@ struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
m_creature->CastSpell(target, Heroic ? SPELL_SHADOWBOLT_HEROIC : SPELL_SHADOWBOLT, true);
ShadowboltTimer = 10000;
}else ShadowboltTimer -= diff;
+
if (!Skeletons)
if ((SummonSkeletonsTimer < diff))
{
Creature* Skeleton;
DoScriptText(SAY_SKELETONS, m_creature);
- for (uint8 i = 0; i < 5; ++i)
+ for(uint8 i = 0; i < 5; ++i)
{
Skeleton = m_creature->SummonCreature(CREATURE_SKELETON, SkeletonSpawnPoint[i][0], SkeletonSpawnPoint[i][1] , SKELETONSPAWN_Z, 0, TEMPSUMMON_CORPSE_TIMED_DESPAWN,20000);
if (Skeleton)
@@ -176,6 +210,7 @@ struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
}
Skeletons = true;
}else SummonSkeletonsTimer -= diff;
+
if (FrostTombTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
@@ -187,36 +222,44 @@ struct TRINITY_DLL_DECL boss_kelesethAI : public ScriptedAI
{
CAST_AI(mob_frost_tombAI, Chains->AI())->SetPrisoner(target);
Chains->CastSpell(target, SPELL_FROST_TOMB, true);
+
DoScriptText(SAY_FROST_TOMB, m_creature);
}
}
FrostTombTimer = 15000;
}else FrostTombTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_vrykul_skeletonAI : public ScriptedAI
{
mob_vrykul_skeletonAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 Respawn_Time;
uint64 Target_Guid;
uint32 Decrepify_Timer;
+
bool isDead;
+
void Reset()
{
Respawn_Time = 12000;
Decrepify_Timer = 10000 + rand()%20000;
isDead = false;
}
+
void EnterCombat(Unit *who){}
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetGUID() == m_creature->GetGUID())
return;
+
if (damage >= m_creature->GetHealth())
{
PretendToDie();
@@ -224,6 +267,7 @@ struct TRINITY_DLL_DECL mob_vrykul_skeletonAI : public ScriptedAI
}
}
+
void PretendToDie()
{
isDead = true;
@@ -234,12 +278,14 @@ struct TRINITY_DLL_DECL mob_vrykul_skeletonAI : public ScriptedAI
m_creature->GetMotionMaster()->MoveIdle();
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
};
+
void Resurrect()
{
isDead = false;
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
m_creature->CastSpell(m_creature,SPELL_SCOURGE_RESSURRECTION,true);
+
if (m_creature->getVictim())
{
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
@@ -248,6 +294,7 @@ struct TRINITY_DLL_DECL mob_vrykul_skeletonAI : public ScriptedAI
else
m_creature->GetMotionMaster()->Initialize();
};
+
void UpdateAI(const uint32 diff)
{
if (pInstance && pInstance->GetData(DATA_PRINCEKELESETH_EVENT) == IN_PROGRESS)
@@ -264,11 +311,13 @@ struct TRINITY_DLL_DECL mob_vrykul_skeletonAI : public ScriptedAI
{
if (!UpdateVictim())
return;
+
if (Decrepify_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_DECREPIFY);
Decrepify_Timer = 30000;
}else Decrepify_Timer -= diff;
+
DoMeleeAttackIfReady();
}
}else
@@ -276,31 +325,39 @@ struct TRINITY_DLL_DECL mob_vrykul_skeletonAI : public ScriptedAI
if (m_creature->isAlive())
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
+
}
};
+
CreatureAI* GetAI_mob_frost_tomb(Creature* pCreature)
{
return new mob_frost_tombAI(pCreature);
}
+
CreatureAI* GetAI_boss_keleseth(Creature* pCreature)
{
return new boss_kelesethAI (pCreature);
}
+
CreatureAI* GetAI_mob_vrykul_skeleton(Creature* pCreature)
{
return new mob_vrykul_skeletonAI (pCreature);
}
+
void AddSC_boss_keleseth()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_keleseth";
newscript->GetAI = &GetAI_boss_keleseth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_frost_tomb";
newscript->GetAI = &GetAI_mob_frost_tomb;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_vrykul_skeleton";
newscript->GetAI = &GetAI_mob_vrykul_skeleton;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp
index 0102efe51af..0770f9209e9 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/boss_skarvald_dalronn.cpp
@@ -15,14 +15,17 @@
* along 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_Skarvald_Dalronn
SD%Complete: 95
SDComment: Needs adjustments to blizzlike timers, Yell Text + Sound to DB
SDCategory: Utgarde Keep
EndScriptData */
+
#include "precompiled.h"
#include "def_utgarde_keep.h"
+
enum eEnums
{
//signed for 24200, but used by 24200,27390
@@ -31,12 +34,14 @@ enum eEnums
YELL_SKARVALD_SKA_DIEDFIRST = -1574013,
YELL_SKARVALD_KILL = -1574014,
YELL_SKARVALD_DAL_DIEDFIRST = -1574015,
+
//signed for 24201, but used by 24201,27389
YELL_DALRONN_AGGRO = -1574016,
YELL_DALRONN_SKA_DIED = -1574017,
YELL_DALRONN_DAL_DIEDFIRST = -1574018,
YELL_DALRONN_KILL = -1574019,
YELL_DALRONN_SKA_DIEDFIRST = -1574020,
+
//Spells of Skarvald and his Ghost
MOB_SKARVALD_THE_CONSTRUCTOR = 24200,
SPELL_CHARGE = 43651,
@@ -52,6 +57,7 @@ enum eEnums
SPELL_SUMMON_DALRONN_GHOST = 48612,
MOB_DALRONN_GHOST = 27389
};
+
struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
{
boss_skarvald_the_constructorAI(Creature *c) : ScriptedAI(c)
@@ -59,7 +65,9 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
+
bool ghost;
bool HeroicMode;
uint32 Charge_Timer;
@@ -67,32 +75,39 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
uint32 Response_Timer;
uint32 Check_Timer;
bool Dalronn_isDead;
+
void Reset()
{
Charge_Timer = 5000;
StoneStrike_Timer = 10000;
Dalronn_isDead = false;
Check_Timer = 5000;
+
ghost = (m_creature->GetEntry() == MOB_SKARVALD_GHOST);
if (!ghost && pInstance)
{
Unit* dalronn = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_DALRONN));
if (dalronn && dalronn->isDead())
CAST_CRE(dalronn)->Respawn();
+
pInstance->SetData(DATA_SKARVALD_DALRONN_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit *who)
{
if (!ghost && pInstance)
{
DoScriptText(YELL_SKARVALD_AGGRO,m_creature);
+
Unit* dalronn = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_DALRONN));
if (dalronn && dalronn->isAlive() && !dalronn->getVictim())
dalronn->getThreatManager().addThreat(who,0.0f);
+
pInstance->SetData(DATA_SKARVALD_DALRONN_EVENT, IN_PROGRESS);
}
}
+
void JustDied(Unit* Killer)
{
if (!ghost && pInstance)
@@ -103,11 +118,13 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
if (dalronn->isDead())
{
DoScriptText(YELL_SKARVALD_DAL_DIED,m_creature);
+
pInstance->SetData(DATA_SKARVALD_DALRONN_EVENT, DONE);
}
else
{
DoScriptText(YELL_SKARVALD_SKA_DIEDFIRST,m_creature);
+
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
//DoCast(m_creature,SPELL_SUMMON_SKARVALD_GHOST,true);
Creature* temp = m_creature->SummonCreature(MOB_SKARVALD_GHOST,m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ(),0,TEMPSUMMON_CORPSE_DESPAWN,5000);
@@ -120,6 +137,7 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
if (!ghost)
@@ -127,6 +145,7 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
DoScriptText(YELL_SKARVALD_KILL,m_creature);
}
}
+
void UpdateAI(const uint32 diff)
{
if (ghost)
@@ -134,8 +153,10 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
if (pInstance && pInstance->GetData(DATA_SKARVALD_DALRONN_EVENT) != IN_PROGRESS)
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
+
if (!UpdateVictim())
return;
+
if (!ghost)
{
if (Check_Timer)
@@ -150,31 +171,38 @@ struct TRINITY_DLL_DECL boss_skarvald_the_constructorAI : public ScriptedAI
Check_Timer = 0;
}
}else Check_Timer -= diff;
+
if (Response_Timer)
if (Dalronn_isDead)
if (Response_Timer < diff)
{
DoScriptText(YELL_SKARVALD_DAL_DIEDFIRST,m_creature);
+
Response_Timer = 0;
}else Response_Timer -= diff;
}
+
if (Charge_Timer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 1), SPELL_CHARGE);
Charge_Timer = 5000+rand()%5000;
}else Charge_Timer -= diff;
+
if (StoneStrike_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_STONE_STRIKE);
StoneStrike_Timer = 5000+rand()%5000;
}else StoneStrike_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_skarvald_the_constructor(Creature* pCreature)
{
return new boss_skarvald_the_constructorAI (pCreature);
}
+
struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
{
boss_dalronn_the_controllerAI(Creature *c) : ScriptedAI(c)
@@ -182,17 +210,21 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
+
bool ghost;
bool HeroicMode;
uint32 ShadowBolt_Timer;
uint32 Debilitate_Timer;
uint32 Summon_Timer;
+
uint32 Response_Timer;
uint32 Check_Timer;
uint32 AggroYell_Timer;
bool Skarvald_isDead;
+
void Reset()
{
ShadowBolt_Timer = 1000;
@@ -201,15 +233,18 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
Check_Timer = 5000;
Skarvald_isDead = false;
AggroYell_Timer = 0;
+
ghost = m_creature->GetEntry() == MOB_DALRONN_GHOST;
if (!ghost && pInstance)
{
Unit* skarvald = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_SKARVALD));
if (skarvald && skarvald->isDead())
CAST_CRE(skarvald)->Respawn();
+
pInstance->SetData(DATA_SKARVALD_DALRONN_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit *who)
{
if (!ghost)
@@ -217,11 +252,14 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
Unit* skarvald = Unit::GetUnit((*m_creature),pInstance->GetData64(DATA_SKARVALD));
if (skarvald && skarvald->isAlive() && !skarvald->getVictim())
skarvald->getThreatManager().addThreat(who,0.0f);
+
AggroYell_Timer = 5000;
+
if (pInstance)
pInstance->SetData(DATA_SKARVALD_DALRONN_EVENT, IN_PROGRESS);
}
}
+
void JustDied(Unit* Killer)
{
if (!ghost)
@@ -231,12 +269,14 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
if (skarvald->isDead())
{
DoScriptText(YELL_DALRONN_SKA_DIED,m_creature);
+
if (pInstance)
pInstance->SetData(DATA_SKARVALD_DALRONN_EVENT, DONE);
}
else
{
DoScriptText(YELL_DALRONN_DAL_DIEDFIRST,m_creature);
+
m_creature->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
//DoCast(m_creature,SPELL_SUMMON_DALRONN_GHOST,true);
Creature* temp = m_creature->SummonCreature(MOB_DALRONN_GHOST,m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ(),0,TEMPSUMMON_CORPSE_DESPAWN,5000);
@@ -248,6 +288,7 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
if (!ghost)
@@ -255,6 +296,7 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
DoScriptText(YELL_DALRONN_KILL,m_creature);
}
}
+
void UpdateAI(const uint32 diff)
{
if (ghost)
@@ -262,14 +304,18 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
if (pInstance && pInstance->GetData(DATA_SKARVALD_DALRONN_EVENT) != IN_PROGRESS)
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
+
if (!UpdateVictim())
return;
+
if (AggroYell_Timer)
if (AggroYell_Timer < diff)
{
DoScriptText(YELL_DALRONN_AGGRO,m_creature);
+
AggroYell_Timer = 0;
}else AggroYell_Timer -= diff;
+
if (!ghost)
{
if (Check_Timer)
@@ -284,14 +330,17 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
Check_Timer = 0;
}
}else Check_Timer -= diff;
+
if (Response_Timer)
if (Skarvald_isDead)
if (Response_Timer < diff)
{
DoScriptText(YELL_DALRONN_SKA_DIEDFIRST,m_creature);
+
Response_Timer = 0;
}else Response_Timer -= diff;
}
+
if (ShadowBolt_Timer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -300,6 +349,7 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
ShadowBolt_Timer = 1000;
}
}else ShadowBolt_Timer -= diff;
+
if (Debilitate_Timer < diff)
{
if (!m_creature->IsNonMeleeSpellCasted(false))
@@ -308,26 +358,32 @@ struct TRINITY_DLL_DECL boss_dalronn_the_controllerAI : public ScriptedAI
Debilitate_Timer = 5000+rand()%5000;
}
}else Debilitate_Timer -= diff;
+
if (HeroicMode)
if (Summon_Timer < diff)
{
DoCast(m_creature,H_SPELL_SUMMON_SKELETONS);
Summon_Timer = (rand()%10000) + 20000;
}else Summon_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_dalronn_the_controller(Creature* pCreature)
{
return new boss_dalronn_the_controllerAI (pCreature);
}
+
void AddSC_boss_skarvald_dalronn()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_skarvald_the_constructor";
newscript->GetAI = &GetAI_boss_skarvald_the_constructor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_dalronn_the_controller";
newscript->GetAI = &GetAI_boss_dalronn_the_controller;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/def_utgarde_keep.h b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/def_utgarde_keep.h
index 0f4beff5d44..b630e156564 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/def_utgarde_keep.h
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/def_utgarde_keep.h
@@ -15,16 +15,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DEF_UTGARDE_KEEP_H
#define DEF_UTGARDE_KEEP_H
+
#define DATA_PRINCEKELESETH 1
#define DATA_SKARVALD 3
#define DATA_DALRONN 4
#define DATA_INGVAR 6
+
#define DATA_PRINCEKELESETH_EVENT 2
#define DATA_SKARVALD_DALRONN_EVENT 5
#define DATA_INGVAR_EVENT 7
+
#define EVENT_FORGE_1 8
#define EVENT_FORGE_2 9
#define EVENT_FORGE_3 10
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp
index 4b15767090a..744891371a9 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/instance_utgarde_keep.cpp
@@ -15,82 +15,104 @@
* along 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_Utgarde_Keep
SD%Complete: 90
SDComment: Instance Data Scripts and functions to acquire mobs and set encounter status for use in various Utgarde Keep Scripts
SDCategory: Utgarde Keep
EndScriptData */
+
#include "precompiled.h"
#include "def_utgarde_keep.h"
+
#define MAX_ENCOUNTER 3
+
#define ENTRY_BELLOW_1 186688
#define ENTRY_BELLOW_2 186689
#define ENTRY_BELLOW_3 186690
+
#define ENTRY_FORGEFIRE_1 186692
#define ENTRY_FORGEFIRE_2 186693
#define ENTRY_FORGEFIRE_3 186691
+
#define ENTRY_GLOWING_ANVIL_1 186609
#define ENTRY_GLOWING_ANVIL_2 186610
#define ENTRY_GLOWING_ANVIL_3 186611
+
#define ENTRY_GIANT_PORTCULLIS_1 186756
#define ENTRY_GIANT_PORTCULLIS_2 186694
+
/* Utgarde Keep encounters:
0 - Prince Keleseth
1 - Skarvald Dalronn
2 - Ingvar the Plunderer
*/
+
struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
{
instance_utgarde_keep(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 Keleseth;
uint64 Skarvald;
uint64 Dalronn;
uint64 Ingvar;
+
uint64 forge_bellow[3];
uint64 forge_fire[3];
uint64 forge_anvil[3];
uint64 portcullis[2];
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint32 forge_event[3];
std::string str_data;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
Keleseth = 0;
Skarvald = 0;
Dalronn = 0;
Ingvar = 0;
- for (uint8 i = 0; i < 3; ++i)
+
+ for(uint8 i = 0; i < 3; ++i)
{
forge_bellow[i] = 0;
forge_fire[i] = 0;
forge_anvil[i] = 0;
forge_event[i] = NOT_STARTED;
}
+
portcullis[0] = 0;
portcullis[1] = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
Player* GetPlayerInMap()
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* plr = itr->getSource())
return plr;
}
}
+
debug_log("TSCR: Instance Utgarde Keep: GetPlayerInMap, but PlayerList is empty!");
return NULL;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -101,6 +123,7 @@ struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
case 23954: Ingvar = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -130,6 +153,7 @@ struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
if (m_auiEncounter[2] == DONE)HandleGameObject(NULL,true,pGo);break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -139,8 +163,10 @@ struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
case DATA_SKARVALD: return Skarvald;
case DATA_INGVAR: return Ingvar;
}
+
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -203,11 +229,13 @@ struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
break;
}
+
if (data == DONE)
{
SaveToDB();
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -216,18 +244,24 @@ struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
case DATA_SKARVALD_DALRONN_EVENT: return m_auiEncounter[1];
case DATA_INGVAR_EVENT: return m_auiEncounter[2];
}
+
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << "U K " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << forge_event[0] << " " << forge_event[1] << " " << forge_event[2];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -235,30 +269,40 @@ struct TRINITY_DLL_DECL instance_utgarde_keep : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0,data1,data2, data3, data4, data5;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4 >> data5;
+
if (dataHead1 == 'U' && dataHead2 == 'K')
{
m_auiEncounter[0] = data0;
m_auiEncounter[1] = data1;
m_auiEncounter[2] = data2;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
forge_event[0] = data3;
forge_event[1] = data4;
forge_event[2] = data5;
+
}else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_utgarde_keep(Map* pMap)
{
return new instance_utgarde_keep(pMap);
}
+
void AddSC_instance_utgarde_keep()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp
index 0faf485eb98..6dd5b242517 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_keep/utgarde_keep.cpp
@@ -15,14 +15,17 @@
* 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"
#include "def_utgarde_keep.h"
+
uint32 entry_search[3] =
{
186609,
186610,
186611
};
+
struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
{
npc_dragonflayer_forge_masterAI(Creature *c) : ScriptedAI(c)
@@ -30,13 +33,16 @@ struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
pInstance = c->GetInstanceData();
fm_Type = 0;
}
+
ScriptedInstance* pInstance;
uint8 fm_Type;
+
void Reset()
{
if (fm_Type == 0) fm_Type = GetForgeMasterType();
CheckForge();
}
+
void CheckForge()
{
if (pInstance)
@@ -55,6 +61,7 @@ struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
}
}
}
+
void JustDied(Unit *killer)
{
if (fm_Type == 0) fm_Type = GetForgeMasterType();
@@ -74,6 +81,7 @@ struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit *who)
{
if (fm_Type == 0) fm_Type = GetForgeMasterType();
@@ -94,11 +102,13 @@ struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
}
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE ,EMOTE_ONESHOT_NONE);
}
+
uint8 GetForgeMasterType()
{
float diff = 30.0f;
int near_f = 0;
- for (uint8 i = 0; i < 3 ; ++i)
+
+ for(uint8 i = 0; i < 3 ; ++i)
{
GameObject* temp;
temp = m_creature->FindNearestGameObject(entry_search[i],30);
@@ -108,9 +118,11 @@ struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
{
near_f = i + 1;
diff = m_creature->GetDistance2d(temp);
+
}
}
}
+
switch (near_f)
{
case 1: return 1;
@@ -119,22 +131,28 @@ struct TRINITY_DLL_DECL npc_dragonflayer_forge_masterAI : public ScriptedAI
default: return 0;
}
}
+
void UpdateAI(const uint32 diff)
{
if (fm_Type == 0)
fm_Type = GetForgeMasterType();
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_dragonflayer_forge_master(Creature* pCreature)
{
return new npc_dragonflayer_forge_masterAI(pCreature);
}
+
void AddSC_utgarde_keep()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_dragonflayer_forge_master";
newscript->GetAI = &GetAI_npc_dragonflayer_forge_master;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp
index cec37e66377..db5394de8c3 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_palehoof.cpp
@@ -5,73 +5,92 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_palehoof' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_pinnacle.h"
+
//Spells
#define SPELL_ARCING_SMASH 48260
#define SPELL_IMPALE 48261
#define H_SPELL_IMPALE 59268
#define SPELL_WITHERING_ROAR 48256
#define H_SPELL_WITHERING_ROAR 59267
+
#define SPELL_FREEZE 16245
+
//ravenous furbolg's spells
#define SPELL_CHAIN_LIGHTING 48140
#define H_SPELL_CHAIN_LIGHTING 59273
#define SPELL_CRAZED 48139
#define SPELL_TERRIFYING_ROAR 48144
+
//frenzied worgen's spells
#define SPELL_MORTAL_WOUND 48137
#define H_SPELL_MORTAL_WOUND 59265
#define SPELL_ENRAGE_1 48138
#define SPELL_ENRAGE_2 48142
+
//ferocious rhino's spells
#define SPELL_GORE 48130
#define H_SPELL_GORE 59264
#define SPELL_GRIEVOUS_WOUND 48105
#define H_SPELL_GRIEVOUS_WOUND 59263
#define SPELL_STOMP 48131
+
//massive jormungar's spells
#define SPELL_ACID_SPIT 48132
#define SPELL_ACID_SPLATTER 48136
#define H_SPELL_ACID_SPLATTER 59272
#define SPELL_POISON_BREATH 48133
#define H_SPELL_POISON_BREATH 59271
+
#define CREATURE_JORMUNGAR_WORM 27228
+
//not in db
//Yell
#define SAY_AGGRO -1575000
#define SAY_SLAY_1 -1575001
#define SAY_SLAY_2 -1575002
#define SAY_DEATH -1575003
+
struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
{
boss_palehoofAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiArcingSmashTimer;
uint32 uiImpaleTimer;
uint32 uiWhiteringRoarTimer;
uint32 uiWaitingTimer;
+
uint8 Phase;
+
bool bWaiting;
+
ScriptedInstance *pInstance;
+
void Reset()
{
uiArcingSmashTimer = 15000;
uiImpaleTimer = 12000;
uiWhiteringRoarTimer = 10000;
+
Phase = 0;
bWaiting = false;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoCast(m_creature, SPELL_FREEZE);
+
if (pInstance)
{
pInstance->SetData(DATA_GORTOK_PALEHOOF_EVENT, NOT_STARTED);
+
Creature* pTemp;
if ((pTemp = Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_MOB_FRENZIED_WORGEN))) && !pTemp->isAlive())
pTemp->Respawn();
@@ -81,6 +100,7 @@ struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
pTemp->Respawn();
if ((pTemp = Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_MOB_RAVENOUS_FURBOLG))) && !pTemp->isAlive())
pTemp->Respawn();
+
if (GameObject* pGo = pInstance->instance->GetGameObject(pInstance->GetData64(DATA_GORTOK_PALEHOOF_SPHERE)))
{
pGo->SetGoState(GO_STATE_READY);
@@ -88,10 +108,12 @@ struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (Phase == 6)
@@ -99,22 +121,26 @@ struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiArcingSmashTimer < diff)
{
DoCast(m_creature,SPELL_ARCING_SMASH);
uiArcingSmashTimer = 13000 + rand()%4000;
} else uiArcingSmashTimer -= diff;
+
if (uiImpaleTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, HEROIC(SPELL_IMPALE, H_SPELL_IMPALE));
uiImpaleTimer = 8000 + rand()%4000;
} else uiImpaleTimer -= diff;
+
if (uiWhiteringRoarTimer < diff)
{
DoCast(m_creature, HEROIC(SPELL_WITHERING_ROAR, H_SPELL_WITHERING_ROAR));
uiWhiteringRoarTimer = 8000 + rand()%4000;
} else uiWhiteringRoarTimer -= diff;
+
DoMeleeAttackIfReady();
}
else if (Phase != 0 && bWaiting)
@@ -130,6 +156,7 @@ struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
case 4: pNext = Unit::GetCreature((*m_creature), pInstance ? pInstance->GetData64(DATA_MOB_FEROCIOUS_RHINO) : 0); break;
case 5: pNext = m_creature; ++Phase; break;
}
+
if (pNext)
{
pNext->RemoveAurasDueToSpell(SPELL_FREEZE);
@@ -138,20 +165,24 @@ struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
((Unit*)pNext)->SetStandState(UNIT_STAND_STATE_STAND);
pNext->SetInCombatWithZone();
}
+
bWaiting = false;
} else uiWaitingTimer -= diff;
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
if (pInstance)
pInstance->SetData(DATA_GORTOK_PALEHOOF_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void NextPhase()
{
++Phase;
@@ -159,53 +190,66 @@ struct TRINITY_DLL_DECL boss_palehoofAI : public ScriptedAI
uiWaitingTimer = 1000;
}
};
+
struct TRINITY_DLL_DECL mob_ravenous_furbolgAI : public ScriptedAI
{
mob_ravenous_furbolgAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiChainLightingTimer;
uint32 uiCrazedTimer;
uint32 uiTerrifyingRoarTimer;
+
ScriptedInstance *pInstance;
+
void Reset()
{
uiChainLightingTimer = 5000;
uiCrazedTimer = 10000;
uiTerrifyingRoarTimer = 15000;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoCast(m_creature, SPELL_FREEZE);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiChainLightingTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_CHAIN_LIGHTING, H_SPELL_CHAIN_LIGHTING));
uiChainLightingTimer = 5000 + rand()%5000;
} else uiChainLightingTimer -= diff;
+
if (uiCrazedTimer < diff)
{
DoCast(m_creature, SPELL_CRAZED);
uiCrazedTimer = 8000 + rand()%4000;
} else uiCrazedTimer -= diff;
+
if (uiTerrifyingRoarTimer < diff)
{
DoCast(m_creature, SPELL_TERRIFYING_ROAR);
uiTerrifyingRoarTimer = 10000 + rand()%10000;
} else uiTerrifyingRoarTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (m_creature->Attack(who, true))
{
m_creature->AddThreat(who, 0.0f);
@@ -214,6 +258,7 @@ struct TRINITY_DLL_DECL mob_ravenous_furbolgAI : public ScriptedAI
DoStartMovement(who);
}
}
+
void JustDied(Unit* killer)
{
if (pInstance)
@@ -224,53 +269,66 @@ struct TRINITY_DLL_DECL mob_ravenous_furbolgAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_frenzied_worgenAI : public ScriptedAI
{
mob_frenzied_worgenAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiMortalWoundTimer;
uint32 uiEnrage1Timer;
uint32 uiEnrage2Timer;
+
ScriptedInstance *pInstance;
+
void Reset()
{
uint32 uiMortalWoundTimer = 5000;
uint32 uiEnrage1Timer = 15000;
uint32 uiEnrage2Timer = 10000;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoCast(m_creature, SPELL_FREEZE);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiMortalWoundTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_MORTAL_WOUND, H_SPELL_MORTAL_WOUND));
uiMortalWoundTimer = 3000 + rand()%4000;
} else uiMortalWoundTimer -= diff;
+
if (uiEnrage1Timer < diff)
{
DoCast(m_creature, SPELL_ENRAGE_1);
uiEnrage1Timer = 15000;
} else uiEnrage1Timer -= diff;
+
if (uiEnrage2Timer < diff)
{
DoCast(m_creature, SPELL_ENRAGE_2);
uiEnrage2Timer = 10000;
} else uiEnrage2Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (m_creature->Attack(who, true))
{
m_creature->AddThreat(who, 0.0f);
@@ -281,6 +339,7 @@ struct TRINITY_DLL_DECL mob_frenzied_worgenAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_GORTOK_PALEHOOF_EVENT, IN_PROGRESS);
}
+
void JustDied(Unit* killer)
{
if (pInstance)
@@ -291,54 +350,67 @@ struct TRINITY_DLL_DECL mob_frenzied_worgenAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_ferocious_rhinoAI : public ScriptedAI
{
mob_ferocious_rhinoAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiStompTimer;
uint32 uiGoreTimer;
uint32 uiGrievousWoundTimer;
+
ScriptedInstance *pInstance;
+
void Reset()
{
uiStompTimer = 10000;
uiGoreTimer = 15000;
uiGrievousWoundTimer = 20000;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoCast(m_creature, SPELL_FREEZE);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiStompTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_STOMP);
uiStompTimer = 8000 + rand()%4000;
} else uiStompTimer -= diff;
+
if (uiGoreTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_GORE, H_SPELL_GORE));
uiGoreTimer = 13000 + rand()%4000;
} else uiGoreTimer -= diff;
+
if (uiGrievousWoundTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, HEROIC(SPELL_GRIEVOUS_WOUND, H_SPELL_GRIEVOUS_WOUND));
uiGrievousWoundTimer = 18000 + rand()%4000;
} else uiGrievousWoundTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (m_creature->Attack(who, true))
{
m_creature->AddThreat(who, 0.0f);
@@ -347,6 +419,7 @@ struct TRINITY_DLL_DECL mob_ferocious_rhinoAI : public ScriptedAI
DoStartMovement(who);
}
}
+
void JustDied(Unit* killer)
{
if (pInstance)
@@ -357,55 +430,68 @@ struct TRINITY_DLL_DECL mob_ferocious_rhinoAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_massive_jormungarAI : public ScriptedAI
{
mob_massive_jormungarAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiAcidSpitTimer;
uint32 uiAcidSplatterTimer;
uint32 uiPoisonBreathTimer;
+
ScriptedInstance *pInstance;
+
void Reset()
{
uiAcidSpitTimer = 3000;
uiAcidSplatterTimer = 12000;
uiPoisonBreathTimer = 10000;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoCast(m_creature, SPELL_FREEZE);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiAcidSpitTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, SPELL_ACID_SPIT);
uiAcidSpitTimer = 2000 + rand()%2000;
} else uiAcidSpitTimer -= diff;
+
if (uiAcidSplatterTimer < diff)
{
DoCast(m_creature, HEROIC(SPELL_POISON_BREATH, H_SPELL_POISON_BREATH));
uiAcidSplatterTimer = 10000 + rand()%4000;
} else uiAcidSplatterTimer -= diff;
+
if (uiPoisonBreathTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, HEROIC(SPELL_POISON_BREATH, H_SPELL_POISON_BREATH));
uiPoisonBreathTimer = 8000 + rand()%4000;
} else uiPoisonBreathTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
void AttackStart(Unit* who)
{
if (!who)
return;
+
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (m_creature->Attack(who, true))
{
m_creature->AddThreat(who, 0.0f);
@@ -414,6 +500,7 @@ struct TRINITY_DLL_DECL mob_massive_jormungarAI : public ScriptedAI
DoStartMovement(who);
}
}
+
void JustDied(Unit* killer)
{
if (pInstance)
@@ -424,62 +511,77 @@ struct TRINITY_DLL_DECL mob_massive_jormungarAI : public ScriptedAI
}
}
};
+
bool GOHello_palehoof_sphere(Player *pPlayer, GameObject *pGO)
{
ScriptedInstance *pInstance = pGO->GetInstanceData();
+
Creature *pPalehoof = Unit::GetCreature(*pGO, pInstance ? pInstance->GetData64(DATA_GORTOK_PALEHOOF) : 0);
if (pPalehoof && pPalehoof->isAlive())
{
// maybe these are hacks :(
pGO->SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_UNK1);
pGO->SetGoState(GO_STATE_ACTIVE);
+
CAST_AI(boss_palehoofAI, pPalehoof->AI())->NextPhase();
}
return true;
}
+
CreatureAI* GetAI_boss_palehoof(Creature* pCreature)
{
return new boss_palehoofAI (pCreature);
}
+
CreatureAI* GetAI_mob_ravenous_furbolg(Creature* pCreature)
{
return new mob_ravenous_furbolgAI (pCreature);
}
+
CreatureAI* GetAI_mob_frenzied_worgen(Creature* pCreature)
{
return new mob_frenzied_worgenAI (pCreature);
}
+
CreatureAI* GetAI_mob_ferocious_rhino(Creature* pCreature)
{
return new mob_ferocious_rhinoAI (pCreature);
}
+
CreatureAI* GetAI_mob_massive_jormungar(Creature* pCreature)
{
return new mob_massive_jormungarAI (pCreature);
}
+
void AddSC_boss_palehoof()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_palehoof";
newscript->GetAI = &GetAI_boss_palehoof;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ravenous_furbolg";
newscript->GetAI = &GetAI_mob_ravenous_furbolg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_frenzied_worgen";
newscript->GetAI = &GetAI_mob_frenzied_worgen;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ferocious_rhino";
newscript->GetAI = &GetAI_mob_ferocious_rhino;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_massive_jormungar";
newscript->GetAI = &GetAI_mob_massive_jormungar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_palehoof_sphere";
newscript->pGOHello=&GOHello_palehoof_sphere;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp
index 1b1fa225049..4710d8dec82 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_skadi.cpp
@@ -5,14 +5,17 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_skadi' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_pinnacle.h"
+
//Phase 0 "gauntlet even" Skadi on a flying mount, waves of adds charging to the group periodicaly carrying harpoons
//Phase 1 Kill the Skadi drake mount with harppons launcher
//Phase 2 Kill the Skadi
+
//Skadi Spells
#define SPELL_CRUSH 50234
#define H_SPELL_CRUSH 59330
@@ -20,11 +23,14 @@ update creature_template set scriptname = 'boss_skadi' where entry = '';
#define H_SPELL_POISONED_SPEAR 59331
#define SPELL_WHIRLWIND 50228 //random target, but not the tank approx. every 20s
#define H_SPELL_WHIRLWIND 59332
+
//Spawned creatures
#define CREATURE_YMIRJAR_WARRIOR 26690
#define CREATURE_YMIRJAR_WITCH_DOCTOR 26691
#define CREATURE_YMIRJAR_HARPOONER 26692
+
#define DATA_MOUNT 27043
+
//not in db
//Yell
#define SAY_AGGRO -1575004
@@ -38,12 +44,14 @@ update creature_template set scriptname = 'boss_skadi' where entry = '';
#define SAY_DRAKE_BREATH_1 -1575012
#define SAY_DRAKE_BREATH_2 -1575013
#define SAY_DRAKE_BREATH_3 -1575014
+
//Spawn locations
struct Locations
{
float x, y, z;
uint32 id;
};
+
static Locations SpawnLoc[]=
{
{340.556, -511.493, 104.352},
@@ -52,50 +60,65 @@ static Locations SpawnLoc[]=
{430.551, -514.320, 105.055},
{468.931, -513.555, 104.723}
};
+
enum CombatPhase
{
FLYING,
SKADI
};
+
struct TRINITY_DLL_DECL boss_skadiAI : public ScriptedAI
{
boss_skadiAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiCrushTimer;
uint32 uiPoisonedSpearTimer;
uint32 uiWhirlwindTimer;
uint32 uiMovementTimer;
uint32 uiWaypointId;
uint32 uiSpawnCounter;
+
CombatPhase Phase;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiCrushTimer = 8000;
uiPoisonedSpearTimer = 10000;
uiWhirlwindTimer = 20000;
uiSpawnCounter = 0;
+
uiWaypointId = 0;
+
Phase = SKADI;
+
m_creature->Unmount();
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_SKADI_THE_RUTHLESS_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
m_creature->Mount(DATA_MOUNT);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->GetMotionMaster()->MovePoint(uiWaypointId, 340.259, -510.541, 120.869);
+
Phase = FLYING;
+
if (pInstance)
pInstance->SetData(DATA_SKADI_THE_RUTHLESS_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
switch(Phase)
@@ -123,40 +146,49 @@ struct TRINITY_DLL_DECL boss_skadiAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiCrushTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_CRUSH, H_SPELL_CRUSH));
uiCrushTimer = 8000;
} else uiCrushTimer -= diff;
+
if (uiPoisonedSpearTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
DoCast(pTarget, HEROIC(SPELL_POISONED_SPEAR, H_SPELL_POISONED_SPEAR));
uiPoisonedSpearTimer = 10000;
} else uiPoisonedSpearTimer -= diff;
+
if (uiWhirlwindTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM,0,100,true))
m_creature->CastSpell(pTarget, HEROIC(SPELL_WHIRLWIND, H_SPELL_WHIRLWIND), false);
} else uiWhirlwindTimer = 20000;
+
DoMeleeAttackIfReady();
break;
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_SKADI_THE_RUTHLESS_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2,SAY_KILL_3), m_creature);
}
+
void MovementInform(uint32 type, uint32 id)
{
if(type != POINT_MOTION_TYPE)
return;
+
if (HeroicMode ? (uiSpawnCounter >= 4) : (uiSpawnCounter >= 5))
{
uiWaypointId = 200;
@@ -181,10 +213,11 @@ struct TRINITY_DLL_DECL boss_skadiAI : public ScriptedAI
}
}
}
+
void SpawnMobs(uint32 spot)
{
uint8 uiMaxSpawn = (HeroicMode ? 6 : 5);
- for (uint8 i = 0; i < uiMaxSpawn; ++i)
+ for(uint8 i = 0; i < uiMaxSpawn; ++i)
{
Creature* pTemp;
switch (rand()%3)
@@ -202,13 +235,16 @@ struct TRINITY_DLL_DECL boss_skadiAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_boss_skadi(Creature* pCreature)
{
return new boss_skadiAI (pCreature);
}
+
void AddSC_boss_skadi()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_skadi";
newscript->GetAI = &GetAI_boss_skadi;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp
index ef2fc77b4c7..137b445e93a 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_svala.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_svala' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_pinnacle.h"
+
enum Spells
{
SPELL_CALL_FLAMES = 48258,
@@ -73,26 +75,35 @@ static Locations RitualChannelerLocations[]=
{302.36, -352.01, 90.54},
{291.39, -350.89, 90.54}
};
+
struct TRINITY_DLL_DECL boss_svalaAI : public ScriptedAI
{
boss_svalaAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiIntroTimer;
+
uint8 uiIntroPhase;
+
IntroPhase Phase;
+
Creature* pArthas;
+
ScriptedInstance* pInstance;
+
void Reset()
{
Phase = IDLE;
uiIntroTimer = 1000;
uiIntroPhase = 0;
pArthas = NULL;
+
if (pInstance)
pInstance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, NOT_STARTED);
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (!pWho)
@@ -101,6 +112,7 @@ struct TRINITY_DLL_DECL boss_svalaAI : public ScriptedAI
{
Phase = INTRO;
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
if (pArthas = m_creature->SummonCreature(CREATURE_ARTHAS, 295.81, -366.16, 92.57, 1.58, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 20000))
{
pArthas->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -109,15 +121,19 @@ struct TRINITY_DLL_DECL boss_svalaAI : public ScriptedAI
}
}
}
+
void AttackStart(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (Phase != INTRO)
return;
+
if (uiIntroTimer < diff)
{
if(!pArthas)
return;
+
switch (uiIntroPhase)
{
case 0:
@@ -166,17 +182,21 @@ struct TRINITY_DLL_DECL boss_svalaAI : public ScriptedAI
} else uiIntroTimer -= diff;
}
};
+
struct TRINITY_DLL_DECL mob_ritual_channelerAI : public ScriptedAI
{
mob_ritual_channelerAI(Creature *c) :ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
void Reset()
{
DoCast(m_creature, SPELL_SHADOWS_IN_THE_DARK);
}
+
void EnterCombat(Unit* who)
{
if (who && who->HasAura(SPELL_PARALYZE,0))
@@ -184,40 +204,53 @@ struct TRINITY_DLL_DECL mob_ritual_channelerAI : public ScriptedAI
return;
}
};
+
struct TRINITY_DLL_DECL boss_svala_sorrowgraveAI : public ScriptedAI
{
boss_svala_sorrowgraveAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiSinsterStrikeTimer;
uint32 uiCallFlamesTimer;
uint32 uiRitualOfSwordTimer;
uint32 uiSacrificeTimer;
+
CombatPhase Phase;
+
Creature* pRitualChanneler[3];
Unit* pSacrificeTarget;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiSinsterStrikeTimer = 7000;
uiCallFlamesTimer = 10000;
uiRitualOfSwordTimer = 20000;
uiSacrificeTimer = 8000;
+
Phase = NORMAL;
+
DoTeleportTo(296.632, -346.075, 90.6307);
+
for (uint8 i = 0; i < 3; ++i)
pRitualChanneler[i] = NULL;
pSacrificeTarget = NULL;
+
if (pInstance)
pInstance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (Phase == NORMAL)
@@ -225,11 +258,13 @@ struct TRINITY_DLL_DECL boss_svala_sorrowgraveAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiSinsterStrikeTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_SINSTER_STRIKE, H_SPELL_SINSTER_STRIKE));
uiSinsterStrikeTimer = 5000 + rand()%4000;
} else uiSinsterStrikeTimer -= diff;
+
if (uiCallFlamesTimer < diff)
{
Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0);
@@ -239,6 +274,7 @@ struct TRINITY_DLL_DECL boss_svala_sorrowgraveAI : public ScriptedAI
uiCallFlamesTimer = 8000 + rand()%4000;
}
} else uiCallFlamesTimer -= diff;
+
if (uiRitualOfSwordTimer < diff)
{
pSacrificeTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
@@ -251,13 +287,16 @@ struct TRINITY_DLL_DECL boss_svala_sorrowgraveAI : public ScriptedAI
m_creature->SetUnitMovementFlags(MOVEMENTFLAG_FLY_MODE);
DoTeleportTo(296.632, -346.075, 120.85);
Phase = SACRIFICING;
+
for (uint8 i = 0; i < 3; ++i)
if (pRitualChanneler[i] = m_creature->SummonCreature(CREATURE_RITUAL_CHANNELER, RitualChannelerLocations[i].x, RitualChannelerLocations[i].y, RitualChannelerLocations[i].z, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000))
if (mob_ritual_channelerAI *pChannelerAI = CAST_AI(mob_ritual_channelerAI,pRitualChanneler[i]->AI()))
pChannelerAI->AttackStartNoMove(pSacrificeTarget);
+
uiRitualOfSwordTimer = urand(18000,22000);
}
} else uiRitualOfSwordTimer -= diff;
+
DoMeleeAttackIfReady();
}
else //SACRIFICING
@@ -275,21 +314,25 @@ struct TRINITY_DLL_DECL boss_svala_sorrowgraveAI : public ScriptedAI
}
if (bSacrificed && pSacrificeTarget && pSacrificeTarget->isAlive())
m_creature->Kill(pSacrificeTarget, false); // durability damage?
+
//go down
Phase = NORMAL;
pSacrificeTarget = NULL;
m_creature->SetUnitMovementFlags(MOVEMENTFLAG_WALK_MODE);
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
m_creature->GetMotionMaster()->MoveChase(pTarget);
+
uiSacrificeTimer = 8000;
}
else uiSacrificeTimer -= diff;
}
}
+
void KilledUnit(Unit* pVictim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void JustDied(Unit* pKiller)
{
if (pInstance)
@@ -297,34 +340,42 @@ struct TRINITY_DLL_DECL boss_svala_sorrowgraveAI : public ScriptedAI
Creature* pSvala = Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_SVALA));
if (pSvala && pSvala->isAlive())
pKiller->Kill(pSvala);
+
pInstance->SetData(DATA_SVALA_SORROWGRAVE_EVENT, IN_PROGRESS);
}
DoScriptText(SAY_DEATH, m_creature);
}
};
+
CreatureAI* GetAI_boss_svala(Creature* pCreature)
{
return new boss_svalaAI (pCreature);
}
+
CreatureAI* GetAI_mob_ritual_channeler(Creature* pCreature)
{
return new mob_ritual_channelerAI(pCreature);
}
+
CreatureAI* GetAI_boss_svala_sorrowgrave(Creature* pCreature)
{
return new boss_svala_sorrowgraveAI(pCreature);
}
+
void AddSC_boss_svala()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_svala";
newscript->GetAI = &GetAI_boss_svala;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ritual_channeler";
newscript->GetAI = &GetAI_mob_ritual_channeler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_svala_sorrowgrave";
newscript->GetAI = &GetAI_boss_svala_sorrowgrave;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp
index 0ac94a7f4f5..df72278c684 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/boss_ymiron.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = 'boss_ymiron' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_pinnacle.h"
+
enum Spells
{
SPELL_BANE = 48294,
@@ -23,13 +25,17 @@ enum Spells
SPELL_SPIRIT_STRIKE = 48423,
H_SPELL_SPIRIT_STRIKE = 59304,
SPELL_ANCESTORS_VENGEANCE = 16939,
+
SPELL_SUMMON_AVENGING_SPIRIT = 48592,
SPELL_SUMMON_SPIRIT_FOUNT = 48386,
+
SPELL_CHANNEL_SPIRIT_TO_YMIRON = 48316,
SPELL_CHANNEL_YMIRON_TO_SPIRIT = 48307,
+
SPELL_SPIRIT_FOUNT = 48380,
H_SPELL_SPIRIT_FOUNT = 59320
};
+
//not in db
enum Yells
{
@@ -44,6 +50,7 @@ enum Yells
SAY_SUMMON_RANULF = -1575036,
SAY_SUMMON_TORGYN = -1575037
};
+
enum Creatures
{
CREATURE_BJORN = 27303,
@@ -57,12 +64,14 @@ enum Creatures
CREATURE_SPIRIT_FOUNT = 27339,
CREATURE_AVENGING_SPIRIT = 27386
};
+
struct ActiveBoatStruct
{
uint32 npc;
uint32 say;
float MoveX,MoveY,MoveZ,SpawnX,SpawnY,SpawnZ,SpawnO;
};
+
static ActiveBoatStruct ActiveBot[4] =
{
{CREATURE_BJORN_VISUAL, SAY_SUMMON_BJORN, 404.379, -335.335, 104.756, 413.594, -335.408, 107.995, 3.157},
@@ -70,6 +79,7 @@ static ActiveBoatStruct ActiveBot[4] =
{CREATURE_RANULF_VISUAL, SAY_SUMMON_RANULF, 381.546, -314.362, 104.756, 370.841, -314.426, 107.995, 6.232},
{CREATURE_TORGYN_VISUAL, SAY_SUMMON_TORGYN, 404.310, -314.761, 104.756, 413.992, -314.703, 107.995, 3.157}
};
+
struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
{
boss_ymironAI(Creature *c) : ScriptedAI(c)
@@ -86,28 +96,36 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
m_uiActiveOrder[r] = temp;
}
}
+
bool m_bIsWalking;
bool m_bIsPause;
bool m_bIsActiveWithBJORN;
bool m_bIsActiveWithHALDOR;
bool m_bIsActiveWithRANULF;
bool m_bIsActiveWithTORGYN;
+
uint8 m_uiActiveOrder[4];
uint8 m_uiActivedNumber;
+
uint32 m_uiFetidRot_Timer;
uint32 m_uiBane_Timer;
uint32 m_uiDarkSlash_Timer;
uint32 m_uiAncestors_Vengeance_Timer;
+
uint32 m_uiAbility_BJORN_Timer;
uint32 m_uiAbility_HALDOR_Timer;
uint32 m_uiAbility_RANULF_Timer;
uint32 m_uiAbility_TORGYN_Timer;
+
uint32 m_uiPause_Timer;
uint32 m_uiHealthAmountModifier;
uint32 m_uiHealthAmountMultipler;
+
uint64 m_uiActivedCreatureGUID;
uint64 m_uiOrbGUID;
+
ScriptedInstance *pInstance;
+
void Reset()
{
m_bIsPause = false;
@@ -115,29 +133,37 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
m_bIsActiveWithHALDOR = false;
m_bIsActiveWithRANULF = false;
m_bIsActiveWithTORGYN = false;
+
m_uiFetidRot_Timer = urand(8000,13000);
m_uiBane_Timer = urand(18000,23000);
m_uiDarkSlash_Timer = urand(28000,33000);
m_uiAncestors_Vengeance_Timer = HEROIC(60000,45000);
m_uiPause_Timer = 0;
+
m_uiAbility_BJORN_Timer = 0;
m_uiAbility_HALDOR_Timer = 0;
m_uiAbility_RANULF_Timer = 0;
m_uiAbility_TORGYN_Timer = 0;
+
m_uiActivedNumber = 0;
m_uiHealthAmountModifier = 1;
m_uiHealthAmountMultipler = HEROIC(20,25);
+
DespawnBoatGhosts(m_uiActivedCreatureGUID);
DespawnBoatGhosts(m_uiOrbGUID);
+
if (pInstance)
pInstance->SetData(DATA_KING_YMIRON_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_KING_YMIRON_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (m_bIsWalking)
@@ -161,6 +187,7 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
case 3: m_bIsActiveWithTORGYN = true; break;
}
}
+
m_bIsPause = true;
m_bIsWalking = false;
m_uiPause_Timer = 3000;
@@ -175,14 +202,17 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
m_uiAbility_HALDOR_Timer = 5000;
m_uiAbility_RANULF_Timer = 5000;
m_uiAbility_TORGYN_Timer = 5000;
+
m_bIsPause = false;
m_uiPause_Timer = 0;
} else m_uiPause_Timer -= diff;
return;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (!m_bIsPause)
{
// Normal spells ------------------------------------------------------------------------
@@ -191,21 +221,25 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
DoCast(m_creature, HEROIC(SPELL_BANE, H_SPELL_BANE));
m_uiBane_Timer = urand(20000,25000);
} else m_uiBane_Timer -= diff;
+
if (m_uiFetidRot_Timer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_FETID_ROT, H_SPELL_FETID_ROT));
m_uiFetidRot_Timer = urand(10000,15000);
} else m_uiFetidRot_Timer -= diff;
+
if (m_uiDarkSlash_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_DARK_SLASH);
m_uiDarkSlash_Timer = urand(30000,35000);
} else m_uiDarkSlash_Timer -= diff;
+
if (m_uiAncestors_Vengeance_Timer < diff)
{
DoCast(m_creature, SPELL_ANCESTORS_VENGEANCE);
m_uiAncestors_Vengeance_Timer = HEROIC(urand(60000,65000),urand(45000,50000));
} else m_uiAncestors_Vengeance_Timer -= diff;
+
// Abilities ------------------------------------------------------------------------------
if (m_bIsActiveWithBJORN && m_uiAbility_BJORN_Timer < diff)
{
@@ -222,23 +256,26 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
}
m_bIsActiveWithBJORN = false; // only one orb
} else m_uiAbility_BJORN_Timer -= diff;
+
if (m_bIsActiveWithHALDOR && m_uiAbility_HALDOR_Timer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_SPIRIT_STRIKE, H_SPELL_SPIRIT_STRIKE));
m_uiAbility_HALDOR_Timer = 5000; // overtime
} else m_uiAbility_HALDOR_Timer -= diff;
+
if (m_bIsActiveWithRANULF && m_uiAbility_RANULF_Timer < diff)
{
DoCast(m_creature, HEROIC(SPELL_SPIRIT_BURST, H_SPELL_SPIRIT_BURST));
m_uiAbility_RANULF_Timer = 10000; // overtime
} else m_uiAbility_RANULF_Timer -= diff;
+
if (m_bIsActiveWithTORGYN && m_uiAbility_TORGYN_Timer < diff)
{
float x,y,z;
x = m_creature->GetPositionX()-5;
y = m_creature->GetPositionY()-5;
z = m_creature->GetPositionZ();
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
//DoCast(m_creature, SPELL_SUMMON_AVENGING_SPIRIT); // works fine, but using summon has better control
if (Creature* pTemp = m_creature->SummonCreature(CREATURE_AVENGING_SPIRIT, x+rand()%10, y+rand()%10, z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
@@ -252,27 +289,33 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
}
m_uiAbility_TORGYN_Timer = 15000; // overtime
} else m_uiAbility_TORGYN_Timer -= diff;
+
// Health check -----------------------------------------------------------------------------
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < (100-(m_uiHealthAmountMultipler * m_uiHealthAmountModifier)))
{
uint8 m_uiOrder = m_uiHealthAmountModifier - 1;
++m_uiHealthAmountModifier;
+
m_creature->InterruptNonMeleeSpells(true);
DoCast(m_creature, SPELL_SCREAMS_OF_THE_DEAD);
m_creature->GetMotionMaster()->Clear();
m_creature->StopMoving();
m_creature->AttackStop();
m_creature->GetMotionMaster()->MovePoint(0, ActiveBot[m_uiActiveOrder[m_uiOrder]].MoveX, ActiveBot[m_uiActiveOrder[m_uiOrder]].MoveY, ActiveBot[m_uiActiveOrder[m_uiOrder]].MoveZ);
+
DespawnBoatGhosts(m_uiActivedCreatureGUID);
DespawnBoatGhosts(m_uiOrbGUID);
+
m_bIsActiveWithBJORN = false;
m_bIsActiveWithHALDOR = false;
m_bIsActiveWithRANULF = false;
m_bIsActiveWithTORGYN = false;
+
m_uiBane_Timer += 8000;
m_uiFetidRot_Timer += 8000;
m_uiDarkSlash_Timer += 8000;
m_uiAncestors_Vengeance_Timer += 8000;
+
m_uiActivedNumber = m_uiOrder;
m_bIsWalking = true;
m_uiPause_Timer = 2000;
@@ -281,33 +324,42 @@ struct TRINITY_DLL_DECL boss_ymironAI : public ScriptedAI
DoMeleeAttackIfReady();
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
DespawnBoatGhosts(m_uiActivedCreatureGUID);
DespawnBoatGhosts(m_uiOrbGUID);
+
if (pInstance)
pInstance->SetData(DATA_KING_YMIRON_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3,SAY_SLAY_4), m_creature);
}
+
void DespawnBoatGhosts(uint64& m_uiCreatureGUID)
{
if (m_uiCreatureGUID)
if (Creature* pTemp = (Creature*)Unit::GetUnit(*m_creature, m_uiCreatureGUID))
pTemp->DisappearAndDie();
+
m_uiCreatureGUID = 0;
}
};
+
CreatureAI* GetAI_boss_ymiron(Creature* pCreature)
{
return new boss_ymironAI(pCreature);
}
+
void AddSC_boss_ymiron()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ymiron";
newscript->GetAI = &GetAI_boss_ymiron;
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/def_pinnacle.h b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/def_pinnacle.h
index fd1e0efa312..b446b441c17 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/def_pinnacle.h
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/def_pinnacle.h
@@ -1,17 +1,21 @@
#ifndef DEF_PINNACLE_H
#define DEF_PINNACLE_H
+
#define DATA_SVALA 1
#define DATA_SVALA_SORROWGRAVE 2
#define DATA_GORTOK_PALEHOOF 3
#define DATA_SKADI_THE_RUTHLESS 4
#define DATA_KING_YMIRON 5
+
#define DATA_SVALA_SORROWGRAVE_EVENT 6
#define DATA_GORTOK_PALEHOOF_EVENT 7
#define DATA_SKADI_THE_RUTHLESS_EVENT 8
#define DATA_KING_YMIRON_EVENT 9
+
#define DATA_MOB_FRENZIED_WORGEN 10
#define DATA_MOB_RAVENOUS_FURBOLG 11
#define DATA_MOB_MASSIVE_JORMUNGAR 12
#define DATA_MOB_FEROCIOUS_RHINO 13
+
#define DATA_GORTOK_PALEHOOF_SPHERE 14
#endif
diff --git a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp
index 0aaa243e229..7eb661668df 100644
--- a/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp
+++ b/src/bindings/scripts/scripts/northrend/utgarde_keep/utgarde_pinnacle/instance_pinnacle.cpp
@@ -1,43 +1,57 @@
#include "precompiled.h"
#include "def_pinnacle.h"
+
#define MAX_ENCOUNTER 4
+
/* Utgarde Pinnacle encounters:
0 - Svala Sorrowgrave
1 - Gortok Palehoof
2 - Skadi the Ruthless
3 - King Ymiron
*/
+
#define ENTRY_SKADI_THE_RUTHLESS_DOOR 192173
#define ENTRY_KING_YMIRON_DOOR 192174
#define ENTRY_GORK_PALEHOOF_SPHERE 188593
+
struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
{
instance_pinnacle(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 uiSvalaSorrowgrave;
uint64 uiGortokPalehoof;
uint64 uiSkadiTheRuthless;
uint64 uiKingYmiron;
+
uint64 uiSkadiTheRuthlessDoor;
uint64 uiKingYmironDoor;
uint64 uiGortokPalehoofSphere;
+
uint64 uiFrenziedWorgen;
uint64 uiRavenousFurbolg;
uint64 uiFerociousRhino;
uint64 uiMassiveJormungar;
+
uint64 uiSvala;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
std::string str_data;
+
void Initialize()
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
m_auiEncounter[i] = NOT_STARTED;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -53,6 +67,7 @@ struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
case 29281: uiSvala = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -75,6 +90,7 @@ struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -96,9 +112,11 @@ struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
m_auiEncounter[3] = data;
break;
}
+
if (data == DONE)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -110,6 +128,7 @@ struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -125,18 +144,24 @@ struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
case DATA_SVALA: return uiSvala;
case DATA_GORTOK_PALEHOOF_SPHERE: return uiGortokPalehoofSphere;
}
+
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << "U P " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << m_auiEncounter[3];
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -144,28 +169,37 @@ struct TRINITY_DLL_DECL instance_pinnacle : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0, data1, data2, data3;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3;
+
if (dataHead1 == 'U' && dataHead2 == 'K')
{
m_auiEncounter[0] = data0;
m_auiEncounter[1] = data1;
m_auiEncounter[2] = data2;
m_auiEncounter[3] = data3;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
} else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_utgarde_pinnacle(Map* pMap)
{
return new instance_pinnacle(pMap);
}
+
void AddSC_instance_utgarde_pinnacle()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_archavon.cpp b/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_archavon.cpp
index 2c81d20a46d..34e0cdc61b2 100644
--- a/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_archavon.cpp
+++ b/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_archavon.cpp
@@ -4,10 +4,12 @@ UPDATE `creature_template` SET `ScriptName`='mob_archavon_warder' WHERE `entry`=
*** SQL END ***/
#include "precompiled.h"
#include "def_vault_of_archavon.h"
+
#define EMOTE_BERSERK -1590002
+
//Spells Archavon
#define SPELL_ROCK_SHARDS 58678
-#define SPELL_CRUSHING_LEAP HEROIC(58960,60894) //Instant (10-80yr range) -- Leaps at an enemy, inflicting 8000 Physical damage, knocking all nearby enemies away, and creating a cloud of choking debris.
+#define SPELL_CRUSHING_LEAP HEROIC(58960,60894)//Instant (10-80yr range) -- Leaps at an enemy, inflicting 8000 Physical damage, knocking all nearby enemies away, and creating a cloud of choking debris.
#define SPELL_STOMP HEROIC(58663,60880)
#define SPELL_IMPALE HEROIC(58666,60882) //Lifts an enemy off the ground with a spiked fist, inflicting 47125 to 52875 Physical damage and 9425 to 10575 additional damage each second for 8 sec.
#define SPELL_BERSERK 47008
@@ -15,39 +17,50 @@ UPDATE `creature_template` SET `ScriptName`='mob_archavon_warder' WHERE `entry`=
#define SPELL_ROCK_SHOWER HEROIC(60919,60923)
#define SPELL_SHIELD_CRUSH HEROIC(60897,60899)
#define SPELL_WHIRL HEROIC(60902,60916)
+
//4 Warders spawned
#define ARCHAVON_WARDER 32353 //npc 32353
+
//Yell
#define SAY_LEAP "Archavon the Stone Watcher lunges for $N!" //$N should be the target
+
#define EVENT_ROCK_SHARDS 1 //15s cd
#define EVENT_CHOKING_CLOUD 2 //30s cd
#define EVENT_STOMP 3 //45s cd
#define EVENT_IMPALE 4
#define EVENT_BERSERK 5 //300s cd
+
//mob
#define EVENT_ROCK_SHOWER 5 //set = 20s cd,unkown cd
#define EVENT_SHIELD_CRUSH 6 //set = 30s cd
#define EVENT_WHIRL 8 //set= 10s cd
+
struct TRINITY_DLL_DECL boss_archavonAI : public ScriptedAI
{
boss_archavonAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
EventMap events;
+
void Reset()
{
events.Reset();
+
if (pInstance)
pInstance->SetData(DATA_ARCHAVON_EVENT, NOT_STARTED);
}
+
void KilledUnit(Unit* Victim){}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(DATA_ARCHAVON_EVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
@@ -55,18 +68,23 @@ struct TRINITY_DLL_DECL boss_archavonAI : public ScriptedAI
events.ScheduleEvent(EVENT_CHOKING_CLOUD, 30000);
events.ScheduleEvent(EVENT_STOMP, 45000);
events.ScheduleEvent(EVENT_BERSERK, 300000);
+
if (pInstance)
pInstance->SetData(DATA_ARCHAVON_EVENT, IN_PROGRESS);
}
+
// Below UpdateAI may need review/debug.
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
events.Update(diff);
+
if (me->hasUnitState(UNIT_STAT_CASTING))
return;
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -95,20 +113,25 @@ struct TRINITY_DLL_DECL boss_archavonAI : public ScriptedAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
/*######
## Mob Archavon Warder
######*/
struct TRINITY_DLL_DECL mob_warderAI : public ScriptedAI //npc 32353
{
mob_warderAI(Creature *c) : ScriptedAI(c) {}
+
EventMap events;
+
void Reset()
{
events.Reset();
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
@@ -116,12 +139,15 @@ struct TRINITY_DLL_DECL mob_warderAI : public ScriptedAI //npc 32353
events.ScheduleEvent(EVENT_SHIELD_CRUSH, 20000);
events.ScheduleEvent(EVENT_WHIRL, 7500);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -146,21 +172,26 @@ struct TRINITY_DLL_DECL mob_warderAI : public ScriptedAI //npc 32353
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_warder(Creature* pCreature)
{
return new mob_warderAI (pCreature);
}
+
CreatureAI* GetAI_boss_archavon(Creature* pCreature)
{
return new boss_archavonAI (pCreature);
}
+
void AddSC_boss_archavon()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_archavon";
newscript->GetAI = &GetAI_boss_archavon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_archavon_warder";
newscript->GetAI = &GetAI_mob_warder;
diff --git a/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_emalon.cpp b/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_emalon.cpp
index d7564ea7a6b..0f240bd8692 100644
--- a/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_emalon.cpp
+++ b/src/bindings/scripts/scripts/northrend/vault_of_archavon/boss_emalon.cpp
@@ -1,27 +1,34 @@
#include "precompiled.h"
#include "def_vault_of_archavon.h"
+
//Emalon spells
#define SPELL_CHAIN_LIGHTNING HEROIC(64213, 64215)
#define SPELL_LIGHTNING_NOVA HEROIC(64216, 65279)
#define SPELL_OVERCHARGE 64218 //Casted every 45 sec on a random Tempest Minion
#define SPELL_BERSERK 26662
+
//Tempest Minion spells
#define SPELL_SHOCK 64363
#define SPELL_OVERCHARGED 64217
#define SPELL_OVERCHARGED_BLAST 64219 //Casted when Overcharged reaches 10 stacks. Mob dies after that
+
//Emotes
#define EMOTE_OVERCHARGE -1590000
#define EMOTE_MINION_RESPAWN -1590001
#define EMOTE_BERSERK -1590002
+
//Events
#define EVENT_CHAIN_LIGHTNING 1
#define EVENT_LIGHTNING_NOVA 2
#define EVENT_OVERCHARGE 3
#define EVENT_BERSERK 4
#define EVENT_SHOCK 5
+
//Creatures
#define MOB_TEMPEST_MINION 33998
+
#define MAX_TEMPEST_MINIONS 4
+
float TempestMinions[4][4] =
{
{-203.980103, -281.287720, 91.650223, 1.598807},
@@ -29,6 +36,7 @@ float TempestMinions[4][4] =
{-233.267578, -297.104645, 91.681915, 1.598807},
{-203.842529, -297.097015, 91.745163, 1.598807}
};
+
/*######
## Emalon the Storm Watcher
######*/
@@ -38,23 +46,31 @@ struct TRINITY_DLL_DECL boss_emalonAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
EventMap events;
+
std::list<uint64> MinionList;
+
void Reset()
{
events.Reset();
+
DespawnAllMinions();
+
for (uint8 i = 0; i < MAX_TEMPEST_MINIONS; ++i)
m_creature->SummonCreature(MOB_TEMPEST_MINION, TempestMinions[i][0], TempestMinions[i][1], TempestMinions[i][2], TempestMinions[i][3], TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (pInstance)
pInstance->SetData(DATA_EMALON_EVENT, NOT_STARTED);
}
+
void DespawnAllMinions()
{
if(!MinionList.empty())
{
- for (std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); itr++)
+ for(std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); itr++)
{
Creature *Minion = Unit::GetCreature(*m_creature, *itr);
if(Minion && Minion->isAlive())
@@ -63,45 +79,56 @@ struct TRINITY_DLL_DECL boss_emalonAI : public ScriptedAI
}
MinionList.clear();
}
+
void JustSummoned(Creature *summoned)
{
MinionList.push_back(summoned->GetGUID());
+
if(m_creature->getVictim())
summoned->AI()->AttackStart(m_creature->getVictim());
}
+
void JustDied(Unit* Killer)
{
DespawnAllMinions();
+
if (pInstance)
pInstance->SetData(DATA_EMALON_EVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
if(!MinionList.empty())
{
- for (std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
+ for(std::list<uint64>::const_iterator itr = MinionList.begin(); itr != MinionList.end(); ++itr)
{
Creature *Minion = Unit::GetCreature(*m_creature, *itr);
if(Minion && Minion->isAlive() && !Minion->getVictim())
Minion->AI()->AttackStart(who);
}
}
+
DoZoneInCombat();
events.ScheduleEvent(EVENT_CHAIN_LIGHTNING, 5000);
events.ScheduleEvent(EVENT_LIGHTNING_NOVA, 40000);
events.ScheduleEvent(EVENT_BERSERK, 360000);
events.ScheduleEvent(EVENT_OVERCHARGE, 45000);
+
if (pInstance)
pInstance->SetData(DATA_EMALON_EVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if(!UpdateVictim())
return;
+
events.Update(diff);
+
if(me->hasUnitState(UNIT_STAT_CASTING))
return;
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -136,9 +163,11 @@ struct TRINITY_DLL_DECL boss_emalonAI : public ScriptedAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
/*######
## Tempest Minion
######*/
@@ -150,16 +179,23 @@ struct TRINITY_DLL_DECL mob_tempest_minionAI : public ScriptedAI
EmalonGUID = pInstance ? pInstance->GetData64(DATA_EMALON) : 0;
Emalon = Unit::GetCreature(*m_creature, EmalonGUID);
}
+
ScriptedInstance* pInstance;
+
EventMap events;
+
uint64 EmalonGUID;
Creature* Emalon;
+
uint32 OverchargedTimer;
+
void Reset()
{
events.Reset();
+
OverchargedTimer = 0;
}
+
void JustDied(Unit* Killer)
{
if(Emalon && Emalon->isAlive())
@@ -168,21 +204,27 @@ struct TRINITY_DLL_DECL mob_tempest_minionAI : public ScriptedAI
DoScriptText(EMOTE_MINION_RESPAWN, m_creature);
}
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
events.ScheduleEvent(EVENT_SHOCK, 20000);
+
if(Emalon && !Emalon->getVictim())
Emalon->AI()->AttackStart(who);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if(!UpdateVictim())
return;
+
events.Update(diff);
+
if(me->hasUnitState(UNIT_STAT_CASTING))
return;
+
if(Aura *OverchargedAura = m_creature->GetAura(SPELL_OVERCHARGED))
{
if(OverchargedAura->GetStackAmount() < 10)
@@ -203,6 +245,7 @@ struct TRINITY_DLL_DECL mob_tempest_minionAI : public ScriptedAI
}
}
}
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -213,24 +256,30 @@ struct TRINITY_DLL_DECL mob_tempest_minionAI : public ScriptedAI
return;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_tempest_minion(Creature *_Creature)
{
return new mob_tempest_minionAI (_Creature);
}
+
CreatureAI* GetAI_boss_emalon(Creature *_Creature)
{
return new boss_emalonAI (_Creature);
}
+
void AddSC_boss_emalon()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_emalon";
newscript->GetAI = &GetAI_boss_emalon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_tempest_minion";
newscript->GetAI = &GetAI_mob_tempest_minion;
diff --git a/src/bindings/scripts/scripts/northrend/vault_of_archavon/def_vault_of_archavon.h b/src/bindings/scripts/scripts/northrend/vault_of_archavon/def_vault_of_archavon.h
index e61edf6776a..95c44035184 100644
--- a/src/bindings/scripts/scripts/northrend/vault_of_archavon/def_vault_of_archavon.h
+++ b/src/bindings/scripts/scripts/northrend/vault_of_archavon/def_vault_of_archavon.h
@@ -1,5 +1,6 @@
#ifndef DEF_ARCHAVON_H
#define DEF_ARCHAVON_H
+
#define DATA_ARCHAVON_EVENT 1
#define DATA_EMALON_EVENT 2
#define DATA_EMALON 3
diff --git a/src/bindings/scripts/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp b/src/bindings/scripts/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp
index f2dc5b19a66..45d0a4be7ae 100644
--- a/src/bindings/scripts/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp
+++ b/src/bindings/scripts/scripts/northrend/vault_of_archavon/instance_vault_of_archavon.cpp
@@ -1,29 +1,39 @@
#include "precompiled.h"
#include "def_vault_of_archavon.h"
+
#define ENCOUNTERS 2
+
/* Vault of Archavon encounters:
1 - Archavon the Stone Watcher event
2 - Emalon the Storm Watcher event
*/
+
struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
{
instance_archavon(Map *Map) : ScriptedInstance(Map) {Initialize();};
+
uint32 Encounters[ENCOUNTERS];
+
uint64 Archavon;
uint64 Emalon;
+
void Initialize()
{
Archavon = 0;
Emalon = 0;
- for (uint8 i = 0; i < ENCOUNTERS; i++)
+
+ for(uint8 i = 0; i < ENCOUNTERS; i++)
Encounters[i] = NOT_STARTED;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < ENCOUNTERS; i++)
+ for(uint8 i = 0; i < ENCOUNTERS; i++)
if(Encounters[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature *creature, bool add)
{
switch(creature->GetEntry())
@@ -32,6 +42,7 @@ struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
case 33993: Emalon = creature->GetGUID(); break;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -41,6 +52,7 @@ struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -50,6 +62,7 @@ struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -57,9 +70,11 @@ struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
case DATA_ARCHAVON_EVENT: Encounters[0] = data; break;
case DATA_EMALON_EVENT: Encounters[1] = data; break;
}
+
if(data == DONE)
SaveToDB();
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -72,8 +87,10 @@ struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
OUT_SAVE_INST_DATA_COMPLETE;
return out;
}
+
return NULL;
}
+
void Load(const char* in)
{
if(!in)
@@ -81,19 +98,22 @@ struct TRINITY_DLL_DECL instance_archavon : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
std::istringstream stream(in);
stream >> Encounters[0] >> Encounters[1];
- for (uint8 i = 0; i < ENCOUNTERS; ++i)
+ for(uint8 i = 0; i < ENCOUNTERS; ++i)
if(Encounters[i] == IN_PROGRESS)
Encounters[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_archavon(Map* map)
{
return new instance_archavon(map);
}
+
void AddSC_instance_archavon()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_cyanigosa.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_cyanigosa.cpp
index 50f2e0c7462..6d4822a01d9 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_cyanigosa.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_cyanigosa.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
enum Spells
{
SPELL_ARCANE_VACUM = 58694,
@@ -21,6 +23,7 @@ enum Spells
SPELL_UNCONTROLLABLE_ENERGY = 58688,
H_SPELL_UNCONTROLLABLE_ENERGY = 59281
};
+
enum Yells
{
SAY_AGGRO = -1608000,
@@ -34,39 +37,49 @@ enum Yells
SAY_SPECIAL_ATTACK_1 = -1608008,
SAY_SPECIAL_ATTACK_2 = -1608009
};
+
struct TRINITY_DLL_DECL boss_cyanigosaAI : public ScriptedAI
{
boss_cyanigosaAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiArcaneVacumTimer;
uint32 uiBlizzardTimer;
uint32 uiManaDestructionTimer;
uint32 uiTailSweepTimer;
uint32 uiUncontrollableEnergyTimer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_CYANIGOSA_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_CYANIGOSA_EVENT, IN_PROGRESS);
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiArcaneVacumTimer < diff)
{
DoCast(m_creature, SPELL_ARCANE_VACUM);
} else uiArcaneVacumTimer -= diff;
+
if (uiBlizzardTimer < diff)
{
Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0);
@@ -75,14 +88,17 @@ struct TRINITY_DLL_DECL boss_cyanigosaAI : public ScriptedAI
if (pTarget)
DoCast(pTarget, HeroicMode ? H_SPELL_BLIZZARD : SPELL_BLIZZARD);
} else uiBlizzardTimer -= diff;
+
if (uiTailSweepTimer < diff)
{
DoCast(m_creature, HeroicMode ? H_SPELL_TAIL_SWEEP : SPELL_TAIL_SWEEP);
} else uiTailSweepTimer -= diff;
+
if (uiUncontrollableEnergyTimer < diff)
{
DoCast(m_creature->getVictim(), HeroicMode ? H_SPELL_UNCONTROLLABLE_ENERGY : SPELL_UNCONTROLLABLE_ENERGY);
} else uiUncontrollableEnergyTimer -= diff;
+
if (HeroicMode)
if (uiManaDestructionTimer < diff)
{
@@ -92,14 +108,18 @@ struct TRINITY_DLL_DECL boss_cyanigosaAI : public ScriptedAI
if (pTarget)
DoCast(pTarget, SPELL_MANA_DESTRUCTION);
} else uiManaDestructionTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_CYANIGOSA_EVENT, DONE);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
@@ -107,13 +127,16 @@ struct TRINITY_DLL_DECL boss_cyanigosaAI : public ScriptedAI
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_cyanigosa(Creature* pCreature)
{
return new boss_cyanigosaAI (pCreature);
}
+
void AddSC_boss_cyanigosa()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_cyanigosa";
newscript->GetAI = &GetAI_boss_cyanigosa;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_erekem.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_erekem.cpp
index 4855079fc06..cf6686421d1 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_erekem.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_erekem.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
enum Spells
{
SPELL_BLOODLUST = 54516,
@@ -22,6 +24,7 @@ enum Spells
SPELL_LIGHTNING_BOLT = 53044,
SPELL_STORMSTRIKE = 51876
};
+
//not in db
enum Yells
{
@@ -34,20 +37,25 @@ enum Yells
SAY_ADD_KILLED = -1608016,
SAY_BOTH_ADDS_KILLED = -1608017
};
+
struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
{
boss_erekemAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiBloodlustTimer;
uint32 uiChainHealTimer;
uint32 uiEarthShockTimer;
uint32 uiLightningBoltTimer;
uint32 uiEarthShieldTimer;
+
Creature* pGuard1;
Creature* pGuard2;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiBloodlustTimer = 15000;
@@ -63,10 +71,12 @@ struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoCast(m_creature, HEROIC(SPELL_EARTH_SHIELD, H_SPELL_EARTH_SHIELD));
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -75,12 +85,15 @@ struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
}
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//spam stormstrike in hc mode if spawns are dead
if (HeroicMode)
{
@@ -89,11 +102,13 @@ struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_STORMSTRIKE);
}
}
+
if (uiEarthShieldTimer < diff)
{
DoCast(m_creature, HEROIC(SPELL_EARTH_SHIELD, H_SPELL_EARTH_SHIELD));
uiEarthShieldTimer = 20000;
} else uiEarthShieldTimer -= diff;
+
if (uiChainHealTimer < diff)
{
if (Creature *pTarget = GetChainHealTarget())
@@ -103,17 +118,20 @@ struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
uiChainHealTimer = ((pGuard1 && !pGuard1->isAlive()) || (pGuard2 && !pGuard2->isAlive()) ? 3000 : 8000) + rand()%3000;
}
} else uiChainHealTimer -= diff;
+
if (uiBloodlustTimer < diff)
{
DoCast(m_creature,SPELL_BLOODLUST);
uiBloodlustTimer = urand(35000,45000);
} else uiBloodlustTimer -= diff;
+
if (uiEarthShockTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_EARTH_SHOCK);
uiEarthShockTimer = urand(8000,13000);
} else uiEarthShockTimer -= diff;
+
if (uiLightningBoltTimer < diff)
{
Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM,0);
@@ -123,11 +141,14 @@ struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
DoCast(pTarget, SPELL_LIGHTNING_BOLT);
uiLightningBoltTimer = urand(18000,24000);
} else uiLightningBoltTimer -= diff;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -142,43 +163,53 @@ struct TRINITY_DLL_DECL boss_erekemAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
Creature* GetChainHealTarget()
{
Creature* pTarget = NULL;
return pTarget;
}
};
+
CreatureAI* GetAI_boss_erekem(Creature* pCreature)
{
return new boss_erekemAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_erekem_guardAI : public ScriptedAI
{
mob_erekem_guardAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiEarthShieldTimer;
uint32 uiLightningBoltTimer;
uint32 uiBloodlustTimer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiEarthShieldTimer = 20000;
uiLightningBoltTimer = urand(0,5000);
uiBloodlustTimer = urand(8000,18000);
}
+
void EnterCombat(Unit* who)
{
DoCast(m_creature, H_SPELL_EARTH_SHIELD);
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (uiEarthShieldTimer < diff)
@@ -186,12 +217,14 @@ struct TRINITY_DLL_DECL mob_erekem_guardAI : public ScriptedAI
DoCast(m_creature, H_SPELL_EARTH_SHIELD);
uiEarthShieldTimer = 20000;
} else uiEarthShieldTimer -= diff;
+
if (uiLightningBoltTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget, SPELL_LIGHTNING_BOLT);
uiLightningBoltTimer = urand(18000,24000);
} else uiLightningBoltTimer -= diff;
+
if (uiBloodlustTimer < diff)
{
DoCast(m_creature,SPELL_BLOODLUST);
@@ -199,18 +232,22 @@ struct TRINITY_DLL_DECL mob_erekem_guardAI : public ScriptedAI
} else uiBloodlustTimer -= diff;
}
};
+
CreatureAI* GetAI_mob_erekem_guard(Creature* pCreature)
{
return new mob_erekem_guardAI (pCreature);
}
+
void AddSC_boss_erekem()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_erekem";
newscript->GetAI = &GetAI_boss_erekem;
newscript->RegisterSelf();
+
newscript->Name = "mob_erekem_guard";
newscript->GetAI = &GetAI_mob_erekem_guard;
newscript->RegisterSelf();
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_ichoron.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_ichoron.cpp
index 3b859c596df..3d9f5bc659c 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_ichoron.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_ichoron.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
enum Spells
{
SPELL_DRAINED = 59820,
@@ -21,6 +23,7 @@ enum Spells
SPELL_WATER_BOLT_VOLLEY = 54241,
H_SPELL_WATER_BOLT_VOLLEY = 59521
};
+
//not in db
enum Yells
{
@@ -34,24 +37,31 @@ enum Yells
SAY_SHATTER = -1608025,
SAY_BUBBLE = -1608026
};
+
enum CombatPhase
{
BUBBLED,
SUMMONS,
DPS
};
+
struct TRINITY_DLL_DECL boss_ichoronAI : public ScriptedAI
{
boss_ichoronAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
CombatPhase Phase;
+
ScriptedInstance* pInstance;
+
void Reset()
{
Phase = BUBBLED;
+
DoCast(m_creature, SPELL_PROTECTIVE_BUBBLE);
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -63,6 +73,7 @@ struct TRINITY_DLL_DECL boss_ichoronAI : public ScriptedAI
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -71,16 +82,20 @@ struct TRINITY_DLL_DECL boss_ichoronAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -95,6 +110,7 @@ struct TRINITY_DLL_DECL boss_ichoronAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
@@ -102,13 +118,16 @@ struct TRINITY_DLL_DECL boss_ichoronAI : public ScriptedAI
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_ichoron(Creature* pCreature)
{
return new boss_ichoronAI (pCreature);
}
+
void AddSC_boss_ichoron()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ichoron";
newscript->GetAI = &GetAI_boss_ichoron;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_lavanthor.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_lavanthor.cpp
index 7316c004219..fc493735a72 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_lavanthor.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_lavanthor.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
enum Spells
{
SPELL_CAUTERIZING_FLAMES = 59466, //Only in heroic
@@ -20,17 +22,21 @@ enum Spells
SPELL_LAVA_BURN = 54249,
H_SPELL_LAVA_BURN = 59594
};
+
struct TRINITY_DLL_DECL boss_lavanthorAI : public ScriptedAI
{
boss_lavanthorAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiFireboltTimer;
uint32 uiFlameBreathTimer;
uint32 uiLavaBurnTimer;
uint32 uiCauterizingFlamesTimer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiFireboltTimer = 1000;
@@ -45,6 +51,7 @@ struct TRINITY_DLL_DECL boss_lavanthorAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
@@ -55,27 +62,33 @@ struct TRINITY_DLL_DECL boss_lavanthorAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
}
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiFireboltTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_FIREBOLT, H_SPELL_FIREBOLT));
uiFireboltTimer = urand(5000,13000);
} else uiFireboltTimer -= diff;
+
if (uiFlameBreathTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_FLAME_BREATH, H_SPELL_FLAME_BREATH));
uiFlameBreathTimer = urand(10000,15000);
} else uiFlameBreathTimer -= diff;
+
if (uiLavaBurnTimer < diff)
{
DoCast(m_creature->getVictim(), HEROIC(SPELL_LAVA_BURN, H_SPELL_LAVA_BURN));
uiLavaBurnTimer = urand(15000,23000);
}
+
if (HeroicMode)
{
if (uiCauterizingFlamesTimer < diff)
@@ -84,8 +97,10 @@ struct TRINITY_DLL_DECL boss_lavanthorAI : public ScriptedAI
uiCauterizingFlamesTimer = urand(10000,16000);
} else uiCauterizingFlamesTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
if (pInstance)
@@ -103,13 +118,16 @@ struct TRINITY_DLL_DECL boss_lavanthorAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_boss_lavanthor(Creature* pCreature)
{
return new boss_lavanthorAI (pCreature);
}
+
void AddSC_boss_lavanthor()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_lavanthor";
newscript->GetAI = &GetAI_boss_lavanthor;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_moragg.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_moragg.cpp
index 00e136139fb..53501a6425a 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_moragg.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_moragg.cpp
@@ -5,31 +5,38 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
//Spells
enum Spells
{
SPELL_CORROSIVE_SALIVA = 54527,
SPELL_OPTIC_LINK = 54396
};
+
struct TRINITY_DLL_DECL boss_moraggAI : public ScriptedAI
{
boss_moraggAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiOpticLinkTimer;
uint32 uiCorrosiveSalivaTimer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
uiOpticLinkTimer = 10000;
uiCorrosiveSalivaTimer = 5000;
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -38,6 +45,7 @@ struct TRINITY_DLL_DECL boss_moraggAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
@@ -48,23 +56,28 @@ struct TRINITY_DLL_DECL boss_moraggAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
}
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (uiOpticLinkTimer < diff)
{
if (Unit* pTarget = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true))
DoCast(pTarget,SPELL_OPTIC_LINK);
uiOpticLinkTimer = 15000;
} else uiOpticLinkTimer -= diff;
+
if (uiCorrosiveSalivaTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_CORROSIVE_SALIVA);
uiCorrosiveSalivaTimer = 10000;
} else uiCorrosiveSalivaTimer -= diff;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
@@ -84,13 +97,16 @@ struct TRINITY_DLL_DECL boss_moraggAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_boss_moragg(Creature* pCreature)
{
return new boss_moraggAI (pCreature);
}
+
void AddSC_boss_moragg()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_moragg";
newscript->GetAI = &GetAI_boss_moragg;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_xevozz.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_xevozz.cpp
index 9ad67768fcf..3f607bcd3f3 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_xevozz.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_xevozz.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
enum Spells
{
SPELL_ARCANE_BARRAGE_VOLLEY = 54202,
@@ -23,17 +25,20 @@ enum Spells
SPELL_SUMMON_ETHEREAL_SPHERE_3 = 54138,
H_SPELL_SUMMON_ETHEREAL_SPHERE_3 = 61339
};
+
enum Creatures
{
CREATURE_ETHEREAL_SPHERE = 29271,
H_CREATURE_ETHEREAL_SPHERE = 32582
};
+
enum CreatureSpells
{
SPELL_ARCANE_POWER = 54160,
H_SPELL_ARCANE_POWER = 59474,
SPELL_SUMMON_PLAYERS = 54164
};
+
//not in db
enum Yells
{
@@ -48,13 +53,16 @@ enum Yells
SAY_REPEAT_SUMMON_2 = -1608035,
SAY_SUMMON_ENERGY = -1608036
};
+
struct TRINITY_DLL_DECL boss_xevozzAI : public ScriptedAI
{
boss_xevozzAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
void Reset()
{
if (pInstance)
@@ -65,6 +73,7 @@ struct TRINITY_DLL_DECL boss_xevozzAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
@@ -76,14 +85,18 @@ struct TRINITY_DLL_DECL boss_xevozzAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
}
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
@@ -108,13 +121,16 @@ struct TRINITY_DLL_DECL boss_xevozzAI : public ScriptedAI
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_xevozz(Creature* pCreature)
{
return new boss_xevozzAI (pCreature);
}
+
void AddSC_boss_xevozz()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_xevozz";
newscript->GetAI = &GetAI_boss_xevozz;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/boss_zuramat.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/boss_zuramat.cpp
index e9a7d5aef20..d573060f50e 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/boss_zuramat.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/boss_zuramat.cpp
@@ -5,11 +5,13 @@ SD%Complete:
SDComment:
SDCategory:
Script Data End */
+
/*** SQL START ***
update creature_template set scriptname = '' where entry = '';
*** SQL END ***/
#include "precompiled.h"
#include "def_violet_hold.h"
+
enum Spells
{
SPELL_SHROUD_OF_DARKNESS = 54524,
@@ -18,10 +20,12 @@ enum Spells
SPELL_VOID_SHIFT = 54361,
H_SPELL_VOID_SHIFT = 59743
};
+
enum Creatures
{
CREATURE_VOID_SENTRY = 29364
};
+
//not in db
enum Yells
{
@@ -34,14 +38,18 @@ enum Yells
SAY_SHIELD = -1608043,
SAY_WHISPER = -1608044
};
+
struct TRINITY_DLL_DECL boss_zuramatAI : public ScriptedAI
{
boss_zuramatAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 void_shift;
+
ScriptedInstance* pInstance;
+
void Reset()
{
if (pInstance)
@@ -52,6 +60,7 @@ struct TRINITY_DLL_DECL boss_zuramatAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, NOT_STARTED);
}
}
+
void EnterCombat(Unit* who)
{
DoScriptText(SAY_AGGRO, m_creature);
@@ -63,17 +72,21 @@ struct TRINITY_DLL_DECL boss_zuramatAI : public ScriptedAI
pInstance->SetData(DATA_2ND_BOSS_EVENT, IN_PROGRESS);
}
}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
if (pInstance->GetData(DATA_WAVE_COUNT) == 6)
@@ -88,20 +101,25 @@ struct TRINITY_DLL_DECL boss_zuramatAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
};
+
CreatureAI* GetAI_boss_zuramat(Creature* pCreature)
{
return new boss_zuramatAI (pCreature);
}
+
void AddSC_boss_zuramat()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_zuramat";
newscript->GetAI = &GetAI_boss_zuramat;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/def_violet_hold.h b/src/bindings/scripts/scripts/northrend/violet_hold/def_violet_hold.h
index 02ce2e251ae..4e989ffb7bb 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/def_violet_hold.h
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/def_violet_hold.h
@@ -1,5 +1,6 @@
#ifndef DEF_VIOLET_HOLD_H
#define DEF_VIOLET_HOLD_H
+
enum Data
{
DATA_1ST_BOSS_EVENT,
@@ -7,6 +8,7 @@ enum Data
DATA_CYANIGOSA_EVENT,
DATA_WAVE_COUNT
};
+
enum Data64
{
DATA_MORAGG,
@@ -27,4 +29,5 @@ enum Data64
DATA_MAIN_DOOR,
DATA_SINCLARI
};
+
#endif
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/instance_violet_hold.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/instance_violet_hold.cpp
index 7a3f462d896..60128f1b931 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/instance_violet_hold.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/instance_violet_hold.cpp
@@ -1,10 +1,13 @@
#include "precompiled.h"
#include "def_violet_hold.h"
+
#define MAX_ENCOUNTER 3
+
/* Violet Hold encounters:
0 - First boss
1 - Second boss
2 - Cyanigosa*/
+
/* Violet hold bosses:
0 - Moragg
1 - Erekem
@@ -38,6 +41,7 @@ const Location PortalLocation[] =
struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
{
instance_violet_hold(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 uiMoragg;
uint64 uiErekem;
uint64 uiIchoron;
@@ -46,6 +50,7 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
uint64 uiZuramat;
uint64 uiCyanigosa;
uint64 uiSinclari;
+
uint64 uiMoraggCell;
uint64 uiErekemCell;
uint64 uiErekemRightGuardCell;
@@ -55,13 +60,18 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
uint64 uiXevozzCell;
uint64 uiZuramatCell;
uint64 uiMainDoor;
+
uint8 uiWaveCount;
uint8 uiLocation;
uint8 uiFirstBoss;
uint8 uiSecondBoss;
+
uint8 m_auiEncounter[MAX_ENCOUNTER];
+
bool HeroicMode;
+
std::string str_data;
+
void Initialize()
{
uiMoragg = 0;
@@ -72,6 +82,7 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
uiZuramat = 0;
uiCyanigosa = 0;
uiSinclari = 0;
+
uiMoraggCell = 0;
uiErekemCell = 0;
uiErekemRightGuardCell = 0;
@@ -81,18 +92,23 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
uiXevozzCell = 0;
uiZuramatCell = 0;
uiMainDoor = 0;
+
uiWaveCount = 0;
uiLocation = 0;
uiFirstBoss = 0;
uiSecondBoss = 0;
+
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -123,6 +139,7 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -165,6 +182,7 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -230,6 +248,7 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -239,8 +258,10 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
case DATA_CYANIGOSA_EVENT: return m_auiEncounter[2];
case DATA_WAVE_COUNT: return uiWaveCount;
}
+
return 0;
}
+
void StartBossEncounter(uint8 uiBoss)
{
Creature* pBoss = NULL;
@@ -301,6 +322,7 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -323,18 +345,24 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
case DATA_MAIN_DOOR: return uiMainDoor;
case DATA_SINCLARI: return uiSinclari;
}
+
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << "V H " << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << uiFirstBoss << " " << uiSecondBoss;
+
str_data = saveStream.str();
+
OUT_SAVE_INST_DATA_COMPLETE;
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -342,29 +370,38 @@ struct TRINITY_DLL_DECL instance_violet_hold : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
char dataHead1, dataHead2;
uint16 data0, data1, data2, data3, data4;
+
std::istringstream loadStream(in);
loadStream >> dataHead1 >> dataHead2 >> data0 >> data1 >> data2 >> data3 >> data4;
+
if (dataHead1 == 'V' && dataHead2 == 'H')
{
m_auiEncounter[0] = data0;
m_auiEncounter[1] = data1;
m_auiEncounter[2] = data2;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
uiFirstBoss = data3;
uiSecondBoss = data4;
}else OUT_LOAD_INST_DATA_FAIL;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_violet_hold(Map* pMap)
{
return new instance_violet_hold(pMap);
}
+
void AddSC_instance_violet_hold()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/northrend/violet_hold/violet_hold.cpp b/src/bindings/scripts/scripts/northrend/violet_hold/violet_hold.cpp
index 1d11a31767d..900b0021336 100644
--- a/src/bindings/scripts/scripts/northrend/violet_hold/violet_hold.cpp
+++ b/src/bindings/scripts/scripts/northrend/violet_hold/violet_hold.cpp
@@ -1,8 +1,10 @@
#include "precompiled.h"
#include "def_violet_hold.h"
+
#define GOSSIP_START_EVENT "[PH]: Start Event"
#define NEXT_WAVE_TIME 90000
#define SPAWN_TIME 9000
+
enum Creatures
{
CREATURE_AZURE_INVADER = 30661,
@@ -11,6 +13,7 @@ enum Creatures
CREATURE_AZURE_MAGE_SLAYER = 30664,
CREATURE_AZURE_CAPTAIN = 30666
};
+
bool GossipHello_npc_sinclari(Player* pPlayer, Creature* pCreature)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
@@ -19,6 +22,7 @@ bool GossipHello_npc_sinclari(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(1, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_sinclari(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
pPlayer->CLOSE_GOSSIP_MENU();
@@ -27,22 +31,28 @@ bool GossipSelect_npc_sinclari(Player* pPlayer, Creature* pCreature, uint32 uiSe
pInstance->SetData(DATA_WAVE_COUNT,1);
return true;
}
+
struct TRINITY_DLL_DECL npc_teleportation_portalAI : public ScriptedAI
{
npc_teleportation_portalAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
uint32 uiDespawnTimer;
uint32 uiSpawnTimer;
+
ScriptedInstance *pInstance;
+
void reset()
{
uiDespawnTimer = NEXT_WAVE_TIME;
uiSpawnTimer = 500;
}
+
void EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (uiSpawnTimer < diff)
@@ -61,24 +71,29 @@ struct TRINITY_DLL_DECL npc_teleportation_portalAI : public ScriptedAI
m_creature->DisappearAndDie();
} else uiDespawnTimer -= diff;
}
+
void JustDied(Unit* killer)
{
if (pInstance)
pInstance->SetData(DATA_WAVE_COUNT,pInstance->GetData(DATA_WAVE_COUNT)+1);
}
};
+
CreatureAI* GetAI_npc_teleportation_portal(Creature *pCreature)
{
return new npc_teleportation_portalAI(pCreature);
}
+
void AddSC_violet_hold()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_sinclari_vh";
newscript->pGossipHello = &GossipHello_npc_sinclari;
newscript->pGossipSelect = &GossipSelect_npc_sinclari;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_teleportation_portal_vh";
newscript->GetAI = &GetAI_npc_teleportation_portal;
diff --git a/src/bindings/scripts/scripts/northrend/wintergrasp.cpp b/src/bindings/scripts/scripts/northrend/wintergrasp.cpp
index 241d9202e9a..53e6fca2af7 100644
--- a/src/bindings/scripts/scripts/northrend/wintergrasp.cpp
+++ b/src/bindings/scripts/scripts/northrend/wintergrasp.cpp
@@ -13,18 +13,22 @@
* 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"
#include "Wintergrasp.h"
+
#define GOSSIP_HELLO_DEMO1 "Build catapult."
#define GOSSIP_HELLO_DEMO2 "Build demolisher."
#define GOSSIP_HELLO_DEMO3 "Build siege engine."
#define GOSSIP_HELLO_DEMO4 "I cannot build more!"
+
struct TRINITY_DLL_DECL npc_demolisher_engineererAI : public ScriptedAI
{
npc_demolisher_engineererAI(Creature* pCreature) : ScriptedAI(pCreature)
{
me->SetReactState(REACT_PASSIVE);
}
+
/*
void JustDied(Unit *killer)
{
@@ -33,14 +37,17 @@ struct TRINITY_DLL_DECL npc_demolisher_engineererAI : public ScriptedAI
}
*/
};
+
CreatureAI* GetAI_npc_demolisher_engineerer(Creature* pCreature)
{
return new npc_demolisher_engineererAI (pCreature);
}
+
bool GossipHello_npc_demolisher_engineerer(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if(pPlayer->isGameMaster() || pCreature->GetZoneScript() && pCreature->GetZoneScript()->GetData(pCreature->GetDBTableGUIDLow()))
{
if (pPlayer->HasAura(SPELL_CORPORAL))
@@ -54,9 +61,11 @@ bool GossipHello_npc_demolisher_engineerer(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_DEMO4, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+9);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_demolisher_engineerer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
pPlayer->CLOSE_GOSSIP_MENU();
@@ -69,11 +78,14 @@ bool GossipSelect_npc_demolisher_engineerer(Player* pPlayer, Creature* pCreature
case 2: pPlayer->CastSpell(pPlayer, pPlayer->GetTeamId() ? 61408 : 56661, false, NULL, NULL, pCreature->GetGUID()); break;
}
}
+
return true;
}
+
void AddSC_wintergrasp()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_demolisher_engineerer";
newscript->GetAI = &GetAI_npc_demolisher_engineerer;
diff --git a/src/bindings/scripts/scripts/northrend/zuldrak.cpp b/src/bindings/scripts/scripts/northrend/zuldrak.cpp
index 6e187e0335b..90a047b4c44 100644
--- a/src/bindings/scripts/scripts/northrend/zuldrak.cpp
+++ b/src/bindings/scripts/scripts/northrend/zuldrak.cpp
@@ -15,22 +15,29 @@
* 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"
+
/*####
## npc_drakuru_shackles
####*/
+
enum eDrakuruShackles
{
SPELL_LEFT_CHAIN = 59951,
SPELL_RIGHT_CHAIN = 59952,
SPELL_UNLOCK_SHACKLE = 55083,
SPELL_FREE_RAGECLAW = 55223,
+
NPC_RAGECLAW = 29686
};
+
struct TRINITY_DLL_DECL npc_drakuru_shacklesAI : public ScriptedAI
{
npc_drakuru_shacklesAI(Creature *c) : ScriptedAI(c) {}
+
Unit* Rageclaw;
+
void Reset()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -40,17 +47,21 @@ struct TRINITY_DLL_DECL npc_drakuru_shacklesAI : public ScriptedAI
if (Unit* summon = m_creature->SummonCreature(NPC_RAGECLAW,x,y,z,0,TEMPSUMMON_DEAD_DESPAWN,1000))
DoActionOnRageclaw(true,summon);
}
+
void DoActionOnRageclaw(bool locking, Unit *who)
{
if (!who)
return;
+
if (locking)
{
if (who)
{
Rageclaw = who;
+
m_creature->SetInFront(Rageclaw);
Rageclaw->SetInFront(m_creature);
+
m_creature->CastSpell(Rageclaw, SPELL_LEFT_CHAIN, true);
m_creature->CastSpell(Rageclaw, SPELL_RIGHT_CHAIN, true);
}
@@ -62,6 +73,7 @@ struct TRINITY_DLL_DECL npc_drakuru_shacklesAI : public ScriptedAI
m_creature->setDeathState(DEAD);
}
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_UNLOCK_SHACKLE)
@@ -70,31 +82,39 @@ struct TRINITY_DLL_DECL npc_drakuru_shacklesAI : public ScriptedAI
DoActionOnRageclaw(false,caster);
else
m_creature->setDeathState(JUST_DIED);
+
}
}
};
+
CreatureAI* GetAI_npc_drakuru_shackles(Creature* pCreature)
{
return new npc_drakuru_shacklesAI (pCreature);
}
+
/*####
## npc_captured_rageclaw
####*/
+
enum eRageclaw
{
SPELL_UNSHACKLED = 55085,
SPELL_KNEEL = 39656
};
+
const char * SAY_RAGECLAW_1 = "I poop on you, trollses!";
const char * SAY_RAGECLAW_2 = "ARRRROOOOGGGGAAAA!";
const char * SAY_RAGECLAW_3 = "No more mister nice wolvar!";
#define SAY_RAGECLAW RAND(SAY_RAGECLAW_1,SAY_RAGECLAW_2,SAY_RAGECLAW_3)
+
struct TRINITY_DLL_DECL npc_captured_rageclawAI : public ScriptedAI
{
npc_captured_rageclawAI(Creature *c) : ScriptedAI(c) {}
+
uint32 DespawnTimer;
bool Despawn;
+
void Reset()
{
Despawn = false;
@@ -102,22 +122,30 @@ struct TRINITY_DLL_DECL npc_captured_rageclawAI : public ScriptedAI
m_creature->setFaction(35);
m_creature->CastSpell(m_creature, SPELL_KNEEL, true); // Little Hack for kneel - Thanks Illy :P
}
+
void MoveInLineOfSight(Unit *who){}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_FREE_RAGECLAW)
{
m_creature->RemoveAurasDueToSpell(SPELL_LEFT_CHAIN);
+
m_creature->RemoveAurasDueToSpell(SPELL_RIGHT_CHAIN);
+
m_creature->RemoveAurasDueToSpell(SPELL_KNEEL);
+
m_creature->setFaction(m_creature->GetCreatureInfo()->faction_H);
+
DoCast(m_creature, SPELL_UNSHACKLED, true);
m_creature->MonsterSay(SAY_RAGECLAW, LANG_UNIVERSAL, NULL);
m_creature->GetMotionMaster()->MoveRandom(10);
+
DespawnTimer = 10000;
Despawn = true;
}
}
+
void UpdateAI(const uint32 diff)
{
if (m_creature->getVictim())
@@ -125,38 +153,48 @@ struct TRINITY_DLL_DECL npc_captured_rageclawAI : public ScriptedAI
DoMeleeAttackIfReady();
return;
}
+
if (!Despawn)
return;
+
if (DespawnTimer < diff)
m_creature->setDeathState(JUST_DIED);
else DespawnTimer-=diff;
}
};
+
CreatureAI* GetAI_npc_captured_rageclaw(Creature* pCreature)
{
return new npc_captured_rageclawAI (pCreature);
}
+
/*####
## npc_gymer
####*/
+
#define GOSSIP_ITEM_G "I'm ready, Gymer. Let's go!"
+
enum eGymer
{
QUEST_STORM_KING_VENGEANCE = 12919,
SPELL_GYMER = 55568
};
+
bool GossipHello_npc_gymer(Player *pPlayer, Creature *pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_STORM_KING_VENGEANCE) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_G, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(13640, pCreature->GetGUID());
}
+
return true;
}
+
bool GossipSelect_npc_gymer(Player *pPlayer, Creature *pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -164,19 +202,24 @@ enum eGymer
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->CastSpell(pPlayer, SPELL_GYMER, true);
}
+
return true;
}
+
void AddSC_zuldrak()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_drakuru_shackles";
newscript->GetAI = &GetAI_npc_drakuru_shackles;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_captured_rageclaw";
newscript->GetAI = &GetAI_npc_captured_rageclaw;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_gymer";
newscript->pGossipHello = &GossipHello_npc_gymer;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp b/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp
index 84a5179b7a3..8b1c610e065 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_exarch_maladaar.cpp
@@ -13,18 +13,22 @@
* along 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
@@ -34,25 +38,32 @@ EndContentData */
#define SPELL_MORTAL_STRIKE 37335
#define SPELL_FREEZING_TRAP 37368
#define SPELL_HAMMER_OF_JUSTICE 37369
+
struct TRINITY_DLL_DECL mob_stolen_soulAI : public ScriptedAI
{
mob_stolen_soulAI(Creature *c) : ScriptedAI(c) {}
+
uint8 myClass;
uint32 Class_Timer;
+
void Reset()
{
Class_Timer = 1000;
}
+
void EnterCombat(Unit *who)
{ }
+
void SetMyClass(uint8 myclass)
{
myClass = myclass;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Class_Timer < diff)
{
switch (myClass)
@@ -95,53 +106,72 @@ struct TRINITY_DLL_DECL mob_stolen_soulAI : public ScriptedAI
break;
}
} else Class_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_stolen_soul(Creature* pCreature)
{
return new mob_stolen_soulAI (pCreature);
}
+
#define SAY_INTRO -1558000
#define SAY_SUMMON -1558001
+
#define SAY_AGGRO_1 -1558002
#define SAY_AGGRO_2 -1558003
#define SAY_AGGRO_3 -1558004
+
#define SAY_ROAR -1558005
#define SAY_SOUL_CLEAVE -1558006
+
#define SAY_SLAY_1 -1558007
#define SAY_SLAY_2 -1558008
+
#define SAY_DEATH -1558009
+
#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 TRINITY_DLL_DECL boss_exarch_maladaarAI : public ScriptedAI
{
boss_exarch_maladaarAI(Creature *c) : ScriptedAI(c)
{
HasTaunted = false;
}
+
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 = 15000 + rand()% 5000;
Ribbon_of_Souls_timer = 5000;
StolenSoul_Timer = 25000 + rand()% 10000;
+
Avatar_summoned = false;
}
+
void MoveInLineOfSight(Unit *who)
{
if (!HasTaunted && m_creature->IsWithinDistInMap(who, 150.0))
@@ -149,13 +179,16 @@ struct TRINITY_DLL_DECL boss_exarch_maladaarAI : public ScriptedAI
DoScriptText(SAY_INTRO, m_creature);
HasTaunted = true;
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void JustSummoned(Creature *summoned)
{
if (summoned->GetEntry() == ENTRY_STOLEN_SOUL)
@@ -164,38 +197,48 @@ struct TRINITY_DLL_DECL boss_exarch_maladaarAI : public ScriptedAI
summoned->CastSpell(summoned,SPELL_STOLEN_SOUL_VISUAL,false);
summoned->SetDisplayId(soulmodel);
summoned->setFaction(m_creature->getFaction());
+
if (Unit *target = Unit::GetUnit(*m_creature,soulholder))
{
+
CAST_AI(mob_stolen_soulAI, summoned->AI())->SetMyClass(soulclass);
summoned->AI()->AttackStart(target);
}
}
}
+
void KilledUnit(Unit* victim)
{
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
//When Exarch Maladar is defeated D'ore appear.
m_creature->SummonCreature(19412, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN, 600000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (!Avatar_summoned && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 25))
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(true);
+
DoScriptText(SAY_SUMMON, m_creature);
+
DoCast(m_creature, SPELL_SUMMON_AVATAR);
Avatar_summoned = true;
StolenSoul_Timer = 15000 + rand()% 15000;
}
+
if (StolenSoul_Timer < diff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0))
@@ -204,78 +247,100 @@ struct TRINITY_DLL_DECL boss_exarch_maladaarAI : public ScriptedAI
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(true);
+
uint32 i = urand(1,2);
if (i == 1)
DoScriptText(SAY_ROAR, m_creature);
else
DoScriptText(SAY_SOUL_CLEAVE, m_creature);
+
soulmodel = target->GetDisplayId();
soulholder = target->GetGUID();
soulclass = target->getClass();
+
DoCast(target,SPELL_STOLEN_SOUL);
m_creature->SummonCreature(ENTRY_STOLEN_SOUL, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
+
StolenSoul_Timer = 20000 + rand()% 10000;
} 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 = 15000 + rand()% 15000;
}else Fear_timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_exarch_maladaar(Creature* pCreature)
{
return new boss_exarch_maladaarAI (pCreature);
}
+
#define SPELL_AV_MORTAL_STRIKE 16856
#define SPELL_AV_SUNDER_ARMOR 16145
+
struct TRINITY_DLL_DECL mob_avatar_of_martyredAI : public ScriptedAI
{
mob_avatar_of_martyredAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Mortal_Strike_timer;
+
void Reset()
{
Mortal_Strike_timer = 10000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Mortal_Strike_timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_AV_MORTAL_STRIKE);
Mortal_Strike_timer = 10000 + rand()%20 * 1000;
} else Mortal_Strike_timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_avatar_of_martyred(Creature* pCreature)
{
return new mob_avatar_of_martyredAI (pCreature);
}
+
void AddSC_boss_exarch_maladaar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_exarch_maladaar";
newscript->GetAI = &GetAI_boss_exarch_maladaar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_avatar_of_martyred";
newscript->GetAI = &GetAI_mob_avatar_of_martyred;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_stolen_soul";
newscript->GetAI = &GetAI_mob_stolen_soul;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp b/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp
index 19e3664f62b..c8dabff9650 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/auchenai_crypts/boss_shirrak_the_dead_watcher.cpp
@@ -13,36 +13,45 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
Name: Boss_Shirrak_the_dead_watcher
%Complete: 80
Comment: InhibitMagic should stack slower far from the boss, proper Visual for Focus Fire, heroic implemented
Category: Auchindoun, Auchenai Crypts
EndScriptData */
+
#include "precompiled.h"
+
#define SPELL_INHIBITMAGIC 32264
#define SPELL_ATTRACTMAGIC 32265
#define N_SPELL_CARNIVOROUSBITE 36383
#define H_SPELL_CARNIVOROUSBITE 39382
#define SPELL_CARNIVOROUSBITE (HeroicMode?H_SPELL_CARNIVOROUSBITE:N_SPELL_CARNIVOROUSBITE)
+
#define ENTRY_FOCUS_FIRE 18374
+
#define N_SPELL_FIERY_BLAST 32302
#define H_SPELL_FIERY_BLAST 38382
#define SPELL_FIERY_BLAST (HeroicMode?H_SPELL_FIERY_BLAST:N_SPELL_FIERY_BLAST)
#define SPELL_FOCUS_FIRE_VISUAL 42075 //need to find better visual
+
#define EMOTE_FOCUSES_ON "focuses on "
+
struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
{
boss_shirrak_the_dead_watcherAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 Inhibitmagic_Timer;
uint32 Attractmagic_Timer;
uint32 Carnivorousbite_Timer;
uint32 FocusFire_Timer;
bool HeroicMode;
Unit *focusedTarget;
+
void Reset()
{
Inhibitmagic_Timer = 0;
@@ -51,8 +60,10 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
FocusFire_Timer = 17000;
focusedTarget = NULL;
}
+
void EnterCombat(Unit *who)
{ }
+
void JustSummoned(Creature *summoned)
{
if (summoned && summoned->GetEntry() == ENTRY_FOCUS_FIRE)
@@ -61,10 +72,12 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
summoned->setFaction(m_creature->getFaction());
summoned->SetLevel(m_creature->getLevel());
summoned->addUnitState(UNIT_STAT_ROOT);
+
if (focusedTarget)
summoned->AI()->AttackStart(focusedTarget);
}
}
+
void UpdateAI(const uint32 diff)
{
//Inhibitmagic_Timer
@@ -73,7 +86,7 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
float dist;
Map* pMap = m_creature->GetMap();
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (Player* i_pl = i->getSource())
if (i_pl->isAlive() && (dist = i_pl->IsWithinDist(m_creature, 45)))
{
@@ -88,9 +101,11 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
}
Inhibitmagic_Timer = 3000+(rand()%1000);
}else Inhibitmagic_Timer -= diff;
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//Attractmagic_Timer
if (Attractmagic_Timer < diff)
{
@@ -98,12 +113,14 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
Attractmagic_Timer = 30000;
Carnivorousbite_Timer = 1500;
}else Attractmagic_Timer -= diff;
+
//Carnivorousbite_Timer
if (Carnivorousbite_Timer < diff)
{
DoCast(m_creature,SPELL_CARNIVOROUSBITE);
Carnivorousbite_Timer = 10000;
}else Carnivorousbite_Timer -= diff;
+
//FocusFire_Timer
if (FocusFire_Timer < diff)
{
@@ -113,6 +130,7 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
{
focusedTarget = target;
m_creature->SummonCreature(ENTRY_FOCUS_FIRE,target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(),0,TEMPSUMMON_TIMED_DESPAWN,5500);
+
// TODO: Find better way to handle emote
// Emote
std::string *emote = new std::string(EMOTE_FOCUSES_ON);
@@ -124,49 +142,62 @@ struct TRINITY_DLL_DECL boss_shirrak_the_dead_watcherAI : public ScriptedAI
}
FocusFire_Timer = 15000+(rand()%5000);
}else FocusFire_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_shirrak_the_dead_watcher(Creature* pCreature)
{
return new boss_shirrak_the_dead_watcherAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_focus_fireAI : public ScriptedAI
{
mob_focus_fireAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
uint32 FieryBlast_Timer;
bool fiery1, fiery2;
+
void Reset()
{
FieryBlast_Timer = 3000+(rand()%1000);
fiery1 = fiery2 = true;
}
+
void EnterCombat(Unit *who)
{ }
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//FieryBlast_Timer
if (fiery2 && FieryBlast_Timer < diff)
{
DoCast(m_creature,SPELL_FIERY_BLAST);
+
if (fiery1) fiery1 = false;
else if (fiery2) fiery2 = false;
+
FieryBlast_Timer = 1000;
}else FieryBlast_Timer -= diff;
+
//DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_focus_fire(Creature* pCreature)
{
return new mob_focus_fireAI (pCreature);
}
+
void AddSC_boss_shirrak_the_dead_watcher()
{
Script *newscript;
@@ -174,6 +205,7 @@ void AddSC_boss_shirrak_the_dead_watcher()
newscript->Name = "boss_shirrak_the_dead_watcher";
newscript->GetAI = &GetAI_boss_shirrak_the_dead_watcher;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_focus_fire";
newscript->GetAI = &GetAI_mob_focus_fire;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp b/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp
index c17a021cc27..9d19da0916f 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_nexusprince_shaffar.cpp
@@ -13,17 +13,21 @@
* along 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"
+
enum ePrince
{
SAY_INTRO = -1557000,
@@ -34,27 +38,36 @@ enum ePrince
SAY_SLAY_2 = -1557005,
SAY_SUMMON = -1557006,
SAY_DEAD = -1557007,
+
SPELL_BLINK = 34605,
SPELL_FROSTBOLT = 32364,
SPELL_FIREBALL = 32363,
SPELL_FROSTNOVA = 32365,
+
SPELL_ETHEREAL_BEACON = 32371, // Summons NPC_BEACON
SPELL_ETHEREAL_BEACON_VISUAL = 32368,
+
NPC_BEACON = 18431,
NPC_SHAFFAR = 18344,
+
NR_INITIAL_BEACONS = 3
};
+
struct TRINITY_DLL_DECL boss_nexusprince_shaffarAI : public ScriptedAI
{
boss_nexusprince_shaffarAI(Creature *c) : ScriptedAI(c), summons(me) { HasTaunted = false; }
+
uint32 Blink_Timer;
uint32 Beacon_Timer;
uint32 FireBall_Timer;
uint32 Frostbolt_Timer;
uint32 FrostNova_Timer;
+
SummonList summons;
+
bool HasTaunted;
bool CanBlink;
+
void Reset()
{
Blink_Timer = 1500;
@@ -62,19 +75,24 @@ struct TRINITY_DLL_DECL boss_nexusprince_shaffarAI : public ScriptedAI
FireBall_Timer = 8000;
Frostbolt_Timer = 4000;
FrostNova_Timer = 15000;
+
CanBlink = false;
+
float dist = 8.0f;
float posX, posY, posZ, angle;
m_creature->GetHomePosition(posX, posY, posZ, angle);
+
m_creature->SummonCreature(NPC_BEACON, posX - dist, posY - dist, posZ, angle, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 7200000);
m_creature->SummonCreature(NPC_BEACON, posX - dist, posY + dist, posZ, angle, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 7200000);
m_creature->SummonCreature(NPC_BEACON, posX + dist, posY, posZ, angle, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 7200000);
}
+
void EnterEvadeMode()
{
summons.DespawnAll();
ScriptedAI::EnterEvadeMode();
}
+
void MoveInLineOfSight(Unit *who)
{
if (!HasTaunted && who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 100.0f))
@@ -83,113 +101,141 @@ struct TRINITY_DLL_DECL boss_nexusprince_shaffarAI : public ScriptedAI
HasTaunted = true;
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
+
DoZoneInCombat();
summons.DoZoneInCombat();
}
+
void JustSummoned(Creature *summoned)
{
if (summoned->GetEntry() == NPC_BEACON)
{
summoned->CastSpell(summoned,SPELL_ETHEREAL_BEACON_VISUAL,false);
+
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0))
summoned->AI()->AttackStart(target);
}
+
summons.Summon(summoned);
}
+
void SummonedCreatureDespawn(Creature *summon)
{
summons.Despawn(summon);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEAD, m_creature);
summons.DespawnAll();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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);
+
//expire movement, will prevent from running right back to victim after cast
//(but should MoveChase be used again at a certain time or should he not move?)
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE)
m_creature->GetMotionMaster()->MovementExpired();
+
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))
DoScriptText(SAY_SUMMON, m_creature);
+
DoCast(m_creature,SPELL_ETHEREAL_BEACON, true);
+
Beacon_Timer = 10000;
}else Beacon_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_nexusprince_shaffar(Creature* pCreature)
{
return new boss_nexusprince_shaffarAI (pCreature);
}
+
enum eEnums
{
SPELL_ARCANE_BOLT = 15254,
SPELL_ETHEREAL_APPRENTICE = 32372 // Summon 18430
};
+
struct TRINITY_DLL_DECL mob_ethereal_beaconAI : public ScriptedAI
{
mob_ethereal_beaconAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
uint32 Apprentice_Timer;
uint32 ArcaneBolt_Timer;
uint32 Check_Timer;
+
void KillSelf()
{
m_creature->Kill(m_creature);
}
+
void Reset()
{
Apprentice_Timer = (HeroicMode ? 10000 : 20000);
ArcaneBolt_Timer = 1000;
Check_Timer = 1000;
}
+
void EnterCombat(Unit *who)
{
// Send Shaffar to fight
@@ -202,14 +248,17 @@ struct TRINITY_DLL_DECL mob_ethereal_beaconAI : public ScriptedAI
if (!Shaffar->isInCombat())
Shaffar->AI()->AttackStart(who);
}
+
void JustSummoned(Creature *summoned)
{
summoned->AI()->AttackStart(m_creature->getVictim());
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Check_Timer < diff)
{
Creature *Shaffar = me->FindNearestCreature(NPC_SHAFFAR, 100);
@@ -220,53 +269,62 @@ struct TRINITY_DLL_DECL mob_ethereal_beaconAI : public ScriptedAI
}
Check_Timer = 1000;
}else Check_Timer -= diff;
+
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);
m_creature->ForcedDespawn();
return;
}else Apprentice_Timer -= diff;
}
};
+
CreatureAI* GetAI_mob_ethereal_beacon(Creature* pCreature)
{
return new mob_ethereal_beaconAI (pCreature);
}
+
enum eEthereal
{
SPELL_ETHEREAL_APPRENTICE_FIREBOLT = 32369,
SPELL_ETHEREAL_APPRENTICE_FROSTBOLT = 32370
};
+
struct TRINITY_DLL_DECL mob_ethereal_apprenticeAI : public ScriptedAI
{
mob_ethereal_apprenticeAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Cast_Timer;
+
bool isFireboltTurn;
+
void Reset()
{
Cast_Timer = 3000;
isFireboltTurn = true;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Cast_Timer < diff)
{
if (isFireboltTurn)
{
m_creature->CastSpell(m_creature->getVictim(), SPELL_ETHEREAL_APPRENTICE_FIREBOLT, true);
isFireboltTurn = false;
- }
- else
- {
+ }else{
m_creature->CastSpell(m_creature->getVictim(), SPELL_ETHEREAL_APPRENTICE_FROSTBOLT, true);
isFireboltTurn = true;
}
@@ -274,21 +332,26 @@ struct TRINITY_DLL_DECL mob_ethereal_apprenticeAI : public ScriptedAI
}else Cast_Timer -= diff;
}
};
+
CreatureAI* GetAI_mob_ethereal_apprentice(Creature* pCreature)
{
return new mob_ethereal_apprenticeAI (pCreature);
}
+
void AddSC_boss_nexusprince_shaffar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_nexusprince_shaffar";
newscript->GetAI = &GetAI_boss_nexusprince_shaffar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ethereal_beacon";
newscript->GetAI = &GetAI_mob_ethereal_beacon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ethereal_apprentice";
newscript->GetAI = &GetAI_mob_ethereal_apprentice;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp b/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp
index c30109335d0..4f91a8a08df 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/mana_tombs/boss_pandemonius.cpp
@@ -13,56 +13,71 @@
* along 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 -1557008
#define SAY_AGGRO_2 -1557009
#define SAY_AGGRO_3 -1557010
+
#define SAY_KILL_1 -1557011
#define SAY_KILL_2 -1557012
+
#define SAY_DEATH -1557013
+
#define EMOTE_DARK_SHELL -1557014
+
#define SPELL_VOID_BLAST 32325
#define H_SPELL_VOID_BLAST 38760
#define SPELL_DARK_SHELL 32358
#define H_SPELL_DARK_SHELL 38759
+
struct TRINITY_DLL_DECL boss_pandemoniusAI : public ScriptedAI
{
boss_pandemoniusAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
uint32 VoidBlast_Timer;
uint32 DarkShell_Timer;
uint32 VoidBlast_Counter;
+
void Reset()
{
VoidBlast_Timer = 8000+rand()%15000;
DarkShell_Timer = 20000;
VoidBlast_Counter = 0;
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (VoidBlast_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -71,30 +86,37 @@ struct TRINITY_DLL_DECL boss_pandemoniusAI : public ScriptedAI
VoidBlast_Timer = 500;
++VoidBlast_Counter;
}
+
if (VoidBlast_Counter == 5)
{
VoidBlast_Timer = 15000+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);
+
DoScriptText(EMOTE_DARK_SHELL, m_creature);
+
DoCast(m_creature,HEROIC(SPELL_DARK_SHELL, H_SPELL_DARK_SHELL));
DarkShell_Timer = 20000;
}else DarkShell_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_pandemonius(Creature* pCreature)
{
return new boss_pandemoniusAI (pCreature);
}
+
void AddSC_boss_pandemonius()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp
index 2d2e60e1e3c..1d0092c2dfc 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_darkweaver_syth.cpp
@@ -13,48 +13,63 @@
* along 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 partly implemented.
SDCategory: Auchindoun, Sethekk Halls
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_SUMMON -1556000
+
#define SAY_AGGRO_1 -1556001
#define SAY_AGGRO_2 -1556002
#define SAY_AGGRO_3 -1556003
+
#define SAY_SLAY_1 -1556004
#define SAY_SLAY_2 -1556005
+
#define SAY_DEATH -1556006
+
#define SPELL_FROST_SHOCK 21401 //37865
#define SPELL_FLAME_SHOCK 34354
#define SPELL_SHADOW_SHOCK 30138
#define SPELL_ARCANE_SHOCK 37132
+
#define SPELL_CHAIN_LIGHTNING 15659 //15305
+
#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 (HeroicMode?38141:33526)
#define SPELL_ARCANE_BUFFET (HeroicMode?38138:33527)
#define SPELL_FROST_BUFFET (HeroicMode?38142:33528)
#define SPELL_SHADOW_BUFFET (HeroicMode?38143:33529)
+
struct TRINITY_DLL_DECL boss_darkweaver_sythAI : public ScriptedAI
{
boss_darkweaver_sythAI(Creature *c) : ScriptedAI(c)
+
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 flameshock_timer;
uint32 arcaneshock_timer;
uint32 frostshock_timer;
uint32 shadowshock_timer;
uint32 chainlightning_timer;
+
bool summon90;
bool summon50;
bool summon10;
bool HeroicMode;
+
void Reset()
{
flameshock_timer = 2000;
@@ -62,252 +77,327 @@ struct TRINITY_DLL_DECL boss_darkweaver_sythAI : public ScriptedAI
frostshock_timer = 6000;
shadowshock_timer = 8000;
chainlightning_timer = 15000;
+
summon90 = false;
summon50 = false;
summon10 = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit* victim)
{
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustSummoned(Creature *summoned)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0))
summoned->AI()->AttackStart(target);
}
+
void SythSummoning()
{
DoScriptText(SAY_SUMMON, m_creature);
+
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 (!UpdateVictim())
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* pCreature)
{
return new boss_darkweaver_sythAI (pCreature);
}
+
/* ELEMENTALS */
+
struct TRINITY_DLL_DECL mob_syth_fireAI : public ScriptedAI
{
mob_syth_fireAI(Creature *c) : ScriptedAI(c)
+
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 flameshock_timer;
uint32 flamebuffet_timer;
bool HeroicMode;
+
void Reset()
{
m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true);
flameshock_timer = 2500;
flamebuffet_timer = 5000;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new mob_syth_fireAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_syth_arcaneAI : public ScriptedAI
{
mob_syth_arcaneAI(Creature *c) : ScriptedAI(c)
+
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 arcaneshock_timer;
uint32 arcanebuffet_timer;
bool HeroicMode;
+
void Reset()
{
m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_ARCANE, true);
arcaneshock_timer = 2500;
arcanebuffet_timer = 5000;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new mob_syth_arcaneAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_syth_frostAI : public ScriptedAI
{
mob_syth_frostAI(Creature *c) : ScriptedAI(c)
+
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 frostshock_timer;
uint32 frostbuffet_timer;
bool HeroicMode;
+
void Reset()
{
m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FROST, true);
frostshock_timer = 2500;
frostbuffet_timer = 5000;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new mob_syth_frostAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_syth_shadowAI : public ScriptedAI
{
mob_syth_shadowAI(Creature *c) : ScriptedAI(c)
+
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 shadowshock_timer;
uint32 shadowbuffet_timer;
bool HeroicMode;
+
void Reset()
{
m_creature->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_SHADOW, true);
shadowshock_timer = 2500;
shadowbuffet_timer = 5000;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new mob_syth_shadowAI (pCreature);
}
+
void AddSC_boss_darkweaver_syth()
{
Script *newscript;
@@ -315,18 +405,22 @@ void AddSC_boss_darkweaver_syth()
newscript->Name = "boss_darkweaver_syth";
newscript->GetAI = &GetAI_boss_darkweaver_syth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_syth_fire";
newscript->GetAI = &GetAI_mob_syth_arcane;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_syth_arcane";
newscript->GetAI = &GetAI_mob_syth_arcane;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_syth_frost";
newscript->GetAI = &GetAI_mob_syth_frost;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_syth_shadow";
newscript->GetAI = &GetAI_mob_syth_shadow;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp
index 672fe5fec58..eafd0b2d0fd 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/boss_tailonking_ikiss.cpp
@@ -13,51 +13,67 @@
* along 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 -1556007
+
#define SAY_AGGRO_1 -1556008
#define SAY_AGGRO_2 -1556009
#define SAY_AGGRO_3 -1556010
+
#define SAY_SLAY_1 -1556011
#define SAY_SLAY_2 -1556012
#define SAY_DEATH -1556013
#define EMOTE_ARCANE_EXP -1556015
+
#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 TRINITY_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI
{
boss_talon_king_ikissAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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;
@@ -66,6 +82,7 @@ struct TRINITY_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI
Intro = false;
ManaShield = false;
}
+
void MoveInLineOfSight(Unit *who)
{
if (!m_creature->getVictim() && who->isTargetableForAttack() && (m_creature->IsHostileTo(who)) && who->isInAccessiblePlaceFor(m_creature))
@@ -75,8 +92,10 @@ struct TRINITY_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI
Intro = true;
DoScriptText(SAY_INTRO, m_creature);
}
+
if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
return;
+
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who))
{
@@ -85,35 +104,43 @@ struct TRINITY_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_IKISSDOOREVENT, DONE);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Blink)
{
DoCast(m_creature,HEROIC(SPELL_ARCANE_EXPLOSION, H_SPELL_ARCANE_EXPLOSION));
m_creature->CastSpell(m_creature,SPELL_ARCANE_BUBBLE,true);
Blink = false;
}
+
if (ArcaneVolley_Timer < diff)
{
DoCast(m_creature,HEROIC(SPELL_ARCANE_VOLLEY, H_SPELL_ARCANE_VOLLEY));
ArcaneVolley_Timer = 7000+rand()%5000;
}else ArcaneVolley_Timer -= diff;
+
if (Sheep_Timer < diff)
{
//second top aggro target in normal, random target in heroic correct?
@@ -123,12 +150,14 @@ struct TRINITY_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI
DoCast(target,HEROIC(SPELL_POLYMORPH, H_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)
@@ -137,32 +166,41 @@ struct TRINITY_DLL_DECL boss_talon_king_ikissAI : public ScriptedAI
Slow_Timer = 15000+rand()%25000;
}else Slow_Timer -= diff;
}
+
if (Blink_Timer < diff)
{
DoScriptText(EMOTE_ARCANE_EXP, m_creature);
+
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();
+
DoTeleportTo(X,Y,Z);
+
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* pCreature)
{
return new boss_talon_king_ikissAI (pCreature);
}
+
void AddSC_boss_talon_king_ikiss()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/def_sethekk_halls.h b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/def_sethekk_halls.h
index e3e4f0c6656..79a6cd4952d 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/def_sethekk_halls.h
+++ b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/def_sethekk_halls.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SETHEKK_HALLS_H
#define DEF_SETHEKK_HALLS_H
+
enum eTypes
{
DATA_IKISSDOOREVENT = 1,
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp
index 44fab706f58..942325bd315 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/sethekk_halls/instance_sethekk_halls.cpp
@@ -13,29 +13,36 @@
* along 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"
+
enum eEnums
{
NPC_ANZU = 23035,
IKISS_DOOR = 177203,
};
+
struct TRINITY_DLL_DECL instance_sethekk_halls : public ScriptedInstance
{
instance_sethekk_halls(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 AnzuEncounter;
uint64 m_uiIkissDoorGUID;
+
void Initialize()
{
AnzuEncounter = NOT_STARTED;
m_uiIkissDoorGUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
if (pCreature->GetEntry() == NPC_ANZU)
@@ -46,11 +53,13 @@ struct TRINITY_DLL_DECL instance_sethekk_halls : public ScriptedInstance
AnzuEncounter = IN_PROGRESS;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
if (pGo->GetEntry() == IKISS_DOOR)
m_uiIkissDoorGUID = pGo->GetGUID();
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -65,10 +74,12 @@ struct TRINITY_DLL_DECL instance_sethekk_halls : public ScriptedInstance
}
}
};
+
InstanceData* GetInstanceData_instance_sethekk_halls(Map* pMap)
{
return new instance_sethekk_halls(pMap);
}
+
void AddSC_instance_sethekk_halls()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp
index d6d92da05b6..aeb9f15b8ba 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_ambassador_hellmaw.cpp
@@ -13,15 +13,18 @@
* along 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: 80
SDComment: Enrage spell missing/not known
SDCategory: Auchindoun, Shadow Labyrinth
EndScriptData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "def_shadow_labyrinth.h"
+
enum eEnums
{
SAY_INTRO = -1555000,
@@ -32,11 +35,13 @@ enum eEnums
SAY_SLAY1 = -1555005,
SAY_SLAY2 = -1555006,
SAY_DEATH = -1555007,
+
SPELL_BANISH = 30231,
SPELL_CORROSIVE_ACID = 33551,
SPELL_FEAR = 33547,
SPELL_ENRAGE = 34970
};
+
struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
{
boss_ambassador_hellmawAI(Creature* pCreature) : npc_escortAI(pCreature)
@@ -44,8 +49,10 @@ struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
m_pInstance = pCreature->GetInstanceData();
HeroicMode = pCreature->GetMap()->IsHeroic();
}
+
ScriptedInstance* m_pInstance;
bool HeroicMode;
+
uint32 EventCheck_Timer;
uint32 CorrosiveAcid_Timer;
uint32 Fear_Timer;
@@ -53,6 +60,7 @@ struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
bool Intro;
bool IsBanished;
bool Enraged;
+
void Reset()
{
EventCheck_Timer = 5000;
@@ -62,32 +70,40 @@ struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
Intro = false;
IsBanished = true;
Enraged = false;
+
if (m_pInstance && m_creature->isAlive())
{
if (m_pInstance->GetData(TYPE_OVERSEER) != DONE)
m_creature->CastSpell(m_creature, SPELL_BANISH, true);
}
}
+
void JustReachedHome()
{
if (m_pInstance)
m_pInstance->SetData(TYPE_HELLMAW, FAIL);
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (m_creature->HasAura(SPELL_BANISH))
return;
+
npc_escortAI::MoveInLineOfSight(pWho);
}
+
void WaypointReached(uint32 i)
{
}
+
void DoIntro()
{
if (m_creature->HasAura(SPELL_BANISH))
m_creature->RemoveAurasDueToSpell(SPELL_BANISH);
+
IsBanished = false;
Intro = true;
+
if (m_pInstance)
{
if (m_pInstance->GetData(TYPE_HELLMAW) != FAIL)
@@ -95,23 +111,29 @@ struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
DoScriptText(SAY_INTRO, m_creature);
Start(true, false, 0, NULL, false, true);
}
+
m_pInstance->SetData(TYPE_HELLMAW, IN_PROGRESS);
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (m_pInstance)
m_pInstance->SetData(TYPE_HELLMAW, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!Intro && !HasEscortState(STATE_ESCORT_ESCORTING))
@@ -135,24 +157,30 @@ struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
return;
}
}
+
npc_escortAI::UpdateAI(diff);
+
if (!UpdateVictim())
return;
+
if (m_creature->HasAura(SPELL_BANISH, 0))
{
EnterEvadeMode();
return;
}
+
if (CorrosiveAcid_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CORROSIVE_ACID);
CorrosiveAcid_Timer = 15000 + rand()%10000;
}else CorrosiveAcid_Timer -= diff;
+
if (Fear_Timer < diff)
{
DoCast(m_creature,SPELL_FEAR);
Fear_Timer = 20000 + rand()%15000;
}else Fear_Timer -= diff;
+
if (HeroicMode)
{
if (!Enraged && Enrage_Timer < diff)
@@ -163,10 +191,12 @@ struct TRINITY_DLL_DECL boss_ambassador_hellmawAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_boss_ambassador_hellmaw(Creature* pCreature)
{
return new boss_ambassador_hellmawAI(pCreature);
}
+
void AddSC_boss_ambassador_hellmaw()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp
index c400cc1de95..1eca5a36734 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_blackheart_the_inciter.cpp
@@ -13,18 +13,22 @@
* along 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_INTRO1 -1555008 //not used
#define SAY_INTRO2 -1555009 //not used
#define SAY_INTRO3 -1555010 //not used
@@ -35,6 +39,7 @@ EndScriptData */
#define SAY_SLAY2 -1555015
#define SAY_HELP -1555016 //not used
#define SAY_DEATH -1555017
+
//below, not used
#define SAY2_INTRO1 -1555018
#define SAY2_INTRO2 -1555019
@@ -46,18 +51,22 @@ EndScriptData */
#define SAY2_SLAY2 -1555025
#define SAY2_HELP -1555026
#define SAY2_DEATH -1555027
+
struct TRINITY_DLL_DECL boss_blackheart_the_inciterAI : public ScriptedAI
{
boss_blackheart_the_inciterAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
bool InciteChaos;
uint32 InciteChaos_Timer;
uint32 InciteChaosWait_Timer;
uint32 Charge_Timer;
uint32 Knockback_Timer;
+
void Reset()
{
InciteChaos = false;
@@ -65,30 +74,38 @@ struct TRINITY_DLL_DECL boss_blackheart_the_inciterAI : public ScriptedAI
InciteChaosWait_Timer = 15000;
Charge_Timer = 5000;
Knockback_Timer = 15000;
+
if (pInstance)
pInstance->SetData(DATA_BLACKHEARTTHEINCITEREVENT, NOT_STARTED);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_BLACKHEARTTHEINCITEREVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
+
if (pInstance)
pInstance->SetData(DATA_BLACKHEARTTHEINCITEREVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (InciteChaos)
{
if (InciteChaosWait_Timer < diff)
@@ -96,23 +113,28 @@ struct TRINITY_DLL_DECL boss_blackheart_the_inciterAI : public ScriptedAI
InciteChaos = false;
InciteChaosWait_Timer = 15000;
}else InciteChaosWait_Timer -= diff;
+
return;
}
+
if (InciteChaos_Timer < diff)
{
DoCast(m_creature, SPELL_INCITE_CHAOS);
+
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::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)
{
@@ -120,12 +142,14 @@ struct TRINITY_DLL_DECL boss_blackheart_the_inciterAI : public ScriptedAI
DoCast(target, SPELL_CHARGE);
Charge_Timer = 15000 + rand()%10000;
}else Charge_Timer -= diff;
+
//Knockback_Timer
if (Knockback_Timer < diff)
{
DoCast(m_creature, SPELL_WAR_STOMP);
Knockback_Timer = 18000 + rand()%6000;
}else Knockback_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -133,6 +157,7 @@ CreatureAI* GetAI_boss_blackheart_the_inciter(Creature* pCreature)
{
return new boss_blackheart_the_inciterAI (pCreature);
}
+
void AddSC_boss_blackheart_the_inciter()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp
index 6fe4eada5e6..34bcdd86175 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_grandmaster_vorpil.cpp
@@ -13,14 +13,17 @@
* along 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: 100
SDComment:
SDCategory: Auchindoun, Shadow Labyrinth
EndScriptData */
+
#include "precompiled.h"
#include "def_shadow_labyrinth.h"
+
#define SAY_INTRO -1555028
#define SAY_AGGRO1 -1555029
#define SAY_AGGRO2 -1555030
@@ -29,19 +32,25 @@ EndScriptData */
#define SAY_SLAY1 -1555033
#define SAY_SLAY2 -1555034
#define SAY_DEATH -1555035
+
#define SPELL_RAIN_OF_FIRE 33617
#define H_SPELL_RAIN_OF_FIRE 39363
+
#define SPELL_DRAW_SHADOWS 33563
#define SPELL_SHADOWBOLT_VOLLEY 33841
#define SPELL_BANISH 38791
+
#define MOB_VOID_TRAVELER 19226
#define SPELL_SACRIFICE 33587
#define SPELL_SHADOW_NOVA 33846
#define SPELL_EMPOWERING_SHADOWS 33783
#define H_SPELL_EMPOWERING_SHADOWS 39364
+
#define MOB_VOID_PORTAL 19224
#define SPELL_VOID_PORTAL_VISUAL 33569
+
float VorpilPosition[3] = {-252.8820,-264.3030,17.1};
+
float VoidPortalCoords[5][3] =
{
{-283.5894, -239.5718, 12.7},
@@ -50,23 +59,28 @@ float VoidPortalCoords[5][3] =
{-209.3401, -262.7564, 17.1},
{-261.4533, -297.3298, 17.1}
};
+
struct TRINITY_DLL_DECL mob_voidtravelerAI : public ScriptedAI
{
mob_voidtravelerAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
Unit *Vorpil;
uint32 move;
bool sacrificed;
+
void Reset()
{
Vorpil = NULL;
move = 0;
sacrificed = false;
}
+
void EnterCombat(Unit *who){}
+
void UpdateAI(const uint32 diff)
{
if (!Vorpil)
@@ -107,6 +121,7 @@ CreatureAI* GetAI_mob_voidtraveler(Creature* pCreature)
{
return new mob_voidtravelerAI (pCreature);
}
+
struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
{
boss_grandmaster_vorpilAI(Creature *c) : ScriptedAI(c)
@@ -115,15 +130,18 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
HeroicMode = c->GetMap()->IsHeroic();
Intro = false;
}
+
ScriptedInstance *pInstance;
bool Intro, HelpYell;
bool sumportals;
bool HeroicMode;
+
uint32 ShadowBoltVolley_Timer;
uint32 DrawShadows_Timer;
uint32 summonTraveler_Timer;
uint32 banish_Timer;
uint64 PortalsGuid[5];
+
void Reset()
{
ShadowBoltVolley_Timer = 7000 + rand()%7000;
@@ -132,9 +150,11 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
banish_Timer = 17000;
HelpYell = false;
destroyPortals();
+
if (pInstance)
pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, NOT_STARTED);
}
+
void summonPortals()
{
if (!sumportals)
@@ -153,6 +173,7 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
summonTraveler_Timer = 5000;
}
}
+
void destroyPortals()
{
if (sumportals)
@@ -167,6 +188,7 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
sumportals = false;
}
}
+
void spawnVoidTraveler()
{
int pos = urand(0,4);
@@ -177,47 +199,58 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
HelpYell = true;
}
}
+
void JustSummoned(Creature *summoned)
{
if (summoned && summoned->GetEntry() == MOB_VOID_TRAVELER)
CAST_AI(mob_voidtravelerAI, summoned->AI())->Vorpil = m_creature;
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
destroyPortals();
+
if (pInstance)
pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
summonPortals();
+
if (pInstance)
pInstance->SetData(DATA_GRANDMASTERVORPILEVENT, IN_PROGRESS);
}
+
void MoveInLineOfSight(Unit *who)
{
ScriptedAI::MoveInLineOfSight(who);
+
if (!Intro && m_creature->IsWithinLOSInMap(who)&& m_creature->IsWithinDistInMap(who, 100) && m_creature->IsHostileTo(who))
{
DoScriptText(SAY_INTRO, m_creature);
Intro = true;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (ShadowBoltVolley_Timer < diff)
{
DoCast(m_creature,SPELL_SHADOWBOLT_VOLLEY);
ShadowBoltVolley_Timer = 15000 + rand()%15000;;
} else ShadowBoltVolley_Timer -= diff;
+
if (HeroicMode && banish_Timer < diff)
{
Unit *target = SelectTarget(SELECT_TARGET_RANDOM,0,30,false);
@@ -227,20 +260,25 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
banish_Timer = 16000;
}
} else banish_Timer -= diff;
+
if (DrawShadows_Timer < diff)
{
Map* pMap = m_creature->GetMap();
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (Player* i_pl = i->getSource())
if (i_pl->isAlive() && !i_pl->HasAura(SPELL_BANISH))
i_pl->TeleportTo(m_creature->GetMapId(), VorpilPosition[0],VorpilPosition[1],VorpilPosition[2], 0, TELE_TO_NOT_LEAVE_COMBAT);
+
m_creature->GetMap()->CreatureRelocation(m_creature, VorpilPosition[0],VorpilPosition[1],VorpilPosition[2],0.0f);
DoCast(m_creature,SPELL_DRAW_SHADOWS,true);
+
DoCast(m_creature,HeroicMode?H_SPELL_RAIN_OF_FIRE:SPELL_RAIN_OF_FIRE);
+
ShadowBoltVolley_Timer = 6000;
DrawShadows_Timer = 30000;
} else DrawShadows_Timer -= diff;
+
if (summonTraveler_Timer < diff)
{
spawnVoidTraveler();
@@ -249,6 +287,7 @@ struct TRINITY_DLL_DECL boss_grandmaster_vorpilAI : public ScriptedAI
if ((m_creature->GetHealth()*5) < m_creature->GetMaxHealth())
summonTraveler_Timer = 5000;
} else summonTraveler_Timer -=diff;
+
DoMeleeAttackIfReady();
}
};
@@ -256,6 +295,7 @@ CreatureAI* GetAI_boss_grandmaster_vorpil(Creature* pCreature)
{
return new boss_grandmaster_vorpilAI (pCreature);
}
+
void AddSC_boss_grandmaster_vorpil()
{
Script *newscript;
@@ -263,6 +303,7 @@ void AddSC_boss_grandmaster_vorpil()
newscript->Name = "boss_grandmaster_vorpil";
newscript->GetAI = &GetAI_boss_grandmaster_vorpil;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_voidtraveler";
newscript->GetAI = &GetAI_mob_voidtraveler;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp
index 6ab5baaf56c..7782b5fed62 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/boss_murmur.cpp
@@ -13,15 +13,19 @@
* along 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: 90
SDComment: Timers may be incorrect
SDCategory: Auchindoun, Shadow Labyrinth
EndScriptData */
+
#include "precompiled.h"
#include "def_shadow_labyrinth.h"
+
#define EMOTE_SONIC_BOOM -1555036
+
#define SPELL_SONIC_BOOM_CAST (HeroicMode?38796:33923)
#define SPELL_SONIC_BOOM_EFFECT (HeroicMode?38795:33666)
#define SPELL_RESONANCE 33657
@@ -29,6 +33,7 @@ EndScriptData */
#define SPELL_MAGNETIC_PULL 33689
#define SPELL_SONIC_SHOCK 38797
#define SPELL_THUNDERING_STORM 39365
+
struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
{
boss_murmurAI(Creature *c) : ScriptedAI(c)
@@ -36,6 +41,7 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
SetCombatMovement(false);
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 SonicBoom_Timer;
uint32 MurmursTouch_Timer;
uint32 Resonance_Timer;
@@ -44,6 +50,7 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
uint32 ThunderingStorm_Timer;
bool HeroicMode;
bool SonicBoom;
+
void Reset()
{
SonicBoom_Timer = 30000;
@@ -53,15 +60,17 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
ThunderingStorm_Timer = 15000;
SonicShock_Timer = 10000;
SonicBoom = false;
+
//database should have `RegenHealth`=0 to prevent regen
uint32 hp = (m_creature->GetMaxHealth()*40)/100;
if (hp) m_creature->SetHealth(hp);
m_creature->ResetPlayerDamageReq();
}
+
void SonicBoomEffect()
{
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
Unit* target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
if (target && target->GetTypeId() == TYPEID_PLAYER)
@@ -75,23 +84,28 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
}
}
}
+
void EnterCombat(Unit *who) { }
+
// Sonic Boom instant damage (needs core fix instead of this)
void SpellHitTarget(Unit *target, const SpellEntry *spell)
{
if (target && target->isAlive() && spell && spell->Id == SPELL_SONIC_BOOM_EFFECT)
m_creature->DealDamage(target,(target->GetHealth()*90)/100,NULL,SPELL_DIRECT_DAMAGE,SPELL_SCHOOL_MASK_NATURE,spell);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target or casting
if (!UpdateVictim() || m_creature->IsNonMeleeSpellCasted(false))
return;
+
// Sonic Boom
if (SonicBoom)
{
DoCast(m_creature, SPELL_SONIC_BOOM_EFFECT, true);
SonicBoomEffect();
+
SonicBoom = false;
Resonance_Timer = 1500;
}
@@ -103,6 +117,7 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
SonicBoom = true;
return;
}else SonicBoom_Timer -= diff;
+
// Murmur's Touch
if (MurmursTouch_Timer < diff)
{
@@ -110,6 +125,7 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
DoCast(target, SPELL_MURMURS_TOUCH);
MurmursTouch_Timer = 25000 + rand()%10000;
}else MurmursTouch_Timer -= diff;
+
// Resonance
if (!SonicBoom && !(m_creature->IsWithinMeleeRange(m_creature->getVictim())))
{
@@ -119,6 +135,7 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
Resonance_Timer = 5000;
}else Resonance_Timer -= diff;
}
+
// Magnetic Pull
if (MagneticPull_Timer < diff)
{
@@ -131,18 +148,20 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
}
MagneticPull_Timer = 500;
}else MagneticPull_Timer -= diff;
+
if (HeroicMode)
{
// Thundering Storm
if (ThunderingStorm_Timer < diff)
{
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::iterator i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
+ for(std::list<HostilReference*>::iterator i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
if (Unit* target = Unit::GetUnit((*m_creature),(*i)->getUnitGuid()))
if (target->isAlive() && !m_creature->IsWithinDist(target, 35, false))
DoCast(target, SPELL_THUNDERING_STORM, true);
ThunderingStorm_Timer = 15000;
}else ThunderingStorm_Timer -= diff;
+
// Sonic Shock
if (SonicShock_Timer < diff)
{
@@ -152,13 +171,14 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
SonicShock_Timer = 10000+rand()%10000;
}else SonicShock_Timer -= diff;
}
+
// Select nearest most aggro target if top aggro too far
if (!m_creature->isAttackReady())
return;
if (!m_creature->IsWithinMeleeRange(m_creature->getVictim()))
{
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference*>::iterator i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
+ for(std::list<HostilReference*>::iterator i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
if (Unit* target = Unit::GetUnit((*m_creature),(*i)->getUnitGuid()))
if (target->isAlive() && m_creature->IsWithinMeleeRange(target))
{
@@ -166,13 +186,16 @@ struct TRINITY_DLL_DECL boss_murmurAI : public ScriptedAI
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_murmur(Creature* pCreature)
{
return new boss_murmurAI (pCreature);
}
+
void AddSC_boss_murmur()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/def_shadow_labyrinth.h b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/def_shadow_labyrinth.h
index a5f233dbbca..a78955368bf 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/def_shadow_labyrinth.h
+++ b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/def_shadow_labyrinth.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SHADOW_LABYRINTH_H
#define DEF_SHADOW_LABYRINTH_H
+
#define TYPE_HELLMAW 1
#define TYPE_OVERSEER 2
#define DATA_BLACKHEARTTHEINCITEREVENT 3
diff --git a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp
index 1897f405415..f8f3b73bffa 100644
--- a/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp
+++ b/src/bindings/scripts/scripts/outland/auchindoun/shadow_labyrinth/instance_shadow_labyrinth.cpp
@@ -13,46 +13,61 @@
* along 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 MAX_ENCOUNTER 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 TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
{
instance_shadow_labyrinth(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
+
uint64 m_uiRefectoryDoorGUID;
uint64 m_uiScreamingHallDoorGUID;
+
uint64 m_uiGrandmasterVorpil;
uint32 m_uiFelOverseerCount;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiRefectoryDoorGUID = 0;
m_uiScreamingHallDoorGUID = 0;
+
m_uiGrandmasterVorpil = 0;
m_uiFelOverseerCount = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -69,6 +84,7 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -85,6 +101,7 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
break;
}
}
+
void SetData(uint32 type, uint32 uiData)
{
switch(type)
@@ -92,6 +109,7 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
case TYPE_HELLMAW:
m_auiEncounter[0] = uiData;
break;
+
case TYPE_OVERSEER:
if (uiData != DONE)
{
@@ -101,6 +119,7 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
if (m_uiFelOverseerCount)
{
--m_uiFelOverseerCount;
+
if (m_uiFelOverseerCount)
debug_log("TSCR: Shadow Labyrinth: %u Fel Overseers left to kill.",m_uiFelOverseerCount);
else
@@ -110,33 +129,42 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
}
}
break;
+
case DATA_BLACKHEARTTHEINCITEREVENT:
if (uiData == DONE)
DoUseDoorOrButton(m_uiRefectoryDoorGUID);
m_auiEncounter[2] = uiData;
break;
+
case DATA_GRANDMASTERVORPILEVENT:
if (uiData == DONE)
DoUseDoorOrButton(m_uiScreamingHallDoorGUID);
m_auiEncounter[3] = uiData;
break;
+
case DATA_MURMUREVENT:
m_auiEncounter[4] = uiData;
break;
}
+
if (uiData == DONE)
{
if (type == TYPE_OVERSEER && m_uiFelOverseerCount != 0)
return;
+
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << m_auiEncounter[3] << " " << m_auiEncounter[4];
+
str_data = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -148,16 +176,20 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
}
return false;
}
+
uint64 GetData64(uint32 identifier)
{
if (identifier == DATA_GRANDMASTERVORPIL)
return m_uiGrandmasterVorpil;
+
return 0;
}
+
std::string GetSaveData()
{
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -165,19 +197,25 @@ struct TRINITY_DLL_DECL instance_shadow_labyrinth : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3] >> m_auiEncounter[4];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_shadow_labyrinth(Map* pMap)
{
return new instance_shadow_labyrinth(pMap);
}
+
void AddSC_instance_shadow_labyrinth()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/black_temple.cpp b/src/bindings/scripts/scripts/outland/black_temple/black_temple.cpp
index 3e8714217cd..270dafabe54 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/black_temple.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/black_temple.cpp
@@ -13,41 +13,53 @@
* along 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* pPlayer, Creature* pCreature)
{
ScriptedInstance* pInstance = pCreature->GetInstanceData();
+
if (pInstance && (pInstance->GetData(DATA_SUPREMUSEVENT) >= DONE) && (pInstance->GetData(DATA_HIGHWARLORDNAJENTUSEVENT) >= DONE))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_OLUM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_spirit_of_olum(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
pPlayer->CLOSE_GOSSIP_MENU();
+
pPlayer->InterruptNonMeleeSpells(false);
pPlayer->CastSpell(pPlayer, 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;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_bloodboil.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_bloodboil.cpp
index c1f7f51e9b3..be303f85980 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_bloodboil.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_bloodboil.cpp
@@ -13,14 +13,17 @@
* along 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"
+
//Speech'n'Sound
#define SAY_AGGRO -1564029
#define SAY_SLAY1 -1564030
@@ -30,6 +33,7 @@ EndScriptData */
#define SAY_ENRAGE1 -1564034
#define SAY_ENRAGE2 -1564035
#define SAY_DEATH -1564036
+
//Spells
#define SPELL_ACID_GEYSER 40630
#define SPELL_ACIDIC_WOUND 40481
@@ -46,16 +50,22 @@ EndScriptData */
#define SPELL_TAUNT_GURTOGG 40603
#define SPELL_INSIGNIFIGANCE 40618
#define SPELL_BERSERK 45078
+
//This is used to sort the players by distance in preparation for the Bloodboil cast.
+
struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
{
boss_gurtogg_bloodboilAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint64 TargetGUID;
+
float TargetThreat;
+
uint32 BloodboilTimer;
uint32 BloodboilCount;
uint32 AcidGeyserTimer;
@@ -66,13 +76,18 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
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;
@@ -83,10 +98,13 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
EjectTimer = 10000;
BewilderingStrikeTimer = 15000;
PhaseChangeTimer = 60000;
+
Phase1 = true;
+
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, false);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, false);
}
+
void EnterCombat(Unit *who)
{
DoZoneInCombat();
@@ -94,55 +112,65 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_GURTOGGBLOODBOILEVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
pInstance->SetData(DATA_GURTOGGBLOODBOILEVENT, DONE);
+
DoScriptText(SAY_DEATH, m_creature);
}
+
// 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<HostilReference *> m_threatlist = m_creature->getThreatManager().getThreatList();
+
if (!m_threatlist.size()) // He doesn't have anyone in his threatlist, useless to continue
return;
+
std::list<Unit *> targets;
std::list<HostilReference *>::iterator itr = m_threatlist.begin();
- for (; itr!= m_threatlist.end(); ++itr) //store the threat list in a different container
+ 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(ObjectDistanceOrderReversed(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<Unit *>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
+ for(std::list<Unit *>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
{
Unit* target = *itr;
if (!target) return;
- for (uint32 i = 0; i<3; ++i)
+ for(uint32 i = 0;i<3; ++i)
{
uint8 eff = spellInfo->Effect[i];
if (eff>=TOTAL_SPELL_EFFECTS)
continue;
+
Aura *Aur = new Aura(spellInfo, i, target, target, target);
target->AddAura(Aur);
}
}
}*/
}
+
void RevertThreatOnTarget(uint64 guid)
{
Unit* pUnit = NULL;
@@ -155,20 +183,24 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
m_creature->AddThreat(pUnit, TargetThreat);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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))
{
if (EnrageTimer < diff)
@@ -177,6 +209,7 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
DoScriptText(RAND(SAY_ENRAGE1,SAY_ENRAGE2), m_creature);
}else EnrageTimer -= diff;
}
+
if (Phase1)
{
if (BewilderingStrikeTimer < diff)
@@ -187,17 +220,20 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
m_creature->AddThreat(target, mt_threat);
BewilderingStrikeTimer = 20000;
}else BewilderingStrikeTimer -= diff;
+
if (EjectTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_EJECT1);
DoModifyThreatPercent(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.
@@ -209,6 +245,7 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
}
}else BloodboilTimer -= diff;
}
+
if (!Phase1)
{
if (AcidGeyserTimer < diff)
@@ -216,12 +253,14 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
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)
@@ -230,6 +269,7 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
if (target && target->isAlive())
{
Phase1 = false;
+
TargetThreat = DoGetThreat(target);
TargetGUID = target->GetGUID();
target->CastSpell(m_creature, SPELL_TAUNT_GURTOGG, true);
@@ -245,9 +285,12 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
/* 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);
+
DoScriptText(RAND(SAY_SPECIAL1,SAY_SPECIAL2), m_creature);
+
AcidGeyserTimer = 1000;
PhaseChangeTimer = 30000;
}
@@ -268,13 +311,16 @@ struct TRINITY_DLL_DECL boss_gurtogg_bloodboilAI : public ScriptedAI
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, false);
}
}else PhaseChangeTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_gurtogg_bloodboil(Creature* pCreature)
{
return new boss_gurtogg_bloodboilAI (pCreature);
}
+
void AddSC_boss_gurtogg_bloodboil()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_illidan.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_illidan.cpp
index 9928ca54fd0..e081e33eb06 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_illidan.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_illidan.cpp
@@ -13,21 +13,26 @@
* along 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: Somewhat of a workaround for Parasitic Shadowfiend, unable to summon GOs for Cage Trap.
SDCategory: Black Temple
EndScriptData */
+
#include "precompiled.h"
#include "def_black_temple.h"
+
#define GETGO(obj, guid) GameObject* obj = pInstance->instance->GetGameObject(guid)
#define GETUNIT(unit, guid) Unit* unit = Unit::GetUnit(*m_creature, guid)
#define GETCRE(cre, guid) Creature* cre = Unit::GetCreature(*m_creature, guid)
#define HPPCT(unit) unit->GetHealth()*100 / unit->GetMaxHealth()
+
/************* 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
@@ -35,25 +40,31 @@ EndScriptData */
#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
const char* SAY_KILL1 = "Who shall be next to taste my blades?!";
#define SOUND_KILL1 11473
const char* 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 37335 // 41032 is bugged, cannot be block/dodge/parry// Reduces Max. Health by 60% for 7 seconds. Can stack 19 times. 1.5 second cast
@@ -117,14 +128,18 @@ const char* SAY_KILL2 = "This is too easy!";
#define SPELL_SHADOW_STRIKE 40685 // 4375 to 5625 every 3 seconds for 12 seconds
#define SPELL_THROW_DAGGER 41152 // 5400 to 6600 damage, need dagger
#define SPELL_FAN_BLADES 39954 // bugged visual
+
// Other defines
#define CENTER_X 676.740
#define CENTER_Y 305.297
#define CENTER_Z 353.192
+
#define FLAME_ENRAGE_DISTANCE 30
#define FLAME_CHARGE_DISTANCE 50
+
#define EQUIP_ID_MAIN_HAND 32837
#define EQUIP_ID_OFF_HAND 32838
+
/**** Creature Summon and Recognition IDs ****/
enum CreatureEntry
{
@@ -144,6 +159,7 @@ enum CreatureEntry
PARASITIC_SHADOWFIEND = 23498,
CAGE_TRAP_TRIGGER = 23292,
};
+
/*** Phase Names ***/
enum PhaseIllidan
{
@@ -158,6 +174,7 @@ enum PhaseIllidan
PHASE_TRANSFORM_SEQUENCE = 8,
PHASE_ILLIDAN_MAX = 9,
};//Maiev uses the same phase
+
enum PhaseAkama
{
PHASE_AKAMA_NULL = 0,
@@ -168,6 +185,7 @@ enum PhaseAkama
PHASE_FIGHT_MINIONS = 5,
PHASE_RETURN = 6,
};
+
enum EventIllidan
{
EVENT_NULL = 0,
@@ -197,6 +215,7 @@ enum EventIllidan
EVENT_FLIGHT_SEQUENCE = 2,
EVENT_TRANSFORM_SEQUENCE = 2,
};
+
enum EventMaiev
{
EVENT_MAIEV_NULL = 0,
@@ -206,6 +225,7 @@ enum EventMaiev
EVENT_MAIEV_THROW_DAGGER = 4,
EVENT_MAIEV_TRAP = 4,
};
+
static EventIllidan MaxTimer[]=
{
EVENT_NULL,
@@ -218,6 +238,7 @@ static EventIllidan MaxTimer[]=
EVENT_FLIGHT_SEQUENCE,
EVENT_TRANSFORM_SEQUENCE
};
+
struct Yells
{
uint32 sound;
@@ -225,6 +246,7 @@ struct Yells
uint32 pCreature, 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},
@@ -250,6 +272,7 @@ static Yells Conversation[]=
{11387, "The Light will fill these dismal halls once again. I swear it.", AKAMA, 8000, 0, true},
{0, NULL, EMPTY, 1000, 0, false}//21
};
+
static Yells RandomTaunts[]=
{
{11467, "I can feel your hatred.", ILLIDAN_STORMRAGE, 0, 0, false},
@@ -257,6 +280,7 @@ static Yells RandomTaunts[]=
{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},
@@ -264,10 +288,12 @@ static Yells MaievTaunts[]=
{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;
};
+
static Locations HoverPosition[]=
{
{657, 340, 355},
@@ -275,6 +301,7 @@ static Locations HoverPosition[]=
{705, 275, 355},
{705, 340, 355}
};
+
static Locations GlaivePosition[]=
{
{695.105, 305.303, 354.256},
@@ -282,11 +309,13 @@ static Locations GlaivePosition[]=
{700.105, 305.303, 354.256},
{664.338, 305.303, 354.256}
};
+
static Locations EyeBlast[]=
{
{677, 350, 354},//start point, pass through glaive point
{677, 260, 354}
};
+
static Locations AkamaWP[]=
{
{770.01, 304.50, 312.29}, // Bottom of the first stairs, at the doors
@@ -309,11 +338,13 @@ static Locations SpiritSpawns[]=
{755.5426, 309.9156, 312.2129},
{755.5426, 298.7923, 312.0834}
};
+
struct Animation // For the demon transformation
{
uint32 aura, unaura, timer, size, displayid, phase;
bool equip;
};
+
static Animation DemonTransformation[]=
{
{SPELL_DEMON_TRANSFORM_1, 0, 1000, 0, 0, 6, true},
@@ -327,9 +358,12 @@ static Animation DemonTransformation[]=
{SPELL_DEMON_TRANSFORM_3, SPELL_DEMON_TRANSFORM_2, 3500, 0, 0, 6, true},
{0, SPELL_DEMON_TRANSFORM_3, 0, 0, 0, 8, true}
};
+
#define EMOTE_SETS_GAZE_ON "sets its gaze on $N!"
#define EMOTE_UNABLE_TO_SUMMON "is unable to summon Maiev Shadowsong and enter Phase 4. Resetting Encounter."
+
+
/************************************** Illidan's AI ***************************************/
struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
{
@@ -337,32 +371,43 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
m_creature->CastSpell(m_creature, SPELL_DUAL_WIELD, true);
+
SpellEntry *TempSpell = GET_SPELL(SPELL_SHADOWFIEND_PASSIVE);
if (TempSpell)
TempSpell->EffectApplyAuraName[0] = 4; // proc debuff, and summon infinite fiends
}
+
ScriptedInstance* pInstance;
+
PhaseIllidan Phase;
EventIllidan Event;
uint32 Timer[EVENT_ENRAGE + 1];
+
uint32 TalkCount;
uint32 TransformCount;
uint32 FlightCount;
+
uint32 HoverPoint;
+
uint64 AkamaGUID;
uint64 MaievGUID;
uint64 FlameGUID[2];
uint64 GlaiveGUID[2];
+
SummonList Summons;
+
void Reset();
+
void JustSummoned(Creature* summon);
+
void SummonedCreatureDespawn(Creature* summon)
{
if (summon->GetCreatureInfo()->Entry == FLAME_OF_AZZINOTH)
{
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
if (summon->GetGUID() == FlameGUID[i])
FlameGUID[i] = 0;
+
if (!FlameGUID[0] && !FlameGUID[1] && Phase != PHASE_ILLIDAN_NULL)
{
m_creature->InterruptNonMeleeSpells(true);
@@ -371,6 +416,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
}
Summons.Despawn(summon);
}
+
void MovementInform(uint32 MovementType, uint32 Data)
{
if (FlightCount == 7) //change hover point
@@ -385,30 +431,39 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
else // handle flight sequence
Timer[EVENT_FLIGHT_SEQUENCE] = 1000;
}
+
void EnterCombat(Unit *who)
{
m_creature->setActive(true);
DoZoneInCombat();
}
+
void AttackStart(Unit *who)
{
if (!who || Phase >= PHASE_TALK_SEQUENCE)
return;
+
if (Phase == PHASE_FLIGHT || Phase == PHASE_DEMON)
AttackStartNoMove(who);
else
ScriptedAI::AttackStart(who);
}
+
void MoveInLineOfSight(Unit *who) {}
+
void JustDied(Unit *killer)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
if (!pInstance)
return;
+
pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, DONE); // Completed
- for (uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L + 1; ++i)
+
+ for(uint8 i = DATA_GAMEOBJECT_ILLIDAN_DOOR_R; i < DATA_GAMEOBJECT_ILLIDAN_DOOR_L + 1; ++i)
pInstance->HandleGameObject(pInstance->GetData64(i), true);
}
+
void KilledUnit(Unit *victim)
{
if (victim == m_creature) return;
@@ -425,6 +480,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
break;
}
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= m_creature->GetHealth() && done_by != m_creature)
@@ -432,6 +488,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
if (done_by->GetGUID() == MaievGUID)
done_by->AddThreat(m_creature, -(3*(float)damage)/4); // do not let maiev tank him
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_GLAIVE_RETURNS) // Re-equip our warblades!
@@ -443,9 +500,10 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
m_creature->SetByteValue(UNIT_FIELD_BYTES_2, 0, SHEATH_STATE_MELEE);
}
}
+
void DeleteFromThreatList(uint64 TargetGUID)
{
- for (std::list<HostilReference*>::iterator itr = m_creature->getThreatManager().getThreatList().begin(); itr != m_creature->getThreatManager().getThreatList().end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = m_creature->getThreatManager().getThreatList().begin(); itr != m_creature->getThreatManager().getThreatList().end(); ++itr)
{
if ((*itr)->getUnitGuid() == TargetGUID)
{
@@ -454,9 +512,11 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
}
}
}
+
void Talk(uint32 count)
{
Timer[EVENT_TALK_SEQUENCE] = Conversation[count].timer;
+
Creature* pCreature = NULL;
if (Conversation[count].pCreature == ILLIDAN_STORMRAGE)
pCreature = m_creature;
@@ -464,6 +524,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
pCreature = (Unit::GetCreature((*m_creature), AkamaGUID));
else if (Conversation[count].pCreature == MAIEV_SHADOWSONG)
pCreature = (Unit::GetCreature((*m_creature), MaievGUID));
+
if (pCreature)
{
if (Conversation[count].emote)
@@ -474,6 +535,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
DoPlaySoundToSet(pCreature, Conversation[count].sound); // Play some sound on the creature
}
}
+
void EnterPhase(PhaseIllidan NextPhase);
void CastEyeBlast();
void SummonFlamesOfAzzinoth();
@@ -539,7 +601,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
Timer[EVENT_FLIGHT_SEQUENCE] = 0;
break;
case 8://glaive return
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
if (GlaiveGUID[i])
{
@@ -557,7 +619,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
m_creature->StopMoving();
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_LAND);
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
if (GlaiveGUID[i])
{
@@ -582,14 +644,18 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
}
FlightCount++;
}
+
void HandleTransformSequence()
{
if (DemonTransformation[TransformCount].unaura)
m_creature->RemoveAurasDueToSpell(DemonTransformation[TransformCount].unaura);
+
if (DemonTransformation[TransformCount].aura)
DoCast(m_creature, DemonTransformation[TransformCount].aura, true);
+
if (DemonTransformation[TransformCount].displayid)
m_creature->SetDisplayId(DemonTransformation[TransformCount].displayid); // It's morphin time!
+
if (DemonTransformation[TransformCount].equip)
{
// Requip warglaives if needed
@@ -601,6 +667,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
// Unequip warglaives if needed
SetEquipmentSlots(false, EQUIP_UNEQUIP, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
}
+
switch(TransformCount)
{
case 2:
@@ -625,12 +692,14 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
Timer[EVENT_TRANSFORM_SEQUENCE] = DemonTransformation[TransformCount].timer;
TransformCount++;
}
+
void UpdateAI(const uint32 diff)
{
if ((!UpdateVictim()) && Phase < PHASE_TALK_SEQUENCE)
return;
+
Event = EVENT_NULL;
- for (uint32 i = 1; i <= MaxTimer[Phase]; ++i)
+ for(uint32 i = 1; i <= MaxTimer[Phase]; ++i)
{
if (Timer[i]) // Event is enabled
if (Timer[i] <= diff)
@@ -640,35 +709,43 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
}
else Timer[i] -= diff;
}
+
switch(Phase)
{
case PHASE_NORMAL:
if (HPPCT(m_creature) < 65)
EnterPhase(PHASE_FLIGHT_SEQUENCE);
break;
+
case PHASE_NORMAL_2:
if (HPPCT(m_creature) < 30)
EnterPhase(PHASE_TALK_SEQUENCE);
break;
+
case PHASE_NORMAL_MAIEV:
if (HPPCT(m_creature) < 1)
EnterPhase(PHASE_TALK_SEQUENCE);
break;
+
case PHASE_TALK_SEQUENCE:
if (Event == EVENT_TALK_SEQUENCE)
HandleTalkSequence();
break;
+
case PHASE_FLIGHT_SEQUENCE:
if (Event == EVENT_FLIGHT_SEQUENCE)
HandleFlightSequence();
break;
+
case PHASE_TRANSFORM_SEQUENCE:
if (Event == EVENT_TRANSFORM_SEQUENCE)
HandleTransformSequence();
break;
}
+
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
if (Phase == PHASE_NORMAL || Phase == PHASE_NORMAL_2 || Phase == PHASE_NORMAL_MAIEV && !m_creature->HasAura(SPELL_CAGED))
{
switch(Event)
@@ -680,6 +757,7 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
DoCast(m_creature, SPELL_BERSERK, true);
Timer[EVENT_BERSERK] = 5000;//The buff actually lasts forever.
break;
+
case EVENT_TAUNT:
{
uint32 random = rand()%4;
@@ -692,46 +770,56 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
}
Timer[EVENT_TAUNT] = 25000 + rand()%10000;
break;
+
case EVENT_SHEAR:
// no longer exists in 3.0.2
//DoCast(m_creature->getVictim(), SPELL_SHEAR);
Timer[EVENT_SHEAR] = 25000 + (rand()%16 * 1000);
break;
+
case EVENT_FLAME_CRASH:
DoCast(m_creature->getVictim(), SPELL_FLAME_CRASH);
Timer[EVENT_FLAME_CRASH] = 30000 + rand()%10000;
break;
+
case EVENT_PARASITIC_SHADOWFIEND:
{
if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 200, true))
m_creature->CastSpell(target, SPELL_PARASITIC_SHADOWFIEND, true);
Timer[EVENT_PARASITIC_SHADOWFIEND] = 35000 + rand()%10000;
}break;
+
case EVENT_PARASITE_CHECK:
Timer[EVENT_PARASITE_CHECK] = 0;
break;
+
case EVENT_DRAW_SOUL:
DoCast(m_creature->getVictim(), SPELL_DRAW_SOUL);
Timer[EVENT_DRAW_SOUL] = 50000 + rand()%10000;
break;
+
//PHASE_NORMAL_2
case EVENT_AGONIZING_FLAMES:
DoCast(SelectUnit(SELECT_TARGET_RANDOM,0), SPELL_AGONIZING_FLAMES);
Timer[EVENT_AGONIZING_FLAMES] = 0;
break;
+
case EVENT_TRANSFORM_NORMAL:
EnterPhase(PHASE_TRANSFORM_SEQUENCE);
break;
+
//PHASE_NORMAL_MAIEV
case EVENT_ENRAGE:
DoCast(m_creature, SPELL_ENRAGE);
Timer[EVENT_ENRAGE] = 0;
break;
+
default:
break;
}
DoMeleeAttackIfReady();
}
+
if (Phase == PHASE_FLIGHT)
{
switch(Event)
@@ -740,14 +828,17 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_FIREBALL);
Timer[EVENT_FIREBALL] = 3000;
break;
+
case EVENT_DARK_BARRAGE:
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_DARK_BARRAGE);
Timer[EVENT_DARK_BARRAGE] = 0;
break;
+
case EVENT_EYE_BLAST:
CastEyeBlast();
Timer[EVENT_EYE_BLAST] = 0;
break;
+
case EVENT_MOVE_POINT:
Phase = PHASE_FLIGHT_SEQUENCE;
Timer[EVENT_FLIGHT_SEQUENCE] = 0;//do not start Event when changing hover point
@@ -756,10 +847,12 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
HoverPoint -= 4;
m_creature->GetMotionMaster()->MovePoint(0, HoverPosition[HoverPoint].x, HoverPosition[HoverPoint].y, HoverPosition[HoverPoint].z);
break;
+
default:
break;
}
}
+
if (Phase == PHASE_DEMON)
{
switch(Event)
@@ -791,20 +884,26 @@ struct TRINITY_DLL_DECL boss_illidan_stormrageAI : public ScriptedAI
}
}
};
+
/********************************** End of Illidan AI ******************************************/
+
struct TRINITY_DLL_DECL flame_of_azzinothAI : public ScriptedAI
{
flame_of_azzinothAI(Creature *c) : ScriptedAI(c) {}
+
uint32 FlameBlastTimer;
uint32 CheckTimer;
uint64 GlaiveGUID;
+
void Reset()
{
FlameBlastTimer = 15000;
CheckTimer = 5000;
GlaiveGUID = 0;
}
+
void EnterCombat(Unit *who) {DoZoneInCombat();}
+
void ChargeCheck()
{
Unit* target = SelectTarget(SELECT_TARGET_FARTHEST, 0, 200, false);
@@ -816,6 +915,7 @@ struct TRINITY_DLL_DECL flame_of_azzinothAI : public ScriptedAI
m_creature->MonsterTextEmote(EMOTE_SETS_GAZE_ON, target->GetGUID());
}
}
+
void EnrageCheck()
{
if (GETUNIT(Glaive, GlaiveGUID))
@@ -839,11 +939,14 @@ struct TRINITY_DLL_DECL flame_of_azzinothAI : public ScriptedAI
}
}
}
+
void SetGlaiveGUID(uint64 guid){ GlaiveGUID = guid; }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (FlameBlastTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_BLAZE_SUMMON, true); //appear at victim
@@ -851,16 +954,20 @@ struct TRINITY_DLL_DECL flame_of_azzinothAI : public ScriptedAI
FlameBlastTimer = 15000; //10000 is official-like?
DoZoneInCombat(); //in case someone is revived
}else FlameBlastTimer -= diff;
+
if (CheckTimer < diff)
{
ChargeCheck();
EnrageCheck();
CheckTimer = 1000;
}else CheckTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
+
/******* Functions and vars for Akama's AI ******/
struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
{
@@ -871,39 +978,46 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
}
bool JustCreated;
ScriptedInstance* pInstance;
+
PhaseAkama Phase;
bool Event;
uint32 Timer;
+
uint64 IllidanGUID;
uint64 ChannelGUID;
uint64 SpiritGUID[2];
uint64 GateGUID;
uint64 DoorGUID[2];
+
uint32 ChannelCount;
uint32 WalkCount;
uint32 TalkCount;
uint32 Check_Timer;
+
void Reset()
{
WalkCount = 0;
if (pInstance)
{
pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, NOT_STARTED);
+
IllidanGUID = pInstance->GetData64(DATA_ILLIDANSTORMRAGE);
GateGUID = pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_GATE);
DoorGUID[0] = pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_DOOR_R);
DoorGUID[1] = pInstance->GetData64(DATA_GAMEOBJECT_ILLIDAN_DOOR_L);
- if(JustCreated) //close all doors at create
+
+ if(JustCreated)//close all doors at create
{
pInstance->HandleGameObject(GateGUID, false);
- for (uint8 i = 0; i < 2; ++i)
+
+ for(uint8 i = 0; i < 2; ++i)
pInstance->HandleGameObject(DoorGUID[i], false);
//JustCreated = false;
}else
{//open all doors, raid wiped
pInstance->HandleGameObject(GateGUID, true);
WalkCount = 1;//skip first wp
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
pInstance->HandleGameObject(DoorGUID[i], true);
}
}
@@ -914,20 +1028,26 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
DoorGUID[0] = 0;
DoorGUID[1] = 0;
}
+
ChannelGUID = 0;
SpiritGUID[0] = 0;
SpiritGUID[1] = 0;
+
Phase = PHASE_AKAMA_NULL;
Timer = 0;
+
ChannelCount = 0;
TalkCount = 0;
Check_Timer = 5000;
+
KillAllElites();
+
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has strange values..
m_creature->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);
m_creature->setActive(false);
m_creature->SetVisibility(VISIBILITY_OFF);
}
+
// 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()
{
@@ -935,38 +1055,44 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
m_creature->DeleteThreatList();
m_creature->CombatStop(true);
}
+
void EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit* who) {}
+
void MovementInform(uint32 MovementType, uint32 Data)
{
if(MovementType == POINT_MOTION_TYPE)
Timer = 1;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage > m_creature->GetHealth() || done_by->GetGUID() != IllidanGUID)
damage = 0;
}
+
void KillAllElites()
{
std::list<HostilReference*>& threatList = m_creature->getThreatManager().getThreatList();
std::vector<Unit*> eliteList;
- for (std::list<HostilReference*>::iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
+ for(std::list<HostilReference*>::iterator itr = threatList.begin(); itr != threatList.end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if (pUnit && pUnit->GetEntry() == ILLIDARI_ELITE)
eliteList.push_back(pUnit);
}
- for (std::vector<Unit*>::iterator itr = eliteList.begin(); itr != eliteList.end(); ++itr)
+ for(std::vector<Unit*>::iterator itr = eliteList.begin(); itr != eliteList.end(); ++itr)
(*itr)->setDeathState(JUST_DIED);
EnterEvadeMode();
}
+
void BeginTalk()
{
if (!pInstance)
return;
+
pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, IN_PROGRESS);
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
pInstance->HandleGameObject(DoorGUID[i], false);
if (GETCRE(Illidan, IllidanGUID))
{
@@ -979,6 +1105,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
CAST_AI(boss_illidan_stormrageAI, Illidan->AI())->EnterPhase(PHASE_TALK_SEQUENCE);
}
}
+
void BeginChannel()
{
m_creature->setActive(true);
@@ -990,25 +1117,29 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
Gate->GetPosition(x, y, z);
else
return;//if door not spawned, don't crash server
+
if (Creature* Channel = m_creature->SummonCreature(ILLIDAN_DOOR_TRIGGER, x, y, z+5, 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 360000))
{
ChannelGUID = Channel->GetGUID();
Channel->SetDisplayId(11686); // Invisible but spell visuals can still be seen.
DoCast(Channel, SPELL_AKAMA_DOOR_FAIL);
}
- for (uint8 i = 0; i < 2; ++i)
+
+ for(uint8 i = 0; i < 2; ++i)
if (Creature* Spirit = m_creature->SummonCreature(i ? SPIRIT_OF_OLUM : SPIRIT_OF_UDALO, SpiritSpawns[i].x, SpiritSpawns[i].y, SpiritSpawns[i].z, 0, TEMPSUMMON_TIMED_DESPAWN, 20000))
{
Spirit->SetVisibility(VISIBILITY_OFF);
SpiritGUID[i] = Spirit->GetGUID();
}
}
+
void BeginWalk()
{
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
m_creature->SetSpeed(MOVE_RUN, 1.0f);
m_creature->GetMotionMaster()->MovePoint(0, AkamaWP[WalkCount].x, AkamaWP[WalkCount].y, AkamaWP[WalkCount].z);
}
+
void EnterPhase(PhaseAkama NextPhase)
{
if (!pInstance)
@@ -1072,6 +1203,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
Phase = NextPhase;
Event = false;
}
+
void HandleTalkSequence()
{
switch(TalkCount)
@@ -1096,6 +1228,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
}
TalkCount++;
}
+
void HandleChannelSequence()
{
Unit* Channel, *Spirit[2];
@@ -1107,6 +1240,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
if (!Channel || !Spirit[0] || !Spirit[1])
return;
}
+
switch(ChannelCount)
{
case 0: // channel failed
@@ -1152,12 +1286,13 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
}
ChannelCount++;
}
+
void HandleWalkSequence()
{
switch(WalkCount)
{
case 6:
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
if(pInstance)
pInstance->HandleGameObject(DoorGUID[i], true);
break;
@@ -1171,6 +1306,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
EnterPhase(PHASE_FIGHT_MINIONS);
break;
}
+
if (Phase == PHASE_WALK)
{
Timer = 0;
@@ -1178,6 +1314,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(WalkCount, AkamaWP[WalkCount].x, AkamaWP[WalkCount].y, AkamaWP[WalkCount].z);
}
}
+
void UpdateAI(const uint32 diff)
{
if(m_creature->GetVisibility() == VISIBILITY_OFF)
@@ -1186,6 +1323,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
{
if(pInstance && pInstance->GetData(DATA_ILLIDARICOUNCILEVENT) == DONE)
m_creature->SetVisibility(VISIBILITY_ON);
+
Check_Timer = 5000;
}else Check_Timer -= diff;
}
@@ -1196,6 +1334,7 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
Event = true;
else Timer -= diff;
}
+
if (Event)
{
switch(Phase)
@@ -1248,22 +1387,29 @@ struct TRINITY_DLL_DECL npc_akama_illidanAI : public ScriptedAI
break;
}
}
+
if (!UpdateVictim())
return;
+
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 20)
DoCast(m_creature, SPELL_HEALING_POTION);
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
{
boss_maievAI(Creature *c) : ScriptedAI(c) {};
+
uint64 IllidanGUID;
+
PhaseIllidan Phase;
EventMaiev Event;
uint32 Timer[5];
uint32 MaxTimer;
+
void Reset()
{
MaxTimer = 0;
@@ -1275,10 +1421,12 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
SetEquipmentSlots(false, 44850, EQUIP_UNEQUIP, EQUIP_NO_CHANGE);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID + 2, 45738);
}
+
void EnterCombat(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
void EnterEvadeMode() {}
void GetIllidanGUID(uint64 guid) { IllidanGUID = guid; }
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetGUID() != IllidanGUID)
@@ -1292,10 +1440,12 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
damage = 0;
}
}
+
void AttackStart(Unit *who)
{
if (!who || Timer[EVENT_MAIEV_STEALTH])
return;
+
if (Phase == PHASE_TALK_SEQUENCE)
AttackStartNoMove(who);
else if (Phase == PHASE_DEMON || Phase == PHASE_TRANSFORM_SEQUENCE)
@@ -1308,12 +1458,14 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
else
ScriptedAI::AttackStart(who);
}
+
void DoAction(const int32 param)
{
if (param > PHASE_ILLIDAN_NULL && param < PHASE_ILLIDAN_MAX)
EnterPhase(PhaseIllidan(param));
}
- void EnterPhase(PhaseIllidan NextPhase) //This is in fact Illidan's phase.
+
+ void EnterPhase(PhaseIllidan NextPhase)//This is in fact Illidan's phase.
{
switch(NextPhase)
{
@@ -1349,6 +1501,7 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
MaxTimer = 1;
Phase = NextPhase;
}
+
void BlinkTo(float x, float y, float z)
{
m_creature->AttackStop();
@@ -1357,11 +1510,13 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
DoTeleportTo(x, y, z);
DoCast(m_creature, SPELL_TELEPORT_VISUAL, true);
}
+
void BlinkToPlayer()
{
if (GETCRE(Illidan, IllidanGUID))
{
Unit* target = CAST_AI(boss_illidan_stormrageAI, Illidan->AI())->SelectUnit(SELECT_TARGET_RANDOM, 0);
+
if (!target || !m_creature->IsWithinDistInMap(target, 80) || Illidan->IsWithinDistInMap(target, 20))
{
uint8 pos = rand()%4;
@@ -1375,19 +1530,22 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if ((!UpdateVictim())
&& !Timer[EVENT_MAIEV_STEALTH])
return;
+
Event = EVENT_MAIEV_NULL;
- for (uint8 i = 1; i <= MaxTimer; ++i)
+ for(uint8 i = 1; i <= MaxTimer; ++i)
if (Timer[i])
{
if (Timer[i] <= diff)
Event = (EventMaiev)i;
else Timer[i] -= diff;
}
+
switch(Event)
{
case EVENT_MAIEV_STEALTH:
@@ -1430,6 +1588,7 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
default:
break;
}
+
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 50)
{
m_creature->SetVisibility(VISIBILITY_OFF);
@@ -1440,11 +1599,13 @@ struct TRINITY_DLL_DECL boss_maievAI : public ScriptedAI
Timer[EVENT_MAIEV_STEALTH] = 60000; //reappear after 1 minute
MaxTimer = 1;
}
+
if (Phase == PHASE_NORMAL_MAIEV)
DoMeleeAttackIfReady();
}
};
+
bool GossipSelect_npc_akama_at_illidan(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF) // Time to begin the Event
@@ -1454,32 +1615,44 @@ bool GossipSelect_npc_akama_at_illidan(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
bool GossipHello_npc_akama_at_illidan(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(10465, pCreature->GetGUID());
+
return true;
}
+
struct TRINITY_DLL_DECL cage_trap_triggerAI : public ScriptedAI
{
cage_trap_triggerAI(Creature *c) : ScriptedAI(c) {}
+
uint64 IllidanGUID;
uint32 DespawnTimer;
+
bool Active;
bool SummonedBeams;
+
void Reset()
{
IllidanGUID = 0;
+
Active = false;
SummonedBeams = false;
+
DespawnTimer = 0;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(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
@@ -1497,12 +1670,14 @@ struct TRINITY_DLL_DECL cage_trap_triggerAI : public ScriptedAI
}
}
}
+
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)
@@ -1512,35 +1687,45 @@ struct TRINITY_DLL_DECL cage_trap_triggerAI : public ScriptedAI
//}
}
};
+
bool GOHello_cage_trap(Player* pPlayer, GameObject* pGo)
{
float x, y, z;
pPlayer->GetPosition(x, y, z);
+
// Grid search for nearest live Creature of entry 23304 within 10 yards
if (Creature* pTrigger = pGo->FindNearestCreature(23304, 10.0f))
CAST_AI(cage_trap_triggerAI, pTrigger->AI())->Active = true;
pGo->SetGoState(GO_STATE_ACTIVE);
return true;
}
+
struct TRINITY_DLL_DECL shadow_demonAI : public ScriptedAI
{
shadow_demonAI(Creature *c) : ScriptedAI(c) {}
+
uint64 TargetGUID;
+
void EnterCombat(Unit *who) {DoZoneInCombat();}
+
void Reset()
{
TargetGUID = 0;
m_creature->CastSpell(m_creature, SPELL_SHADOW_DEMON_PASSIVE, true);
}
+
void JustDied(Unit *killer)
{
if (Unit* target = Unit::GetUnit((*m_creature), TargetGUID))
target->RemoveAurasDueToSpell(SPELL_PARALYZE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim()) return;
+
if (m_creature->getVictim()->GetTypeId() != TYPEID_PLAYER) return; // Only cast the below on players.
+
if (!m_creature->getVictim()->HasAura(SPELL_PARALYZE))
{
TargetGUID = m_creature->getVictim()->GetGUID();
@@ -1553,6 +1738,7 @@ struct TRINITY_DLL_DECL shadow_demonAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_CONSUME_SOUL);
}
};
+
// Shadowfiends interact with Illidan, setting more targets in Illidan's hashmap
struct TRINITY_DLL_DECL mob_parasitic_shadowfiendAI : public ScriptedAI
{
@@ -1560,19 +1746,24 @@ struct TRINITY_DLL_DECL mob_parasitic_shadowfiendAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
uint64 IllidanGUID;
uint32 CheckTimer;
+
void Reset()
{
if (pInstance)
IllidanGUID = pInstance->GetData64(DATA_ILLIDANSTORMRAGE);
else
IllidanGUID = 0;
+
CheckTimer = 5000;
DoCast(m_creature, SPELL_SHADOWFIEND_PASSIVE, true);
}
+
void EnterCombat(Unit* who) { DoZoneInCombat(); }
+
void DoMeleeAttackIfReady()
{
if (m_creature->isAttackReady() && m_creature->IsWithinMeleeRange(m_creature->getVictim()))
@@ -1580,7 +1771,7 @@ struct TRINITY_DLL_DECL mob_parasitic_shadowfiendAI : public ScriptedAI
if (!m_creature->getVictim()->HasAura(SPELL_PARASITIC_SHADOWFIEND)
&& !m_creature->getVictim()->HasAura(SPELL_PARASITIC_SHADOWFIEND2))
{
- if (Creature* illidan = Unit::GetCreature((*m_creature),IllidanGUID)) //summon only in 1. phase
+ if (Creature* illidan = Unit::GetCreature((*m_creature),IllidanGUID))//summon only in 1. phase
if (CAST_AI(boss_illidan_stormrageAI, illidan->AI())->Phase == PHASE_NORMAL)
m_creature->CastSpell(m_creature->getVictim(), SPELL_PARASITIC_SHADOWFIEND2, true, 0, 0, IllidanGUID); //do not stack
}
@@ -1588,6 +1779,7 @@ struct TRINITY_DLL_DECL mob_parasitic_shadowfiendAI : public ScriptedAI
m_creature->resetAttackTimer();
}
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->getVictim())
@@ -1601,6 +1793,7 @@ struct TRINITY_DLL_DECL mob_parasitic_shadowfiendAI : public ScriptedAI
return;
}
}
+
if (CheckTimer < diff)
{
GETUNIT(Illidan, IllidanGUID);
@@ -1611,22 +1804,27 @@ struct TRINITY_DLL_DECL mob_parasitic_shadowfiendAI : public ScriptedAI
return;
}else CheckTimer = 5000;
}else CheckTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL blade_of_azzinothAI : public NullCreatureAI
{
blade_of_azzinothAI(Creature* c) : NullCreatureAI(c) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_THROW_GLAIVE2 || spell->Id == SPELL_THROW_GLAIVE)
me->SetDisplayId(21431);//appear when hit by Illidan's glaive
}
};
+
void boss_illidan_stormrageAI::Reset()
{
if (pInstance)
pInstance->SetData(DATA_ILLIDANSTORMRAGEEVENT, NOT_STARTED);
+
if (AkamaGUID)
{
if (GETCRE(Akama, AkamaGUID))
@@ -1642,19 +1840,23 @@ void boss_illidan_stormrageAI::Reset()
}
AkamaGUID = 0;
}
+
MaievGUID = 0;
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
FlameGUID[i] = 0;
GlaiveGUID[i] = 0;
}
+
Phase = PHASE_ILLIDAN_NULL;
Event = EVENT_NULL;
Timer[EVENT_BERSERK] = 1500000;
+
HoverPoint = 0;
TalkCount = 0;
FlightCount = 0;
TransformCount = 0;
+
m_creature->SetDisplayId(21135);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -1664,6 +1866,7 @@ void boss_illidan_stormrageAI::Reset()
m_creature->setActive(false);
Summons.DespawnAll();
}
+
void boss_illidan_stormrageAI::JustSummoned(Creature* summon)
{
Summons.Summon(summon);
@@ -1706,6 +1909,7 @@ void boss_illidan_stormrageAI::JustSummoned(Creature* summon)
break;
}
}
+
void boss_illidan_stormrageAI::HandleTalkSequence()
{
switch(TalkCount)
@@ -1789,42 +1993,51 @@ void boss_illidan_stormrageAI::HandleTalkSequence()
TalkCount++;
}
+
void boss_illidan_stormrageAI::CastEyeBlast()
{
m_creature->InterruptNonMeleeSpells(false);
+
m_creature->MonsterYell(SAY_EYE_BLAST, LANG_UNIVERSAL, 0);
DoPlaySoundToSet(m_creature, SOUND_EYE_BLAST);
+
float distx, disty, dist[2];
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
distx = EyeBlast[i].x - HoverPosition[HoverPoint].x;
disty = EyeBlast[i].y - HoverPosition[HoverPoint].y;
dist[i] = distx * distx + disty * disty;
}
Locations initial = EyeBlast[dist[0] < dist[1] ? 0 : 1];
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
{
distx = GlaivePosition[i].x - HoverPosition[HoverPoint].x;
disty = GlaivePosition[i].y - HoverPosition[HoverPoint].y;
dist[i] = distx * distx + disty * disty;
}
Locations final = GlaivePosition[dist[0] < dist[1] ? 0 : 1];
+
final.x = 2 * final.x - initial.x;
final.y = 2 * final.y - initial.y;
+
Creature* Trigger = m_creature->SummonTrigger(initial.x, initial.y, initial.z, 0, 13000);
if (!Trigger) return;
+
Trigger->SetSpeed(MOVE_WALK, 3);
Trigger->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
Trigger->GetMotionMaster()->MovePoint(0, final.x, final.y, final.z);
+
//Trigger->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, Trigger->GetGUID());
DoCast(Trigger, SPELL_EYE_BLAST);
}
+
void boss_illidan_stormrageAI::SummonFlamesOfAzzinoth()
{
m_creature->MonsterYell(SAY_SUMMONFLAMES, LANG_UNIVERSAL, 0);
DoPlaySoundToSet(m_creature, SOUND_SUMMONFLAMES);
- for (uint8 i = 0; i < 2; ++i)
+
+ for(uint8 i = 0; i < 2; ++i)
{
if (GETUNIT(Glaive, GlaiveGUID[i]))
{
@@ -1840,6 +2053,7 @@ void boss_illidan_stormrageAI::SummonFlamesOfAzzinoth()
}
}
}
+
void boss_illidan_stormrageAI::SummonMaiev()
{
m_creature->CastSpell(m_creature, SPELL_SHADOW_PRISON, true);
@@ -1852,6 +2066,7 @@ void boss_illidan_stormrageAI::SummonMaiev()
}
}
+
void boss_illidan_stormrageAI::EnterPhase(PhaseIllidan NextPhase)
{
DoZoneInCombat();
@@ -1936,75 +2151,93 @@ void boss_illidan_stormrageAI::EnterPhase(PhaseIllidan NextPhase)
Phase = NextPhase;
Event = EVENT_NULL;
}
+
CreatureAI* GetAI_boss_illidan_stormrage(Creature* pCreature)
{
return new boss_illidan_stormrageAI (pCreature);
}
+
CreatureAI* GetAI_npc_akama_at_illidan(Creature* pCreature)
{
return new npc_akama_illidanAI(pCreature);
}
+
CreatureAI* GetAI_boss_maiev(Creature* pCreature)
{
return new boss_maievAI (pCreature);
}
+
CreatureAI* GetAI_mob_flame_of_azzinoth(Creature* pCreature)
{
return new flame_of_azzinothAI (pCreature);
}
+
CreatureAI* GetAI_cage_trap_trigger(Creature* pCreature)
{
return new cage_trap_triggerAI (pCreature);
}
+
CreatureAI* GetAI_shadow_demon(Creature* pCreature)
{
return new shadow_demonAI (pCreature);
}
+
CreatureAI* GetAI_blade_of_azzinoth(Creature* pCreature)
{
return new blade_of_azzinothAI (pCreature);
}
+
CreatureAI* GetAI_parasitic_shadowfiend(Creature* pCreature)
{
return new mob_parasitic_shadowfiendAI (pCreature);
}
+
void AddSC_boss_illidan()
{
Script* newscript;
+
newscript = new Script;
newscript->Name = "boss_illidan_stormrage";
newscript->GetAI = &GetAI_boss_illidan_stormrage;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_maiev_shadowsong";
newscript->GetAI = &GetAI_boss_maiev;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_flame_of_azzinoth";
newscript->GetAI = &GetAI_mob_flame_of_azzinoth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_blade_of_azzinoth";
newscript->GetAI = &GetAI_blade_of_azzinoth;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "gameobject_cage_trap";
newscript->pGOHello = &GOHello_cage_trap;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_cage_trap_trigger";
newscript->GetAI = &GetAI_cage_trap_trigger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shadow_demon";
newscript->GetAI = &GetAI_shadow_demon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_parasitic_shadowfiend";
newscript->GetAI = &GetAI_parasitic_shadowfiend;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_mother_shahraz.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_mother_shahraz.cpp
index 3b3fc3d6da6..524ab213d73 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_mother_shahraz.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_mother_shahraz.cpp
@@ -13,14 +13,17 @@
* along 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"
+
//Speech'n'Sounds
#define SAY_TAUNT1 -1564018
#define SAY_TAUNT2 -1564019
@@ -33,6 +36,7 @@ EndScriptData */
#define SAY_SLAY2 -1564026
#define SAY_ENRAGE -1564027
#define SAY_DEATH -1564028
+
//Spells
#define SPELL_BEAM_SINISTER 40859
#define SPELL_BEAM_VILE 40860
@@ -45,6 +49,7 @@ EndScriptData */
#define SPELL_SABER_LASH_IMM 43690
#define SPELL_TELEPORT_VISUAL 40869
#define SPELL_BERSERK 45078
+
uint32 PrismaticAuras[]=
{
40880, // Shadow
@@ -54,10 +59,12 @@ uint32 PrismaticAuras[]=
40896, // Frost
40897, // Holy
};
+
struct Locations
{
float x,y,z;
};
+
static Locations TeleportPoint[]=
{
{959.996, 212.576, 193.843},
@@ -68,13 +75,16 @@ static Locations TeleportPoint[]=
{930.548, 284.888, 193.367},
{965.997, 278.398, 195.777}
};
+
struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
{
boss_shahrazAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint64 TargetGUID[3];
uint32 BeamTimer;
uint32 BeamCount;
@@ -87,13 +97,17 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
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)
+
+ for(uint8 i = 0; i<3; ++i)
TargetGUID[i] = 0;
+
BeamTimer = 20000; // Timers may be incorrect
BeamCount = 0;
CurrentBeam = 0; // 0 - Sinister, 1 - Vile, 2 - Wicked, 3 - Sinful
@@ -105,32 +119,39 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
RandomYellTimer = 70000 + rand()%41 * 1000;
EnrageTimer = 600000;
ExplosionCount = 0;
+
Enraged = false;
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_MOTHERSHAHRAZEVENT, IN_PROGRESS);
+
DoZoneInCombat();
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
pInstance->SetData(DATA_MOTHERSHAHRAZEVENT, DONE);
+
DoScriptText(SAY_DEATH, m_creature);
}
+
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)
+ for(uint8 i = 0; i < 3; ++i)
{
Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 1);
if (pUnit && pUnit->isAlive() && (pUnit->GetTypeId() == TYPEID_PLAYER))
@@ -141,23 +162,28 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 10) && !Enraged)
{
Enraged = true;
DoCast(m_creature, SPELL_ENRAGE, true);
DoScriptText(SAY_ENRAGE, m_creature);
}
+
//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:
@@ -178,7 +204,9 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
if (BeamCount > 3)
while(CurrentBeam == Beam)
CurrentBeam = rand()%3;
+
}else BeamTimer -= diff;
+
// Random Prismatic Shield every 15 seconds.
if (PrismaticShieldTimer < diff)
{
@@ -187,21 +215,25 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
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();
+
DoScriptText(RAND(SAY_SPELL2,SAY_SPELL3), m_creature);
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 < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
Unit* pUnit = NULL;
if (TargetGUID[i])
@@ -212,6 +244,7 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
TargetGUID[i] = 0;
}
}
+
ExplosionCount++;
FatalAttractionExplodeTimer = 1000;
}
@@ -221,16 +254,19 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
ExplosionCount = 0;
}
}else FatalAttractionExplodeTimer -= diff;
+
if (ShriekTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SILENCING_SHRIEK);
ShriekTimer = 25000+rand()%10 * 1000;
}else ShriekTimer -= diff;
+
if (SaberTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SABER_LASH);
SaberTimer = 25000+rand()%10 * 1000;
}else SaberTimer -= diff;
+
//Enrage
if (!m_creature->HasAura(SPELL_BERSERK))
if (EnrageTimer < diff)
@@ -238,19 +274,23 @@ struct TRINITY_DLL_DECL boss_shahrazAI : public ScriptedAI
DoCast(m_creature, SPELL_BERSERK);
DoScriptText(SAY_ENRAGE, m_creature);
}else EnrageTimer -= diff;
+
//Random taunts
if (RandomYellTimer < diff)
{
DoScriptText(RAND(SAY_TAUNT1,SAY_TAUNT2,SAY_TAUNT3), m_creature);
RandomYellTimer = 60000 + rand()%91 * 1000;
}else RandomYellTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_shahraz(Creature* pCreature)
{
return new boss_shahrazAI (pCreature);
}
+
void AddSC_boss_mother_shahraz()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_reliquary_of_souls.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_reliquary_of_souls.cpp
index 04921585793..f1e0873dd95 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_reliquary_of_souls.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_reliquary_of_souls.cpp
@@ -13,15 +13,18 @@
* along 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:
SDCategory: Black Temple
EndScriptData */
+
#include "precompiled.h"
#include "def_black_temple.h"
#include "Spell.h"
+
//Sound'n'speech
//Suffering
#define SUFF_SAY_FREED -1564047
@@ -32,6 +35,7 @@ EndScriptData */
#define SUFF_SAY_RECAP -1564052
#define SUFF_SAY_AFTER -1564053
#define SUFF_EMOTE_ENRAGE -1564054
+
//Desire
#define DESI_SAY_FREED -1564055
#define DESI_SAY_SLAY1 -1564056
@@ -40,6 +44,7 @@ EndScriptData */
#define DESI_SAY_SPEC -1564059
#define DESI_SAY_RECAP -1564060
#define DESI_SAY_AFTER -1564061
+
//Anger
#define ANGER_SAY_FREED -1564062
#define ANGER_SAY_FREED2 -1564063
@@ -48,6 +53,7 @@ EndScriptData */
#define ANGER_SAY_SPEC -1564066
#define ANGER_SAY_BEFORE -1564067
#define ANGER_SAY_DEATH -1564068
+
//Spells
#define AURA_OF_SUFFERING 41292
#define AURA_OF_SUFFERING_ARMOR 42017 // linked aura, need core support
@@ -57,26 +63,32 @@ EndScriptData */
#define SPELL_FIXATE_TAUNT 41295 // force taunt
#define SPELL_ENRAGE 41305
#define SPELL_SOUL_DRAIN 41303
+
#define AURA_OF_DESIRE 41350
#define AURA_OF_DESIRE_DAMAGE 41352
#define SPELL_RUNE_SHIELD 41431
#define SPELL_DEADEN 41410
#define SPELL_SOUL_SHOCK 41426
+
#define AURA_OF_ANGER 41337
#define SPELL_SELF_SEETHE 41364 // force cast 41520
#define SPELL_ENEMY_SEETHE 41520
#define SPELL_SOUL_SCREAM 41545
#define SPELL_SPITE_TARGET 41376 // cast 41377 after 6 sec
#define SPELL_SPITE_DAMAGE 41377
+
#define ENSLAVED_SOUL_PASSIVE 41535
#define SPELL_SOUL_RELEASE 41542
#define SPELL_SUBMERGE 37550 //dropout 'head'
+
#define CREATURE_ENSLAVED_SOUL 23469
#define NUMBER_ENSLAVED_SOUL 8
+
struct Position2d
{
float x,y;
};
+
static Position2d Coords[]=
{
{450.4, 212.3},
@@ -86,18 +98,24 @@ static Position2d Coords[]=
{450.4, 137.4},
{450.4, 168.3}
};
+
struct TRINITY_DLL_DECL npc_enslaved_soulAI : public ScriptedAI
{
npc_enslaved_soulAI(Creature *c) : ScriptedAI(c) {}
+
uint64 ReliquaryGUID;
+
void Reset() {ReliquaryGUID = 0;}
+
void EnterCombat(Unit* who)
{
m_creature->CastSpell(m_creature, ENSLAVED_SOUL_PASSIVE, true);
DoZoneInCombat();
}
+
void JustDied(Unit *killer);
};
+
struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
{
boss_reliquary_of_soulsAI(Creature *c) : ScriptedAI(c)
@@ -105,17 +123,23 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
pInstance = c->GetInstanceData();
EssenceGUID = 0;
}
+
ScriptedInstance* pInstance;
+
uint64 EssenceGUID;
+
uint32 Phase;
uint32 Counter;
uint32 Timer;
+
uint32 SoulCount;
uint32 SoulDeathCount;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_RELIQUARYOFSOULSEVENT, NOT_STARTED);
+
if (EssenceGUID)
{
if (Creature* Essence = Unit::GetCreature(*m_creature, EssenceGUID))
@@ -124,21 +148,26 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
}
EssenceGUID = 0;
}
+
Phase = 0;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_ONESHOT_NONE);
m_creature->RemoveAurasDueToSpell(SPELL_SUBMERGE);
}
+
void EnterCombat(Unit* who)
{
m_creature->AddThreat(who, 10000.0f);
DoZoneInCombat();
if (pInstance)
pInstance->SetData(DATA_RELIQUARYOFSOULSEVENT, IN_PROGRESS);
+
Phase = 1;
Counter = 0;
Timer = 0;
}
+
bool SummonSoul()
{
uint32 random = rand()%6;
@@ -153,13 +182,15 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
}else EnterEvadeMode();
return true;
}
+
void MergeThreatList(Creature* target)
{
if (!target)
return;
+
std::list<HostilReference*>& m_threatlist = target->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator itr = m_threatlist.begin();
- for (; itr != m_threatlist.end(); itr++)
+ for(; itr != m_threatlist.end(); itr++)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if (pUnit)
@@ -170,20 +201,24 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* killer)
{
if (pInstance)
pInstance->SetData(DATA_RELIQUARYOFSOULSEVENT, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!Phase)
return;
+
if (m_creature->getThreatManager().getThreatList().empty()) // Reset if event is begun and we don't have a threatlist
{
EnterEvadeMode();
return;
}
+
Creature* Essence;
if (EssenceGUID)
{
@@ -194,6 +229,7 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
return;
}
}
+
if (Timer < diff)
{
switch(Counter)
@@ -294,24 +330,30 @@ struct TRINITY_DLL_DECL boss_reliquary_of_soulsAI : public ScriptedAI
}else Timer -= diff;
}
};
+
struct TRINITY_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI
{
boss_essence_of_sufferingAI(Creature *c) : ScriptedAI(c) {}
+
uint64 StatAuraGUID;
+
uint32 AggroYellTimer;
uint32 FixateTimer;
uint32 EnrageTimer;
uint32 SoulDrainTimer;
uint32 AuraTimer;
+
void Reset()
{
StatAuraGUID = 0;
+
AggroYellTimer = 5000;
FixateTimer = 8000;
EnrageTimer = 30000;
SoulDrainTimer = 45000;
AuraTimer = 5000;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (damage >= m_creature->GetHealth())
@@ -322,6 +364,7 @@ struct TRINITY_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI
DoScriptText(SUFF_SAY_RECAP, m_creature);
}
}
+
void EnterCombat(Unit *who)
{
if (!m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE))
@@ -334,10 +377,12 @@ struct TRINITY_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI
}
else return;
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SUFF_SAY_SLAY1,SUFF_SAY_SLAY2,SUFF_SAY_SLAY3), m_creature);
}
+
void CastFixate()
{
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
@@ -345,7 +390,7 @@ struct TRINITY_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI
return; // No point continuing if empty threatlist.
std::list<Unit*> targets;
std::list<HostilReference*>::iterator itr = m_threatlist.begin();
- for (; itr != m_threatlist.end(); ++itr)
+ for(; itr != m_threatlist.end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if (pUnit && pUnit->isAlive() && (pUnit->GetTypeId() == TYPEID_PLAYER)) // Only alive players
@@ -361,6 +406,7 @@ struct TRINITY_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI
DoResetThreat();
m_creature->AddThreat(target,1000000);
}
+
void UpdateAI(const uint32 diff)
{
if (m_creature->isInCombat())
@@ -376,29 +422,36 @@ struct TRINITY_DLL_DECL boss_essence_of_sufferingAI : public ScriptedAI
}
}else FixateTimer -= diff;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
if (EnrageTimer < diff)
{
DoCast(m_creature, SPELL_ENRAGE);
EnrageTimer = 60000;
DoScriptText(SUFF_EMOTE_ENRAGE, m_creature);
}else EnrageTimer -= diff;
+
if (SoulDrainTimer < diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM,0), SPELL_SOUL_DRAIN);
SoulDrainTimer = 60000;
}else SoulDrainTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_essence_of_desireAI : public ScriptedAI
{
boss_essence_of_desireAI(Creature *c) : ScriptedAI(c) {}
+
uint32 RuneShieldTimer;
uint32 DeadenTimer;
uint32 SoulShockTimer;
+
void Reset()
{
RuneShieldTimer = 60000;
@@ -406,10 +459,12 @@ struct TRINITY_DLL_DECL boss_essence_of_desireAI : public ScriptedAI
SoulShockTimer = 5000;
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_CONFUSE, true);
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by == m_creature)
return;
+
if (damage >= m_creature->GetHealth())
{
damage = 0;
@@ -422,29 +477,34 @@ struct TRINITY_DLL_DECL boss_essence_of_desireAI : public ScriptedAI
m_creature->CastCustomSpell(done_by, AURA_OF_DESIRE_DAMAGE, &bp0, NULL, NULL, true);
}
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (m_creature->GetCurrentSpell(CURRENT_GENERIC_SPELL))
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
if (spell->Effect[i] == SPELL_EFFECT_INTERRUPT_CAST)
if (m_creature->GetCurrentSpell(CURRENT_GENERIC_SPELL)->m_spellInfo->Id == SPELL_SOUL_SHOCK
|| m_creature->GetCurrentSpell(CURRENT_GENERIC_SPELL)->m_spellInfo->Id == SPELL_DEADEN)
m_creature->InterruptSpell(CURRENT_GENERIC_SPELL, false);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(DESI_SAY_FREED, m_creature);
DoZoneInCombat();
DoCast(m_creature, AURA_OF_DESIRE, true);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(DESI_SAY_SLAY1,DESI_SAY_SLAY2,DESI_SAY_SLAY3), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (RuneShieldTimer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
@@ -453,11 +513,13 @@ struct TRINITY_DLL_DECL boss_essence_of_desireAI : public ScriptedAI
DeadenTimer += 2000;
RuneShieldTimer = 60000;
}else RuneShieldTimer -= diff;
+
if (SoulShockTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SOUL_SHOCK);
SoulShockTimer = 5000;
}else SoulShockTimer -= diff;
+
if (DeadenTimer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
@@ -468,51 +530,68 @@ struct TRINITY_DLL_DECL boss_essence_of_desireAI : public ScriptedAI
DoScriptText(DESI_SAY_SPEC, m_creature);
}
}else DeadenTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_essence_of_angerAI : public ScriptedAI
{
boss_essence_of_angerAI(Creature *c) : ScriptedAI(c) {}
+
uint64 AggroTargetGUID;
+
uint32 CheckTankTimer;
uint32 SoulScreamTimer;
uint32 SpiteTimer;
+
std::list<uint64> SpiteTargetGUID;
+
bool CheckedAggro;
+
void Reset()
{
AggroTargetGUID = 0;
+
CheckTankTimer = 5000;
SoulScreamTimer = 10000;
SpiteTimer = 30000;
+
SpiteTargetGUID.clear();
+
CheckedAggro = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(ANGER_SAY_FREED,ANGER_SAY_FREED2), m_creature);
+
DoZoneInCombat();
DoCast(m_creature, AURA_OF_ANGER, true);
}
+
void JustDied(Unit *victim)
{
DoScriptText(ANGER_SAY_DEATH, m_creature);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(ANGER_SAY_SLAY1,ANGER_SAY_SLAY2), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (!CheckedAggro)
{
AggroTargetGUID = m_creature->getVictim()->GetGUID();
CheckedAggro = true;
}
+
if (CheckTankTimer < diff)
{
if (m_creature->getVictim()->GetGUID() != AggroTargetGUID)
@@ -523,6 +602,7 @@ struct TRINITY_DLL_DECL boss_essence_of_angerAI : public ScriptedAI
}
CheckTankTimer = 2000;
}else CheckTankTimer -= diff;
+
if (SoulScreamTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SOUL_SCREAM);
@@ -532,15 +612,18 @@ struct TRINITY_DLL_DECL boss_essence_of_angerAI : public ScriptedAI
DoScriptText(ANGER_SAY_SPEC, m_creature);
}
}else SoulScreamTimer -= diff;
+
if (SpiteTimer < diff)
{
DoCast(m_creature, SPELL_SPITE_TARGET);
SpiteTimer = 30000;
DoScriptText(ANGER_SAY_SPEC, m_creature);
}else SpiteTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
void npc_enslaved_soulAI::JustDied(Unit *killer)
{
if (ReliquaryGUID)
@@ -551,26 +634,32 @@ void npc_enslaved_soulAI::JustDied(Unit *killer)
}
DoCast(m_creature, SPELL_SOUL_RELEASE, true);
}
+
CreatureAI* GetAI_boss_reliquary_of_souls(Creature* pCreature)
{
return new boss_reliquary_of_soulsAI (pCreature);
}
+
CreatureAI* GetAI_boss_essence_of_suffering(Creature* pCreature)
{
return new boss_essence_of_sufferingAI (pCreature);
}
+
CreatureAI* GetAI_boss_essence_of_desire(Creature* pCreature)
{
return new boss_essence_of_desireAI (pCreature);
}
+
CreatureAI* GetAI_boss_essence_of_anger(Creature* pCreature)
{
return new boss_essence_of_angerAI (pCreature);
}
+
CreatureAI* GetAI_npc_enslaved_soul(Creature* pCreature)
{
return new npc_enslaved_soulAI (pCreature);
}
+
void AddSC_boss_reliquary_of_souls()
{
Script *newscript;
@@ -578,18 +667,22 @@ void AddSC_boss_reliquary_of_souls()
newscript->Name = "boss_reliquary_of_souls";
newscript->GetAI = &GetAI_boss_reliquary_of_souls;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_essence_of_suffering";
newscript->GetAI = &GetAI_boss_essence_of_suffering;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_essence_of_desire";
newscript->GetAI = &GetAI_boss_essence_of_desire;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_essence_of_anger";
newscript->GetAI = &GetAI_boss_essence_of_anger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_enslaved_soul";
newscript->GetAI = &GetAI_npc_enslaved_soul;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_shade_of_akama.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_shade_of_akama.cpp
index c4365bc8168..ada3230a6f6 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_shade_of_akama.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_shade_of_akama.cpp
@@ -13,25 +13,31 @@
* along 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: 90
SDComment: Seems to be complete.
SDCategory: Black Temple
EndScriptData */
+
#include "precompiled.h"
#include "def_black_temple.h"
+
#define SAY_DEATH -1564013
#define SAY_LOW_HEALTH -1564014
// Ending cinematic text
#define SAY_FREE -1564015
#define SAY_BROKEN_FREE_01 -1564016
#define SAY_BROKEN_FREE_02 -1564017
+
#define GOSSIP_ITEM "We are ready to fight alongside you, Akama"
+
struct Location
{
float x, y, o, z;
};
+
static Location ChannelerLocations[]=
{
{463.161285, 401.219757, 3.141592},
@@ -41,16 +47,19 @@ static Location ChannelerLocations[]=
{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
@@ -58,6 +67,7 @@ static Location BrokenCoords[]=
{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},
@@ -65,6 +75,7 @@ static Location BrokenWP[]=
{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
@@ -75,6 +86,7 @@ static Location BrokenWP[]=
#define AKAMA_X 514.583984
#define AKAMA_Y 400.601013
#define AKAMA_Z 112.783997
+
// Spells
#define SPELL_VERTEX_SHADE_BLACK 39833
#define SPELL_SHADE_SOUL_CHANNEL 40401
@@ -84,16 +96,21 @@ static Location BrokenWP[]=
#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 TRINITY_DLL_DECL mob_ashtongue_channelerAI : public ScriptedAI
{
mob_ashtongue_channelerAI(Creature* c) : ScriptedAI(c) {ShadeGUID = 0;}
+
uint64 ShadeGUID;
+
void Reset() {}
void JustDied(Unit* killer);
void EnterCombat(Unit* who) {}
@@ -101,17 +118,21 @@ struct TRINITY_DLL_DECL mob_ashtongue_channelerAI : public ScriptedAI
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff) {}
};
+
struct TRINITY_DLL_DECL mob_ashtongue_sorcererAI : public ScriptedAI
{
mob_ashtongue_sorcererAI(Creature* c) : ScriptedAI(c) {ShadeGUID = 0;}
+
uint64 ShadeGUID;
uint32 CheckTimer;
bool StartBanishing;
+
void Reset()
{
StartBanishing = false;
CheckTimer = 5000;
}
+
void JustDied(Unit* killer);
void EnterCombat(Unit* who) {}
void AttackStart(Unit* who) {}
@@ -120,6 +141,7 @@ struct TRINITY_DLL_DECL mob_ashtongue_sorcererAI : public ScriptedAI
{
if (StartBanishing)
return;
+
if (CheckTimer < diff)
{
Creature* Shade = Unit::GetCreature((*m_creature), ShadeGUID);
@@ -131,6 +153,7 @@ struct TRINITY_DLL_DECL mob_ashtongue_sorcererAI : public ScriptedAI
m_creature->GetMotionMaster()->MoveIdle();
DoCast(Shade, SPELL_SHADE_SOUL_CHANNEL, true);
DoCast(Shade, SPELL_SHADE_SOUL_CHANNEL_2, true);
+
StartBanishing = true;
}
}
@@ -138,6 +161,7 @@ struct TRINITY_DLL_DECL mob_ashtongue_sorcererAI : public ScriptedAI
}else CheckTimer -= diff;
}
};
+
struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
{
boss_shade_of_akamaAI(Creature* c) : ScriptedAI(c), summons(m_creature)
@@ -148,29 +172,38 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
m_creature->ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
m_creature->ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true);
}
+
ScriptedInstance* pInstance;
+
std::list<uint64> Channelers;
std::list<uint64> 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;
bool reseting;
bool GridSearcherSucceeded;
bool HasKilledAkamaAndReseting;
SummonList summons;
+
void Reset()
{
reseting = true;
HasKilledAkamaAndReseting = false;
+
GridSearcherSucceeded = false;
+
Sorcerers.clear();
summons.DespawnAll();//despawn all adds
+
if (Creature* Akama = Unit::GetCreature(*m_creature, AkamaGUID))
{
Akama->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP);//turn gossip on so players can restart the event
@@ -182,20 +215,25 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}
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)
pInstance->SetData(DATA_SHADEOFAKAMAEVENT, NOT_STARTED);
+
reseting = false;
}
void JustDied(Unit* killer)
@@ -212,14 +250,16 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
if (summon->GetEntry() == CREATURE_DEFENDER || summon->GetEntry() == 23523 || summon->GetEntry() == 23318 || summon->GetEntry() == 23524)
summons.Despawn(summon);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!GridSearcherSucceeded)
{
FindChannelers();
+
if (!Channelers.empty())
{
- for (std::list<uint64>::iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
+ for(std::list<uint64>::iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
{
Creature* Channeler = (Unit::GetCreature(*m_creature, *itr));
if (Channeler)
@@ -229,6 +269,7 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
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);
@@ -238,16 +279,20 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}else error_log("SD2 ERROR: No Channelers are stored in the list. This encounter will not work properly");
}
}
+
void AttackStart(Unit* who)
{
if (!who || IsBanished) return;
+
if (who->isTargetableForAttack() && who != m_creature)
DoStartMovement(who);
}
+
void IncrementDeathCount(uint64 guid = 0) // If guid is set, will remove it from list of sorcerer
{
if (reseting)
return;
+
debug_log("TSCR: Increasing Death Count for Shade of Akama encounter");
++DeathCount;
m_creature->RemoveAuraFromStack(SPELL_SHADE_SOUL_CHANNEL_2);
@@ -258,6 +303,7 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
else Sorcerers.remove(guid);
}
}
+
void SummonCreature()
{
uint32 random = rand()%2;
@@ -280,7 +326,7 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}
else
{
- for (uint8 i = 0; i < 3; ++i)
+ 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)
@@ -293,13 +339,15 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}
}
}
+
void FindChannelers()
{
std::list<Creature*> ChannelerList;
m_creature->GetCreatureListWithEntryInGrid(ChannelerList,CREATURE_CHANNELER,50.0f);
+
if (!ChannelerList.empty())
{
- for (std::list<Creature*>::iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr)
+ for(std::list<Creature*>::iterator itr = ChannelerList.begin(); itr != ChannelerList.end(); ++itr)
{
CAST_AI(mob_ashtongue_channelerAI, (*itr)->AI())->ShadeGUID = m_creature->GetGUID();
Channelers.push_back((*itr)->GetGUID());
@@ -308,6 +356,7 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}
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())
@@ -315,15 +364,19 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
error_log("SD2 ERROR: Channeler List is empty, Shade of Akama encounter will be buggy");
return;
}
- for (std::list<uint64>::iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
+
+ for(std::list<uint64>::iterator itr = Channelers.begin(); itr != Channelers.end(); ++itr)
if (Creature* Channeler = (Unit::GetCreature(*m_creature, *itr)))
Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void SetAkamaGUID(uint64 guid) { AkamaGUID = guid; }
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isInCombat())
return;
+
if (IsBanished)
{
// Akama is set in the threatlist so when we reset, we make sure that he is not included in our check
@@ -332,6 +385,7 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
EnterEvadeMode();
return;
}
+
if (DefenderTimer < diff)
{
uint32 ran = rand()%2;
@@ -356,11 +410,13 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}
DefenderTimer = 15000;
}else DefenderTimer -= diff;
+
if (SummonTimer < diff)
{
SummonCreature();
SummonTimer = 35000;
}else SummonTimer -= diff;
+
if (DeathCount >= 6)
{
if (AkamaGUID)
@@ -399,9 +455,10 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
}
}
}else ReduceHealthTimer -= diff;
+
if (HasKilledAkama)
{
- if (!HasKilledAkamaAndReseting) //do not let players kill Shade if Akama is dead and Shade is waiting for ResetTimer!! event would bug
+ if (!HasKilledAkamaAndReseting)//do not let players kill Shade if Akama is dead and Shade is waiting for ResetTimer!! event would bug
{
HasKilledAkamaAndReseting = true;
m_creature->RemoveAllAuras();
@@ -417,10 +474,12 @@ struct TRINITY_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
return;
} else ResetTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
}
};
+
void mob_ashtongue_channelerAI::JustDied(Unit* killer)
{
Creature* Shade = (Unit::GetCreature((*m_creature), ShadeGUID));
@@ -428,6 +487,7 @@ void mob_ashtongue_channelerAI::JustDied(Unit* killer)
CAST_AI(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 = (Unit::GetCreature((*m_creature), ShadeGUID));
@@ -435,6 +495,7 @@ void mob_ashtongue_sorcererAI::JustDied(Unit* killer)
CAST_AI(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 TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
{
npc_akamaAI(Creature* c) : ScriptedAI(c), summons(m_creature)
@@ -454,8 +515,11 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
BrokenList.clear();
HasYelledOnce = false;
}
+
ScriptedInstance* pInstance;
+
uint64 ShadeGUID;
+
uint32 DestructivePoisonTimer;
uint32 LightningBoltTimer;
uint32 CheckTimer;
@@ -465,17 +529,21 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
uint32 EndingTalkCount;
uint32 WayPointId;
uint32 BrokenSummonIndex;
+
std::list<uint64> BrokenList;
+
bool EventBegun;
bool ShadeHasDied;
bool StartCombat;
bool HasYelledOnce;
SummonList summons;
+
void Reset()
{
DestructivePoisonTimer = 15000;
LightningBoltTimer = 10000;
CheckTimer = 2000;
+
if (!EventBegun)
{
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Database sometimes has very very strange values
@@ -483,6 +551,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
}
summons.DespawnAll();
}
+
void JustSummoned(Creature *summon)
{
if (summon->GetEntry() == CREATURE_BROKEN)
@@ -493,14 +562,18 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
if (summon->GetEntry() == CREATURE_BROKEN)
summons.Despawn(summon);
}
+
void EnterCombat(Unit* who) {}
+
void BeginEvent(Player* pl)
{
if (!pInstance)
return;
+
ShadeGUID = pInstance->GetData64(DATA_SHADEOFAKAMA);
if (!ShadeGUID)
return;
+
Creature* Shade = (Unit::GetCreature((*m_creature), ShadeGUID));
if (Shade)
{
@@ -518,13 +591,16 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
EventBegun = true;
}
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
switch(id)
{
case 0: ++WayPointId; break;
+
case 1:
if (Creature* Shade = Unit::GetCreature(*m_creature, ShadeGUID))
{
@@ -536,6 +612,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
break;
}
}
+
void JustDied(Unit* killer)
{
DoScriptText(SAY_DEATH, m_creature);
@@ -555,15 +632,18 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
CAST_AI(boss_shade_of_akamaAI, Shade->AI())->HasKilledAkama = true;
summons.DespawnAll();
}
+
void UpdateAI(const uint32 diff)
{
if (!EventBegun)
return;
+
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 15 && !HasYelledOnce)
{
DoScriptText(SAY_LOW_HEALTH, m_creature);
HasYelledOnce = true;
}
+
if (ShadeGUID && !StartCombat)
{
Creature* Shade = (Unit::GetCreature((*m_creature), ShadeGUID));
@@ -584,6 +664,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
}
}
}
+
if (ShadeHasDied && (WayPointId == 1))
{
if (pInstance)
@@ -591,6 +672,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(WayPointId, AkamaWP[1].x, AkamaWP[1].y, AkamaWP[1].z);
++WayPointId;
}
+
if (!ShadeHasDied && StartCombat)
{
if (CheckTimer < diff)
@@ -614,11 +696,12 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
CheckTimer = 5000;
}else CheckTimer -= diff;
}
+
if (SummonBrokenTimer && BrokenSummonIndex < 4)
{
if (SummonBrokenTimer <= diff)
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
float x = BrokenCoords[BrokenSummonIndex].x + (i*5);
float y = BrokenCoords[BrokenSummonIndex].y + (1*5);
@@ -639,6 +722,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
SummonBrokenTimer = 1000;
}else SummonBrokenTimer -= diff;
}
+
if (SoulRetrieveTimer)
if (SoulRetrieveTimer <= diff)
{
@@ -659,7 +743,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
if (!BrokenList.empty())
{
bool Yelled = false;
- for (std::list<uint64>::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr)
+ for(std::list<uint64>::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr)
if (Creature* pUnit = Unit::GetCreature(*m_creature, *itr))
{
if (!Yelled)
@@ -676,7 +760,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
case 3:
if (!BrokenList.empty())
{
- for (std::list<uint64>::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr)
+ for(std::list<uint64>::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr)
if (Creature* pUnit = Unit::GetCreature(*m_creature, *itr))
// This is the incorrect spell, but can't seem to find the right one.
pUnit->CastSpell(pUnit, 39656, true);
@@ -687,7 +771,7 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
case 4:
if (!BrokenList.empty())
{
- for (std::list<uint64>::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr)
+ for(std::list<uint64>::iterator itr = BrokenList.begin(); itr != BrokenList.end(); ++itr)
if (Creature* pUnit = Unit::GetCreature((*m_creature), *itr))
pUnit->MonsterYell(SAY_BROKEN_FREE_02, LANG_UNIVERSAL, 0);
}
@@ -695,8 +779,10 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
break;
}
}else SoulRetrieveTimer -= diff;
+
if (!UpdateVictim())
return;
+
if (DestructivePoisonTimer < diff)
{
Creature* Shade = Unit::GetCreature((*m_creature), ShadeGUID);
@@ -704,30 +790,37 @@ struct TRINITY_DLL_DECL npc_akamaAI : public ScriptedAI
DoCast(Shade, SPELL_DESTRUCTIVE_POISON);
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* pCreature)
{
return new boss_shade_of_akamaAI (pCreature);
}
+
CreatureAI* GetAI_mob_ashtongue_channeler(Creature* pCreature)
{
return new mob_ashtongue_channelerAI (pCreature);
}
+
CreatureAI* GetAI_mob_ashtongue_sorcerer(Creature* pCreature)
{
return new mob_ashtongue_sorcererAI (pCreature);
}
+
CreatureAI* GetAI_npc_akama_shade(Creature* pCreature)
{
return new npc_akamaAI (pCreature);
}
+
bool GossipSelect_npc_akama(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1) //Fight time
@@ -735,8 +828,10 @@ bool GossipSelect_npc_akama(Player* pPlayer, Creature* pCreature, uint32 uiSende
pPlayer->CLOSE_GOSSIP_MENU();
CAST_AI(npc_akamaAI, pCreature->AI())->BeginEvent(pPlayer);
}
+
return true;
}
+
bool GossipHello_npc_akama(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->isAlive())
@@ -744,8 +839,10 @@ bool GossipHello_npc_akama(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(907, pCreature->GetGUID());
}
+
return true;
}
+
void AddSC_boss_shade_of_akama()
{
Script *newscript;
@@ -753,14 +850,17 @@ void AddSC_boss_shade_of_akama()
newscript->Name = "boss_shade_of_akama";
newscript->GetAI = &GetAI_boss_shade_of_akama;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ashtongue_channeler";
newscript->GetAI = &GetAI_mob_ashtongue_channeler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ashtongue_sorcerer";
newscript->GetAI = &GetAI_mob_ashtongue_sorcerer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_akama_shade";
newscript->GetAI = &GetAI_npc_akama_shade;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_supremus.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_supremus.cpp
index aedfdd97ccc..a4e1b2d5428 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_supremus.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_supremus.cpp
@@ -13,17 +13,21 @@
* along 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 molten punch
SDCategory: Black Temple
EndScriptData */
+
#include "precompiled.h"
#include "def_black_temple.h"
+
#define EMOTE_NEW_TARGET -1564010
#define EMOTE_PUNCH_GROUND -1564011 //DoScriptText(EMOTE_PUNCH_GROUND, m_creature);
#define EMOTE_GROUND_CRACK -1564012
+
//Spells
#define SPELL_MOLTEN_PUNCH 40126
#define SPELL_HATEFUL_STRIKE 41926
@@ -31,20 +35,26 @@ EndScriptData */
#define SPELL_VOLCANIC_ERUPTION 40117
#define SPELL_VOLCANIC_SUMMON 40276
#define SPELL_BERSERK 45078
+
#define CREATURE_VOLCANO 23085
#define CREATURE_STALKER 23095
+
#define PHASE_STRIKE 1
#define PHASE_CHASE 2
+
#define EVENT_BERSERK 1
#define EVENT_SWITCH_PHASE 2
#define EVENT_FLAME 3
#define EVENT_VOLCANO 4
#define EVENT_SWITCH_TARGET 5
#define EVENT_HATEFUL_STRIKE 6
+
#define GCD_CAST 1
+
struct TRINITY_DLL_DECL molten_flameAI : public NullCreatureAI
{
molten_flameAI(Creature *c) : NullCreatureAI(c) {}
+
void InitializeAI()
{
float x, y, z;
@@ -54,16 +64,19 @@ struct TRINITY_DLL_DECL molten_flameAI : public NullCreatureAI
me->CastSpell(me,SPELL_MOLTEN_FLAME,true);
}
};
+
struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
{
boss_supremusAI(Creature *c) : ScriptedAI(c), summons(m_creature)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
EventMap events;
SummonList summons;
uint32 phase;
+
void Reset()
{
if (pInstance)
@@ -75,18 +88,23 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
}
//else ToggleDoors(false);
}
+
phase = 0;
+
events.Reset();
summons.DespawnAll();
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_SUPREMUSEVENT, IN_PROGRESS);
+
ChangePhase();
events.ScheduleEvent(EVENT_BERSERK, 900000, GCD_CAST);
events.ScheduleEvent(EVENT_FLAME, 20000, GCD_CAST);
}
+
void ChangePhase()
{
if (!phase || phase == PHASE_CHASE)
@@ -112,6 +130,7 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
events.SetPhase(phase);
events.ScheduleEvent(EVENT_SWITCH_PHASE, 60000, GCD_CAST);
}
+
void JustDied(Unit *killer)
{
if (pInstance)
@@ -121,12 +140,15 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
}
summons.DespawnAll();
}
+
void JustSummoned(Creature *summon) {summons.Summon(summon);}
void SummonedCreatureDespawn(Creature *summon) {summons.Despawn(summon);}
+
Unit* CalculateHatefulStrikeTarget()
{
uint32 health = 0;
Unit* target = NULL;
+
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator i = m_threatlist.begin();
for (i = m_threatlist.begin(); i!= m_threatlist.end(); ++i)
@@ -141,13 +163,17 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
}
}
}
+
return target;
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -194,12 +220,15 @@ struct TRINITY_DLL_DECL boss_supremusAI : public ScriptedAI
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL npc_volcanoAI : public Scripted_NoMovementAI
{
npc_volcanoAI(Creature *c) : Scripted_NoMovementAI(c) {}
+
void Reset()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
@@ -209,34 +238,43 @@ struct TRINITY_DLL_DECL npc_volcanoAI : public Scripted_NoMovementAI
wait = 3000;
}
uint32 wait;
+
void EnterCombat(Unit *who) {}
+
void MoveInLineOfSight(Unit *who) {}
+
void DoAction(const uint32 info)
{
m_creature->RemoveAura(SPELL_VOLCANIC_ERUPTION);
}
+
void UpdateAI(const uint32 diff)
{
- if (wait<=diff) //wait 3secs before casting
+ if (wait<=diff)//wait 3secs before casting
{
DoCast(m_creature, SPELL_VOLCANIC_ERUPTION);
wait = 60000;
}
else wait -= diff;
}
+
};
+
CreatureAI* GetAI_boss_supremus(Creature* pCreature)
{
return new boss_supremusAI (pCreature);
}
+
CreatureAI* GetAI_molten_flame(Creature* pCreature)
{
return new molten_flameAI (pCreature);
}
+
CreatureAI* GetAI_npc_volcano(Creature* pCreature)
{
return new npc_volcanoAI (pCreature);
}
+
void AddSC_boss_supremus()
{
Script *newscript;
@@ -244,10 +282,12 @@ void AddSC_boss_supremus()
newscript->Name = "boss_supremus";
newscript->GetAI = &GetAI_boss_supremus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "molten_flame";
newscript->GetAI = &GetAI_molten_flame;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_volcano";
newscript->GetAI = &GetAI_npc_volcano;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_teron_gorefiend.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_teron_gorefiend.cpp
index 70fad658bb9..d4f06f13274 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_teron_gorefiend.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_teron_gorefiend.cpp
@@ -13,14 +13,17 @@
* along 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"
+
//Speech'n'sound
#define SAY_INTRO -1564037
#define SAY_AGGRO -1564038
@@ -32,6 +35,7 @@ EndScriptData */
#define SAY_SPECIAL2 -1564044
#define SAY_ENRAGE -1564045
#define SAY_DEATH -1564046
+
//Spells
#define SPELL_INCINERATE 40239
#define SPELL_CRUSHING_SHADOWS 40243
@@ -39,29 +43,37 @@ EndScriptData */
#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
+
#define CREATURE_DOOM_BLOSSOM 23123
#define CREATURE_SHADOWY_CONSTRUCT 23111
+
struct TRINITY_DLL_DECL mob_doom_blossomAI : public ScriptedAI
{
mob_doom_blossomAI(Creature *c) : ScriptedAI(c) {}
+
uint32 CheckTeronTimer;
uint32 ShadowBoltTimer;
uint64 TeronGUID;
+
void Reset()
{
CheckTeronTimer = 5000;
ShadowBoltTimer = 12000;
TeronGUID = 0;
}
+
void EnterCombat(Unit *who) { }
void AttackStart(Unit* who) { }
void MoveInLineOfSight(Unit* who) { }
+
void Despawn()
{
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 (CheckTeronTimer < diff)
@@ -69,14 +81,17 @@ struct TRINITY_DLL_DECL mob_doom_blossomAI : public ScriptedAI
if (TeronGUID)
{
DoZoneInCombat();
+
Creature* Teron = (Unit::GetCreature((*m_creature), TeronGUID));
if ((Teron) && (!Teron->isAlive() || Teron->IsInEvadeMode()))
Despawn();
}
else
Despawn();
+
CheckTeronTimer = 5000;
}else CheckTeronTimer -= diff;
+
if (ShadowBoltTimer < diff && m_creature->isInCombat())
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM, 0), SPELL_SHADOWBOLT);
@@ -84,29 +99,39 @@ struct TRINITY_DLL_DECL mob_doom_blossomAI : public ScriptedAI
}else ShadowBoltTimer -= diff;
return;
}
+
void SetTeronGUID(uint64 guid){ TeronGUID = guid; }
};
+
struct TRINITY_DLL_DECL mob_shadowy_constructAI : public ScriptedAI
{
mob_shadowy_constructAI(Creature* c) : ScriptedAI(c) {}
+
uint64 GhostGUID;
uint64 TeronGUID;
+
uint32 CheckPlayerTimer;
uint32 CheckTeronTimer;
+
void Reset()
{
GhostGUID = 0;
TeronGUID = 0;
+
CheckPlayerTimer = 2000;
CheckTeronTimer = 5000;
}
+
void EnterCombat(Unit* who) { }
+
void MoveInLineOfSight(Unit *who)
{
if (!who || (!who->isAlive()) || (who->GetGUID() == GhostGUID))
return;
+
ScriptedAI::MoveInLineOfSight(who);
}
+
/* 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)
{
@@ -114,6 +139,7 @@ struct TRINITY_DLL_DECL mob_shadowy_constructAI : public ScriptedAI
damage = 0; // Only the ghost can deal damage.
}
*/
+
void CheckPlayers()
{
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
@@ -121,7 +147,7 @@ struct TRINITY_DLL_DECL mob_shadowy_constructAI : public ScriptedAI
return; // No threat list. Don't continue.
std::list<HostilReference*>::iterator itr = m_threatlist.begin();
std::list<Unit*> targets;
- for (; itr != m_threatlist.end(); ++itr)
+ for(; itr != m_threatlist.end(); ++itr)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*itr)->getUnitGuid());
if (pUnit && pUnit->isAlive())
@@ -135,6 +161,7 @@ struct TRINITY_DLL_DECL mob_shadowy_constructAI : public ScriptedAI
m_creature->AI()->AttackStart(target);
}
}
+
void UpdateAI(const uint32 diff)
{
if (CheckPlayerTimer < diff)
@@ -142,22 +169,27 @@ struct TRINITY_DLL_DECL mob_shadowy_constructAI : public ScriptedAI
CheckPlayers();
CheckPlayerTimer = 3000;
}else CheckPlayerTimer -= diff;
+
if (CheckTeronTimer < diff)
{
Creature* Teron = (Unit::GetCreature((*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 TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
{
boss_teron_gorefiendAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 IncinerateTimer;
uint32 SummonDoomBlossomTimer;
uint32 EnrageTimer;
@@ -166,29 +198,37 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
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;
bool Done;
+
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;
Done = false;
}
+
void EnterCombat(Unit *who) {}
+
void MoveInLineOfSight(Unit* pWho)
{
if (!Intro && pWho->GetTypeId() == TYPEID_PLAYER && pWho->isTargetableForAttack() && m_creature->IsHostileTo(pWho) && pWho->isInAccessiblePlaceFor(m_creature))
@@ -197,6 +237,7 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
{
if (pInstance)
pInstance->SetData(DATA_TERONGOREFIENDEVENT, IN_PROGRESS);
+
m_creature->GetMotionMaster()->Clear(false);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
DoScriptText(SAY_INTRO, m_creature);
@@ -208,16 +249,20 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
if (Done)
ScriptedAI::MoveInLineOfSight(pWho);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
pInstance->SetData(DATA_TERONGOREFIENDEVENT, DONE);
+
DoScriptText(SAY_DEATH, m_creature);
}
+
float CalculateRandomLocation(float Loc, uint32 radius)
{
float coord = Loc;
@@ -232,12 +277,14 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
}
return coord;
}
+
void SetThreatList(Creature* Blossom)
{
if (!Blossom) return;
+
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator i = m_threatlist.begin();
- for (i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
+ for(i = m_threatlist.begin(); i != m_threatlist.end(); ++i)
{
Unit* pUnit = Unit::GetUnit((*m_creature), (*i)->getUnitGuid());
if (pUnit && pUnit->isAlive())
@@ -247,6 +294,7 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
}
}
}
+
void MindControlGhost()
{
/************************************************************************/
@@ -255,6 +303,7 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
/** 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);
@@ -269,7 +318,7 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
Ghost->DealDamage(Ghost, Ghost->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL,
false);
}*/
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
Creature* Construct = NULL;
float X = CalculateRandomLocation(Ghost->GetPositionX(), 10);
@@ -283,12 +332,14 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
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 && !Done)
@@ -305,6 +356,7 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
Unit* pUnit = Unit::GetUnit((*m_creature), AggroTargetGUID);
if (pUnit)
AttackStart(pUnit);
+
DoZoneInCombat();
}
else
@@ -314,12 +366,15 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
}
}else AggroTimer -= diff;
}
+
if (!UpdateVictim() || !Done)
return;
+
if (SummonShadowsTimer < diff)
{
//MindControlGhost();
- for (uint8 i = 0; i < 2; ++i)
+
+ for(uint8 i = 0; i < 2; ++i)
{
Creature* Shadow = NULL;
float X = CalculateRandomLocation(m_creature->GetPositionX(), 10);
@@ -329,12 +384,14 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
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)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -356,11 +413,13 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
}
}
}else SummonDoomBlossomTimer -= diff;
+
if (IncinerateTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1);
if (!target)
target = m_creature->getVictim();
+
if (target)
{
DoScriptText(RAND(SAY_SPECIAL1,SAY_SPECIAL2), m_creature);
@@ -368,6 +427,7 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
IncinerateTimer = 20000 + rand()%31 * 1000;
}
}else IncinerateTimer -= diff;
+
if (CrushingShadowsTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
@@ -375,12 +435,15 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
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);
@@ -389,11 +452,13 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
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)
{
DoScriptText(RAND(SAY_SPELL1,SAY_SPELL2), m_creature);
RandomYellTimer = 50000 + rand()%51 * 1000;
}else RandomYellTimer -= diff;
+
if (!m_creature->HasAura(SPELL_BERSERK))
{
if (EnrageTimer < diff)
@@ -402,21 +467,26 @@ struct TRINITY_DLL_DECL boss_teron_gorefiendAI : public ScriptedAI
DoScriptText(SAY_ENRAGE, m_creature);
}else EnrageTimer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_doom_blossom(Creature* pCreature)
{
return new mob_doom_blossomAI(pCreature);
}
+
CreatureAI* GetAI_mob_shadowy_construct(Creature* pCreature)
{
return new mob_shadowy_constructAI(pCreature);
}
+
CreatureAI* GetAI_boss_teron_gorefiend(Creature* pCreature)
{
return new boss_teron_gorefiendAI (pCreature);
}
+
void AddSC_boss_teron_gorefiend()
{
Script *newscript;
@@ -424,10 +494,12 @@ void AddSC_boss_teron_gorefiend()
newscript->Name = "mob_doom_blossom";
newscript->GetAI = &GetAI_mob_doom_blossom;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shadowy_construct";
newscript->GetAI = &GetAI_mob_shadowy_construct;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_teron_gorefiend";
newscript->GetAI = &GetAI_boss_teron_gorefiend;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/boss_warlord_najentus.cpp b/src/bindings/scripts/scripts/outland/black_temple/boss_warlord_najentus.cpp
index 928e0903d9c..0d7e2a7e61d 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/boss_warlord_najentus.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/boss_warlord_najentus.cpp
@@ -13,14 +13,17 @@
* along 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: 95
SDComment:
SDCategory: Black Temple
EndScriptData */
+
#include "precompiled.h"
#include "def_black_temple.h"
+
enum eEnums
{
SAY_AGGRO = -1564000,
@@ -33,6 +36,7 @@ enum eEnums
SAY_ENRAGE1 = -1564007, //is this text actually in use?
SAY_ENRAGE2 = -1564008,
SAY_DEATH = -1564009,
+
//Spells
SPELL_NEEDLE_SPINE = 39992,
SPELL_TIDAL_BURST = 39878,
@@ -41,42 +45,55 @@ enum eEnums
SPELL_CREATE_NAJENTUS_SPINE = 39956,
SPELL_HURL_SPINE = 39948,
SPELL_BERSERK = 26662,
+
GOBJECT_SPINE = 185584,
+
EVENT_BERSERK = 1,
EVENT_YELL = 2,
EVENT_NEEDLE = 3,
EVENT_SPINE = 4,
EVENT_SHIELD = 5,
+
GCD_CAST = 1,
GCD_YELL = 2
};
+
struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
{
boss_najentusAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
EventMap events;
+
uint64 SpineTargetGUID;
+
void Reset()
{
events.Reset();
+
SpineTargetGUID = 0;
+
if (pInstance)
pInstance->SetData(DATA_HIGHWARLORDNAJENTUSEVENT, NOT_STARTED);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(rand()%2 ? SAY_SLAY1 : SAY_SLAY2, m_creature);
events.DelayEvents(5000, GCD_YELL);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
pInstance->SetData(DATA_HIGHWARLORDNAJENTUSEVENT, DONE);
+
DoScriptText(SAY_DEATH, m_creature);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_HURL_SPINE && m_creature->HasAura(SPELL_TIDAL_SHIELD))
@@ -86,16 +103,19 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
ResetTimer();
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_HIGHWARLORDNAJENTUSEVENT, IN_PROGRESS);
+
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
events.ScheduleEvent(EVENT_BERSERK, 480000, GCD_CAST);
events.ScheduleEvent(EVENT_YELL, 45000 + (rand()%76)*1000, GCD_YELL);
ResetTimer();
}
+
bool RemoveImpalingSpine()
{
if (!SpineTargetGUID) return false;
@@ -105,17 +125,21 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
SpineTargetGUID=0;
return true;
}
+
void ResetTimer(uint32 inc = 0)
{
events.RescheduleEvent(EVENT_NEEDLE, 10000 + inc, GCD_CAST);
events.RescheduleEvent(EVENT_SPINE, 20000 + inc, GCD_CAST);
events.RescheduleEvent(EVENT_SHIELD, 60000 + inc);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
events.Update(diff);
+
while(uint32 eventId = events.ExecuteEvent())
{
switch(eventId)
@@ -151,7 +175,7 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
//m_creature->CastSpell(m_creature, SPELL_NEEDLE_SPINE, true);
std::list<Unit*> target;
SelectTargetList(target, 3, SELECT_TARGET_RANDOM, 80, true);
- for (std::list<Unit*>::iterator i = target.begin(); i != target.end(); ++i)
+ for(std::list<Unit*>::iterator i = target.begin(); i != target.end(); ++i)
m_creature->CastSpell(*i, 39835, true);
events.ScheduleEvent(EVENT_NEEDLE, 15000+rand()%10000, GCD_CAST);
events.DelayEvents(1500, GCD_CAST);
@@ -164,9 +188,11 @@ struct TRINITY_DLL_DECL boss_najentusAI : public ScriptedAI
break;
}
}
+
DoMeleeAttackIfReady();
}
};
+
bool GOHello_go_najentus_spine(Player* pPlayer, GameObject* pGo)
{
if (ScriptedInstance* pInstance = pGo->GetInstanceData())
@@ -178,10 +204,12 @@ bool GOHello_go_najentus_spine(Player* pPlayer, GameObject* pGo)
}
return true;
}
+
CreatureAI* GetAI_boss_najentus(Creature* pCreature)
{
return new boss_najentusAI (pCreature);
}
+
void AddSC_boss_najentus()
{
Script *newscript;
@@ -189,6 +217,7 @@ void AddSC_boss_najentus()
newscript->Name = "boss_najentus";
newscript->GetAI = &GetAI_boss_najentus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_najentus_spine";
newscript->pGOHello = &GOHello_go_najentus_spine;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/def_black_temple.h b/src/bindings/scripts/scripts/outland/black_temple/def_black_temple.h
index 9b22ff1c0e9..b7432cd5b20 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/def_black_temple.h
+++ b/src/bindings/scripts/scripts/outland/black_temple/def_black_temple.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_BLACK_TEMPLE_H
#define DEF_BLACK_TEMPLE_H
+
enum eTypes
{
DATA_AKAMA = 1,
@@ -32,5 +34,6 @@ enum eTypes
DATA_GAMEOBJECT_SUPREMUS_DOORS = 25,
DATA_BLOOD_ELF_COUNCIL_VOICE = 26
};
+
#endif
diff --git a/src/bindings/scripts/scripts/outland/black_temple/illidari_council.cpp b/src/bindings/scripts/scripts/outland/black_temple/illidari_council.cpp
index ec486675595..7530909cb11 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/illidari_council.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/illidari_council.cpp
@@ -13,42 +13,52 @@
* along 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"
+
//Speech'n'Sounds
#define SAY_GATH_SLAY -1564085
#define SAY_GATH_SLAY_COMNT -1564089
#define SAY_GATH_DEATH -1564093
#define SAY_GATH_SPECIAL1 -1564077
#define SAY_GATH_SPECIAL2 -1564081
+
#define SAY_VERA_SLAY -1564086
#define SAY_VERA_COMNT -1564089 //signed for 22949
#define SAY_VERA_DEATH -1564094
#define SAY_VERA_SPECIAL1 -1564078
#define SAY_VERA_SPECIAL2 -1564082
+
#define SAY_MALA_SLAY -1564087
#define SAY_MALA_COMNT -1564090
#define SAY_MALA_DEATH -1564095
#define SAY_MALA_SPECIAL1 -1564079
#define SAY_MALA_SPECIAL2 -1564083
+
#define SAY_ZERE_SLAY -1564088
#define SAY_ZERE_COMNT -1564091
#define SAY_ZERE_DEATH -1564096
#define SAY_ZERE_SPECIAL1 -1564080
#define SAY_ZERE_SPECIAL2 -1564084
+
#define ERROR_INST_DATA "SD2 ERROR: Instance Data for Black Temple not set properly; Illidari Council event will not function properly."
+
#define AKAMAID 23089
+
struct CouncilYells
{
int32 entry;
uint32 timer;
};
+
static CouncilYells CouncilAggro[]=
{
{-1564069, 5000}, // Gathios
@@ -56,6 +66,7 @@ static CouncilYells CouncilAggro[]=
{-1564071, 5000}, // Malande
{-1564072, 0}, // Zerevor
};
+
// Need to get proper timers for this later
static CouncilYells CouncilEnrage[]=
{
@@ -64,18 +75,21 @@ static CouncilYells CouncilEnrage[]=
{-1564075, 5000}, // Malande
{-1564076, 0}, // Zerevor
};
+
// 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
@@ -85,30 +99,41 @@ static CouncilYells CouncilEnrage[]=
#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
+
struct TRINITY_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)
+ for(uint8 i = 0; i < 4; ++i)
Council[i] = 0;
}
+
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()
{
@@ -120,15 +145,20 @@ struct TRINITY_DLL_DECL mob_blood_elf_council_voice_triggerAI : public ScriptedA
Council[3] = pInstance->GetData64(DATA_HIGHNETHERMANCERZEREVOR);
}else error_log(ERROR_INST_DATA);
}
+
void EnterCombat(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)
@@ -143,6 +173,7 @@ struct TRINITY_DLL_DECL mob_blood_elf_council_voice_triggerAI : public ScriptedA
YellCounter = 0; // Reuse for Enrage Yells
}else AggroYellTimer -= diff;
}
+
if (EnrageTimer)
{
if (EnrageTimer <= diff)
@@ -158,27 +189,36 @@ struct TRINITY_DLL_DECL mob_blood_elf_council_voice_triggerAI : public ScriptedA
}
}
};
+
struct TRINITY_DLL_DECL mob_illidari_councilAI : public ScriptedAI
{
mob_illidari_councilAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
Council[i] = 0;
}
+
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)
+ for(uint8 i = 0; i < 4; ++i)
{
if (pMember = (Unit::GetCreature((*m_creature), Council[i])))
{
@@ -190,37 +230,45 @@ struct TRINITY_DLL_DECL mob_illidari_councilAI : public ScriptedAI
pMember->AI()->EnterEvadeMode();
}
}
+
if (pInstance)
{
pInstance->SetData(DATA_ILLIDARICOUNCILEVENT, NOT_STARTED);
if (Creature* VoiceTrigger = (Unit::GetCreature(*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->SetDisplayId(11686);
}
+
void EnterCombat(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 = (Unit::GetCreature(*m_creature, pInstance->GetData64(DATA_BLOOD_ELF_COUNCIL_VOICE))))
{
CAST_AI(mob_blood_elf_council_voice_triggerAI, VoiceTrigger->AI())->LoadCouncilGUIDs();
CAST_AI(mob_blood_elf_council_voice_triggerAI, VoiceTrigger->AI())->EventStarted = true;
}
- for (uint8 i = 0; i < 4; ++i)
+
+ for(uint8 i = 0; i < 4; ++i)
{
Unit* Member = NULL;
if (Council[i])
@@ -230,13 +278,17 @@ struct TRINITY_DLL_DECL mob_illidari_councilAI : public ScriptedAI
CAST_CRE(Member)->AI()->AttackStart(target);
}
}
+
pInstance->SetData(DATA_ILLIDARICOUNCILEVENT, IN_PROGRESS);
+
EventBegun = true;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!EventBegun) return;
+
if (EndEventTimer)
{
if (EndEventTimer <= diff)
@@ -253,6 +305,7 @@ struct TRINITY_DLL_DECL mob_illidari_councilAI : public ScriptedAI
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
return;
}
+
Creature* pMember = (Unit::GetCreature(*m_creature, Council[DeathCount]));
if (pMember && pMember->isAlive())
pMember->DealDamage(pMember, pMember->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
@@ -260,12 +313,13 @@ struct TRINITY_DLL_DECL mob_illidari_councilAI : public ScriptedAI
EndEventTimer = 1500;
}else EndEventTimer -= diff;
}
+
if (CheckTimer)
{
if (CheckTimer <= diff)
{
uint8 EvadeCheck = 0;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (Council[i])
{
@@ -283,25 +337,33 @@ struct TRINITY_DLL_DECL mob_illidari_councilAI : public ScriptedAI
}
}
}
+
if (EvadeCheck > 3)
Reset();
+
CheckTimer = 2000;
}else CheckTimer -= diff;
}
+
}
};
+
struct TRINITY_DLL_DECL boss_illidari_councilAI : public ScriptedAI
{
boss_illidari_councilAI(Creature* c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
Council[i] = 0;
LoadedGUIDs = false;
}
+
uint64 Council[4];
+
ScriptedInstance* pInstance;
+
bool LoadedGUIDs;
+
void EnterCombat(Unit* who)
{
if (pInstance)
@@ -324,9 +386,10 @@ struct TRINITY_DLL_DECL boss_illidari_councilAI : public ScriptedAI
if (!LoadedGUIDs)
LoadGUIDs();
}
+
void EnterEvadeMode()
{
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (Unit* pUnit = Unit::GetUnit(*m_creature, Council[i]))
if (pUnit != m_creature && pUnit->getVictim())
@@ -337,12 +400,14 @@ struct TRINITY_DLL_DECL boss_illidari_councilAI : public ScriptedAI
}
ScriptedAI::EnterEvadeMode();
}
+
void DamageTaken(Unit* done_by, uint32 &damage)
{
if (done_by == m_creature)
return;
+
damage /= 4;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (Creature* pUnit = Unit::GetCreature(*m_creature, Council[i]))
if (pUnit != m_creature && damage < pUnit->GetHealth())
@@ -352,6 +417,7 @@ struct TRINITY_DLL_DECL boss_illidari_councilAI : public ScriptedAI
}
}
}
+
void LoadGUIDs()
{
if (!pInstance)
@@ -359,21 +425,26 @@ struct TRINITY_DLL_DECL boss_illidari_councilAI : public ScriptedAI
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 TRINITY_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_councilAI
{
boss_gathios_the_shattererAI(Creature *c) : boss_illidari_councilAI(c) {}
+
uint32 ConsecrationTimer;
uint32 HammerOfJusticeTimer;
uint32 SealTimer;
uint32 AuraTimer;
uint32 BlessingTimer;
+
void Reset()
{
ConsecrationTimer = 40000;
@@ -382,24 +453,30 @@ struct TRINITY_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_coun
AuraTimer = 90000;
BlessingTimer = 60000;
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_GATH_SLAY, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_GATH_DEATH, m_creature);
}
+
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;
@@ -408,17 +485,19 @@ struct TRINITY_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_coun
case 0: spellid = SPELL_DEVOTION_AURA; break;
case 1: spellid = SPELL_CHROMATIC_AURA; break;
}
- for (uint8 i = 0; i < 4; ++i)
+ 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 (!UpdateVictim())
return;
+
if (BlessingTimer < diff)
{
if (Unit* pUnit = SelectCouncilMember())
@@ -431,11 +510,13 @@ struct TRINITY_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_coun
}
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))
@@ -448,6 +529,7 @@ struct TRINITY_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_coun
}
}
}else HammerOfJusticeTimer -= diff;
+
if (SealTimer < diff)
{
switch(rand()%2)
@@ -457,23 +539,28 @@ struct TRINITY_DLL_DECL boss_gathios_the_shattererAI : public boss_illidari_coun
}
SealTimer = 40000;
}else SealTimer -= diff;
+
if (AuraTimer < diff)
{
CastAuraOnCouncil();
AuraTimer = 90000;
}else AuraTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_councilAI
{
boss_high_nethermancer_zerevorAI(Creature *c) : boss_illidari_councilAI(c) {}
+
uint32 BlizzardTimer;
uint32 FlamestrikeTimer;
uint32 ArcaneBoltTimer;
uint32 DampenMagicTimer;
uint32 Cooldown;
uint32 ArcaneExplosionTimer;
+
void Reset()
{
BlizzardTimer = 30000 + rand()%61 * 1000;
@@ -483,18 +570,22 @@ struct TRINITY_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_
ArcaneExplosionTimer = 14000;
Cooldown = 0;
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_ZERE_SLAY, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_ZERE_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Cooldown)
{
if (Cooldown < diff) Cooldown = 0;
@@ -504,6 +595,7 @@ struct TRINITY_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_
return; // Don't cast any other spells if global cooldown is still ticking
}
}
+
if (DampenMagicTimer < diff)
{
DoCast(m_creature, SPELL_DAMPEN_MAGIC);
@@ -511,18 +603,21 @@ struct TRINITY_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_
DampenMagicTimer = 67200; // almost 1,12 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))
@@ -533,6 +628,7 @@ struct TRINITY_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_
Cooldown = 1000;
}
}else BlizzardTimer -= diff;
+
if (FlamestrikeTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -545,13 +641,16 @@ struct TRINITY_DLL_DECL boss_high_nethermancer_zerevorAI : public boss_illidari_
}else FlamestrikeTimer -= diff;
}
};
+
struct TRINITY_DLL_DECL boss_lady_malandeAI : public boss_illidari_councilAI
{
boss_lady_malandeAI(Creature *c) : boss_illidari_councilAI(c) {}
+
uint32 EmpoweredSmiteTimer;
uint32 CircleOfHealingTimer;
uint32 DivineWrathTimer;
uint32 ReflectiveShieldTimer;
+
void Reset()
{
EmpoweredSmiteTimer = 38000;
@@ -559,18 +658,22 @@ struct TRINITY_DLL_DECL boss_lady_malandeAI : public boss_illidari_councilAI
DivineWrathTimer = 40000;
ReflectiveShieldTimer = 0;
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(SAY_MALA_SLAY, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_MALA_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (EmpoweredSmiteTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -579,11 +682,13 @@ struct TRINITY_DLL_DECL boss_lady_malandeAI : public boss_illidari_councilAI
EmpoweredSmiteTimer = 38000;
}
}else EmpoweredSmiteTimer -= diff;
+
if (CircleOfHealingTimer < diff)
{
DoCast(m_creature, SPELL_CIRCLE_OF_HEALING);
CircleOfHealingTimer = 60000;
}else CircleOfHealingTimer -= diff;
+
if (DivineWrathTimer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -592,44 +697,57 @@ struct TRINITY_DLL_DECL boss_lady_malandeAI : public boss_illidari_councilAI
DivineWrathTimer = 40000 + rand()%41 * 1000;
}
}else DivineWrathTimer -= diff;
+
if (ReflectiveShieldTimer < diff)
{
DoCast(m_creature, SPELL_REFLECTIVE_SHIELD);
ReflectiveShieldTimer = 65000;
}else ReflectiveShieldTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_veras_darkshadowAI : public boss_illidari_councilAI
{
boss_veras_darkshadowAI(Creature *c) : boss_illidari_councilAI(c) {}
+
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)
{
DoScriptText(SAY_VERA_SLAY, m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_VERA_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (!HasVanished)
{
if (DeadlyPoisonTimer < diff)
@@ -637,11 +755,13 @@ struct TRINITY_DLL_DECL boss_veras_darkshadowAI : public boss_illidari_councilAI
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))
@@ -657,6 +777,7 @@ struct TRINITY_DLL_DECL boss_veras_darkshadowAI : public boss_illidari_councilAI
m_creature->GetMotionMaster()->MoveChase(target);
}
}else VanishTimer -= diff;
+
DoMeleeAttackIfReady();
}
else
@@ -673,6 +794,7 @@ struct TRINITY_DLL_DECL boss_veras_darkshadowAI : public boss_illidari_councilAI
AppearEnvenomTimer = 4000;
HasVanished = false;
}else VanishTimer -= diff;
+
if (AppearEnvenomTimer < diff) // Appear 2 seconds before becoming attackable (Shifting out of vanish)
{
m_creature->GetMotionMaster()->Clear();
@@ -683,53 +805,66 @@ struct TRINITY_DLL_DECL boss_veras_darkshadowAI : public boss_illidari_councilAI
}
}
};
+
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* pCreature)
{
return new mob_illidari_councilAI (pCreature);
}
+
CreatureAI* GetAI_boss_gathios_the_shatterer(Creature* pCreature)
{
return new boss_gathios_the_shattererAI (pCreature);
}
+
CreatureAI* GetAI_boss_lady_malande(Creature* pCreature)
{
return new boss_lady_malandeAI (pCreature);
}
+
CreatureAI* GetAI_boss_veras_darkshadow(Creature* pCreature)
{
return new boss_veras_darkshadowAI (pCreature);
}
+
CreatureAI* GetAI_boss_high_nethermancer_zerevor(Creature* pCreature)
{
return new boss_high_nethermancer_zerevorAI (pCreature);
}
+
void AddSC_boss_illidari_council()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_illidari_council";
newscript->GetAI = &GetAI_mob_illidari_council;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_blood_elf_council_voice_trigger";
newscript->GetAI = &GetAI_mob_blood_elf_council_voice_trigger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_gathios_the_shatterer";
newscript->GetAI = &GetAI_boss_gathios_the_shatterer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lady_malande";
newscript->GetAI = &GetAI_boss_lady_malande;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_veras_darkshadow";
newscript->GetAI = &GetAI_boss_veras_darkshadow;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_high_nethermancer_zerevor";
newscript->GetAI = &GetAI_boss_high_nethermancer_zerevor;
diff --git a/src/bindings/scripts/scripts/outland/black_temple/instance_black_temple.cpp b/src/bindings/scripts/scripts/outland/black_temple/instance_black_temple.cpp
index 0d49c7970a7..cd67af9a8f3 100644
--- a/src/bindings/scripts/scripts/outland/black_temple/instance_black_temple.cpp
+++ b/src/bindings/scripts/scripts/outland/black_temple/instance_black_temple.cpp
@@ -13,15 +13,19 @@
* along 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 MAX_ENCOUNTER 9
+
/* Black Temple encounters:
0 - High Warlord Naj'entus event
1 - Supremus Event
@@ -33,11 +37,14 @@ EndScriptData */
7 - Illidari Council Event
8 - Illidan Stormrage Event
*/
+
struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
{
instance_black_temple(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
+
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.
@@ -50,6 +57,7 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
uint64 IllidariCouncil;
uint64 BloodElfCouncilVoice;
uint64 IllidanStormrage;
+
uint64 NajentusGate;
uint64 MainTempleDoors;
uint64 ShadeOfAkamaDoor;
@@ -62,9 +70,11 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
uint64 SimpleDoor;//council
uint64 IllidanGate;
uint64 IllidanDoor[2];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
Najentus = 0;
Akama = 0;
Akama_Shade = 0;
@@ -77,6 +87,7 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
IllidariCouncil = 0;
BloodElfCouncilVoice = 0;
IllidanStormrage = 0;
+
NajentusGate = 0;
MainTempleDoors = 0;
ShadeOfAkamaDoor= 0;
@@ -91,26 +102,32 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
IllidanDoor[0] = 0;
IllidanDoor[1] = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
Player* GetPlayerInMap()
{
Map::PlayerList const& players = instance->GetPlayers();
+
if (!players.isEmpty())
{
- for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
if (Player* plr = itr->getSource())
return plr;
}
}
+
debug_log("TSCR: Instance Black Temple: GetPlayerInMap, but PlayerList is empty!");
return NULL;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -129,6 +146,7 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
case 23499: BloodElfCouncilVoice = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -157,6 +175,7 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
case 186262: IllidanDoor[1] = pGo->GetGUID(); break; // Left door at Temple Summit
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -179,8 +198,10 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
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)
@@ -245,19 +266,24 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
m_auiEncounter[7] = data; break;
case DATA_ILLIDANSTORMRAGEEVENT: m_auiEncounter[8] = data; break;
}
+
if (data == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " "
<< m_auiEncounter[2] << " " << m_auiEncounter[3] << " " << m_auiEncounter[4]
<< " " << m_auiEncounter[5] << " " << m_auiEncounter[6] << " " << m_auiEncounter[7]
<< " " << m_auiEncounter[8];
+
str_data = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -272,12 +298,15 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
case DATA_ILLIDARICOUNCILEVENT: return m_auiEncounter[7];
case DATA_ILLIDANSTORMRAGEEVENT: return m_auiEncounter[8];
}
+
return 0;
}
+
std::string GetSaveData()
{
return str_data;
}
+
void Load(const char* in)
{
if (!in)
@@ -285,21 +314,27 @@ struct TRINITY_DLL_DECL instance_black_temple : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2]
>> m_auiEncounter[3] >> m_auiEncounter[4] >> m_auiEncounter[5] >> m_auiEncounter[6]
>> m_auiEncounter[7] >> m_auiEncounter[8];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_black_temple(Map* pMap)
{
return new instance_black_temple(pMap);
}
+
void AddSC_instance_black_temple()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/blades_edge_mountains.cpp b/src/bindings/scripts/scripts/outland/blades_edge_mountains.cpp
index 4ee593cf194..4c8d148d565 100644
--- a/src/bindings/scripts/scripts/outland/blades_edge_mountains.cpp
+++ b/src/bindings/scripts/scripts/outland/blades_edge_mountains.cpp
@@ -13,12 +13,14 @@
* along 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, 10821, 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
@@ -27,26 +29,34 @@ npc_overseer_nuaar
npc_saikkal_the_elder
go_legion_obelisk
EndContentData */
+
#include "precompiled.h"
+
//Support for quest: You're Fired! (10821)
bool obelisk_one, obelisk_two, obelisk_three, obelisk_four, obelisk_five;
+
#define LEGION_OBELISK_ONE 185193
#define LEGION_OBELISK_TWO 185195
#define LEGION_OBELISK_THREE 185196
#define LEGION_OBELISK_FOUR 185197
#define LEGION_OBELISK_FIVE 185198
+
/*######
## mobs_bladespire_ogre
######*/
+
//TODO: add support for quest 10512 + Creature abilities
struct TRINITY_DLL_DECL mobs_bladespire_ogreAI : public ScriptedAI
{
mobs_bladespire_ogreAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
@@ -54,9 +64,11 @@ CreatureAI* GetAI_mobs_bladespire_ogre(Creature* pCreature)
{
return new mobs_bladespire_ogreAI (pCreature);
}
+
/*######
## mobs_nether_drake
######*/
+
enum eNetherdrake
{
SAY_NIHIL_1 = -1000396, //signed for 5955
@@ -64,46 +76,59 @@ enum eNetherdrake
SAY_NIHIL_3 = -1000398, //signed for 5955
SAY_NIHIL_4 = -1000399, //signed for 20021, used by 20021,21817,21820,21821,21823
SAY_NIHIL_INTERRUPT = -1000400, //signed for 20021, used by 20021,21817,21820,21821,21823
+
ENTRY_WHELP = 20021,
ENTRY_PROTO = 21821,
ENTRY_ADOLE = 21817,
ENTRY_MATUR = 21820,
ENTRY_NIHIL = 21823,
+
SPELL_T_PHASE_MODULATOR = 37573,
+
SPELL_ARCANE_BLAST = 38881,
SPELL_MANA_BURN = 38884,
SPELL_INTANGIBLE_PRESENCE = 36513
};
+
struct TRINITY_DLL_DECL mobs_nether_drakeAI : public ScriptedAI
{
mobs_nether_drakeAI(Creature *c) : ScriptedAI(c) {}
+
bool IsNihil;
uint32 NihilSpeech_Timer;
uint32 NihilSpeech_Phase;
+
uint32 ArcaneBlast_Timer;
uint32 ManaBurn_Timer;
uint32 IntangiblePresence_Timer;
+
void Reset()
{
IsNihil = false;
NihilSpeech_Timer = 3000;
NihilSpeech_Phase = 0;
+
ArcaneBlast_Timer = 7500;
ManaBurn_Timer = 10000;
IntangiblePresence_Timer = 15000;
}
+
void EnterCombat(Unit* who) { }
+
void MoveInLineOfSight(Unit *who)
{
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
ScriptedAI::MoveInLineOfSight(who);
}
+
//in case Creature was not summoned (not expected)
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id == 0)
{
m_creature->setDeathState(JUST_DIED);
@@ -111,14 +136,17 @@ struct TRINITY_DLL_DECL mobs_nether_drakeAI : public ScriptedAI
m_creature->SetHealth(0);
}
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == SPELL_T_PHASE_MODULATOR && caster->GetTypeId() == TYPEID_PLAYER)
{
const uint32 entry_list[4] = {ENTRY_PROTO, ENTRY_ADOLE, ENTRY_MATUR, ENTRY_NIHIL};
int cid = rand()%(4-1);
+
if (entry_list[cid] == m_creature->GetEntry())
++cid;
+
//we are nihil, so say before transform
if (m_creature->GetEntry() == ENTRY_NIHIL)
{
@@ -126,6 +154,7 @@ struct TRINITY_DLL_DECL mobs_nether_drakeAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
IsNihil = false;
}
+
if (m_creature->UpdateEntry(entry_list[cid]))
{
if (entry_list[cid] == ENTRY_NIHIL)
@@ -138,6 +167,7 @@ struct TRINITY_DLL_DECL mobs_nether_drakeAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (IsNihil)
@@ -171,16 +201,20 @@ struct TRINITY_DLL_DECL mobs_nether_drakeAI : public ScriptedAI
}
NihilSpeech_Timer = 5000;
}else NihilSpeech_Timer -=diff;
+
//anything below here is not interesting for Nihil, so skip it
return;
}
+
if (!UpdateVictim())
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();
@@ -188,31 +222,40 @@ struct TRINITY_DLL_DECL mobs_nether_drakeAI : public ScriptedAI
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* pCreature)
{
return new mobs_nether_drakeAI (pCreature);
}
+
/*######
## npc_daranelle
######*/
+
enum eDaranelle
{
SAY_SPELL_INFLUENCE = -1000174,
SPELL_LASHHAN_CHANNEL = 36904
};
+
struct TRINITY_DLL_DECL npc_daranelleAI : public ScriptedAI
{
npc_daranelleAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
+
void EnterCombat(Unit* who) { }
+
void MoveInLineOfSight(Unit *who)
{
if (who->GetTypeId() == TYPEID_PLAYER)
@@ -224,24 +267,32 @@ struct TRINITY_DLL_DECL npc_daranelleAI : public ScriptedAI
m_creature->CastSpell(who,37028,true);
}
}
+
ScriptedAI::MoveInLineOfSight(who);
}
};
+
CreatureAI* GetAI_npc_daranelle(Creature* pCreature)
{
return new npc_daranelleAI (pCreature);
}
+
/*######
## npc_overseer_nuaar
######*/
+
#define GOSSIP_HELLO_ON "Overseer, I am here to negotiate on behalf of the Cenarion Expedition."
+
bool GossipHello_npc_overseer_nuaar(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(10682) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_ON, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(10532, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_overseer_nuaar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -251,18 +302,24 @@ bool GossipSelect_npc_overseer_nuaar(Player* pPlayer, Creature* pCreature, uint3
}
return true;
}
+
/*######
## npc_saikkal_the_elder
######*/
+
#define GOSSIP_HELLO_STE "Yes... yes, it's me."
#define GOSSIP_SELECT_STE "Yes elder. Tell me more of the book."
+
bool GossipHello_npc_saikkal_the_elder(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(10980) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_STE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(10794, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_saikkal_the_elder(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -278,9 +335,11 @@ bool GossipSelect_npc_saikkal_the_elder(Player* pPlayer, Creature* pCreature, ui
}
return true;
}
+
/*######
## go_legion_obelisk
######*/
+
bool GOHello_go_legion_obelisk(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestStatus(10821) == QUEST_STATUS_INCOMPLETE)
@@ -303,6 +362,7 @@ bool GOHello_go_legion_obelisk(Player* pPlayer, GameObject* pGo)
obelisk_five = true;
break;
}
+
if (obelisk_one == true && obelisk_two == true && obelisk_three == true && obelisk_four == true && obelisk_five == true)
{
pGo->SummonCreature(19963,2943.40f,4778.20f,284.49f,0.94f,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,120000);
@@ -314,36 +374,45 @@ bool GOHello_go_legion_obelisk(Player* pPlayer, GameObject* pGo)
obelisk_five = false;
}
}
+
return true;
}
+
/*######
## AddSC
######*/
+
void AddSC_blades_edge_mountains()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mobs_bladespire_ogre";
newscript->GetAI = &GetAI_mobs_bladespire_ogre;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mobs_nether_drake";
newscript->GetAI = &GetAI_mobs_nether_drake;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_daranelle";
newscript->GetAI = &GetAI_npc_daranelle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_overseer_nuaar";
newscript->pGossipHello = &GossipHello_npc_overseer_nuaar;
newscript->pGossipSelect = &GossipSelect_npc_overseer_nuaar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_saikkal_the_elder";
newscript->pGossipHello = &GossipHello_npc_saikkal_the_elder;
newscript->pGossipSelect = &GossipSelect_npc_saikkal_the_elder;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_legion_obelisk";
newscript->pGOHello = &GOHello_go_legion_obelisk;
diff --git a/src/bindings/scripts/scripts/outland/boss_doomlord_kazzak.cpp b/src/bindings/scripts/scripts/outland/boss_doomlord_kazzak.cpp
index e9edad38db4..902c3e347b5 100644
--- a/src/bindings/scripts/scripts/outland/boss_doomlord_kazzak.cpp
+++ b/src/bindings/scripts/scripts/outland/boss_doomlord_kazzak.cpp
@@ -13,13 +13,16 @@
* along 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 SAY_INTRO -1000375 //signed for 3465
#define SAY_AGGRO1 -1000376 //signed for 3465
#define SAY_AGGRO2 -1000377 //signed for 3465
@@ -32,6 +35,7 @@ EndScriptData */
#define EMOTE_FRENZY -1000384 //signed for 21027
#define SAY_RAND1 -1000385 //signed for 21027
#define SAY_RAND2 -1000386 //signed for 21027
+
#define SPELL_SHADOWVOLLEY 32963
#define SPELL_CLEAVE 31779
#define SPELL_THUNDERCLAP 36706
@@ -40,9 +44,11 @@ EndScriptData */
#define SPELL_ENRAGE 32964
#define SPELL_CAPTURESOUL 32966
#define SPELL_TWISTEDREFLECTION 21063
+
struct TRINITY_DLL_DECL boss_doomlordkazzakAI : public ScriptedAI
{
boss_doomlordkazzakAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ShadowVolley_Timer;
uint32 Cleave_Timer;
uint32 ThunderClap_Timer;
@@ -50,6 +56,7 @@ struct TRINITY_DLL_DECL boss_doomlordkazzakAI : public ScriptedAI
uint32 MarkOfKazzak_Timer;
uint32 Enrage_Timer;
uint32 Twisted_Reflection_Timer;
+
void Reset()
{
ShadowVolley_Timer = 6000 + rand()%4000;
@@ -60,55 +67,67 @@ struct TRINITY_DLL_DECL boss_doomlordkazzakAI : public ScriptedAI
Enrage_Timer = 60000;
Twisted_Reflection_Timer = 33000; // Timer may be incorrect
}
+
void JustRespawned()
{
DoScriptText(SAY_INTRO, m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2), m_creature);
}
+
void KilledUnit(Unit* victim)
{
// When Kazzak kills a player (not pets/totems), he regens some health
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
+
DoCast(m_creature,SPELL_CAPTURESOUL);
+
DoScriptText(RAND(SAY_KILL1,SAY_KILL2,SAY_KILL3), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
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)
{
@@ -119,6 +138,7 @@ struct TRINITY_DLL_DECL boss_doomlordkazzakAI : public ScriptedAI
MarkOfKazzak_Timer = 20000;
}
}else MarkOfKazzak_Timer -= diff;
+
//Enrage_Timer
if (Enrage_Timer < diff)
{
@@ -126,18 +146,23 @@ struct TRINITY_DLL_DECL boss_doomlordkazzakAI : public ScriptedAI
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* pCreature)
{
return new boss_doomlordkazzakAI (pCreature);
}
+
void AddSC_boss_doomlordkazzak()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/boss_doomwalker.cpp b/src/bindings/scripts/scripts/outland/boss_doomwalker.cpp
index af01d09454a..4eba3e1e38d 100644
--- a/src/bindings/scripts/scripts/outland/boss_doomwalker.cpp
+++ b/src/bindings/scripts/scripts/outland/boss_doomwalker.cpp
@@ -13,13 +13,16 @@
* along 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"
+
#define SAY_AGGRO -1000387 //signed for 21027
#define SAY_EARTHQUAKE_1 -1000388 //signed for 21027
#define SAY_EARTHQUAKE_2 -1000389 //signed for 21027
@@ -29,6 +32,7 @@ EndScriptData */
#define SAY_SLAY_2 -1000393 //signed for 5955
#define SAY_SLAY_3 -1000394 //signed for 5955
#define SAY_DEATH -1000395 //signed for 5955
+
#define SPELL_EARTHQUAKE 32686
#define SPELL_SUNDER_ARMOR 33661
#define SPELL_CHAIN_LIGHTNING 33665
@@ -36,15 +40,19 @@ EndScriptData */
#define SPELL_ENRAGE 33653
#define SPELL_MARK_DEATH 37128
#define SPELL_AURA_DEATH 37131
+
struct TRINITY_DLL_DECL boss_doomwalkerAI : public ScriptedAI
{
boss_doomwalkerAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Chain_Timer;
uint32 Enrage_Timer;
uint32 Overrun_Timer;
uint32 Quake_Timer;
uint32 Armor_Timer;
+
bool InEnrage;
+
void Reset()
{
Enrage_Timer = 0;
@@ -52,23 +60,30 @@ struct TRINITY_DLL_DECL boss_doomwalkerAI : public ScriptedAI
Chain_Timer = 10000 + rand()%20000;
Quake_Timer = 25000 + rand()%10000;
Overrun_Timer = 30000 + rand()%15000;
+
InEnrage = false;
}
+
void KilledUnit(Unit* Victim)
{
Victim->CastSpell(Victim,SPELL_MARK_DEATH,0);
+
if (rand()%5)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void MoveInLineOfSight(Unit *who)
{
if (who && who->GetTypeId() == TYPEID_PLAYER && m_creature->IsHostileTo(who))
@@ -79,10 +94,12 @@ struct TRINITY_DLL_DECL boss_doomwalkerAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Spell Enrage, when hp <= 20% gain enrage
if (((m_creature->GetHealth()*100)/ m_creature->GetMaxHealth()) <= 20)
{
@@ -93,49 +110,63 @@ struct TRINITY_DLL_DECL boss_doomwalkerAI : public ScriptedAI
InEnrage = true;
}else Enrage_Timer -= diff;
}
+
//Spell Overrun
if (Overrun_Timer < diff)
{
DoScriptText(RAND(SAY_OVERRUN_1,SAY_OVERRUN_2), m_creature);
+
DoCast(m_creature->getVictim(),SPELL_OVERRUN);
Overrun_Timer = 25000 + rand()%15000;
}else Overrun_Timer -= diff;
+
//Spell Earthquake
if (Quake_Timer < diff)
{
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_EARTHQUAKE_1,SAY_EARTHQUAKE_2), m_creature);
+
//remove enrage before casting earthquake because enrage + earthquake = 16000dmg over 8sec and all dead
if (InEnrage)
m_creature->RemoveAura(SPELL_ENRAGE);
+
DoCast(m_creature,SPELL_EARTHQUAKE);
Quake_Timer = 30000 + rand()%25000;
}else Quake_Timer -= diff;
+
//Spell Chain Lightning
if (Chain_Timer < diff)
{
Unit* target = NULL;
target = SelectUnit(SELECT_TARGET_RANDOM,1);
+
if (!target)
target = m_creature->getVictim();
+
if (target)
DoCast(target,SPELL_CHAIN_LIGHTNING);
+
Chain_Timer = 7000 + rand()%20000;
}else Chain_Timer -= diff;
+
//Spell Sunder Armor
if (Armor_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SUNDER_ARMOR);
Armor_Timer = 10000 + rand()%15000;
}else Armor_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_doomwalker(Creature* pCreature)
{
return new boss_doomwalkerAI (pCreature);
}
+
void AddSC_boss_doomwalker()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp
index fa9e079f8e4..7b25584cf64 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_fathomlord_karathress.cpp
@@ -13,15 +13,18 @@
* along 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: 70
SDComment: Cyclone workaround
SDCategory: Coilfang Resevoir, Serpent Shrine Cavern
EndScriptData */
+
#include "precompiled.h"
#include "def_serpent_shrine.h"
#include "escort_ai.h"
+
#define SAY_AGGRO -1548021
#define SAY_GAIN_BLESSING -1548022
#define SAY_GAIN_ABILITY1 -1548023
@@ -31,6 +34,7 @@ EndScriptData */
#define SAY_SLAY2 -1548027
#define SAY_SLAY3 -1548028
#define SAY_DEATH -1548029
+
//Karathress spells
#define SPELL_CATACLYSMIC_BOLT 38441
#define SPELL_POWER_OF_SHARKKIS 38455
@@ -39,6 +43,7 @@ EndScriptData */
#define SPELL_ENRAGE 24318
#define SPELL_SEAR_NOVA 38445
#define SPELL_BLESSING_OF_THE_TIDES 38449
+
//Sharkkis spells
#define SPELL_LEECHING_THROW 29436
#define SPELL_THE_BEAST_WITHIN 38373
@@ -46,6 +51,7 @@ EndScriptData */
#define SPELL_SUMMON_FATHOM_LURKER 38433
#define SPELL_SUMMON_FATHOM_SPOREBAT 38431
#define SPELL_PET_ENRAGE 19574
+
//Tidalvess spells
#define SPELL_FROST_SHOCK 38234
#define SPELL_SPITFIRE_TOTEM 38236
@@ -55,6 +61,7 @@ EndScriptData */
#define SPELL_EARTHBIND_TOTEM 38304
#define SPELL_EARTHBIND_TOTEM_EFFECT 6474
#define SPELL_WINDFURY_WEAPON 38184
+
//Caribdis Spells
#define SPELL_WATER_BOLT_VOLLEY 38335
#define SPELL_TIDAL_SURGE 38358
@@ -62,11 +69,13 @@ EndScriptData */
#define SPELL_HEAL 38330
#define SPELL_SUMMON_CYCLONE 38337
#define SPELL_CYCLONE_CYCLONE 29538
+
//Yells and Quotes
#define SAY_GAIN_BLESSING_OF_TIDES "Your overconfidence will be your undoing! Guards, lend me your strength!"
#define SOUND_GAIN_BLESSING_OF_TIDES 11278
#define SAY_MISC "Alana be'lendor!" //don't know what use this
#define SOUND_MISC 11283
+
//Summoned Unit GUIDs
#define CREATURE_CYCLONE 22104
#define CREATURE_FATHOM_SPOREBAT 22120
@@ -74,12 +83,14 @@ EndScriptData */
#define CREATURE_SPITFIRE_TOTEM 22091
#define CREATURE_EARTHBIND_TOTEM 22486
#define CREATURE_POISON_CLEANSING_TOTEM 22487
+
//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 TRINITY_DLL_DECL boss_fathomlord_karathressAI : public ScriptedAI
{
@@ -90,19 +101,27 @@ struct TRINITY_DLL_DECL boss_fathomlord_karathressAI : public ScriptedAI
Advisors[1] = 0;
Advisors[2] = 0;
}
+
ScriptedInstance* pInstance;
+
uint32 CataclysmicBolt_Timer;
uint32 Enrage_Timer;
uint32 SearNova_Timer;
+
bool BlessingOfTides;
+
uint64 Advisors[3];
+
void Reset()
{
CataclysmicBolt_Timer = 10000;
Enrage_Timer = 600000; //10 minutes
SearNova_Timer = 20000+rand()%40000; // 20 - 60 seconds
+
BlessingOfTides = false;
+
+
if (pInstance)
{
uint64 RAdvisors[3];
@@ -111,7 +130,8 @@ struct TRINITY_DLL_DECL boss_fathomlord_karathressAI : public ScriptedAI
RAdvisors[2] = pInstance->GetData64(DATA_CARIBDIS);
//Respawn of the 3 Advisors
Creature* pAdvisor = NULL;
- for (int i=0; i<3; ++i)
+ for(int i=0; i<3; ++i)
+
if (RAdvisors[i])
{
pAdvisor = (Unit::GetCreature((*m_creature), RAdvisors[i]));
@@ -125,108 +145,133 @@ struct TRINITY_DLL_DECL boss_fathomlord_karathressAI : public ScriptedAI
pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED);
}
+
}
+
void EventSharkkisDeath()
{
DoScriptText(SAY_GAIN_ABILITY1, m_creature);
DoCast(m_creature, SPELL_POWER_OF_SHARKKIS);
}
+
void EventTidalvessDeath()
{
DoScriptText(SAY_GAIN_ABILITY2, m_creature);
DoCast(m_creature, SPELL_POWER_OF_TIDALVESS);
}
+
void EventCaribdisDeath()
{
DoScriptText(SAY_GAIN_ABILITY3, m_creature);
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();
+
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
+
pInstance->SetData64(DATA_KARATHRESSEVENT_STARTER, who->GetGUID());
pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit *killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_FATHOMLORDKARATHRESSEVENT, DONE);
+
//support for quest 10944
m_creature->SummonCreature(SEER_OLUM, OLUM_X, OLUM_Y, OLUM_Z, OLUM_O, TEMPSUMMON_TIMED_DESPAWN, 3600000);
}
+
void EnterCombat(Unit *who)
{
StartEvent(who);
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER));
+
if (target)
{
AttackStart(target);
GetAdvisors();
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT))
{
EnterEvadeMode();
return;
}
+
//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();
+
if (target)
DoCast(target, SPELL_CATACLYSMIC_BOLT);
CataclysmicBolt_Timer = 10000;
}else CataclysmicBolt_Timer -= diff;
+
//SearNova_Timer
if (SearNova_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SEAR_NOVA);
SearNova_Timer = 20000+rand()%40000;
}else SearNova_Timer -= diff;
+
//Enrage_Timer
if (Enrage_Timer < diff)
{
DoCast(m_creature, SPELL_ENRAGE);
Enrage_Timer = 90000;
}else Enrage_Timer -= diff;
+
//Blessing of Tides Trigger
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= 75 && !BlessingOfTides)
{
BlessingOfTides = true;
bool continueTriggering;
Creature* Advisor;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
if (Advisors[i])
{
Advisor = (Unit::GetCreature(*m_creature, Advisors[i]));
@@ -246,9 +291,11 @@ struct TRINITY_DLL_DECL boss_fathomlord_karathressAI : public ScriptedAI
DoPlaySoundToSet(m_creature, SOUND_GAIN_BLESSING_OF_TIDES);
}
}
+
DoMeleeAttackIfReady();
}
};
+
//Fathom-Guard Sharkkis AI
struct TRINITY_DLL_DECL boss_fathomguard_sharkkisAI : public ScriptedAI
{
@@ -256,41 +303,53 @@ struct TRINITY_DLL_DECL boss_fathomguard_sharkkisAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 LeechingThrow_Timer;
uint32 TheBeastWithin_Timer;
uint32 Multishot_Timer;
uint32 Pet_Timer;
+
bool pet;
+
uint64 SummonedPet;
+
void Reset()
{
LeechingThrow_Timer = 20000;
TheBeastWithin_Timer = 30000;
Multishot_Timer = 15000;
Pet_Timer = 10000;
+
pet = false;
+
Creature *Pet = Unit::GetCreature(*m_creature, SummonedPet);
if (Pet && Pet->isAlive())
{
Pet->DealDamage(Pet, Pet->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
}
+
SummonedPet = 0;
+
if (pInstance)
pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
{
Creature *Karathress = NULL;
Karathress = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_KARATHRESS)));
+
if (Karathress)
CAST_AI(boss_fathomlord_karathressAI, Karathress->AI())->EventSharkkisDeath();
CAST_AI(boss_fathomlord_karathressAI, Karathress->AI())->EventSharkkisDeath();
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -299,38 +358,45 @@ struct TRINITY_DLL_DECL boss_fathomguard_sharkkisAI : public ScriptedAI
pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS);
}
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT))
{
EnterEvadeMode();
return;
}
+
//LeechingThrow_Timer
if (LeechingThrow_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_LEECHING_THROW);
LeechingThrow_Timer = 20000;
}else LeechingThrow_Timer -= diff;
+
//Multishot_Timer
if (Multishot_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_MULTISHOT);
Multishot_Timer = 20000;
}else Multishot_Timer -= diff;
+
//TheBeastWithin_Timer
if (TheBeastWithin_Timer < diff)
{
@@ -342,6 +408,7 @@ struct TRINITY_DLL_DECL boss_fathomguard_sharkkisAI : public ScriptedAI
}
TheBeastWithin_Timer = 30000;
}else TheBeastWithin_Timer -= diff;
+
//Pet_Timer
if (Pet_Timer < diff && pet == false)
{
@@ -368,9 +435,11 @@ struct TRINITY_DLL_DECL boss_fathomguard_sharkkisAI : public ScriptedAI
SummonedPet = Pet->GetGUID();
}
}else Pet_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Fathom-Guard Tidalvess AI
struct TRINITY_DLL_DECL boss_fathomguard_tidalvessAI : public ScriptedAI
{
@@ -378,31 +447,38 @@ struct TRINITY_DLL_DECL boss_fathomguard_tidalvessAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 FrostShock_Timer;
uint32 Spitfire_Timer;
uint32 PoisonCleansing_Timer;
uint32 Earthbind_Timer;
+
void Reset()
{
FrostShock_Timer = 25000;
Spitfire_Timer = 60000;
PoisonCleansing_Timer = 30000;
Earthbind_Timer = 45000;
+
if (pInstance)
pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
{
Creature *Karathress = NULL;
Karathress = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_KARATHRESS)));
+
if (Karathress)
if (!m_creature->isAlive() && Karathress)
CAST_AI(boss_fathomlord_karathressAI, Karathress->AI())->EventTidalvessDeath();
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -412,36 +488,43 @@ struct TRINITY_DLL_DECL boss_fathomguard_tidalvessAI : public ScriptedAI
}
DoCast(m_creature, SPELL_WINDFURY_WEAPON);
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT))
{
EnterEvadeMode();
return;
}
+
if (!m_creature->HasAura(SPELL_WINDFURY_WEAPON))
{
DoCast(m_creature, SPELL_WINDFURY_WEAPON);
}
+
//FrostShock_Timer
if (FrostShock_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FROST_SHOCK);
FrostShock_Timer = 25000+rand()%5000;
}else FrostShock_Timer -= diff;
+
//Spitfire_Timer
if (Spitfire_Timer < diff)
{
@@ -453,21 +536,25 @@ struct TRINITY_DLL_DECL boss_fathomguard_tidalvessAI : public ScriptedAI
}
Spitfire_Timer = 60000;
}else Spitfire_Timer -= diff;
+
//PoisonCleansing_Timer
if (PoisonCleansing_Timer < diff)
{
DoCast(m_creature, SPELL_POISON_CLEANSING_TOTEM);
PoisonCleansing_Timer = 30000;
}else PoisonCleansing_Timer -= diff;
+
//Earthbind_Timer
if (Earthbind_Timer < diff)
{
DoCast(m_creature, SPELL_EARTHBIND_TOTEM);
Earthbind_Timer = 45000;
}else Earthbind_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Fathom-Guard Caribdis AI
struct TRINITY_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI
{
@@ -475,31 +562,38 @@ struct TRINITY_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 WaterBoltVolley_Timer;
uint32 TidalSurge_Timer;
uint32 Heal_Timer;
uint32 Cyclone_Timer;
+
void Reset()
{
WaterBoltVolley_Timer = 35000;
TidalSurge_Timer = 15000+rand()%5000;
Heal_Timer = 55000;
Cyclone_Timer = 30000+rand()%10000;
+
if (pInstance)
pInstance->SetData(DATA_KARATHRESSEVENT, NOT_STARTED);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
{
Creature *Karathress = NULL;
Karathress = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_KARATHRESS)));
+
if (Karathress)
if (!m_creature->isAlive() && Karathress)
CAST_AI(boss_fathomlord_karathressAI, Karathress->AI())->EventCaribdisDeath();
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -508,32 +602,38 @@ struct TRINITY_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI
pInstance->SetData(DATA_KARATHRESSEVENT, IN_PROGRESS);
}
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_KARATHRESSEVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_KARATHRESSEVENT_STARTER));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_KARATHRESSEVENT))
{
EnterEvadeMode();
return;
}
+
//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)
{
@@ -542,6 +642,7 @@ struct TRINITY_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI
m_creature->getVictim()->CastSpell(m_creature->getVictim(), SPELL_TIDAL_SURGE_FREEZE, true);
TidalSurge_Timer = 15000+rand()%5000;
}else TidalSurge_Timer -= diff;
+
//Cyclone_Timer
if (Cyclone_Timer < diff)
{
@@ -561,21 +662,26 @@ struct TRINITY_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI
}
}
}else Cyclone_Timer -= diff;
+
//Heal_Timer
if (Heal_Timer < diff)
{
// It can be cast on any of the mobs
Unit *pUnit = NULL;
+
while(pUnit == NULL || !pUnit->isAlive())
{
pUnit = selectAdvisorUnit();
}
+
if (pUnit && pUnit->isAlive())
DoCast(pUnit, SPELL_HEAL);
Heal_Timer = 60000;
}else Heal_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
Unit* selectAdvisorUnit()
{
Unit* pUnit = NULL;
@@ -597,25 +703,31 @@ struct TRINITY_DLL_DECL boss_fathomguard_caribdisAI : public ScriptedAI
break;
}
}else pUnit = m_creature;
+
return pUnit;
}
};
+
CreatureAI* GetAI_boss_fathomlord_karathress(Creature* pCreature)
{
return new boss_fathomlord_karathressAI (pCreature);
}
+
CreatureAI* GetAI_boss_fathomguard_sharkkis(Creature* pCreature)
{
return new boss_fathomguard_sharkkisAI (pCreature);
}
+
CreatureAI* GetAI_boss_fathomguard_tidalvess(Creature* pCreature)
{
return new boss_fathomguard_tidalvessAI (pCreature);
}
+
CreatureAI* GetAI_boss_fathomguard_caribdis(Creature* pCreature)
{
return new boss_fathomguard_caribdisAI (pCreature);
}
+
void AddSC_boss_fathomlord_karathress()
{
Script *newscript;
@@ -623,14 +735,17 @@ void AddSC_boss_fathomlord_karathress()
newscript->Name = "boss_fathomlord_karathress";
newscript->GetAI = &GetAI_boss_fathomlord_karathress;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_fathomguard_sharkkis";
newscript->GetAI = &GetAI_boss_fathomguard_sharkkis;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_fathomguard_tidalvess";
newscript->GetAI = &GetAI_boss_fathomguard_tidalvess;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_fathomguard_caribdis";
newscript->GetAI = &GetAI_boss_fathomguard_caribdis;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp
index ba02cd9805d..288b8cf917e 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_hydross_the_unstable.cpp
@@ -13,14 +13,17 @@
* along 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 SAY_AGGRO -1548000
#define SAY_SWITCH_TO_CLEAN -1548001
#define SAY_CLEAN_SLAY1 -1548002
@@ -30,9 +33,12 @@ EndScriptData */
#define SAY_CORRUPT_SLAY1 -1548006
#define SAY_CORRUPT_SLAY2 -1548007
#define SAY_CORRUPT_DEATH -1548008
+
#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
@@ -51,11 +57,14 @@ EndScriptData */
#define SPELL_SUMMON_WATER_ELEMENT 36459 //not in use yet(in use ever?)
#define SPELL_ELEMENTAL_SPAWNIN 25035
#define SPELL_BLUE_BEAM 40227 //channeled Hydross Beam Helper (not in use yet)
+
#define ENTRY_PURE_SPAWN 22035
#define ENTRY_TAINTED_SPAWN 22036
#define ENTRY_BEAM_DUMMY 21934
+
#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
@@ -64,13 +73,16 @@ EndScriptData */
#define SPAWN_Y_DIFF3 -4.72702
#define SPAWN_X_DIFF4 12.577011
#define SPAWN_Y_DIFF4 4.72702
+
struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
{
boss_hydross_the_unstableAI(Creature *c) : ScriptedAI(c), Summons(m_creature)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint64 beams[2];
uint32 PosCheck_Timer;
uint32 MarkOfHydross_Timer;
@@ -83,6 +95,7 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
bool CorruptedForm;
bool beam;
SummonList Summons;
+
void Reset()
{
DeSummonBeams();
@@ -96,16 +109,20 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
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->SetDisplayId(MODEL_CLEAN);
+
if (pInstance)
pInstance->SetData(DATA_HYDROSSTHEUNSTABLEEVENT, NOT_STARTED);
beam = false;
Summons.DespawnAll();
}
+
void SummonBeams()
{
Creature* beamer = m_creature->SummonCreature(ENTRY_BEAM_DUMMY,-258.333,-356.34,22.0499,5.90835,TEMPSUMMON_CORPSE_DESPAWN,0);
@@ -127,7 +144,7 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
}
void DeSummonBeams()
{
- for (uint8 i=0; i<2; ++i)
+ for(uint8 i=0;i<2; ++i)
{
Creature* mob = Unit::GetCreature(*m_creature,beams[i]);
if (mob)
@@ -140,9 +157,11 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_HYDROSSTHEUNSTABLEEVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
if (CorruptedForm)
@@ -154,6 +173,7 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
DoScriptText(RAND(SAY_CLEAN_SLAY1,SAY_CLEAN_SLAY2), m_creature);
}
}
+
void JustSummoned(Creature* summoned)
{
if (summoned->GetEntry() == ENTRY_PURE_SPAWN)
@@ -169,20 +189,24 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
Summons.Summon(summoned);
}
}
+
void SummonedCreatureDespawn(Creature *summon)
{
Summons.Despawn(summon);
}
+
void JustDied(Unit *victim)
{
if (CorruptedForm)
DoScriptText(SAY_CORRUPT_DEATH, m_creature);
else
DoScriptText(SAY_CLEAN_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_HYDROSSTHEUNSTABLEEVENT, DONE);
Summons.DespawnAll();
}
+
void UpdateAI(const uint32 diff)
{
if (!beam)
@@ -193,6 +217,7 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
// corrupted form
if (CorruptedForm)
{
@@ -202,6 +227,7 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
if (MarkOfCorruption_Count <= 5)
{
uint32 mark_spell;
+
switch(MarkOfCorruption_Count)
{
case 0: mark_spell = SPELL_MARK_OF_CORRUPTION1; break;
@@ -211,20 +237,26 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
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)
{
@@ -234,18 +266,22 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
m_creature->SetDisplayId(MODEL_CLEAN);
CorruptedForm = false;
MarkOfHydross_Count = 0;
+
DoScriptText(SAY_SWITCH_TO_CLEAN, m_creature);
DoResetThreat();
SummonBeams();
+
// spawn 4 adds
DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF1, SPAWN_Y_DIFF1, 3, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF2, SPAWN_Y_DIFF2, 3, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF3, SPAWN_Y_DIFF3, 3, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
DoSpawnCreature(ENTRY_PURE_SPAWN, SPAWN_X_DIFF4, SPAWN_Y_DIFF4, 3, 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;
}
@@ -258,6 +294,7 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
if (MarkOfHydross_Count <= 5)
{
uint32 mark_spell;
+
switch(MarkOfHydross_Count)
{
case 0: mark_spell = SPELL_MARK_OF_HYDROSS1; break;
@@ -267,20 +304,26 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
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 = SelectTarget(SELECT_TARGET_RANDOM, 0, 100, true);
if (target)
DoCast(target, SPELL_WATER_TOMB);
+
WaterTomb_Timer = 7000;
}else WaterTomb_Timer -= diff;
+
//PosCheck_Timer
if (PosCheck_Timer < diff)
{
@@ -290,27 +333,33 @@ struct TRINITY_DLL_DECL boss_hydross_the_unstableAI : public ScriptedAI
m_creature->SetDisplayId(MODEL_CORRUPT);
MarkOfCorruption_Count = 0;
CorruptedForm = true;
+
DoScriptText(SAY_SWITCH_TO_CORRUPT, m_creature);
DoResetThreat();
DeSummonBeams();
+
// spawn 4 adds
DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF1, SPAWN_Y_DIFF1, 3, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF2, SPAWN_Y_DIFF2, 3, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF3, SPAWN_Y_DIFF3, 3, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
DoSpawnCreature(ENTRY_TAINTED_SPAWN, SPAWN_X_DIFF4, SPAWN_Y_DIFF4, 3, 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();
}
};
@@ -318,6 +367,7 @@ CreatureAI* GetAI_boss_hydross_the_unstable(Creature* pCreature)
{
return new boss_hydross_the_unstableAI (pCreature);
}
+
void AddSC_boss_hydross_the_unstable()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp
index efdc19d1cc5..0ee1a68ee0e 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lady_vashj.cpp
@@ -13,16 +13,19 @@
* 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 "simple_ai.h"
#include "Spell.h"
+
#define SAY_INTRO -1548042
#define SAY_AGGRO1 -1548043
#define SAY_AGGRO2 -1548044
@@ -37,6 +40,7 @@ EndScriptData */
#define SAY_SLAY2 -1548053
#define SAY_SLAY3 -1548054
#define SAY_DEATH -1548055
+
#define SPELL_SURGE 38044
#define SPELL_MULTI_SHOT 38310
#define SPELL_SHOCK_BLAST 38509
@@ -47,13 +51,16 @@ EndScriptData */
#define SPELL_POISON_BOLT 40095
#define SPELL_TOXIC_SPORES 38575
#define SPELL_MAGIC_BARRIER 38112
+
#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
@@ -61,8 +68,10 @@ EndScriptData */
#define COILFANG_ELITE 22055
#define TOXIC_SPOREBAT 22140
#define TOXIC_SPORES_TRIGGER 22207
+
#define TEXT_NOT_INITIALIZED "Instance script not initialized"
#define TEXT_ALREADY_DEACTIVATED "Already deactivated"
+
float ElementPos[8][4] =
{
{8.3, -835.3, 21.9, 5},
@@ -74,6 +83,7 @@ float ElementPos[8][4] =
{-35, -987.6, 21.5, 0.8},
{-58.9, -901.6, 21.5, 6}
};
+
float ElementWPPos[8][3] =
{
{71.700752, -883.905884, 41.097168},
@@ -85,6 +95,7 @@ float ElementWPPos[8][3] =
{43.466549, -979.406677, 41.097027},
{69.945908, -964.663940, 41.097054}
};
+
float SporebatWPPos[8][3] =
{
{31.6,-896.3,59.1},
@@ -96,18 +107,21 @@ float SporebatWPPos[8][3] =
{42.2, -912.4, 51.7},
{27, -905.9, 50}
};
+
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},
@@ -115,6 +129,7 @@ float ShieldGeneratorChannelPos[4][4] =
{10.3859, -944.036, 42.5446, 0.779888},
{49.3126, -943.398, 42.5501, 2.40174}
};
+
//Lady Vashj AI
struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
{
@@ -125,8 +140,11 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
JustCreated = true;
c->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); //set it only once on Creature create (no need do intro if wiped)
}
+
ScriptedInstance *pInstance;
+
uint64 ShieldGeneratorChannel[4];
+
uint32 AggroTimer;
uint32 ShockBlast_Timer;
uint32 Entangle_Timer;
@@ -141,10 +159,12 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
uint32 SummonSporebat_StaticTimer;
uint8 EnchantedElemental_Pos;
uint8 Phase;
+
bool Entangle;
bool Intro;
bool CanAttack;
bool JustCreated;
+
void Reset()
{
AggroTimer = 19000;
@@ -161,27 +181,32 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
SummonSporebat_StaticTimer = 30000;
EnchantedElemental_Pos = 0;
Phase = 0;
+
Entangle = false;
if (JustCreated)
{
CanAttack = false;
JustCreated = false;
}else CanAttack = true;
+
Unit *remo;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
remo = Unit::GetUnit(*m_creature, ShieldGeneratorChannel[i]);
if (remo)
remo->setDeathState(JUST_DIED);
}
+
if (pInstance)
pInstance->SetData(DATA_LADYVASHJEVENT, NOT_STARTED);
ShieldGeneratorChannel[0] = 0;
ShieldGeneratorChannel[1] = 0;
ShieldGeneratorChannel[2] = 0;
ShieldGeneratorChannel[3] = 0;
+
m_creature->SetCorpseDelay(1000*60*60);
}
+
//Called when a tainted elemental dies
void EventTaintedElementalDeath()
{
@@ -193,19 +218,25 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_LADYVASHJEVENT, DONE);
}
+
void StartEvent()
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3,SAY_AGGRO4), m_creature);
+
Phase = 1;
+
if (pInstance)
pInstance->SetData(DATA_LADYVASHJEVENT, IN_PROGRESS);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -213,7 +244,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
//remove old tainted cores to prevent cheating in phase 2
Map* pMap = m_creature->GetMap();
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
+ for(Map::PlayerList::const_iterator i = PlayerList.begin();i != PlayerList.end(); ++i)
{
if (Player* i_pl = i->getSource())
{
@@ -222,9 +253,11 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
}
}
StartEvent();//this is EnterCombat(), so were are 100% in combat, start the event
+
if (Phase != 2)
AttackStart(who);
}
+
void MoveInLineOfSight(Unit *who)
{
if (!Intro)
@@ -236,6 +269,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
return;
if (!who || m_creature->getVictim())
return;
+
if (who->isTargetableForAttack() && who->isInAccessiblePlaceFor(m_creature) && m_creature->IsHostileTo(who))
{
float attackRadius = m_creature->GetAttackDistance(who);
@@ -243,13 +277,16 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
{
//if (who->HasStealthAura())
// who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);
- if (!m_creature->isInCombat()) //AttackStart() sets UNIT_FLAG_IN_COMBAT, so this msut be before attacking
+
+ if (!m_creature->isInCombat())//AttackStart() sets UNIT_FLAG_IN_COMBAT, so this msut be before attacking
StartEvent();
+
if (Phase != 2)
AttackStart(who);
}
}
}
+
void CastShootOrMultishot()
{
switch(rand()%2)
@@ -270,6 +307,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
DoScriptText(RAND(SAY_BOWSHOT1,SAY_BOWSHOT2), m_creature);
}
}
+
void UpdateAI(const uint32 diff)
{
if (!CanAttack && Intro)
@@ -294,6 +332,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Phase == 1 || Phase == 3)
{
//ShockBlast_Timer
@@ -303,8 +342,10 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
//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)
{
@@ -312,11 +353,14 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
//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 = SelectTarget(SELECT_TARGET_RANDOM, 0, 200, true);
+
if (target && !target->HasAura(SPELL_STATIC_CHARGE_TRIGGER))
//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)
{
@@ -335,6 +379,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
Entangle_Timer = 20000+rand()%5000;
}
}else Entangle_Timer -= diff;
+
//Phase 1
if (Phase == 1)
{
@@ -343,10 +388,12 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
{
//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->GetMotionMaster()->Clear();
DoTeleportTo(MIDDLE_X, MIDDLE_Y, MIDDLE_Z);
+
Creature *pCreature;
- for (uint8 i = 0; i < 4; ++i)
+ 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)
@@ -363,6 +410,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
{
Creature *Sporebat = NULL;
Sporebat = m_creature->SummonCreature(TOXIC_SPOREBAT, SPOREBAT_X, SPOREBAT_Y, SPOREBAT_Z, SPOREBAT_O, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (Sporebat)
{
Unit *target = NULL;
@@ -370,23 +418,29 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
if (target)
Sporebat->AI()->AttackStart(target);
}
+
//summon sporebats faster and faster
if (SummonSporebat_StaticTimer > 1000)
SummonSporebat_StaticTimer -= 1000;
+
SummonSporebat_Timer = SummonSporebat_StaticTimer;
+
if (SummonSporebat_Timer < 5000)
SummonSporebat_Timer = 5000;
+
}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<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
//if in melee range
@@ -396,9 +450,11 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
break;
}
}
+
//if nobody is in melee range
if (!InMeleeRange)
CastShootOrMultishot();
+
Check_Timer = 5000;
}else Check_Timer -= diff;
}
@@ -412,30 +468,39 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
//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_CORPSE_DESPAWN, 0);
+
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_DEAD_DESPAWN, 0);
+
TaintedElemental_Timer = 120000;
}else TaintedElemental_Timer -= diff;
+
//CoilfangElite_Timer
if (CoilfangElite_Timer < diff)
{
@@ -453,6 +518,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
}
CoilfangElite_Timer = 45000+rand()%5000;
}else CoilfangElite_Timer -= diff;
+
//CoilfangStrider_Timer
if (CoilfangStrider_Timer < diff)
{
@@ -470,6 +536,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
}
CoilfangStrider_Timer = 60000+rand()%10000;
}else CoilfangStrider_Timer -= diff;
+
//Check_Timer
if (Check_Timer < diff)
{
@@ -478,9 +545,13 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
{
//set life 50%
m_creature->SetHealth(m_creature->GetMaxHealth()/2);
+
m_creature->RemoveAurasDueToSpell(SPELL_MAGIC_BARRIER);
+
DoScriptText(SAY_PHASE3, m_creature);
+
Phase = 3;
+
//return to the tank
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
}
@@ -489,6 +560,7 @@ struct TRINITY_DLL_DECL boss_lady_vashjAI : public ScriptedAI
}
}
};
+
//Enchanted Elemental
//If one of them reaches Vashj he will increase her damage done by 5%.
struct TRINITY_DLL_DECL mob_enchanted_elementalAI : public ScriptedAI
@@ -497,11 +569,13 @@ struct TRINITY_DLL_DECL mob_enchanted_elementalAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 move;
uint32 phase;
float x, y, z;
Creature *Vashj;
+
void Reset()
{
m_creature->SetSpeed(MOVE_WALK,0.6);//walk
@@ -509,7 +583,8 @@ struct TRINITY_DLL_DECL mob_enchanted_elementalAI : public ScriptedAI
move = 0;
phase = 1;
Vashj = NULL;
- for (int i = 0; i<8; ++i) //search for nearest waypoint (up on stairs)
+
+ for (int i = 0;i<8; ++i)//search for nearest waypoint (up on stairs)
{
if (!x || !y || !z)
{
@@ -530,16 +605,21 @@ struct TRINITY_DLL_DECL mob_enchanted_elementalAI : public ScriptedAI
if (pInstance)
Vashj = Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_LADYVASHJ));
}
+
void EnterCombat(Unit *who) { return; }
+
void MoveInLineOfSight(Unit *who){return;}
+
void UpdateAI(const uint32 diff)
{
if (!pInstance)
return;
+
if (!Vashj)
{
return;
}
+
if (move < diff)
{
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
@@ -581,6 +661,7 @@ struct TRINITY_DLL_DECL mob_enchanted_elementalAI : public ScriptedAI
} else move -= 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 TRINITY_DLL_DECL mob_tainted_elementalAI : public ScriptedAI
@@ -589,28 +670,35 @@ struct TRINITY_DLL_DECL mob_tainted_elementalAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 PoisonBolt_Timer;
uint32 Despawn_Timer;
+
void Reset()
{
PoisonBolt_Timer = 5000+rand()%5000;
Despawn_Timer = 30000;
}
+
void JustDied(Unit *killer)
{
if (pInstance)
{
Creature *Vashj = NULL;
Vashj = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_LADYVASHJ)));
+
if (Vashj)
CAST_AI(boss_lady_vashjAI, Vashj->AI())->EventTaintedElementalDeath();
}
}
+
void EnterCombat(Unit *who)
{
m_creature->AddThreat(who, 0.1f);
}
+
void UpdateAI(const uint32 diff)
{
//PoisonBolt_Timer
@@ -618,20 +706,25 @@ struct TRINITY_DLL_DECL mob_tainted_elementalAI : public ScriptedAI
{
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;
+
//Despawn_Timer
if (Despawn_Timer < diff)
{
//call Unsummon()
m_creature->setDeathState(DEAD);
+
//to prevent crashes
Despawn_Timer = 1000;
}else Despawn_Timer -= diff;
}
};
+
//Toxic 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 TRINITY_DLL_DECL mob_toxic_sporebatAI : public ScriptedAI
@@ -641,11 +734,14 @@ struct TRINITY_DLL_DECL mob_toxic_sporebatAI : public ScriptedAI
pInstance = c->GetInstanceData();
EnterEvadeMode();
}
+
ScriptedInstance *pInstance;
+
uint32 movement_timer;
uint32 ToxicSpore_Timer;
uint32 bolt_timer;
uint32 Check_Timer;
+
void Reset()
{
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
@@ -655,19 +751,26 @@ struct TRINITY_DLL_DECL mob_toxic_sporebatAI : public ScriptedAI
bolt_timer = 5500;
Check_Timer = 1000;
}
+
void EnterCombat(Unit *who)
{
+
}
+
void MoveInLineOfSight(Unit *who)
{
+
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id == 1)
movement_timer = 0;
}
+
void UpdateAI (const uint32 diff)
{
//Random movement
@@ -677,6 +780,7 @@ struct TRINITY_DLL_DECL mob_toxic_sporebatAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(1,SporebatWPPos[rndpos][0], SporebatWPPos[rndpos][1], SporebatWPPos[rndpos][2]);
movement_timer = 6000;
}else movement_timer -= diff;
+
//toxic spores
if (bolt_timer < diff)
{
@@ -694,6 +798,7 @@ struct TRINITY_DLL_DECL mob_toxic_sporebatAI : public ScriptedAI
bolt_timer = 10000+rand()%5000;
}
else bolt_timer -= diff;
+
//Check_Timer
if (Check_Timer < diff)
{
@@ -710,45 +815,57 @@ struct TRINITY_DLL_DECL mob_toxic_sporebatAI : public ScriptedAI
m_creature->setFaction(35);
}
}
+
Check_Timer = 1000;
}else Check_Timer -= diff;
}
};
+
//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* pCreature)
{
SimpleAI* ai = new SimpleAI (pCreature);
+
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 Strider
//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* pCreature)
{
SimpleAI* ai = new SimpleAI (pCreature);
+
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 TRINITY_DLL_DECL mob_shield_generator_channelAI : public ScriptedAI
{
mob_shield_generator_channelAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
uint32 Check_Timer;
bool Casted;
@@ -757,18 +874,24 @@ struct TRINITY_DLL_DECL mob_shield_generator_channelAI : public ScriptedAI
Check_Timer = 0;
Casted = false;
m_creature->SetDisplayId(11686); //invisible
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void EnterCombat(Unit *who) { return; }
+
void MoveInLineOfSight(Unit *who) { return; }
+
void UpdateAI (const uint32 diff)
{
if (!pInstance)
return;
+
if (Check_Timer < diff)
{
Unit *Vashj = NULL;
Vashj = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_LADYVASHJ));
+
if (Vashj && Vashj->isAlive())
{
//start visual channel
@@ -782,14 +905,17 @@ struct TRINITY_DLL_DECL mob_shield_generator_channelAI : public ScriptedAI
}else Check_Timer -= diff;
}
};
+
bool ItemUse_item_tainted_core(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
ScriptedInstance *pInstance = pPlayer->GetInstanceData();
+
if (!pInstance)
{
pPlayer->GetSession()->SendNotification(TEXT_NOT_INITIALIZED);
return true;
}
+
Creature *Vashj = NULL;
Vashj = (Unit::GetCreature((*pPlayer), pInstance->GetData64(DATA_LADYVASHJ)));
if (Vashj && CAST_AI(boss_lady_vashjAI, Vashj->AI())->Phase == 2)
@@ -819,11 +945,13 @@ bool ItemUse_item_tainted_core(Player* pPlayer, Item* _Item, SpellCastTargets co
default:
return true;
}
+
if (pInstance->GetData(identifier))
{
pPlayer->GetSession()->SendNotification(TEXT_ALREADY_DEACTIVATED);
return true;
}
+
//get and remove channel
Unit *Channel = NULL;
Channel = Unit::GetCreature(*Vashj, CAST_AI(boss_lady_vashjAI, Vashj->AI())->ShieldGeneratorChannel[channel_identifier]);
@@ -832,7 +960,9 @@ bool ItemUse_item_tainted_core(Player* pPlayer, Item* _Item, SpellCastTargets co
//call Unsummon()
Channel->setDeathState(JUST_DIED);
}
+
pInstance->SetData(identifier, 1);
+
//remove this item
pPlayer->DestroyItemCount(31088, 1, true);
return true;
@@ -848,26 +978,32 @@ bool ItemUse_item_tainted_core(Player* pPlayer, Item* _Item, SpellCastTargets co
}
return true;
}
+
CreatureAI* GetAI_boss_lady_vashj(Creature* pCreature)
{
return new boss_lady_vashjAI (pCreature);
}
+
CreatureAI* GetAI_mob_enchanted_elemental(Creature* pCreature)
{
return new mob_enchanted_elementalAI (pCreature);
}
+
CreatureAI* GetAI_mob_tainted_elemental(Creature* pCreature)
{
return new mob_tainted_elementalAI (pCreature);
}
+
CreatureAI* GetAI_mob_toxic_sporebat(Creature* pCreature)
{
return new mob_toxic_sporebatAI (pCreature);
}
+
CreatureAI* GetAI_mob_shield_generator_channel(Creature* pCreature)
{
return new mob_shield_generator_channelAI (pCreature);
}
+
void AddSC_boss_lady_vashj()
{
Script *newscript;
@@ -875,30 +1011,37 @@ void AddSC_boss_lady_vashj()
newscript->Name = "boss_lady_vashj";
newscript->GetAI = &GetAI_boss_lady_vashj;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_enchanted_elemental";
newscript->GetAI = &GetAI_mob_enchanted_elemental;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_tainted_elemental";
newscript->GetAI = &GetAI_mob_tainted_elemental;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_toxic_sporebat";
newscript->GetAI = &GetAI_mob_toxic_sporebat;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_coilfang_elite";
newscript->GetAI = &GetAI_mob_coilfang_elite;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_coilfang_strider";
newscript->GetAI = &GetAI_mob_coilfang_strider;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shield_generator_channel";
newscript->GetAI = &GetAI_mob_shield_generator_channel;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_tainted_core";
newscript->pItemUse = &ItemUse_item_tainted_core;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp
index cf3657439c4..f57f0542bdf 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_leotheras_the_blind.cpp
@@ -13,37 +13,45 @@
* along 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: 80
SDComment: Possesion Support
SDCategory: Coilfang Resevoir, Serpent Shrine Cavern
EndScriptData */
+
#include "precompiled.h"
#include "def_serpent_shrine.h"
+
// --- Spells used by Leotheras The Blind
#define SPELL_WHIRLWIND 37640
#define SPELL_CHAOS_BLAST 37674
#define SPELL_BERSERK 26662
#define SPELL_INSIDIOUS_WHISPER 37676
#define SPELL_DUAL_WIELD 42459
+
// --- Spells used in banish phase ---
#define BANISH_BEAM 38909
#define AURA_BANISH 37833
+
// --- Spells used by Greyheart Spellbinders
#define SPELL_EARTHSHOCK 39076
#define SPELL_MINDBLAST 37531
+
// --- Spells used by Inner Demons and Creature ID
#define INNER_DEMON_ID 21857
#define AURA_DEMONIC_ALIGNMENT 37713
#define SPELL_SHADOWBOLT 39309
#define SPELL_SOUL_LINK 38007
#define SPELL_CONSUMING_MADNESS 37749 //not supported by core yet
+
//Misc.
#define MODEL_DEMON 20125
#define MODEL_NIGHTELF 20514
#define DEMON_FORM 21875
#define MOB_SPELLBINDER 21806
+
#define SAY_AGGRO -1548009
#define SAY_SWITCH_TO_DEMON -1548010
#define SAY_INNER_DEMONS -1548011
@@ -56,15 +64,19 @@ EndScriptData */
#define SAY_FINAL_FORM -1548018
#define SAY_FREE -1548019
#define SAY_DEATH -1548020
+
struct TRINITY_DLL_DECL mob_inner_demonAI : public ScriptedAI
{
mob_inner_demonAI(Creature *c) : ScriptedAI(c)
{
victimGUID = 0;
}
+
uint32 ShadowBolt_Timer;
+
uint32 Link_Timer;
uint64 victimGUID;
+
void Reset()
{
ShadowBolt_Timer = 10000;
@@ -76,6 +88,7 @@ struct TRINITY_DLL_DECL mob_inner_demonAI : public ScriptedAI
if (pUnit && pUnit->HasAura(SPELL_INSIDIOUS_WHISPER))
pUnit->RemoveAurasDueToSpell(SPELL_INSIDIOUS_WHISPER);
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetGUID() != victimGUID && done_by->GetGUID() != m_creature->GetGUID())
@@ -84,15 +97,18 @@ struct TRINITY_DLL_DECL mob_inner_demonAI : public ScriptedAI
DoModifyThreatPercent(done_by, -100);
}
}
+
void EnterCombat(Unit *who)
{
if (!victimGUID) return;
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (m_creature->getVictim()->GetGUID() != victimGUID)
{
DoModifyThreatPercent(m_creature->getVictim(), -100);
@@ -107,19 +123,23 @@ struct TRINITY_DLL_DECL mob_inner_demonAI : public ScriptedAI
return;
}
}
+
if (Link_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SOUL_LINK, true);
Link_Timer = 1000;
}else Link_Timer -= diff;
+
if (!m_creature->HasAura(AURA_DEMONIC_ALIGNMENT))
DoCast(m_creature, AURA_DEMONIC_ALIGNMENT,true);
+
if (ShadowBolt_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SHADOWBOLT, false);
ShadowBolt_Timer = 10000;
}else ShadowBolt_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -131,10 +151,13 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
c->GetPosition(x,y,z);
pInstance = c->GetInstanceData();
Demon = 0;
- for (uint8 i = 0; i < 3; ++i) //clear guids
+
+ for(uint8 i = 0; i < 3; ++i)//clear guids
SpellBinderGUID[i] = 0;
}
+
ScriptedInstance *pInstance;
+
uint32 Whirlwind_Timer;
uint32 ChaosBlast_Timer;
uint32 SwitchToDemon_Timer;
@@ -142,16 +165,19 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
uint32 Berserk_Timer;
uint32 InnerDemons_Timer;
uint32 BanishTimer;
+
bool DealDamage;
bool NeedThreatReset;
bool DemonForm;
bool IsFinalForm;
bool EnrageUsed;
float x,y,z;
+
uint64 InnderDemon[5];
uint32 InnderDemon_Count;
uint64 Demon;
uint64 SpellBinderGUID[3];
+
void Reset()
{
CheckChannelers();
@@ -178,12 +204,14 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_LEOTHERASTHEBLINDEVENT, NOT_STARTED);
}
+
void CheckChannelers(bool DoEvade = true)
{
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
if (Creature *add = Unit::GetCreature(*m_creature,SpellBinderGUID[i]))
add->DisappearAndDie();
+
float nx = x;
float ny = y;
float o = 2.4f;
@@ -193,16 +221,19 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
Creature* binder = m_creature->SummonCreature(MOB_SPELLBINDER,nx,ny,z,o,TEMPSUMMON_DEAD_DESPAWN,0);
if (binder)
SpellBinderGUID[i] = binder->GetGUID();
+
}
}
void MoveInLineOfSight(Unit *who)
{
if (m_creature->HasAura(AURA_BANISH))
return;
+
if (!m_creature->getVictim() && who->isTargetableForAttack() && (m_creature->IsHostileTo(who)) && who->isInAccessiblePlaceFor(m_creature))
{
if (m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
return;
+
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDistInMap(who, attackRadius))
{
@@ -214,32 +245,39 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
}
}
}
+
void StartEvent()
{
DoScriptText(SAY_AGGRO, m_creature);
if (pInstance)
pInstance->SetData(DATA_LEOTHERASTHEBLINDEVENT, IN_PROGRESS);
}
+
void CheckBanish()
{
uint8 AliveChannelers = 0;
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
Unit *add = Unit::GetUnit(*m_creature,SpellBinderGUID[i]);
if (add && add->isAlive())
AliveChannelers++;
}
+
// channelers == 0 remove banish aura
if (AliveChannelers == 0 && m_creature->HasAura(AURA_BANISH))
{
// removing banish aura
m_creature->RemoveAurasDueToSpell(AURA_BANISH);
+
// Leotheras is getting immune again
m_creature->ApplySpellImmune(AURA_BANISH, IMMUNITY_MECHANIC, MECHANIC_BANISH, true);
+
// changing model to bloodelf
m_creature->SetDisplayId(MODEL_NIGHTELF);
+
// and reseting equipment
m_creature->LoadEquipment(m_creature->GetEquipmentId());
+
if (pInstance && pInstance->GetData64(DATA_LEOTHERAS_EVENT_STARTER))
{
Unit *victim = NULL;
@@ -255,17 +293,20 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
// removing Leotheras banish immune to apply AURA_BANISH
m_creature->ApplySpellImmune(AURA_BANISH, IMMUNITY_MECHANIC, MECHANIC_BANISH, false);
DoCast(m_creature, AURA_BANISH);
+
// changing model
m_creature->SetDisplayId(MODEL_DEMON);
+
// and removing weapons
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID , 0);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID+1, 0);
}
}
+
//Despawn all Inner Demon summoned
void DespawnDemon()
{
- for (uint8 i=0; i<5; ++i)
+ for(uint8 i=0; i<5; ++i)
{
if (InnderDemon[i])
{
@@ -278,11 +319,13 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
InnderDemon[i] = 0;
}
}
+
InnderDemon_Count = 0;
}
+
void CastConsumingMadness() //remove this once SPELL_INSIDIOUS_WHISPER is supported by core
{
- for (uint8 i=0; i<5; ++i)
+ for(uint8 i=0; i<5; ++i)
{
if (InnderDemon[i] > 0)
{
@@ -299,10 +342,12 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit *victim)
{
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
+
if (DemonForm)
{
DoScriptText(RAND(SAY_DEMON_SLAY1,SAY_DEMON_SLAY2,SAY_DEMON_SLAY3), m_creature);
@@ -312,9 +357,11 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
DoScriptText(RAND(SAY_NIGHTELF_SLAY1,SAY_NIGHTELF_SLAY2,SAY_NIGHTELF_SLAY3), m_creature);
}
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
//despawn copy
if (Demon)
{
@@ -324,12 +371,15 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
if (pInstance)
pInstance->SetData(DATA_LEOTHERASTHEBLINDEVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
if (m_creature->HasAura(AURA_BANISH))
return;
+
m_creature->LoadEquipment(m_creature->GetEquipmentId());
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
@@ -354,6 +404,7 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
}
Whirlwind_Timer = 2000;
}else Whirlwind_Timer -= diff;
+
// reseting after changing forms and after ending whirlwind
if (NeedThreatReset && !m_creature->HasAura(SPELL_WHIRLWIND))
{
@@ -362,11 +413,13 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
InnerDemons_Timer = 30000;
else
Whirlwind_Timer = 15000;
+
NeedThreatReset = false;
DoResetThreat();
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim());
}
+
//Enrage_Timer (10 min)
if (Berserk_Timer < diff && !EnrageUsed)
{
@@ -374,6 +427,7 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
DoCast(m_creature, SPELL_BERSERK);
EnrageUsed = true;
}else Berserk_Timer -= diff;
+
if (!DemonForm)
{
//Whirldind Timer
@@ -388,6 +442,7 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
}else Whirlwind_Timer -= diff;
}
//Switch_Timer
+
if (!IsFinalForm)
if (SwitchToDemon_Timer < diff)
{
@@ -426,14 +481,14 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
{
std::list<HostilReference *>& ThreatList = m_creature->getThreatManager().getThreatList();
std::vector<Unit *> TargetList;
- for (std::list<HostilReference *>::iterator itr = ThreatList.begin(); itr != ThreatList.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = ThreatList.begin(); itr != ThreatList.end(); ++itr)
{
Unit *tempTarget = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
if (tempTarget && tempTarget->GetTypeId() == TYPEID_PLAYER && tempTarget->GetGUID() != m_creature->getVictim()->GetGUID() && TargetList.size()<5)
TargetList.push_back(tempTarget);
}
SpellEntry *spell = GET_SPELL(SPELL_INSIDIOUS_WHISPER);
- for (std::vector<Unit *>::iterator itr = TargetList.begin(); itr != TargetList.end(); ++itr)
+ for(std::vector<Unit *>::iterator itr = TargetList.begin(); itr != TargetList.end(); ++itr)
{
if ((*itr) && (*itr)->isAlive())
{
@@ -442,6 +497,7 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
{
demon->AI()->AttackStart((*itr));
CAST_AI(mob_inner_demonAI, demon->AI())->victimGUID = (*itr)->GetGUID();
+
uint8 eff_mask=0;
for (int i=0; i<3; ++i)
{
@@ -451,29 +507,37 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
}
(*itr)->AddAura(new Aura(spell, eff_mask, *itr, *itr, *itr));
if (InnderDemon_Count > 4) InnderDemon_Count = 0;
+
//Safe storing of creatures
InnderDemon[InnderDemon_Count] = demon->GetGUID();
+
//Update demon count
InnderDemon_Count++;
}
}
}
DoScriptText(SAY_INNER_DEMONS, m_creature);
+
InnerDemons_Timer = 999999;
}else InnerDemons_Timer -= diff;
+
//Switch_Timer
if (SwitchToHuman_Timer < diff)
{
//switch to nightelf form
m_creature->SetDisplayId(MODEL_NIGHTELF);
m_creature->LoadEquipment(m_creature->GetEquipmentId());
+
CastConsumingMadness();
DespawnDemon();
+
DemonForm = false;
NeedThreatReset = true;
+
SwitchToHuman_Timer = 60000;
}else SwitchToHuman_Timer -= diff;
}
+
if (!IsFinalForm && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 15)
{
//at this point he divides himself in two parts
@@ -490,42 +554,52 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blindAI : public ScriptedAI
//set nightelf final form
IsFinalForm = true;
DemonForm = false;
+
DoScriptText(SAY_FINAL_FORM, m_creature);
m_creature->SetDisplayId(MODEL_NIGHTELF);
m_creature->LoadEquipment(m_creature->GetEquipmentId());
}
}
};
+
//Leotheras the Blind Demon Form AI
struct TRINITY_DLL_DECL boss_leotheras_the_blind_demonformAI : public ScriptedAI
{
boss_leotheras_the_blind_demonformAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ChaosBlast_Timer;
bool DealDamage;
+
void Reset()
{
ChaosBlast_Timer = 1000;
DealDamage = true;
}
+
void StartEvent()
{
DoScriptText(SAY_FREE, m_creature);
}
+
void KilledUnit(Unit *victim)
{
if (victim->GetTypeId() != TYPEID_PLAYER)
return;
+
DoScriptText(RAND(SAY_DEMON_SLAY1,SAY_DEMON_SLAY2,SAY_DEMON_SLAY3), m_creature);
}
+
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 EnterCombat(Unit *who)
{
StartEvent();
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
@@ -534,6 +608,7 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blind_demonformAI : public ScriptedAI
//ChaosBlast_Timer
if (m_creature->IsWithinDist(m_creature->getVictim(), 30))
m_creature->StopMoving();
+
if (ChaosBlast_Timer < diff)
{
// will cast only when in range od spell
@@ -545,6 +620,7 @@ struct TRINITY_DLL_DECL boss_leotheras_the_blind_demonformAI : public ScriptedAI
ChaosBlast_Timer = 3000;
}
}else ChaosBlast_Timer -= diff;
+
//Do NOT deal any melee damage to the target.
}
};
@@ -556,15 +632,21 @@ struct TRINITY_DLL_DECL mob_greyheart_spellbinderAI : public ScriptedAI
leotherasGUID = 0;
AddedBanish = false;
}
+
ScriptedInstance *pInstance;
+
uint64 leotherasGUID;
+
uint32 Mindblast_Timer;
uint32 Earthshock_Timer;
+
bool AddedBanish;
+
void Reset()
{
Mindblast_Timer = 3000 + rand()%5000;
Earthshock_Timer = 5000 + rand()%5000;
+
if (pInstance)
{
pInstance->SetData64(DATA_LEOTHERAS_EVENT_STARTER, 0);
@@ -573,17 +655,20 @@ struct TRINITY_DLL_DECL mob_greyheart_spellbinderAI : public ScriptedAI
CAST_AI(boss_leotheras_the_blindAI, leotheras->AI())->CheckChannelers(false);
}
}
+
void EnterCombat(Unit *who)
{
m_creature->InterruptNonMeleeSpells(false);
if (pInstance)
pInstance->SetData64(DATA_LEOTHERAS_EVENT_STARTER, who->GetGUID());
}
+
void JustRespawned()
{
AddedBanish = false;
Reset();
}
+
void CastChanneling()
{
if (!m_creature->isInCombat() && !m_creature->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
@@ -596,12 +681,14 @@ struct TRINITY_DLL_DECL mob_greyheart_spellbinderAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (pInstance)
{
if (!leotherasGUID)
leotherasGUID = pInstance->GetData64(DATA_LEOTHERAS);
+
if (!m_creature->isInCombat() && pInstance->GetData64(DATA_LEOTHERAS_EVENT_STARTER))
{
Unit *victim = NULL;
@@ -610,35 +697,42 @@ struct TRINITY_DLL_DECL mob_greyheart_spellbinderAI : public ScriptedAI
AttackStart(victim);
}
}
+
if (!UpdateVictim())
{
CastChanneling();
return;
}
+
if (pInstance && !pInstance->GetData64(DATA_LEOTHERAS_EVENT_STARTER))
{
EnterEvadeMode();
return;
}
+
if (Mindblast_Timer < diff)
{
Unit* target = NULL;
target = SelectUnit(SELECT_TARGET_RANDOM,0);
+
if (target)DoCast(target, SPELL_MINDBLAST);
+
Mindblast_Timer = 10000 + rand()%5000;
}else Mindblast_Timer -= diff;
+
if (Earthshock_Timer < diff)
{
Map* pMap = m_creature->GetMap();
Map::PlayerList const &PlayerList = pMap->GetPlayers();
- for (Map::PlayerList::const_iterator itr = PlayerList.begin(); itr != PlayerList.end(); ++itr)
+ for(Map::PlayerList::const_iterator itr = PlayerList.begin();itr != PlayerList.end(); ++itr)
{
if (Player* i_pl = itr->getSource())
{
bool isCasting = false;
- for (uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
+ for(uint8 i = 0; i < CURRENT_MAX_SPELL; ++i)
if (i_pl->GetCurrentSpell(i))
isCasting = true;
+
if (isCasting)
{
DoCast(i_pl, SPELL_EARTHSHOCK);
@@ -650,20 +744,24 @@ struct TRINITY_DLL_DECL mob_greyheart_spellbinderAI : public ScriptedAI
}else Earthshock_Timer -= diff;
DoMeleeAttackIfReady();
}
+
void JustDied(Unit *killer) {}
};
CreatureAI* GetAI_boss_leotheras_the_blind(Creature* pCreature)
{
return new boss_leotheras_the_blindAI (pCreature);
}
+
CreatureAI* GetAI_boss_leotheras_the_blind_demonform(Creature* pCreature)
{
return new boss_leotheras_the_blind_demonformAI (pCreature);
}
+
CreatureAI* GetAI_mob_greyheart_spellbinder(Creature* pCreature)
{
return new mob_greyheart_spellbinderAI (pCreature);
}
+
CreatureAI* GetAI_mob_inner_demon(Creature* pCreature)
{
return new mob_inner_demonAI (pCreature);
@@ -671,18 +769,22 @@ CreatureAI* GetAI_mob_inner_demon(Creature* pCreature)
void AddSC_boss_leotheras_the_blind()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_leotheras_the_blind";
newscript->GetAI = &GetAI_boss_leotheras_the_blind;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_leotheras_the_blind_demonform";
newscript->GetAI = &GetAI_boss_leotheras_the_blind_demonform;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_greyheart_spellbinder";
newscript->GetAI = &GetAI_mob_greyheart_spellbinder;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_inner_demon";
newscript->GetAI = &GetAI_mob_inner_demon;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp
index a9fbf6a2506..1146cf96b24 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_lurker_below.cpp
@@ -13,16 +13,19 @@
* along 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_lurker_below
SD%Complete: 80
SDComment: Coilfang Frenzy, find out how could we fishing in the strangepool
SDCategory: The Lurker Below
EndScriptData */
+
#include "precompiled.h"
#include "def_serpent_shrine.h"
#include "simple_ai.h"
#include "Spell.h"
+
#define SPELL_SPOUT 37433
#define SPELL_SPOUT_ANIM 42835
#define SPELL_SPOUT_BREATH 37431
@@ -32,16 +35,22 @@ EndScriptData */
#define SPELL_WATERBOLT 37138
#define SPELL_SUBMERGE 37550
#define SPELL_EMERGE 20568
+
#define EMOTE_SPOUT "The Lurker Below takes a deep breath."
+
#define SPOUT_DIST 100
+
#define MOB_COILFANG_GUARDIAN 21873
#define MOB_COILFANG_AMBUSHER 21865
+
//Ambusher spells
#define SPELL_SPREAD_SHOT 37790
#define SPELL_SHOOT 37770
+
//Guardian spells
#define SPELL_ARCINGSMASH 38761 // Wrong SpellId. Can't find the right one.
#define SPELL_HAMSTRING 26211
+
float AddPos[9][3] =
{
{2.8553810, -459.823914, -19.182686}, //MOVE_AMBUSHER_1 X, Y, Z
@@ -54,6 +63,7 @@ float AddPos[9][3] =
{14.388216, -423.468018, -19.625271}, //MOVE_GUARDIAN_2 X, Y, Z
{42.471519, -445.115295, -19.769423} //MOVE_GUARDIAN_3 X, Y, Z
};
+
struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
{
boss_the_lurker_belowAI(Creature *c) : Scripted_NoMovementAI(c), Summons(m_creature)
@@ -67,8 +77,10 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
TempSpell->Effect[2] = 0;
}
}
+
ScriptedInstance* pInstance;
SummonList Summons;
+
bool Spawned;
bool Submerged;
bool InRange;
@@ -83,7 +95,8 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
uint32 CheckTimer;
uint32 WaitTimer;
uint32 WaitTimer2;
- bool CheckCanStart() //check if players fished
+
+ bool CheckCanStart()//check if players fished
{
if(pInstance && pInstance->GetData(DATA_STRANGE_POOL) == NOT_STARTED)
return false;
@@ -102,11 +115,14 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
CheckTimer = 15000;//give time to get in range when fight starts
WaitTimer = 60000;//never reached
WaitTimer2 = 60000;//never reached
+
Submerged = true;//will be false at combat start
Spawned = false;
InRange = false;
CanStartEvent = false;
+
Summons.DespawnAll();
+
if (pInstance)
{
pInstance->SetData(DATA_THELURKERBELOWEVENT, NOT_STARTED);
@@ -117,21 +133,25 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(DATA_THELURKERBELOWEVENT, DONE);
+
Summons.DespawnAll();
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_THELURKERBELOWEVENT, IN_PROGRESS);
Scripted_NoMovementAI::EnterCombat(who);
}
+
void MoveInLineOfSight(Unit *who)
{
- if(!CanStartEvent) //boss is invisible, don't attack
+ if(!CanStartEvent)//boss is invisible, don't attack
return;
if (!m_creature->getVictim() && who->isTargetableForAttack() && (m_creature->IsHostileTo(who)))
{
@@ -142,14 +162,16 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
}
}
}
+
void MovementInform(uint32 type, uint32 id)
{
if(type == ROTATE_MOTION_TYPE)
me->SetReactState(REACT_AGGRESSIVE);
}
+
void UpdateAI(const uint32 diff)
{
- if(!CanStartEvent) //boss is invisible, don't attack
+ if(!CanStartEvent)//boss is invisible, don't attack
{
if(CheckCanStart())
{
@@ -159,7 +181,7 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
Submerged = false;
WaitTimer2 = 500;
}
- if(!Submerged && WaitTimer2 < diff) //wait 500ms before emerge anim
+ if(!Submerged && WaitTimer2 < diff)//wait 500ms before emerge anim
{
m_creature->RemoveAllAuras();
m_creature->RemoveFlag(UNIT_NPC_EMOTESTATE,EMOTE_STATE_SUBMERGED);
@@ -167,7 +189,8 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
WaitTimer2 = 60000;//never reached
WaitTimer = 3000;
}else WaitTimer2 -= diff;
- if(WaitTimer < diff) //wait 3secs for emerge anim, then attack
+
+ if(WaitTimer < diff)//wait 3secs for emerge anim, then attack
{
WaitTimer = 3000;
CanStartEvent=true;//fresh fished from pool
@@ -178,7 +201,9 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
return;
}
- if(m_creature->getThreatManager().getThreatList().empty()) //check if should evade
+
+
+ if(m_creature->getThreatManager().getThreatList().empty())//check if should evade
{
if(m_creature->isInCombat())
EnterEvadeMode();
@@ -193,6 +218,7 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
PhaseTimer = 60000;//60secs submerged
Submerged = true;
}else PhaseTimer-=diff;
+
if (SpoutTimer < diff)
{
m_creature->MonsterTextEmote(EMOTE_SPOUT,0,true);
@@ -203,13 +229,15 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
RotTimer = 20000;
return;
}else SpoutTimer -= diff;
+
//Whirl directly after a Spout and at random times
if (WhirlTimer < diff)
{
WhirlTimer = 18000;
DoCast(m_creature,SPELL_WHIRL);
}else WhirlTimer -= diff;
- if(CheckTimer < diff) //check if there are players in melee range
+
+ if(CheckTimer < diff)//check if there are players in melee range
{
InRange = false;
Map* pMap = m_creature->GetMap();
@@ -224,6 +252,7 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
}
CheckTimer = 2000;
}else CheckTimer -= diff;
+
if(RotTimer)
{
Map* pMap = m_creature->GetMap();
@@ -236,17 +265,20 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
DoCast(i->getSource(),SPELL_SPOUT,true);//only knock back palyers in arc, in 100yards, not in water
}
}
+
if(SpoutAnimTimer < diff)
{
DoCast(m_creature,SPELL_SPOUT_ANIM,true);
SpoutAnimTimer = 1000;
}else SpoutAnimTimer -= diff;
+
if(RotTimer < diff)
{
RotTimer = 0;
}else RotTimer -= diff;
return;
}
+
if (GeyserTimer < diff)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1);
@@ -256,7 +288,8 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
DoCast(target,SPELL_GEYSER,true);
GeyserTimer = rand()%5000 + 15000;
}else GeyserTimer -= diff;
- if(!InRange) //if on players in melee range cast Waterbolt
+
+ if(!InRange)//if on players in melee range cast Waterbolt
{
if (WaterboltTimer < diff)
{
@@ -268,9 +301,12 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
WaterboltTimer = 3000;
}else WaterboltTimer -= diff;
}
+
if (!UpdateCombatState())
return;
+
DoMeleeAttackIfReady();
+
}else//submerged
{
if (PhaseTimer < diff)
@@ -286,13 +322,15 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
PhaseTimer = 120000;
return;
}else PhaseTimer-=diff;
- if(m_creature->getThreatManager().getThreatList().empty()) //check if should evade
+
+ if(m_creature->getThreatManager().getThreatList().empty())//check if should evade
{
EnterEvadeMode();
return;
}
if (!m_creature->isInCombat())
DoZoneInCombat();
+
if (!Spawned)
{
m_creature->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
@@ -303,6 +341,7 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
if (i < 6)
Summoned = m_creature->SummonCreature(MOB_COILFANG_AMBUSHER,AddPos[i][0],AddPos[i][1],AddPos[i][2], 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
else Summoned = m_creature->SummonCreature(MOB_COILFANG_GUARDIAN,AddPos[i][0],AddPos[i][1],AddPos[i][2], 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
+
if (Summoned)
Summons.Summon(Summoned);
}
@@ -311,21 +350,26 @@ struct TRINITY_DLL_DECL boss_the_lurker_belowAI : public Scripted_NoMovementAI
}
}
};
+
CreatureAI* GetAI_mob_coilfang_guardian(Creature* pCreature)
{
SimpleAI* ai = new SimpleAI (pCreature);
+
ai->Spell[0].Enabled = true;
ai->Spell[0].Spell_Id = SPELL_ARCINGSMASH;
ai->Spell[0].Cooldown = 15000;
ai->Spell[0].First_Cast = 5000;
ai->Spell[0].Cast_Target_Type = CAST_HOSTILE_TARGET;
+
ai->Spell[1].Enabled = true;
ai->Spell[1].Spell_Id = SPELL_HAMSTRING;
ai->Spell[1].Cooldown = 10000;
ai->Spell[1].First_Cast = 2000;
ai->Spell[1].Cast_Target_Type = CAST_HOSTILE_TARGET;
+
return ai;
}
+
struct TRINITY_DLL_DECL mob_coilfang_ambusherAI : public Scripted_NoMovementAI
{
mob_coilfang_ambusherAI(Creature *c) : Scripted_NoMovementAI(c)
@@ -334,33 +378,43 @@ struct TRINITY_DLL_DECL mob_coilfang_ambusherAI : public Scripted_NoMovementAI
if (TempSpell)
TempSpell->Effect[0] = 2;//change spell effect from weapon % dmg to simple phisical dmg
}
+
uint32 MultiShotTimer;
uint32 ShootBowTimer;
+
void Reset()
{
MultiShotTimer = 10000;
ShootBowTimer = 4000;
+
}
+
void EnterCombat(Unit *who)
{
+
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim()) return;
+
if (who->isTargetableForAttack() && who->isInAccessiblePlaceFor(m_creature) && m_creature->IsHostileTo(who) && m_creature->IsWithinDistInMap(who, 45))
{
AttackStart(who);
}
}
+
void UpdateAI(const uint32 diff)
{
if (MultiShotTimer < diff)
{
if (m_creature->getVictim())
DoCast(m_creature->getVictim(), SPELL_SPREAD_SHOT, true);
+
MultiShotTimer = 10000+rand()%10000;
ShootBowTimer += 1500;//add global cooldown
}else MultiShotTimer -= diff;
+
if (ShootBowTimer < diff)
{
Unit* target = NULL;
@@ -373,14 +427,17 @@ struct TRINITY_DLL_DECL mob_coilfang_ambusherAI : public Scripted_NoMovementAI
}else ShootBowTimer -= diff;
}
};
+
CreatureAI* GetAI_mob_coilfang_ambusher(Creature* pCreature)
{
return new mob_coilfang_ambusherAI (pCreature);
}
+
CreatureAI* GetAI_boss_the_lurker_below(Creature* pCreature)
{
return new boss_the_lurker_belowAI (pCreature);
}
+
void AddSC_boss_the_lurker_below()
{
Script *newscript;
@@ -388,13 +445,16 @@ void AddSC_boss_the_lurker_below()
newscript->Name = "boss_the_lurker_below";
newscript->GetAI = &GetAI_boss_the_lurker_below;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_coilfang_guardian";
newscript->GetAI = &GetAI_mob_coilfang_guardian;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_coilfang_ambusher";
newscript->GetAI = &GetAI_mob_coilfang_ambusher;
newscript->RegisterSelf();
}
+
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp
index 5715ef78e6c..b624f5ba827 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/boss_morogrim_tidewalker.cpp
@@ -13,14 +13,17 @@
* along 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, remove hacks
SDCategory: Coilfang Resevoir, Serpent Shrine Cavern
EndScriptData */
+
#include "precompiled.h"
#include "def_serpent_shrine.h"
+
#define SAY_AGGRO -1548030
#define SAY_SUMMON1 -1548031
#define SAY_SUMMON2 -1548032
@@ -33,10 +36,12 @@ EndScriptData */
#define EMOTE_WATERY_GRAVE -1548039
#define EMOTE_EARTHQUAKE -1548040
#define EMOTE_WATERY_GLOBULES -1548041
+
#define SPELL_TIDAL_WAVE 37730
#define SPELL_WATERY_GRAVE 38049
#define SPELL_EARTHQUAKE 37764
#define SPELL_WATERY_GRAVE_EXPLOSION 37852
+
#define WATERY_GRAVE_X1 334.64
#define WATERY_GRAVE_Y1 -728.89
#define WATERY_GRAVE_Z1 -14.42
@@ -49,24 +54,29 @@ EndScriptData */
#define WATERY_GRAVE_X4 372.93
#define WATERY_GRAVE_Y4 -690.96
#define WATERY_GRAVE_Z4 -14.44
+
#define SPELL_WATERY_GRAVE_1 38023
#define SPELL_WATERY_GRAVE_2 38024
#define SPELL_WATERY_GRAVE_3 38025
#define SPELL_WATERY_GRAVE_4 37850
+
#define SPELL_SUMMON_WATER_GLOBULE_1 37854
#define SPELL_SUMMON_WATER_GLOBULE_2 37858
#define SPELL_SUMMON_WATER_GLOBULE_3 37860
#define SPELL_SUMMON_WATER_GLOBULE_4 37861
+
/*#define SPELL_SUMMON_MURLOC_A6 39813
#define SPELL_SUMMON_MURLOC_A7 39814
#define SPELL_SUMMON_MURLOC_A8 39815
#define SPELL_SUMMON_MURLOC_A9 39816
#define SPELL_SUMMON_MURLOC_A10 39817
+
#define SPELL_SUMMON_MURLOC_B6 39818
#define SPELL_SUMMON_MURLOC_B7 39819
#define SPELL_SUMMON_MURLOC_B8 39820
#define SPELL_SUMMON_MURLOC_B9 39821
#define SPELL_SUMMON_MURLOC_B10 39822*/
+
float MurlocCords[10][5] =
{
{21920, 424.36, -715.4, -7.14, 0.124},
@@ -80,9 +90,11 @@ float MurlocCords[10][5] =
{21920, 321.05, -718.73, -13.15, 0.124},
{21920, 321.05, -714.24, -13.15, 0.124}
};
+
//Creatures
#define WATER_GLOBULE 21913
#define TIDEWALKER_LURKER 21920
+
//Morogrim Tidewalker AI
struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
{
@@ -90,8 +102,11 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
Map::PlayerList const *PlayerList;
+
uint32 TidalWave_Timer;
uint32 WateryGrave_Timer;
uint32 Earthquake_Timer;
@@ -99,8 +114,10 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
uint32 globulespell[4];
int8 Playercount;
int8 counter;
+
bool Earthquake;
bool Phase2;
+
void Reset()
{
TidalWave_Timer = 10000;
@@ -111,33 +128,42 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
globulespell[1] = SPELL_SUMMON_WATER_GLOBULE_2;
globulespell[2] = SPELL_SUMMON_WATER_GLOBULE_3;
globulespell[3] = SPELL_SUMMON_WATER_GLOBULE_4;
+
Earthquake = false;
Phase2 = false;
+
if (pInstance)
pInstance->SetData(DATA_MOROGRIMTIDEWALKEREVENT, NOT_STARTED);
}
+
void StartEvent()
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_MOROGRIMTIDEWALKEREVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_MOROGRIMTIDEWALKEREVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
PlayerList = &m_creature->GetMap()->GetPlayers();
Playercount = PlayerList->getSize();
StartEvent();
}
+
void ApplyWateryGrave(Unit* pPlayer, uint8 i)
{
switch(i)
@@ -148,11 +174,13 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
case 3: pPlayer->CastSpell(pPlayer, SPELL_WATERY_GRAVE_4, true); break;
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Earthquake_Timer
if (Earthquake_Timer < diff)
{
@@ -165,7 +193,8 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
else
{
DoScriptText(RAND(SAY_SUMMON1,SAY_SUMMON2), m_creature);
- for (uint8 i = 0; i < 10; ++i)
+
+ for(uint8 i = 0; i < 10; ++i)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0);
Creature* Murloc = m_creature->SummonCreature(MurlocCords[i][0],MurlocCords[i][1],MurlocCords[i][2],MurlocCords[i][3],MurlocCords[i][4], TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 10000);
@@ -177,12 +206,14 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
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
@@ -193,7 +224,7 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
using std::set;
set<int>list;
set<int>::iterator itr;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
counter = 0;
do{target = SelectTarget(SELECT_TARGET_RANDOM, 1, 50, true); //target players only
@@ -206,10 +237,13 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
ApplyWateryGrave(target, i);
}
}
+
DoScriptText(RAND(SAY_SUMMON_BUBL1,SAY_SUMMON_BUBL2), m_creature);
+
DoScriptText(EMOTE_WATERY_GRAVE, m_creature);
WateryGrave_Timer = 30000;
}else WateryGrave_Timer -= diff;
+
//Start Phase2
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25)
Phase2 = true;
@@ -239,27 +273,36 @@ struct TRINITY_DLL_DECL boss_morogrim_tidewalkerAI : public ScriptedAI
WateryGlobules_Timer = 25000;
}else WateryGlobules_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
//Water Globule AI
#define SPELL_GLOBULE_EXPLOSION 37871
+
struct TRINITY_DLL_DECL mob_water_globuleAI : public ScriptedAI
{
mob_water_globuleAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who) {}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || m_creature->getVictim())
return;
+
if (who->isTargetableForAttack() && who->isInAccessiblePlaceFor(m_creature) && m_creature->IsHostileTo(who))
{
//no attack radius check - it attacks the first target that moves in his los
@@ -267,25 +310,30 @@ struct TRINITY_DLL_DECL mob_water_globuleAI : public ScriptedAI
AttackStart(who);
}
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Check_Timer < diff)
{
if (m_creature->IsWithinDistInMap(m_creature->getVictim(), 5))
{
DoCast(m_creature->getVictim(), SPELL_GLOBULE_EXPLOSION);
+
//despawn
m_creature->ForcedDespawn();
return;
}
Check_Timer = 500;
}else Check_Timer -= diff;
+
//do NOT deal any melee damage to the target.
}
};
+
CreatureAI* GetAI_boss_morogrim_tidewalker(Creature* pCreature)
{
return new boss_morogrim_tidewalkerAI (pCreature);
@@ -294,13 +342,16 @@ CreatureAI* GetAI_mob_water_globule(Creature* pCreature)
{
return new mob_water_globuleAI (pCreature);
}
+
void AddSC_boss_morogrim_tidewalker()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_morogrim_tidewalker";
newscript->GetAI = &GetAI_boss_morogrim_tidewalker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_water_globule";
newscript->GetAI = &GetAI_mob_water_globule;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h
index 64ea603c15b..adfa39dc7e0 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/def_serpent_shrine.h
@@ -1,6 +1,7 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_SERPENT_SHRINE_H
#define DEF_SERPENT_SHRINE_H
enum LurkerEventState
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp
index f3a89b1a185..bd917a6e5c7 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/serpent_shrine/instance_serpent_shrine.cpp
@@ -13,23 +13,29 @@
* along 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: 100
SDComment: Instance Data Scripts and functions to acquire mobs and set encounter status for use in various Serpent Shrine Scripts
SDCategory: Coilfang Resevoir, Serpent Shrine Cavern
EndScriptData */
+
#include "precompiled.h"
#include "def_serpent_shrine.h"
+
#define MAX_ENCOUNTER 6
#define SPELL_SCALDINGWATER 37284
#define MOB_COILFANG_FRENZY 21508
#define TRASHMOB_COILFANG_PRIESTESS 21220 //6*2
#define TRASHMOB_COILFANG_SHATTERER 21301 //6*3
+
#define MIN_KILLS 30
+
//NOTE: there are 6 platforms
//there should be 3 shatterers and 2 priestess on all platforms, total of 30 elites, else it won't work!
//delete all other elites not on platforms! these mobs should only be on those platforms nowhere else.
+
/* Serpentshrine cavern encounters:
0 - Hydross The Unstable event
1 - Leotheras The Blind Event
@@ -38,18 +44,24 @@ EndScriptData */
4 - Morogrim Tidewalker Event
5 - Lady Vashj Event
*/
+
bool GOHello_go_bridge_console(Player* pPlayer, GameObject* pGo)
{
ScriptedInstance* pInstance = pGo->GetInstanceData();
+
if (!pInstance)
return false;
+
if (pInstance)
pInstance->SetData(DATA_CONTROL_CONSOLE, DONE);
+
return true;
}
+
struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
{
instance_serpentshrine_cavern(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 LurkerBelow;
uint64 Sharkkis;
uint64 Tidalvess;
@@ -59,6 +71,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
uint64 KarathressEvent_Starter;
uint64 LeotherasTheBlind;
uint64 LeotherasEventStarter;
+
uint64 ControlConsole;
uint64 BridgePart[3];
uint32 StrangePool;
@@ -68,12 +81,15 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
uint32 FrenzySpawnTimer;
uint32 Water;
uint32 TrashCount;
+
bool ShieldGeneratorDeactivated[4];
uint32 m_auiEncounter[MAX_ENCOUNTER];
bool DoSpawnFrenzy;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
LurkerBelow = 0;
Sharkkis = 0;
Tidalvess = 0;
@@ -83,12 +99,14 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
KarathressEvent_Starter = 0;
LeotherasTheBlind = 0;
LeotherasEventStarter = 0;
+
ControlConsole = 0;
BridgePart[0] = 0;
BridgePart[1] = 0;
BridgePart[2] = 0;
StrangePool = 0;
Water = WATERSTATE_FRENZY;
+
ShieldGeneratorDeactivated[0] = false;
ShieldGeneratorDeactivated[1] = false;
ShieldGeneratorDeactivated[2] = false;
@@ -99,13 +117,17 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
FrenzySpawnTimer = 2000;
DoSpawnFrenzy = false;
TrashCount = 0;
+
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void Update (uint32 diff)
{
//Lurker Fishing event
@@ -124,6 +146,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
Water = WATERSTATE_SCALDING;
else
Water = WATERSTATE_FRENZY;
+
Map::PlayerList const &PlayerList = instance->GetPlayers();
if (PlayerList.isEmpty())
return;
@@ -135,6 +158,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
{
if(Water == WATERSTATE_SCALDING)
{
+
if(!pPlayer->HasAura(SPELL_SCALDINGWATER))
{
pPlayer->CastSpell(pPlayer, SPELL_SCALDINGWATER,true);
@@ -156,6 +180,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
if(!pPlayer->IsInWater())
pPlayer->RemoveAurasDueToSpell(SPELL_SCALDINGWATER);
}
+
}
WaterCheckTimer = 500;//remove stress from core
}else WaterCheckTimer -= diff;
@@ -165,6 +190,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
FrenzySpawnTimer = 2000;
}else FrenzySpawnTimer -= diff;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -173,14 +199,17 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
ControlConsole = pGo->GetGUID();
pGo->setActive(true);
break;
+
case 184203:
BridgePart[0] = pGo->GetGUID();
pGo->setActive(true);
break;
+
case 184204:
BridgePart[1] = pGo->GetGUID();
pGo->setActive(true);
break;
+
case 184205:
BridgePart[2] = pGo->GetGUID();
pGo->setActive(true);
@@ -194,6 +223,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -212,6 +242,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
break;*/
}
}
+
void SetData64(uint32 type, uint64 data)
{
if (type == DATA_KARATHRESSEVENT_STARTER)
@@ -219,6 +250,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
if (type == DATA_LEOTHERAS_EVENT_STARTER)
LeotherasEventStarter = data;
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -235,6 +267,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -282,9 +315,11 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
case DATA_SHIELDGENERATOR3:ShieldGeneratorDeactivated[2] = (data) ? true : false; break;
case DATA_SHIELDGENERATOR4:ShieldGeneratorDeactivated[3] = (data) ? true : false; break;
}
+
if (data == DONE)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -322,6 +357,7 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
}
return NULL;
}
+
void Load(const char* in)
{
if (!in)
@@ -333,23 +369,27 @@ struct TRINITY_DLL_DECL instance_serpentshrine_cavern : public ScriptedInstance
std::istringstream stream(in);
stream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3]
>> m_auiEncounter[4] >> m_auiEncounter[5] >> TrashCount;
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead.
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_serpentshrine_cavern(Map* pMap)
{
return new instance_serpentshrine_cavern(pMap);
}
+
void AddSC_instance_serpentshrine_cavern()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "instance_serpent_shrine";
newscript->GetInstanceData = &GetInstanceData_instance_serpentshrine_cavern;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_bridge_console";
newscript->pGOHello = &GOHello_go_bridge_console;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp
index eaba84be358..fc23f175931 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_hydromancer_thespia.cpp
@@ -13,18 +13,22 @@
* along 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: 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 -1545000
#define SAY_AGGRO_1 -1545001
#define SAY_AGGRO_2 -1545002
@@ -32,9 +36,11 @@ EndContentData */
#define SAY_SLAY_1 -1545004
#define SAY_SLAY_2 -1545005
#define SAY_DEAD -1545006
+
#define SPELL_LIGHTNING_CLOUD 25033
#define SPELL_LUNG_BURST 31481
#define SPELL_ENVELOPING_WINDS 31718
+
struct TRINITY_DLL_DECL boss_thespiaAI : public ScriptedAI
{
boss_thespiaAI(Creature *c) : ScriptedAI(c)
@@ -42,39 +48,50 @@ struct TRINITY_DLL_DECL boss_thespiaAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
bool HeroicMode;
+
uint32 LightningCloud_Timer;
uint32 LungBurst_Timer;
uint32 EnvelopingWinds_Timer;
+
void Reset()
{
LightningCloud_Timer = 15000;
LungBurst_Timer = 7000;
EnvelopingWinds_Timer = 9000;
+
if (pInstance)
pInstance->SetData(TYPE_HYDROMANCER_THESPIA, NOT_STARTED);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEAD, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_HYDROMANCER_THESPIA, DONE);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_HYDROMANCER_THESPIA, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//LightningCloud_Timer
if (LightningCloud_Timer < diff)
{
@@ -86,6 +103,7 @@ struct TRINITY_DLL_DECL boss_thespiaAI : public ScriptedAI
DoCast(target, SPELL_LIGHTNING_CLOUD);
LightningCloud_Timer = 15000+rand()%10000;
}else LightningCloud_Timer -=diff;
+
//LungBurst_Timer
if (LungBurst_Timer < diff)
{
@@ -93,6 +111,7 @@ struct TRINITY_DLL_DECL boss_thespiaAI : public ScriptedAI
DoCast(target, SPELL_LUNG_BURST);
LungBurst_Timer = 7000+rand()%5000;
}else LungBurst_Timer -=diff;
+
//EnvelopingWinds_Timer
if (EnvelopingWinds_Timer < diff)
{
@@ -104,49 +123,63 @@ struct TRINITY_DLL_DECL boss_thespiaAI : public ScriptedAI
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 TRINITY_DLL_DECL mob_coilfang_waterelementalAI : public ScriptedAI
{
mob_coilfang_waterelementalAI(Creature *c) : ScriptedAI(c) {}
+
bool HeroicMode;
uint32 WaterBoltVolley_Timer;
+
void Reset()
{
HeroicMode = m_creature->GetMap()->IsHeroic();
WaterBoltVolley_Timer = 3000+rand()%3000;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (WaterBoltVolley_Timer < diff)
{
DoCast(m_creature, HEROIC(SPELL_WATER_BOLT_VOLLEY, H_SPELL_WATER_BOLT_VOLLEY));
WaterBoltVolley_Timer = 7000+rand()%5000;
}else WaterBoltVolley_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_thespiaAI(Creature* pCreature)
{
return new boss_thespiaAI (pCreature);
}
+
CreatureAI* GetAI_mob_coilfang_waterelementalAI(Creature* pCreature)
{
return new mob_coilfang_waterelementalAI (pCreature);
}
+
void AddSC_boss_hydromancer_thespia()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_hydromancer_thespia";
newscript->GetAI = &GetAI_boss_thespiaAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_coilfang_waterelemental";
newscript->GetAI = &GetAI_mob_coilfang_waterelementalAI;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp
index 14dd8c90afb..d24279be1ec 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_mekgineer_steamrigger.cpp
@@ -13,18 +13,22 @@
* along 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 -1545007
#define SAY_AGGRO_1 -1545008
#define SAY_AGGRO_2 -1545009
@@ -34,11 +38,14 @@ EndContentData */
#define SAY_SLAY_2 -1545013
#define SAY_SLAY_3 -1545014
#define SAY_DEATH -1545015
+
#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 TRINITY_DLL_DECL boss_mekgineer_steamriggerAI : public ScriptedAI
{
boss_mekgineer_steamriggerAI(Creature *c) : ScriptedAI(c)
@@ -46,76 +53,95 @@ struct TRINITY_DLL_DECL boss_mekgineer_steamriggerAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
bool HeroicMode;
+
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)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, DONE);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2,SAY_SLAY_3), m_creature);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER, IN_PROGRESS);
}
+
//no known summon spells exist
void SummonMechanichs()
{
DoScriptText(SAY_MECHANICS, m_creature);
+
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 (!UpdateVictim())
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)
@@ -124,6 +150,7 @@ struct TRINITY_DLL_DECL boss_mekgineer_steamriggerAI : public ScriptedAI
Summon75 = true;
}
}
+
if (!Summon50)
{
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50)
@@ -132,6 +159,7 @@ struct TRINITY_DLL_DECL boss_mekgineer_steamriggerAI : public ScriptedAI
Summon50 = true;
}
}
+
if (!Summon25)
{
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 25)
@@ -140,18 +168,23 @@ struct TRINITY_DLL_DECL boss_mekgineer_steamriggerAI : public ScriptedAI
Summon25 = true;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_mekgineer_steamrigger(Creature* pCreature)
{
return new boss_mekgineer_steamriggerAI (pCreature);
}
+
#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 TRINITY_DLL_DECL mob_steamrigger_mechanicAI : public ScriptedAI
{
mob_steamrigger_mechanicAI(Creature *c) : ScriptedAI(c)
@@ -159,19 +192,25 @@ struct TRINITY_DLL_DECL mob_steamrigger_mechanicAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint32 Repair_Timer;
+
void Reset()
{
Repair_Timer = 2000;
}
+
void MoveInLineOfSight(Unit* who)
{
//react only if attacked
return;
}
+
void EnterCombat(Unit *who) { }
+
void UpdateAI(const uint32 diff)
{
if (Repair_Timer < diff)
@@ -187,6 +226,7 @@ struct TRINITY_DLL_DECL mob_steamrigger_mechanicAI : public ScriptedAI
{
//m_creature->GetMotionMaster()->MovementExpired();
//m_creature->GetMotionMaster()->MoveIdle();
+
DoCast(m_creature,HEROIC(SPELL_REPAIR, H_SPELL_REPAIR), true);
}
Repair_Timer = 5000;
@@ -199,22 +239,28 @@ struct TRINITY_DLL_DECL mob_steamrigger_mechanicAI : public ScriptedAI
}
}else Repair_Timer = 5000;
}else Repair_Timer -= diff;
+
if (!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_steamrigger_mechanic(Creature* pCreature)
{
return new mob_steamrigger_mechanicAI (pCreature);
}
+
void AddSC_boss_mekgineer_steamrigger()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_mekgineer_steamrigger";
newscript->GetAI = &GetAI_boss_mekgineer_steamrigger;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_steamrigger_mechanic";
newscript->GetAI = &GetAI_mob_steamrigger_mechanic;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp
index ce6302a04eb..f3a0be3bcfb 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/boss_warlord_kalithresh.cpp
@@ -13,14 +13,17 @@
* along 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 -1545016
#define SAY_REGEN -1545017
#define SAY_AGGRO1 -1545018
@@ -29,22 +32,28 @@ EndScriptData */
#define SAY_SLAY1 -1545021
#define SAY_SLAY2 -1545022
#define SAY_DEATH -1545023
+
#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 TRINITY_DLL_DECL mob_naga_distillerAI : public ScriptedAI
{
mob_naga_distillerAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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)
{
@@ -55,15 +64,20 @@ struct TRINITY_DLL_DECL mob_naga_distillerAI : public ScriptedAI
}
}
}
+
void EnterCombat(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)
@@ -71,36 +85,45 @@ struct TRINITY_DLL_DECL mob_naga_distillerAI : public ScriptedAI
pInstance->SetData(TYPE_DISTILLER,DONE);
}
};
+
struct TRINITY_DLL_DECL boss_warlord_kalithreshAI : public ScriptedAI
{
boss_warlord_kalithreshAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 Reflection_Timer;
uint32 Impale_Timer;
uint32 Rage_Timer;
bool CanRage;
+
void Reset()
{
Reflection_Timer = 10000;
Impale_Timer = 7000+rand()%7000;
Rage_Timer = 45000;
CanRage = false;
+
if (pInstance)
pInstance->SetData(TYPE_WARLORD_KALITHRESH, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_WARLORD_KALITHRESH, IN_PROGRESS);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
//hack :(
@@ -109,16 +132,20 @@ struct TRINITY_DLL_DECL boss_warlord_kalithreshAI : public ScriptedAI
if (pInstance->GetData(TYPE_DISTILLER) == DONE)
m_creature->RemoveAurasDueToSpell(SPELL_WARLORDS_RAGE_PROC);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(TYPE_WARLORD_KALITHRESH, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Rage_Timer < diff)
{
if (Creature* distiller = me->FindNearestCreature(17954, 100.0f))
@@ -129,37 +156,46 @@ struct TRINITY_DLL_DECL boss_warlord_kalithreshAI : public ScriptedAI
}
Rage_Timer = 3000+rand()%15000;
}else Rage_Timer -= diff;
+
//Reflection_Timer
if (Reflection_Timer < diff)
{
DoCast(m_creature, SPELL_SPELL_REFLECTION);
Reflection_Timer = 15000+rand()%10000;
}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* pCreature)
{
return new mob_naga_distillerAI (pCreature);
}
+
CreatureAI* GetAI_boss_warlord_kalithresh(Creature* pCreature)
{
return new boss_warlord_kalithreshAI (pCreature);
}
+
void AddSC_boss_warlord_kalithresh()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_naga_distiller";
newscript->GetAI = &GetAI_mob_naga_distiller;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_warlord_kalithresh";
newscript->GetAI = &GetAI_boss_warlord_kalithresh;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/def_steam_vault.h b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/def_steam_vault.h
index 8ee0f69acb8..4b407ac4816 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/def_steam_vault.h
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/def_steam_vault.h
@@ -1,12 +1,15 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef 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
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp
index 4af96fb71b6..1ee00f2ef81 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/steam_vault/instance_steam_vault.cpp
@@ -13,47 +13,63 @@
* along 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: 80
SDComment: Instance script and access panel GO
SDCategory: Coilfang Resevoir, The Steamvault
EndScriptData */
+
#include "precompiled.h"
#include "def_steam_vault.h"
+
#define MAX_ENCOUNTER 4
+
#define MAIN_CHAMBERS_DOOR 183049
#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
*/
+
bool GOHello_go_main_chambers_access_panel(Player* pPlayer, GameObject* pGo)
{
ScriptedInstance* pInstance = pGo->GetInstanceData();
+
if (!pInstance)
return false;
+
if (pGo->GetEntry() == ACCESS_PANEL_HYDRO && (pInstance->GetData(TYPE_HYDROMANCER_THESPIA) == DONE || pInstance->GetData(TYPE_HYDROMANCER_THESPIA) == SPECIAL))
pInstance->SetData(TYPE_HYDROMANCER_THESPIA,SPECIAL);
+
if (pGo->GetEntry() == ACCESS_PANEL_MEK && (pInstance->GetData(TYPE_MEKGINEER_STEAMRIGGER) == DONE || pInstance->GetData(TYPE_MEKGINEER_STEAMRIGGER) == SPECIAL))
pInstance->SetData(TYPE_MEKGINEER_STEAMRIGGER,SPECIAL);
+
return true;
}
+
struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
{
instance_steam_vault(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
uint64 ThespiaGUID;
uint64 MekgineerGUID;
uint64 KalithreshGUID;
+
uint64 MainChambersDoor;
uint64 AccessPanelHydro;
uint64 AccessPanelMek;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
ThespiaGUID = 0;
MekgineerGUID = 0;
KalithreshGUID = 0;
@@ -61,13 +77,16 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
AccessPanelHydro = 0;
AccessPanelMek = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -77,6 +96,7 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
case 17798: KalithreshGUID = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -86,6 +106,7 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
case ACCESS_PANEL_MEK: AccessPanelMek = pGo->GetGUID(); break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -94,8 +115,10 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
if (data == SPECIAL)
{
HandleGameObject(AccessPanelHydro, true);
+
if (GetData(TYPE_MEKGINEER_STEAMRIGGER) == SPECIAL)
HandleGameObject(MainChambersDoor, true);
+
debug_log("TSCR: Instance Steamvault: Access panel used.");
}
m_auiEncounter[0] = data;
@@ -104,8 +127,10 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
if (data == SPECIAL)
{
HandleGameObject(AccessPanelMek, true);
+
if (GetData(TYPE_HYDROMANCER_THESPIA) == SPECIAL)
HandleGameObject(MainChambersDoor, true);
+
debug_log("TSCR: Instance Steamvault: Access panel used.");
}
m_auiEncounter[1] = data;
@@ -117,9 +142,11 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
m_auiEncounter[3] = data;
break;
}
+
if (data == DONE || data == SPECIAL)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -135,6 +162,7 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -148,6 +176,7 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -162,6 +191,7 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
}
return NULL;
}
+
void Load(const char* in)
{
if (!in)
@@ -172,23 +202,27 @@ struct TRINITY_DLL_DECL instance_steam_vault : public ScriptedInstance
OUT_LOAD_INST_DATA(in);
std::istringstream stream(in);
stream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_steam_vault(Map* pMap)
{
return new instance_steam_vault(pMap);
}
+
void AddSC_instance_steam_vault()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_main_chambers_access_panel";
newscript->pGOHello = &GOHello_go_main_chambers_access_panel;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "instance_steam_vault";
newscript->GetInstanceData = &GetInstanceData_instance_steam_vault;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp
index fb5570f4fd9..c8337023b78 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_hungarfen.cpp
@@ -13,38 +13,47 @@
* along 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 TRINITY_DLL_DECL boss_hungarfenAI : public ScriptedAI
{
boss_hungarfenAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
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 EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 20)
{
if (!Root)
@@ -53,20 +62,24 @@ struct TRINITY_DLL_DECL boss_hungarfenAI : public ScriptedAI
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();
}
};
@@ -74,35 +87,46 @@ CreatureAI* GetAI_boss_hungarfen(Creature* pCreature)
{
return new boss_hungarfenAI (pCreature);
}
+
#define SPELL_SPORE_CLOUD 34168
#define SPELL_PUTRID_MUSHROOM 31690
#define SPELL_GROW 31698
+
struct TRINITY_DLL_DECL mob_underbog_mushroomAI : public ScriptedAI
{
mob_underbog_mushroomAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(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);
@@ -114,13 +138,16 @@ CreatureAI* GetAI_mob_underbog_mushroom(Creature* pCreature)
{
return new mob_underbog_mushroomAI (pCreature);
}
+
void AddSC_boss_hungarfen()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_hungarfen";
newscript->GetAI = &GetAI_boss_hungarfen;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_underbog_mushroom";
newscript->GetAI = &GetAI_mob_underbog_mushroom;
diff --git a/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp b/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp
index 44adb9b39bc..1b76860cf60 100644
--- a/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp
+++ b/src/bindings/scripts/scripts/outland/coilfang_resevoir/underbog/boss_the_black_stalker.cpp
@@ -13,13 +13,16 @@
* along 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_black_stalker
SD%Complete: 95
SDComment: Timers may be incorrect
SDCategory: Coilfang Resevoir, Underbog
EndScriptData */
+
#include "precompiled.h"
+
#define SPELL_LEVITATE 31704
#define SPELL_SUSPENSION 31719
#define SPELL_LEVITATION_PULSE 31701
@@ -27,13 +30,16 @@ EndScriptData */
#define SPELL_CHAIN_LIGHTNING 31717
#define SPELL_STATIC_CHARGE 31715
#define SPELL_SUMMON_SPORE_STRIDER 38755
+
#define ENTRY_SPORE_STRIDER 22299
+
struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
{
boss_the_black_stalkerAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
uint32 SporeStriders_Timer;
uint32 Levitate_Timer;
@@ -44,6 +50,7 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
bool InAir;
uint32 check_Timer;
std::list<uint64> Striders;
+
void Reset()
{
Levitate_Timer = 12000;
@@ -55,7 +62,9 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
LevitatedTarget_Timer = 0;
Striders.clear();
}
+
void EnterCombat(Unit *who) {}
+
void JustSummoned(Creature *summon)
{
if (summon && summon->GetEntry() == ENTRY_SPORE_STRIDER)
@@ -68,16 +77,19 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
summon->AI()->AttackStart(m_creature->getVictim());
}
}
+
void JustDied(Unit *who)
{
- for (std::list<uint64>::iterator i = Striders.begin(); i != Striders.end(); ++i)
+ for(std::list<uint64>::iterator i = Striders.begin(); i != Striders.end(); ++i)
if (Creature *strider = Unit::GetCreature(*m_creature, *i))
strider->DisappearAndDie();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
// Evade if too far
if (check_Timer < diff)
{
@@ -90,12 +102,14 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
}
check_Timer = 1000;
}else check_Timer -= diff;
+
// Spore Striders
if (HeroicMode && SporeStriders_Timer < diff)
{
DoCast(m_creature,SPELL_SUMMON_SPORE_STRIDER);
SporeStriders_Timer = 10000+rand()%5000;
}else SporeStriders_Timer -= diff;
+
// Levitate
if (LevitatedTarget)
{
@@ -135,6 +149,7 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
}
Levitate_Timer = 12000+rand()%3000;
}else Levitate_Timer -= diff;
+
// Chain Lightning
if (ChainLightning_Timer < diff)
{
@@ -142,6 +157,7 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
DoCast(target, SPELL_CHAIN_LIGHTNING);
ChainLightning_Timer = 7000;
}else ChainLightning_Timer -= diff;
+
// Static Charge
if (StaticCharge_Timer < diff)
{
@@ -149,16 +165,20 @@ struct TRINITY_DLL_DECL boss_the_black_stalkerAI : public ScriptedAI
DoCast(target, SPELL_STATIC_CHARGE);
StaticCharge_Timer = 10000;
}else StaticCharge_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_the_black_stalker(Creature* pCreature)
{
return new boss_the_black_stalkerAI (pCreature);
}
+
void AddSC_boss_the_black_stalker()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_the_black_stalker";
newscript->GetAI = &GetAI_boss_the_black_stalker;
diff --git a/src/bindings/scripts/scripts/outland/gruuls_lair/boss_gruul.cpp b/src/bindings/scripts/scripts/outland/gruuls_lair/boss_gruul.cpp
index 3a5a61a00af..97de7ab4149 100644
--- a/src/bindings/scripts/scripts/outland/gruuls_lair/boss_gruul.cpp
+++ b/src/bindings/scripts/scripts/outland/gruuls_lair/boss_gruul.cpp
@@ -13,14 +13,17 @@
* along 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: 60
SDComment: Ground Slam need further development (knock back effect and shatter effect must be added to mangos)
SDCategory: Gruul's Lair
EndScriptData */
+
#include "precompiled.h"
#include "def_gruuls_lair.h"
+
enum eEnums
{
SAY_AGGRO = -1565010,
@@ -32,32 +35,41 @@ enum eEnums
SAY_SLAY2 = -1565016,
SAY_SLAY3 = -1565017,
SAY_DEATH = -1565018,
+
EMOTE_GROW = -1565019,
+
SPELL_GROWTH = 36300,
SPELL_CAVE_IN = 36240,
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)
SPELL_REVERBERATION = 36297,
SPELL_SHATTER = 33654,
+
SPELL_SHATTER_EFFECT = 33671,
SPELL_HURTFUL_STRIKE = 33813,
SPELL_STONED = 33652, //Spell is self cast by target
+
SPELL_MAGNETIC_PULL = 28337,
SPELL_KNOCK_BACK = 24199, //Knockback spell until correct implementation is made
};
+
struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
{
boss_gruulAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 m_uiGrowth_Timer;
uint32 m_uiCaveIn_Timer;
uint32 m_uiCaveIn_StaticTimer;
uint32 m_uiGroundSlamTimer;
uint32 m_uiHurtfulStrike_Timer;
uint32 m_uiReverberation_Timer;
+
bool m_bPerformingGroundSlam;
+
void Reset()
{
m_uiGrowth_Timer= 30000;
@@ -67,28 +79,35 @@ struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
m_bPerformingGroundSlam= false;
m_uiHurtfulStrike_Timer= 8000;
m_uiReverberation_Timer= 60000+45000;
+
if (pInstance)
pInstance->SetData(DATA_GRUULEVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_GRUULEVENT, IN_PROGRESS);
}
+
void KilledUnit()
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
{
pInstance->SetData(DATA_GRUULEVENT, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_GRUULDOOR), true); // Open the encounter door
}
}
+
void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell)
{
//This to emulate effect1 (77) of SPELL_GROUND_SLAM, knock back to any direction
@@ -104,17 +123,21 @@ struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
}
}
}
+
//this part should be in mangos
if (pSpell->Id == SPELL_SHATTER)
{
//this spell must have custom handling in mangos, dealing damage based on distance
pTarget->CastSpell(pTarget, SPELL_SHATTER_EFFECT, true);
+
if (pTarget->HasAura(SPELL_STONED))
pTarget->RemoveAurasDueToSpell(SPELL_STONED);
+
//clear this, if we are still performing
if (m_bPerformingGroundSlam)
{
m_bPerformingGroundSlam = false;
+
//and correct movement, if not already
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != TARGETED_MOTION_TYPE)
{
@@ -124,11 +147,13 @@ struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
// Growth
// Gruul can cast this spell up to 30 times
if (m_uiGrowth_Timer < uiDiff)
@@ -139,14 +164,17 @@ struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
}
else
m_uiGrowth_Timer -= uiDiff;
+
if (m_bPerformingGroundSlam)
{
if (m_uiGroundSlamTimer < uiDiff)
{
m_uiGroundSlamTimer =120000;
m_uiHurtfulStrike_Timer= 8000;
+
if (m_uiReverberation_Timer < 10000) //Give a little time to the players to undo the damage from shatter
m_uiReverberation_Timer += 10000;
+
DoCast(m_creature, SPELL_SHATTER);
}
else
@@ -158,14 +186,17 @@ struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
if (m_uiHurtfulStrike_Timer < uiDiff)
{
Unit* target = SelectUnit(SELECT_TARGET_TOPAGGRO,1);
+
if (target && m_creature->IsWithinMeleeRange(m_creature->getVictim()))
DoCast(target,SPELL_HURTFUL_STRIKE);
else
DoCast(m_creature->getVictim(),SPELL_HURTFUL_STRIKE);
+
m_uiHurtfulStrike_Timer= 8000;
}
else
m_uiHurtfulStrike_Timer -= uiDiff;
+
// Reverberation
if (m_uiReverberation_Timer < uiDiff)
{
@@ -174,36 +205,45 @@ struct TRINITY_DLL_DECL boss_gruulAI : public ScriptedAI
}
else
m_uiReverberation_Timer -= uiDiff;
+
// Cave In
if (m_uiCaveIn_Timer < uiDiff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_CAVE_IN);
+
if (m_uiCaveIn_StaticTimer >= 4000)
m_uiCaveIn_StaticTimer -= 2000;
+
m_uiCaveIn_Timer = m_uiCaveIn_StaticTimer;
}
else
m_uiCaveIn_Timer -= uiDiff;
+
// Ground Slam, Gronn Lord's Grasp, Stoned, Shatter
if (m_uiGroundSlamTimer < uiDiff)
{
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
+
m_bPerformingGroundSlam= true;
m_uiGroundSlamTimer = 10000;
+
DoCast(m_creature, SPELL_GROUND_SLAM);
}
else
m_uiGroundSlamTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
}
};
+
CreatureAI* GetAI_boss_gruul(Creature* pCreature)
{
return new boss_gruulAI (pCreature);
}
+
void AddSC_boss_gruul()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp b/src/bindings/scripts/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp
index 148f7b1ff1d..849712ab336 100644
--- a/src/bindings/scripts/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp
+++ b/src/bindings/scripts/scripts/outland/gruuls_lair/boss_high_king_maulgar.cpp
@@ -13,14 +13,17 @@
* along 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: 90
SDComment: Correct timers, after whirlwind melee attack bug, prayer of healing
SDCategory: Gruul's Lair
EndScriptData */
+
#include "precompiled.h"
#include "def_gruuls_lair.h"
+
#define SAY_AGGRO -1565000
#define SAY_ENRAGE -1565001
#define SAY_OGRE_DEATH1 -1565002
@@ -31,6 +34,7 @@ EndScriptData */
#define SAY_SLAY2 -1565007
#define SAY_SLAY3 -1565008
#define SAY_DEATH -1565009
+
// High King Maulgar
#define SPELL_ARCING_SMASH 39144
#define SPELL_MIGHTY_BLOW 33230
@@ -39,70 +43,88 @@ EndScriptData */
#define SPELL_ROAR 16508
#define SPELL_FLURRY 33232
#define SPELL_DUAL_WIELD 29651 //used in phase
+
// Olm the Summoner
#define SPELL_DARK_DECAY 33129
#define SPELL_DEATH_COIL 33130
#define SPELL_SUMMON_WFH 33131
+
//Kiggler the Craed
#define SPELL_GREATER_POLYMORPH 33173
#define SPELL_LIGHTNING_BOLT 36152
#define SPELL_ARCANE_SHOCK 33175
#define SPELL_ARCANE_EXPLOSION 33237
+
//Blindeye the Seer
#define SPELL_GREATER_PW_SHIELD 33147
#define SPELL_HEAL 33144
#define SPELL_PRAYER_OH 33152
+
//Krosh Firehand
#define SPELL_GREATER_FIREBALL 33051
#define SPELL_SPELLSHIELD 33054
#define SPELL_BLAST_WAVE 33061
+
bool CheckAllBossDied(ScriptedInstance* pInstance, Creature* m_creature)
{
if (!pInstance || !m_creature)
return false;
+
uint64 MaulgarGUID = 0;
uint64 KigglerGUID = 0;
uint64 BlindeyeGUID = 0;
uint64 OlmGUID = 0;
uint64 KroshGUID = 0;
+
Creature* Maulgar = NULL;
Creature* Kiggler = NULL;
Creature* Blindeye = NULL;
Creature* Olm = NULL;
Creature* Krosh = NULL;
+
MaulgarGUID = pInstance->GetData64(DATA_MAULGAR);
KigglerGUID = pInstance->GetData64(DATA_KIGGLERTHECRAZED);
BlindeyeGUID = pInstance->GetData64(DATA_BLINDEYETHESEER);
OlmGUID = pInstance->GetData64(DATA_OLMTHESUMMONER);
KroshGUID = pInstance->GetData64(DATA_KROSHFIREHAND);
+
Maulgar = (Unit::GetCreature((*m_creature), MaulgarGUID));
Kiggler = (Unit::GetCreature((*m_creature), KigglerGUID));
Blindeye = (Unit::GetCreature((*m_creature), BlindeyeGUID));
Olm = (Unit::GetCreature((*m_creature), OlmGUID));
Krosh = (Unit::GetCreature((*m_creature), KroshGUID));
+
if (!Maulgar || !Kiggler || !Blindeye || !Olm || !Krosh)
return false;
+
if (!Maulgar->isAlive() && !Kiggler->isAlive() && !Blindeye->isAlive() && !Olm->isAlive() && !Krosh->isAlive())
return true;
+
return false;
}
+
//High King Maulgar AI
struct TRINITY_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI
{
boss_high_king_maulgarAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
Council[i] = 0;
}
+
ScriptedInstance* pInstance;
+
uint32 ArcingSmash_Timer;
uint32 MightyBlow_Timer;
uint32 Whirlwind_Timer;
uint32 Charging_Timer;
uint32 Roar_Timer;
+
bool Phase2;
+
uint64 Council[4];
+
void Reset()
{
ArcingSmash_Timer = 10000;
@@ -110,10 +132,13 @@ struct TRINITY_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI
Whirlwind_Timer = 30000;
Charging_Timer = 0;
Roar_Timer = 0;
+
m_creature->CastSpell(m_creature, SPELL_DUAL_WIELD, false);
+
Phase2 = false;
+
Creature *pCreature = NULL;
- for (uint8 i = 0; i < 4; ++i)
+ for(uint8 i = 0; i < 4; ++i)
{
if (Council[i])
{
@@ -125,29 +150,36 @@ struct TRINITY_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI
}
}
}
+
//reset encounter
if (pInstance)
pInstance->SetData(DATA_MAULGAREVENT, NOT_STARTED);
}
+
void KilledUnit()
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (CheckAllBossDied(pInstance, m_creature))
pInstance->SetData(DATA_MAULGAREVENT, DONE);
}
+
void AddDeath()
{
DoScriptText(RAND(SAY_OGRE_DEATH1,SAY_OGRE_DEATH2,SAY_OGRE_DEATH3,SAY_OGRE_DEATH4), m_creature);
}
+
void EnterCombat(Unit *who)
{
StartEvent(who);
}
+
void GetCouncil()
{
if(pInstance)
@@ -159,64 +191,79 @@ struct TRINITY_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI
Council[3] = pInstance->GetData64(DATA_KROSHFIREHAND);
}
}
+
void StartEvent(Unit *who)
{
if (!pInstance)
return;
+
GetCouncil();
+
DoScriptText(SAY_AGGRO, m_creature);
+
pInstance->SetData64(DATA_MAULGAREVENT_TANK, who->GetGUID());
pInstance->SetData(DATA_MAULGAREVENT, IN_PROGRESS);
+
DoZoneInCombat();
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_MAULGAREVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK));
+
if (target)
{
AttackStart(target);
GetCouncil();
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_MAULGAREVENT))
{
EnterEvadeMode();
return;
}
+
//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;
DoScriptText(SAY_ENRAGE, m_creature);
+
m_creature->CastSpell(m_creature, SPELL_DUAL_WIELD, true);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID, 0);
m_creature->SetUInt32Value(UNIT_VIRTUAL_ITEM_SLOT_ID+1, 0);
}
+
if (Phase2)
{
//Charging_Timer
@@ -231,6 +278,7 @@ struct TRINITY_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI
}
Charging_Timer = 20000;
}else Charging_Timer -= diff;
+
//Intimidating Roar
if (Roar_Timer < diff)
{
@@ -238,9 +286,11 @@ struct TRINITY_DLL_DECL boss_high_king_maulgarAI : public ScriptedAI
Roar_Timer = 40000+(rand()%10000);
}else Roar_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
//Olm The Summoner AI
struct TRINITY_DLL_DECL boss_olm_the_summonerAI : public ScriptedAI
{
@@ -248,31 +298,39 @@ struct TRINITY_DLL_DECL boss_olm_the_summonerAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
uint32 DarkDecay_Timer;
uint32 Summon_Timer;
uint32 DeathCoil_Timer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
DarkDecay_Timer = 10000;
Summon_Timer = 15000;
DeathCoil_Timer = 20000;
+
//reset encounter
if (pInstance)
pInstance->SetData(DATA_MAULGAREVENT, NOT_STARTED);
}
+
void AttackStart(Unit* pWho)
{
if (!pWho)
return;
+
if (m_creature->Attack(pWho, true))
{
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
m_creature->GetMotionMaster()->MoveChase(pWho, 30.0f);
}
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -281,50 +339,60 @@ struct TRINITY_DLL_DECL boss_olm_the_summonerAI : public ScriptedAI
pInstance->SetData(DATA_MAULGAREVENT, IN_PROGRESS);
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
{
Creature *Maulgar = NULL;
Maulgar = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_MAULGAR)));
+
if (Maulgar)
CAST_AI(boss_high_king_maulgarAI, Maulgar->AI())->AddDeath();
+
if (CheckAllBossDied(pInstance, m_creature))
pInstance->SetData(DATA_MAULGAREVENT, DONE);
}
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_MAULGAREVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_MAULGAREVENT))
{
EnterEvadeMode();
return;
}
+
//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)
{
DoCast(m_creature, SPELL_SUMMON_WFH);
Summon_Timer = 30000;
}else Summon_Timer -= diff;
+
//DeathCoil Timer /need correct timer
if (DeathCoil_Timer < diff)
{
@@ -335,9 +403,11 @@ struct TRINITY_DLL_DECL boss_olm_the_summonerAI : public ScriptedAI
DeathCoil_Timer = 20000;
}else DeathCoil_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Kiggler The Crazed AI
struct TRINITY_DLL_DECL boss_kiggler_the_crazedAI : public ScriptedAI
{
@@ -345,21 +415,26 @@ struct TRINITY_DLL_DECL boss_kiggler_the_crazedAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
uint32 GreaterPolymorph_Timer;
uint32 LightningBolt_Timer;
uint32 ArcaneShock_Timer;
uint32 ArcaneExplosion_Timer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
GreaterPolymorph_Timer = 5000;
LightningBolt_Timer = 10000;
ArcaneShock_Timer = 20000;
ArcaneExplosion_Timer = 30000;
+
//reset encounter
if (pInstance)
pInstance->SetData(DATA_MAULGAREVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -368,67 +443,81 @@ struct TRINITY_DLL_DECL boss_kiggler_the_crazedAI : public ScriptedAI
pInstance->SetData(DATA_MAULGAREVENT, IN_PROGRESS);
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
{
Creature *Maulgar = NULL;
Maulgar = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_MAULGAR)));
+
if (Maulgar)
CAST_AI(boss_high_king_maulgarAI, Maulgar->AI())->AddDeath();
+
if (CheckAllBossDied(pInstance, m_creature))
pInstance->SetData(DATA_MAULGAREVENT, DONE);
}
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_MAULGAREVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_MAULGAREVENT))
{
EnterEvadeMode();
return;
}
+
//GreaterPolymorph_Timer
if (GreaterPolymorph_Timer < diff)
{
Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0);
if (target)
DoCast(target, SPELL_GREATER_POLYMORPH);
+
GreaterPolymorph_Timer = 15000 + rand()%5000;
}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 TRINITY_DLL_DECL boss_blindeye_the_seerAI : public ScriptedAI
{
@@ -436,19 +525,24 @@ struct TRINITY_DLL_DECL boss_blindeye_the_seerAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
uint32 GreaterPowerWordShield_Timer;
uint32 Heal_Timer;
uint32 PrayerofHealing_Timer;
+
ScriptedInstance* pInstance;
+
void Reset()
{
GreaterPowerWordShield_Timer = 5000;
Heal_Timer = 25000 + rand()%15000;
PrayerofHealing_Timer = 45000 + rand()%10000;
+
//reset encounter
if (pInstance)
pInstance->SetData(DATA_MAULGAREVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -457,59 +551,71 @@ struct TRINITY_DLL_DECL boss_blindeye_the_seerAI : public ScriptedAI
pInstance->SetData(DATA_MAULGAREVENT, IN_PROGRESS);
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
{
Creature *Maulgar = NULL;
Maulgar = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_MAULGAR)));
+
if (Maulgar)
CAST_AI(boss_high_king_maulgarAI, Maulgar->AI())->AddDeath();
+
if (CheckAllBossDied(pInstance, m_creature))
pInstance->SetData(DATA_MAULGAREVENT, DONE);
}
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_MAULGAREVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_MAULGAREVENT))
{
EnterEvadeMode();
return;
}
+
//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 = 15000 + rand()%25000;
}else Heal_Timer -= diff;
+
//PrayerofHealing_Timer
if (PrayerofHealing_Timer < diff)
{
DoCast(m_creature, SPELL_PRAYER_OH);
PrayerofHealing_Timer = 35000 + rand()%15000;
}else PrayerofHealing_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Krosh Firehand AI
struct TRINITY_DLL_DECL boss_krosh_firehandAI : public ScriptedAI
{
@@ -517,19 +623,24 @@ struct TRINITY_DLL_DECL boss_krosh_firehandAI : public ScriptedAI
{
pInstance = c->GetInstanceData();
}
+
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, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
@@ -538,44 +649,53 @@ struct TRINITY_DLL_DECL boss_krosh_firehandAI : public ScriptedAI
pInstance->SetData(DATA_MAULGAREVENT, IN_PROGRESS);
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
{
Creature *Maulgar = NULL;
Maulgar = (Unit::GetCreature((*m_creature), pInstance->GetData64(DATA_MAULGAR)));
+
if (Maulgar)
CAST_AI(boss_high_king_maulgarAI, Maulgar->AI())->AddDeath();
+
if (CheckAllBossDied(pInstance, m_creature))
pInstance->SetData(DATA_MAULGAREVENT, DONE);
}
}
+
void UpdateAI(const uint32 diff)
{
//Only if not incombat check if the event is started
if (!m_creature->isInCombat() && pInstance && pInstance->GetData(DATA_MAULGAREVENT))
{
Unit* target = Unit::GetUnit((*m_creature), pInstance->GetData64(DATA_MAULGAREVENT_TANK));
+
if (target)
{
AttackStart(target);
}
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//someone evaded!
if (pInstance && !pInstance->GetData(DATA_MAULGAREVENT))
{
EnterEvadeMode();
return;
}
+
//GreaterFireball_Timer
if (GreaterFireball_Timer < diff || m_creature->IsWithinDist(m_creature->getVictim(), 30))
{
DoCast(m_creature->getVictim(), SPELL_GREATER_FIREBALL);
GreaterFireball_Timer = 2000;
}else GreaterFireball_Timer -= diff;
+
//SpellShield_Timer
if (SpellShield_Timer < diff)
{
@@ -583,13 +703,14 @@ struct TRINITY_DLL_DECL boss_krosh_firehandAI : public ScriptedAI
DoCast(m_creature->getVictim(), SPELL_SPELLSHIELD);
SpellShield_Timer = 30000;
}else SpellShield_Timer -= diff;
+
//BlastWave_Timer
if (BlastWave_Timer < diff)
{
Unit *target;
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
std::vector<Unit *> target_list;
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
//15 yard radius minimum
@@ -599,51 +720,63 @@ struct TRINITY_DLL_DECL boss_krosh_firehandAI : public ScriptedAI
}
if (target_list.size())
target = *(target_list.begin()+rand()%target_list.size());
+
m_creature->InterruptNonMeleeSpells(false);
DoCast(target, SPELL_BLAST_WAVE);
BlastWave_Timer = 60000;
}else BlastWave_Timer -= diff;
}
};
+
CreatureAI* GetAI_boss_high_king_maulgar(Creature* pCreature)
{
return new boss_high_king_maulgarAI (pCreature);
}
+
CreatureAI* GetAI_boss_olm_the_summoner(Creature* pCreature)
{
return new boss_olm_the_summonerAI (pCreature);
}
+
CreatureAI *GetAI_boss_kiggler_the_crazed(Creature* pCreature)
{
return new boss_kiggler_the_crazedAI (pCreature);
}
+
CreatureAI *GetAI_boss_blindeye_the_seer(Creature* pCreature)
{
return new boss_blindeye_the_seerAI (pCreature);
}
+
CreatureAI *GetAI_boss_krosh_firehand(Creature* pCreature)
{
return new boss_krosh_firehandAI (pCreature);
}
+
void AddSC_boss_high_king_maulgar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_high_king_maulgar";
newscript->GetAI = &GetAI_boss_high_king_maulgar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_kiggler_the_crazed";
newscript->GetAI = &GetAI_boss_kiggler_the_crazed;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_blindeye_the_seer";
newscript->GetAI = &GetAI_boss_blindeye_the_seer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_olm_the_summoner";
newscript->GetAI = &GetAI_boss_olm_the_summoner;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_krosh_firehand";
newscript->GetAI = &GetAI_boss_krosh_firehand;
diff --git a/src/bindings/scripts/scripts/outland/gruuls_lair/def_gruuls_lair.h b/src/bindings/scripts/scripts/outland/gruuls_lair/def_gruuls_lair.h
index f2ffa525311..7003dcb1e26 100644
--- a/src/bindings/scripts/scripts/outland/gruuls_lair/def_gruuls_lair.h
+++ b/src/bindings/scripts/scripts/outland/gruuls_lair/def_gruuls_lair.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_GRUULS_LAIR_H
#define DEF_GRUULS_LAIR_H
+
#define DATA_BLINDEYETHESEER 1
#define DATA_GRUULEVENT 2
#define DATA_KIGGLERTHECRAZED 3
@@ -13,6 +15,7 @@
#define DATA_MAULGARDOOR 8
#define DATA_GRUULDOOR 9
#define DATA_MAULGAR 10
+
#define ERROR_INST_DATA "TSCR Error: Instance Data not set properly for Gruul's Lair instance (map 565). Encounters will be buggy."
#endif
diff --git a/src/bindings/scripts/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp b/src/bindings/scripts/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp
index b52887990db..0ac5cbcaf7a 100644
--- a/src/bindings/scripts/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp
+++ b/src/bindings/scripts/scripts/outland/gruuls_lair/instance_gruuls_lair.cpp
@@ -13,49 +13,63 @@
* along 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 MAX_ENCOUNTER 2
+
/* Gruuls Lair encounters:
1 - High King Maulgar event
2 - Gruul event
*/
+
struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
{
instance_gruuls_lair(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
uint64 MaulgarEvent_Tank;
uint64 KigglerTheCrazed;
uint64 BlindeyeTheSeer;
uint64 OlmTheSummoner;
uint64 KroshFirehand;
uint64 Maulgar;
+
uint64 MaulgarDoor;
uint64 GruulDoor;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
MaulgarEvent_Tank = 0;
KigglerTheCrazed = 0;
BlindeyeTheSeer = 0;
OlmTheSummoner = 0;
KroshFirehand = 0;
Maulgar = 0;
+
MaulgarDoor = 0;
GruulDoor = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -67,6 +81,7 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
case 18831: Maulgar = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -78,11 +93,13 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
case 184662: GruulDoor = pGo->GetGUID(); break;
}
}
+
void SetData64(uint32 type, uint64 data)
{
if (type == DATA_MAULGAREVENT_TANK)
MaulgarEvent_Tank = data;
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -98,6 +115,7 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -110,9 +128,11 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
else HandleGameObject(GruulDoor, true);
m_auiEncounter[1] = data; break;
}
+
if (data == DONE)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -122,6 +142,7 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -134,8 +155,10 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
OUT_SAVE_INST_DATA_COMPLETE;
return out;
}
+
return NULL;
}
+
void Load(const char* in)
{
if (!in)
@@ -143,19 +166,22 @@ struct TRINITY_DLL_DECL instance_gruuls_lair : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
std::istringstream stream(in);
stream >> m_auiEncounter[0] >> m_auiEncounter[1];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead.
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_gruuls_lair(Map* pMap)
{
return new instance_gruuls_lair(pMap);
}
+
void AddSC_instance_gruuls_lair()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp
index 3ee5c895546..d359c9c25f7 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_broggok.cpp
@@ -13,32 +13,41 @@
* along 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: 70
SDComment: pre-event not made
SDCategory: Hellfire Citadel, Blood Furnace
EndScriptData */
+
#include "precompiled.h"
#include "def_blood_furnace.h"
+
enum eEnums
{
SAY_AGGRO = -1542008,
+
SPELL_SLIME_SPRAY = 30913,
SPELL_POISON_CLOUD = 30916,
SPELL_POISON_BOLT = 30917,
+
SPELL_POISON = 30914
};
+
struct TRINITY_DLL_DECL boss_broggokAI : public ScriptedAI
{
boss_broggokAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 AcidSpray_Timer;
uint32 PoisonSpawn_Timer;
uint32 PoisonBolt_Timer;
+
void Reset()
{
AcidSpray_Timer = 10000;
@@ -50,6 +59,7 @@ struct TRINITY_DLL_DECL boss_broggokAI : public ScriptedAI
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR4), true);
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
@@ -59,6 +69,7 @@ struct TRINITY_DLL_DECL boss_broggokAI : public ScriptedAI
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR4), false);
}
}
+
void JustSummoned(Creature *summoned)
{
summoned->setFaction(16);
@@ -66,27 +77,33 @@ struct TRINITY_DLL_DECL boss_broggokAI : public ScriptedAI
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
summoned->CastSpell(summoned,SPELL_POISON,false,0,0,m_creature->GetGUID());
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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();
}
+
void JustDied(Unit* who)
{
if (pInstance)
@@ -96,11 +113,14 @@ struct TRINITY_DLL_DECL boss_broggokAI : public ScriptedAI
pInstance->SetData(TYPE_BROGGOK_EVENT, DONE);
}
}
+
};
+
CreatureAI* GetAI_boss_broggok(Creature* pCreature)
{
return new boss_broggokAI (pCreature);
}
+
void AddSC_boss_broggok()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp
index fb782f94322..d47c3eecceb 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_kelidan_the_breaker.cpp
@@ -13,18 +13,22 @@
* along 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: 100
SDComment:
SDCategory: Hellfire Citadel, Blood Furnace
EndScriptData */
+
/* ContentData
boss_kelidan_the_breaker
mob_shadowmoon_channeler
EndContentData */
+
#include "precompiled.h"
#include "def_blood_furnace.h"
+
enum eKelidan
{
SAY_WAKE = -1542000,
@@ -35,17 +39,23 @@ enum eKelidan
SAY_KILL_2 = -1542005,
SAY_NOVA = -1542006,
SAY_DIE = -1542007,
+
SPELL_CORRUPTION = 30938,
SPELL_EVOCATION = 30935,
+
SPELL_FIRE_NOVA = 33132,
H_SPELL_FIRE_NOVA = 37371,
+
SPELL_SHADOW_BOLT_VOLLEY = 28599,
H_SPELL_SHADOW_BOLT_VOLLEY = 40070,
+
SPELL_BURNING_NOVA = 30940,
SPELL_VORTEX = 37370,
+
ENTRY_KELIDAN = 17377,
ENTRY_CHANNELER = 17653
};
+
const float ShadowmoonChannelers[5][4]=
{
{302,-87,-24.4,0.157},
@@ -54,17 +64,20 @@ const float ShadowmoonChannelers[5][4]=
{344,-103.5,-24.5,2.356},
{316,-109,-24.6,1.257}
};
+
struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
{
boss_kelidan_the_breakerAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
- for (uint8 i=0; i<5; ++i)
+ for(uint8 i=0; i<5; ++i)
Channelers[i] = 0;
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint32 ShadowVolley_Timer;
uint32 BurningNova_Timer;
uint32 Firenova_Timer;
@@ -73,6 +86,7 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
bool Firenova;
bool addYell;
uint64 Channelers[5];
+
void Reset()
{
ShadowVolley_Timer = 1000;
@@ -85,6 +99,7 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
if (pInstance)
pInstance->SetData(TYPE_KELIDAN_THE_BREAKER_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_WAKE, m_creature);
@@ -94,12 +109,15 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
if (pInstance)
pInstance->SetData(TYPE_KELIDAN_THE_BREAKER_EVENT, IN_PROGRESS);
}
+
void KilledUnit(Unit* victim)
{
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void ChannelerEngaged(Unit* who)
{
if (who && !addYell)
@@ -107,30 +125,33 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
addYell = true;
DoScriptText(RAND(SAY_ADD_AGGRO_1,SAY_ADD_AGGRO_2,SAY_ADD_AGGRO_3), m_creature);
}
- for (uint8 i=0; i<5; ++i)
+ for(uint8 i=0; i<5; ++i)
{
Creature *channeler = Unit::GetCreature(*m_creature, Channelers[i]);
if (who && channeler && !channeler->isInCombat())
channeler->AI()->AttackStart(who);
}
}
+
void ChannelerDied(Unit* killer)
{
- for (uint8 i=0; i<5; ++i)
+ for(uint8 i=0; i<5; ++i)
{
Creature *channeler = Unit::GetCreature(*m_creature, Channelers[i]);
if (channeler && channeler->isAlive())
return;
}
+
if (killer)
m_creature->AI()->AttackStart(killer);
}
+
uint64 GetChanneled(Creature *channeler1)
{
SummonChannelers();
if (!channeler1) return NULL;
uint8 i;
- for (i=0; i<5; ++i)
+ for(i=0; i<5; ++i)
{
Creature *channeler = Unit::GetCreature(*m_creature, Channelers[i]);
if (channeler && channeler->GetGUID() == channeler1->GetGUID())
@@ -138,9 +159,10 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
}
return Channelers[(i+2)%5];
}
+
void SummonChannelers()
{
- for (uint8 i=0; i<5; ++i)
+ for(uint8 i=0; i<5; ++i)
{
Creature *channeler = Unit::GetCreature(*m_creature, Channelers[i]);
if (!channeler || channeler->isDead())
@@ -151,15 +173,19 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
Channelers[i] = 0;
}
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DIE, m_creature);
+
if (!pInstance)
return;
+
pInstance->SetData(TYPE_KELIDAN_THE_BREAKER_EVENT, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR1), true);
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR6), true);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -172,6 +198,7 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
}else check_Timer -= diff;
return;
}
+
if (Firenova)
{
if (Firenova_Timer < diff)
@@ -180,23 +207,29 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
Firenova = false;
ShadowVolley_Timer = 2000;
}else Firenova_Timer -=diff;
+
return;
}
+
if (ShadowVolley_Timer < diff)
{
DoCast(m_creature, HEROIC(SPELL_SHADOW_BOLT_VOLLEY, H_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);
+
DoScriptText(SAY_NOVA, m_creature);
+
if (SpellEntry *nova = GET_SPELL(SPELL_BURNING_NOVA))
{
uint8 eff_mask=0;
@@ -208,39 +241,51 @@ struct TRINITY_DLL_DECL boss_kelidan_the_breakerAI : public ScriptedAI
}
m_creature->AddAura(new Aura(nova, eff_mask, me, me, me));
}
+
if (HeroicMode)
DoTeleportAll(m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ(),m_creature->GetOrientation());
+
BurningNova_Timer = 20000+rand()%8000;
Firenova_Timer= 5000;
Firenova = true;
}else BurningNova_Timer -=diff;
+
DoMeleeAttackIfReady();
}
+
};
+
CreatureAI* GetAI_boss_kelidan_the_breaker(Creature* pCreature)
{
return new boss_kelidan_the_breakerAI (pCreature);
}
+
/*######
## mob_shadowmoon_channeler
######*/
+
enum eShadowmoon
{
SPELL_SHADOW_BOLT = 12739,
H_SPELL_SHADOW_BOLT = 15472,
+
SPELL_MARK_OF_SHADOW = 30937,
SPELL_CHANNELING = 39123
};
+
struct TRINITY_DLL_DECL mob_shadowmoon_channelerAI : public ScriptedAI
{
mob_shadowmoon_channelerAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
uint32 ShadowBolt_Timer;
uint32 MarkOfShadow_Timer;
uint32 check_Timer;
+
void Reset()
{
ShadowBolt_Timer = 1000+rand()%1000;
@@ -249,6 +294,7 @@ struct TRINITY_DLL_DECL mob_shadowmoon_channelerAI : public ScriptedAI
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(true);
}
+
void EnterCombat(Unit* who)
{
if (Creature *Kelidan = me->FindNearestCreature(ENTRY_KELIDAN, 100))
@@ -257,11 +303,13 @@ struct TRINITY_DLL_DECL mob_shadowmoon_channelerAI : public ScriptedAI
m_creature->InterruptNonMeleeSpells(true);
DoStartMovement(who);
}
+
void JustDied(Unit* Killer)
{
if (Creature *Kelidan = me->FindNearestCreature(ENTRY_KELIDAN, 100))
CAST_AI(boss_kelidan_the_breakerAI, Kelidan->AI())->ChannelerDied(Killer);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -279,31 +327,38 @@ struct TRINITY_DLL_DECL mob_shadowmoon_channelerAI : public ScriptedAI
}else check_Timer -= diff;
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(), HEROIC(SPELL_SHADOW_BOLT, H_SPELL_SHADOW_BOLT));
ShadowBolt_Timer = 5000+rand()%1000;
}else ShadowBolt_Timer -=diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_shadowmoon_channeler(Creature* pCreature)
{
return new mob_shadowmoon_channelerAI (pCreature);
}
+
void AddSC_boss_kelidan_the_breaker()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_kelidan_the_breaker";
newscript->GetAI = &GetAI_boss_kelidan_the_breaker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_shadowmoon_channeler";
newscript->GetAI = &GetAI_mob_shadowmoon_channeler;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp
index 44fa1ea95ff..9c19888c92c 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/boss_the_maker.cpp
@@ -13,14 +13,17 @@
* along 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"
#include "def_blood_furnace.h"
+
enum eEnums
{
SAY_AGGRO_1 = -1542009,
@@ -29,91 +32,116 @@ enum eEnums
SAY_KILL_1 = -1542012,
SAY_KILL_2 = -1542013,
SAY_DIE = -1542014,
+
SPELL_ACID_SPRAY = 38153, // heroic 38973 ??? 38153
SPELL_EXPLODING_BREAKER = 30925,
SPELL_KNOCKDOWN = 20276,
SPELL_DOMINATION = 25772 // ???
};
+
struct TRINITY_DLL_DECL boss_the_makerAI : public ScriptedAI
{
boss_the_makerAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 AcidSpray_Timer;
uint32 ExplodingBreaker_Timer;
uint32 Domination_Timer;
uint32 Knockdown_Timer;
+
void Reset()
{
AcidSpray_Timer = 15000;
ExplodingBreaker_Timer = 6000;
Domination_Timer = 120000;
Knockdown_Timer = 10000;
+
if (!pInstance)
return;
+
pInstance->SetData(TYPE_THE_MAKER_EVENT, NOT_STARTED);
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR2), true);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
+
if (!pInstance)
return;
+
pInstance->SetData(TYPE_THE_MAKER_EVENT, IN_PROGRESS);
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR2), false);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DIE, m_creature);
+
if (!pInstance)
return;
+
pInstance->SetData(TYPE_THE_MAKER_EVENT, DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR2), true);
pInstance->HandleGameObject(pInstance->GetData64(DATA_DOOR3), true);
+
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (AcidSpray_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ACID_SPRAY);
AcidSpray_Timer = 15000+rand()%8000;
}else AcidSpray_Timer -=diff;
+
if (ExplodingBreaker_Timer < diff)
{
if (Unit* 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* pCreature)
{
return new boss_the_makerAI (pCreature);
}
+
void AddSC_boss_the_maker()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/def_blood_furnace.h b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/def_blood_furnace.h
index 9774cb7a00c..b845c66823f 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/def_blood_furnace.h
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/def_blood_furnace.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_BLOOD_FURNACE_H
#define DEF_BLOOD_FURNACE_H
+
#define DATA_THE_MAKER 1
#define DATA_BROGGOK 2
#define DATA_KELIDAN_THE_MAKER 3
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp
index 4e980eec361..5a18ded0c08 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/blood_furnace/instance_blood_furnace.cpp
@@ -13,29 +13,36 @@
* along 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_Blood_Furnace
SD%Complete: 85
SDComment:
SDCategory: Hellfire Citadel, Blood Furnace
EndScriptData */
+
#include "precompiled.h"
#include "def_blood_furnace.h"
+
#define ENTRY_SEWER1 181823
#define ENTRY_SEWER2 181766
#define MAX_ENCOUNTER 3
+
struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
{
instance_blood_furnace(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 The_MakerGUID;
uint64 BroggokGUID;
uint64 Kelidan_The_BreakerGUID;
+
uint64 Door1GUID;
uint64 Door2GUID;
uint64 Door3GUID;
uint64 Door4GUID;
uint64 Door5GUID;
uint64 Door6GUID;
+
uint64 PrisonCell1GUID;
uint64 PrisonCell2GUID;
uint64 PrisonCell3GUID;
@@ -44,20 +51,25 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
uint64 PrisonCell6GUID;
uint64 PrisonCell7GUID;
uint64 PrisonCell8GUID;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
std::string str_data;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
The_MakerGUID = 0;
BroggokGUID = 0;
Kelidan_The_BreakerGUID = 0;
+
Door1GUID = 0;
Door2GUID = 0;
Door3GUID = 0;
Door4GUID = 0;
Door5GUID = 0;
Door6GUID = 0;
+
PrisonCell1GUID = 0;
PrisonCell2GUID = 0;
PrisonCell3GUID = 0;
@@ -68,10 +80,12 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
PrisonCell8GUID = 0;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
if (!add)
return;
+
switch(pCreature->GetEntry())
{
case 17381: The_MakerGUID = pCreature->GetGUID(); break;
@@ -79,10 +93,12 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
case 17377: Kelidan_The_BreakerGUID = pCreature->GetGUID(); break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
if (!add)
return;
+
if (pGo->GetEntry() == 181766) //Final exit door
Door1GUID = pGo->GetGUID();
if (pGo->GetEntry() == 181811) //The Maker Front door
@@ -95,6 +111,7 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
Door5GUID = pGo->GetGUID();
if (pGo->GetEntry() == 181823) //Kelidan exit door
Door6GUID = pGo->GetGUID();
+
if (pGo->GetEntry() == 181813) //The Maker prison cell front right
PrisonCell1GUID = pGo->GetGUID();
if (pGo->GetEntry() == 181814) //The Maker prison cell back right
@@ -112,6 +129,7 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
if (pGo->GetEntry() == 181817) //Broggok prison cell back left
PrisonCell8GUID = pGo->GetGUID();
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -134,8 +152,10 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
case DATA_PRISON_CELL7: return PrisonCell7GUID;
case DATA_PRISON_CELL8: return PrisonCell8GUID;
}
+
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(data)
@@ -144,16 +164,21 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
case TYPE_BROGGOK_EVENT: m_auiEncounter[1] = data; break;
case TYPE_KELIDAN_THE_BREAKER_EVENT: m_auiEncounter[2] = data; break;
}
+
if (data == DONE)
{
OUT_SAVE_INST_DATA;
+
std::ostringstream saveStream;
saveStream << m_auiEncounter[0] << " " << m_auiEncounter[1] << " " << m_auiEncounter[2];
+
str_data = saveStream.str();
+
SaveToDB();
OUT_SAVE_INST_DATA_COMPLETE;
}
}
+
uint32 GetData(uint32 data)
{
switch(data)
@@ -162,12 +187,15 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
case TYPE_BROGGOK_EVENT: return m_auiEncounter[1];
case TYPE_KELIDAN_THE_BREAKER_EVENT: return m_auiEncounter[2];
}
+
return 0;
}
+
const char* Save()
{
return str_data.c_str();
}
+
void Load(const char* in)
{
if (!in)
@@ -175,20 +203,26 @@ struct TRINITY_DLL_DECL instance_blood_furnace : public ScriptedInstance
OUT_LOAD_INST_DATA_FAIL;
return;
}
+
OUT_LOAD_INST_DATA(in);
+
std::istringstream loadStream(in);
loadStream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS || m_auiEncounter[i] == FAIL)
m_auiEncounter[i] = NOT_STARTED;
+
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_blood_furnace(Map* pMap)
{
return new instance_blood_furnace(pMap);
}
+
void AddSC_instance_blood_furnace()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp
index f2ab659f93c..d1ec8b79b57 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_omor_the_unscarred.cpp
@@ -13,13 +13,16 @@
* along 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: 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 SAY_AGGRO_1 -1543009
#define SAY_AGGRO_2 -1543010
#define SAY_AGGRO_3 -1543011
@@ -28,6 +31,7 @@ EndScriptData */
#define SAY_KILL_1 -1543014
#define SAY_DIE -1543015
#define SAY_WIPE -1543016
+
#define SPELL_ORBITAL_STRIKE 30637
#define SPELL_SHADOW_WHIP 30638
#define SPELL_TREACHEROUS_AURA 30695
@@ -36,6 +40,7 @@ EndScriptData */
#define SPELL_SHADOW_BOLT 30686
#define H_SPELL_SHADOW_BOLT 39297
#define SPELL_SUMMON_FIENDISH_HOUND 30707
+
struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
{
boss_omor_the_unscarredAI(Creature *c) : ScriptedAI(c)
@@ -43,7 +48,9 @@ struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
SetCombatMovement(false);
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
uint32 OrbitalStrike_Timer;
uint32 ShadowWhip_Timer;
uint32 Aura_Timer;
@@ -53,9 +60,11 @@ struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
uint32 SummonedCount;
uint64 PlayerGUID;
bool CanPullBack;
+
void Reset()
{
DoScriptText(SAY_WIPE, m_creature);
+
OrbitalStrike_Timer = 25000;
ShadowWhip_Timer = 2000;
Aura_Timer = 10000;
@@ -66,31 +75,40 @@ struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
PlayerGUID = 0;
CanPullBack = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void KilledUnit(Unit* victim)
{
if (rand()%2)
return;
+
DoScriptText(SAY_KILL_1, m_creature);
}
+
void JustSummoned(Creature* summoned)
{
DoScriptText(SAY_SUMMON, m_creature);
+
if (Unit* random = SelectUnit(SELECT_TARGET_RANDOM,0))
summoned->AI()->AttackStart(random);
+
++SummonedCount;
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DIE, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//only two may be wrong, perhaps increase timer and spawn periodically instead.
if (SummonedCount < 2)
{
@@ -101,6 +119,7 @@ struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
Summon_Timer = 15000+rand()%15000;
}else Summon_Timer -= diff;
}
+
if (CanPullBack)
{
if (ShadowWhip_Timer < diff)
@@ -125,15 +144,18 @@ struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
if (m_creature->IsWithinMeleeRange(m_creature->getVictim()))
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)
@@ -142,35 +164,43 @@ struct TRINITY_DLL_DECL boss_omor_the_unscarredAI : public ScriptedAI
DemonicShield_Timer = 15000;
}else DemonicShield_Timer -= diff;
}
+
if (Aura_Timer < diff)
{
DoScriptText(SAY_CURSE, m_creature);
+
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
{
DoCast(target,HEROIC(SPELL_TREACHEROUS_AURA, H_SPELL_BANE_OF_TREACHERY));
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();
+
DoCast(target,HEROIC(SPELL_SHADOW_BOLT, H_SPELL_SHADOW_BOLT));
Shadowbolt_Timer = 4000+rand()%2500;
}
}else Shadowbolt_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_omor_the_unscarredAI(Creature* pCreature)
{
return new boss_omor_the_unscarredAI (pCreature);
}
+
void AddSC_boss_omor_the_unscarred()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_omor_the_unscarred";
newscript->GetAI = &GetAI_boss_omor_the_unscarredAI;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp
index fc793b83c8e..667c672c97d 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_vazruden_the_herald.cpp
@@ -13,13 +13,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/* ScriptData
Name: Boss_Vazruden_the_Herald
%Complete: 90
Comment:
Category: Hellfire Citadel, Hellfire Ramparts
EndScriptData */
+
#include "precompiled.h"
+
#define SPELL_FIREBALL (HeroicMode?36920:34653)
#define SPELL_CONE_OF_FIRE (HeroicMode?36921:30926)
#define SPELL_SUMMON_LIQUID_FIRE (HeroicMode?30928:23971)
@@ -27,12 +30,14 @@ EndScriptData */
#define SPELL_REVENGE (HeroicMode?40392:19130)
#define SPELL_KIDNEY_SHOT 30621
#define SPELL_FIRE_NOVA_VISUAL 19823
+
#define ENTRY_HELLFIRE_SENTRY 17517
#define ENTRY_VAZRUDEN_HERALD 17307
#define ENTRY_VAZRUDEN 17537
#define ENTRY_NAZAN 17536
#define ENTRY_LIQUID_FIRE 22515
#define ENTRY_REINFORCED_FEL_IRON_CHEST (HeroicMode?185169:185168)
+
#define SAY_INTRO -1543017
#define SAY_WIPE -1543018
#define SAY_AGGRO_1 -1543019
@@ -42,13 +47,16 @@ EndScriptData */
#define SAY_KILL_2 -1543023
#define SAY_DIE -1543024
#define EMOTE -1543025
+
#define PATH_ENTRY 2081
+
const float VazrudenMiddle[3] = {-1406.5, 1746.5, 81.2};
const float VazrudenRing[2][3] =
{
{-1430, 1705, 112},
{-1377, 1760, 112}
};
+
struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
{
boss_nazanAI(Creature *c) : ScriptedAI(c)
@@ -57,6 +65,7 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
VazrudenGUID = 0;
flight = true;
}
+
uint32 Fireball_Timer;
uint32 ConeOfFire_Timer;
uint32 BellowingRoar_Timer;
@@ -67,6 +76,7 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
uint64 VazrudenGUID;
bool HeroicMode;
SpellEntry *liquid_fire;
+
void Reset()
{
Fireball_Timer = 4000;
@@ -74,7 +84,9 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
Turn_Timer = 0;
UnsummonCheck = 5000;
}
+
void EnterCombat(Unit* who) {}
+
void JustSummoned(Creature *summoned)
{
if (summoned && summoned->GetEntry() == ENTRY_LIQUID_FIRE)
@@ -85,11 +97,13 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
summoned->CastSpell(summoned,SPELL_FIRE_NOVA_VISUAL,true);
}
}
+
void SpellHitTarget(Unit* target, const SpellEntry* entry)
{
if (target && entry->Id == SPELL_FIREBALL)
m_creature->SummonCreature(ENTRY_LIQUID_FIRE,target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(),target->GetOrientation(),TEMPSUMMON_TIMED_DESPAWN,30000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -100,12 +114,14 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
UnsummonCheck -= diff;
return;
}
+
if (Fireball_Timer < diff)
{
if (Unit *victim = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(victim, SPELL_FIREBALL,true);
Fireball_Timer = urand(4000,7000);
} else Fireball_Timer -= diff;
+
if (flight) // phase 1 - the flight
{
Creature *Vazruden = Unit::GetCreature(*m_creature,VazrudenGUID);
@@ -123,6 +139,7 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
DoScriptText(EMOTE, m_creature);
return;
}else Fly_Timer -= diff;
+
if (Turn_Timer < diff)
{
uint32 waypoint = (Fly_Timer/10000)%2;
@@ -139,45 +156,54 @@ struct TRINITY_DLL_DECL boss_nazanAI : public ScriptedAI
ConeOfFire_Timer = 12000;
Fireball_Timer = 4000;
} else ConeOfFire_Timer -= diff;
+
if (HeroicMode && BellowingRoar_Timer < diff)
{
DoCast(m_creature, SPELL_BELLOWING_ROAR);
BellowingRoar_Timer = 45000;
} else BellowingRoar_Timer -= diff;
+
DoMeleeAttackIfReady();
}
}
};
+
struct TRINITY_DLL_DECL boss_vazrudenAI : public ScriptedAI
{
boss_vazrudenAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 Revenge_Timer;
bool HeroicMode;
bool WipeSaid;
uint32 UnsummonCheck;
+
void Reset()
{
Revenge_Timer = 4000;
UnsummonCheck = 2000;
WipeSaid = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void KilledUnit(Unit* who)
{
if (who && who->GetEntry()!=ENTRY_VAZRUDEN)
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void JustDied(Unit* who)
{
if (who && who != m_creature)
DoScriptText(SAY_DIE, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -193,15 +219,18 @@ struct TRINITY_DLL_DECL boss_vazrudenAI : public ScriptedAI
} else UnsummonCheck -= diff;
return;
}
+
if (Revenge_Timer < diff)
{
if (Unit *victim = m_creature->getVictim())
DoCast(victim, SPELL_REVENGE);
Revenge_Timer = 5000;
} else Revenge_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
{
boss_vazruden_the_heraldAI(Creature *c) : ScriptedAI(c)
@@ -212,6 +241,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
VazrudenGUID = 0;
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
uint32 phase;
uint32 waypoint;
uint32 check;
@@ -220,6 +250,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
uint64 VazrudenGUID;
bool summoned;
bool HeroicMode;
+
void Reset()
{
phase = 0;
@@ -228,6 +259,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
UnsummonAdds();
m_creature->GetMotionMaster()->MovePath(PATH_ENTRY, true);
}
+
void UnsummonAdds()
{
if (summoned)
@@ -249,6 +281,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
m_creature->SetVisibility(VISIBILITY_ON);
}
}
+
void SummonAdds()
{
if (!summoned)
@@ -262,6 +295,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
m_creature->addUnitState(UNIT_STAT_ROOT);
}
}
+
void EnterCombat(Unit *who)
{
if (phase==0)
@@ -271,6 +305,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
DoScriptText(SAY_INTRO, m_creature);
}
}
+
void JustSummoned(Creature *summoned)
{
if (!summoned) return;
@@ -286,6 +321,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
else if (victim)
summoned->AI()->AttackStart(victim);
}
+
void SentryDownBy(Unit* killer)
{
if (sentryDown)
@@ -296,6 +332,7 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
else
sentryDown = true;
}
+
void UpdateAI(const uint32 diff)
{
switch(phase)
@@ -348,50 +385,63 @@ struct TRINITY_DLL_DECL boss_vazruden_the_heraldAI : public ScriptedAI
}
}
};
+
struct TRINITY_DLL_DECL mob_hellfire_sentryAI : public ScriptedAI
{
mob_hellfire_sentryAI(Creature *c) : ScriptedAI(c) {}
+
uint32 KidneyShot_Timer;
+
void Reset()
{
KidneyShot_Timer = urand(3000,7000);
}
+
void EnterCombat(Unit* who) {}
+
void JustDied(Unit* who)
{
if (Creature *herald = me->FindNearestCreature(ENTRY_VAZRUDEN_HERALD,150))
CAST_AI(boss_vazruden_the_heraldAI, herald->AI())->SentryDownBy(who);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (KidneyShot_Timer < diff)
{
if (Unit *victim = m_creature->getVictim())
DoCast(victim, SPELL_KIDNEY_SHOT);
KidneyShot_Timer = 20000;
} else KidneyShot_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_vazruden_the_herald(Creature* pCreature)
{
return new boss_vazruden_the_heraldAI (pCreature);
}
+
CreatureAI* GetAI_boss_vazruden(Creature* pCreature)
{
return new boss_vazrudenAI (pCreature);
}
+
CreatureAI* GetAI_boss_nazan(Creature* pCreature)
{
return new boss_nazanAI (pCreature);
}
+
CreatureAI* GetAI_mob_hellfire_sentry(Creature* pCreature)
{
return new mob_hellfire_sentryAI (pCreature);
}
+
void AddSC_boss_vazruden_the_herald()
{
Script *newscript;
@@ -399,14 +449,17 @@ void AddSC_boss_vazruden_the_herald()
newscript->Name = "boss_vazruden_the_herald";
newscript->GetAI = &GetAI_boss_vazruden_the_herald;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_vazruden";
newscript->GetAI = &GetAI_boss_vazruden;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_nazan";
newscript->GetAI = &GetAI_boss_nazan;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_hellfire_sentry";
newscript->GetAI = &GetAI_mob_hellfire_sentry;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp
index e746e3d6259..aa8fe3e20e9 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/boss_watchkeeper_gargolmar.cpp
@@ -13,13 +13,16 @@
* along 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: 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_TAUNT -1543000
#define SAY_HEAL -1543001
#define SAY_SURGE -1543002
@@ -29,40 +32,50 @@ EndScriptData */
#define SAY_KILL_1 -1543006
#define SAY_KILL_2 -1543007
#define SAY_DIE -1543008
+
#define SPELL_MORTAL_WOUND 30641
#define H_SPELL_MORTAL_WOUND 36814
#define SPELL_SURGE 34645
#define SPELL_RETALIATION 22857
+
struct TRINITY_DLL_DECL boss_watchkeeper_gargolmarAI : public ScriptedAI
{
boss_watchkeeper_gargolmarAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
uint32 Surge_Timer;
uint32 MortalWound_Timer;
uint32 Retaliation_Timer;
+
bool HasTaunted;
bool YelledForHeal;
+
void Reset()
{
Surge_Timer = 5000;
MortalWound_Timer = 4000;
Retaliation_Timer = 0;
+
HasTaunted = false;
YelledForHeal = false;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void MoveInLineOfSight(Unit* who)
{
if (!m_creature->getVictim() && who->isTargetableForAttack() && (m_creature->IsHostileTo(who)) && who->isInAccessiblePlaceFor(m_creature))
{
if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
return;
+
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who))
{
@@ -76,30 +89,38 @@ struct TRINITY_DLL_DECL boss_watchkeeper_gargolmarAI : public ScriptedAI
}
}
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DIE, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (MortalWound_Timer < diff)
{
DoCast(m_creature->getVictim(),HEROIC(SPELL_MORTAL_WOUND, H_SPELL_MORTAL_WOUND));
MortalWound_Timer = 5000+rand()%8000;
}else MortalWound_Timer -= diff;
+
if (Surge_Timer < diff)
{
DoScriptText(SAY_SURGE, m_creature);
+
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)
@@ -108,6 +129,7 @@ struct TRINITY_DLL_DECL boss_watchkeeper_gargolmarAI : public ScriptedAI
Retaliation_Timer = 30000;
}else Retaliation_Timer -= diff;
}
+
if (!YelledForHeal)
{
if ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 40)
@@ -116,13 +138,16 @@ struct TRINITY_DLL_DECL boss_watchkeeper_gargolmarAI : public ScriptedAI
YelledForHeal = true;
}
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_watchkeeper_gargolmarAI(Creature* pCreature)
{
return new boss_watchkeeper_gargolmarAI (pCreature);
}
+
void AddSC_boss_watchkeeper_gargolmar()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/def_hellfire_ramparts.h b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/def_hellfire_ramparts.h
index d6227237bca..3c453949baa 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/def_hellfire_ramparts.h
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/def_hellfire_ramparts.h
@@ -1,12 +1,16 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_RAMPARTS_H
#define DEF_RAMPARTS_H
+
#define MAX_ENCOUNTER 2
+
enum eEnums
{
TYPE_VAZRUDEN = 1,
TYPE_NAZAN = 2
};
+
#endif
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp
index 26e8c9c9195..5942f0142ef 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/hellfire_ramparts/instance_hellfire_ramparts.cpp
@@ -13,26 +13,33 @@
* along 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_Hellfire_Ramparts
SD%Complete: 50
SDComment:
SDCategory: Hellfire Ramparts
EndScriptData */
+
#include "precompiled.h"
#include "def_hellfire_ramparts.h"
+
struct TRINITY_DLL_DECL instance_ramparts : public ScriptedInstance
{
instance_ramparts(Map* pMap) : ScriptedInstance(pMap) {Initialize();}
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint64 m_uiChestNGUID;
uint64 m_uiChestHGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
m_uiChestNGUID = 0;
m_uiChestHGUID = 0;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -41,9 +48,11 @@ struct TRINITY_DLL_DECL instance_ramparts : public ScriptedInstance
case 185169: m_uiChestHGUID = pGo->GetGUID(); break;
}
}
+
void SetData(uint32 uiType, uint32 uiData)
{
debug_log("TSCR: Instance Ramparts: SetData received for type %u with data %u",uiType,uiData);
+
switch(uiType)
{
case TYPE_VAZRUDEN:
@@ -59,10 +68,12 @@ struct TRINITY_DLL_DECL instance_ramparts : public ScriptedInstance
}
}
};
+
InstanceData* GetInstanceData_instance_ramparts(Map* pMap)
{
return new instance_ramparts(pMap);
}
+
void AddSC_instance_ramparts()
{
Script* pNewScript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp
index 239eec11480..3f2dba74aa8 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/boss_magtheridon.cpp
@@ -13,18 +13,22 @@
* along 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"
+
struct Yell
{
int32 id;
};
+
static Yell RandomTaunt[]=
{
{-1544000},
@@ -34,19 +38,23 @@ static Yell RandomTaunt[]=
{-1544004},
{-1544005},
};
+
#define SAY_FREED -1544006
#define SAY_AGGRO -1544007
#define SAY_BANISH -1544008
#define SAY_CHAMBER_DESTROY -1544009
#define SAY_PLAYER_KILLED -1544010
#define SAY_DEATH -1544011
+
#define EMOTE_BERSERK -1544012
#define EMOTE_BLASTNOVA -1544013
#define EMOTE_BEGIN -1544014
+
#define MOB_MAGTHERIDON 17257
#define MOB_ROOM 17516
#define MOB_CHANNELLER 17256
#define MOB_ABYSSAL 17454
+
#define SPELL_BLASTNOVA 30616
#define SPELL_CLEAVE 30619
#define SPELL_QUAKE_TRIGGER 30657 // must be cast with 30561 as the proc spell
@@ -58,21 +66,28 @@ static Yell RandomTaunt[]=
#define SPELL_DEBRIS_DAMAGE 30631 // core bug, does not support target 8
#define SPELL_CAMERA_SHAKE 36455
#define SPELL_BERSERK 27680
+
#define SPELL_SHADOW_CAGE 30168
#define SPELL_SHADOW_GRASP 30410
#define SPELL_SHADOW_GRASP_VISUAL 30166
#define SPELL_MIND_EXHAUSTION 44032 //Casted by the cubes when channeling ends
+
#define SPELL_SHADOW_CAGE_C 30205
#define SPELL_SHADOW_GRASP_C 30207
+
#define SPELL_SHADOW_BOLT_VOLLEY 30510
#define SPELL_DARK_MENDING 30528
#define SPELL_FEAR 30530 //39176
#define SPELL_BURNING_ABYSSAL 30511
#define SPELL_SOUL_TRANSFER 30531 // core bug, does not support target 7
+
#define SPELL_FIRE_BLAST 37110
+
// count of clickers needed to interrupt blast nova
#define CLICKERS_COUNT 5
+
typedef std::map<uint64, uint64> CubeMap;
+
struct TRINITY_DLL_DECL mob_abyssalAI : public ScriptedAI
{
mob_abyssalAI(Creature *c) : ScriptedAI(c)
@@ -80,13 +95,16 @@ struct TRINITY_DLL_DECL mob_abyssalAI : public ScriptedAI
trigger = 0;
Despawn_Timer = 60000;
}
+
uint32 FireBlast_Timer;
uint32 Despawn_Timer;
uint32 trigger;
+
void Reset()
{
FireBlast_Timer = 6000;
}
+
void SpellHit(Unit*, const SpellEntry *spell)
{
if (trigger == 2 && spell->Id == SPELL_BLAZE_TARGET)
@@ -96,6 +114,7 @@ struct TRINITY_DLL_DECL mob_abyssalAI : public ScriptedAI
Despawn_Timer = 130000;
}
}
+
void SetTrigger(uint32 _trigger)
{
trigger = _trigger;
@@ -108,9 +127,11 @@ struct TRINITY_DLL_DECL mob_abyssalAI : public ScriptedAI
Despawn_Timer = 10000;
}
}
+
void EnterCombat(Unit* who) {DoZoneInCombat();}
void AttackStart(Unit *who) {if (!trigger) ScriptedAI::AttackStart(who);}
void MoveInLineOfSight(Unit *who) {if (!trigger) ScriptedAI::MoveInLineOfSight(who);}
+
void UpdateAI(const uint32 diff)
{
if (trigger)
@@ -125,20 +146,25 @@ struct TRINITY_DLL_DECL mob_abyssalAI : public ScriptedAI
}
return;
}
+
if (Despawn_Timer < diff)
{
m_creature->ForcedDespawn();
}else Despawn_Timer -= diff;
+
if (!UpdateVictim())
return;
+
if (FireBlast_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_FIRE_BLAST);
FireBlast_Timer = 5000+rand()%10000;
}else FireBlast_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
{
boss_magtheridonAI(Creature *c) : ScriptedAI(c)
@@ -146,6 +172,7 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
pInstance = c->GetInstanceData();
m_creature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 10);
m_creature->SetFloatValue(UNIT_FIELD_COMBATREACH, 10);
+
// target 7, random target with certain entry spell, need core fix
SpellEntry *TempSpell;
TempSpell = GET_SPELL(SPELL_BLAZE_TARGET);
@@ -160,8 +187,11 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
TempSpell->EffectTriggerSpell[0] = SPELL_QUAKE_KNOCKBACK;
}
}
+
CubeMap Cube;
+
ScriptedInstance* pInstance;
+
uint32 Berserk_Timer;
uint32 Quake_Timer;
uint32 Cleave_Timer;
@@ -169,8 +199,10 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
uint32 Blaze_Timer;
uint32 Debris_Timer;
uint32 RandChat_Timer;
+
bool Phase3;
bool NeedCheckCube;
+
void Reset()
{
Berserk_Timer = 1320000;
@@ -180,14 +212,17 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
BlastNova_Timer = 60000;
Cleave_Timer = 15000;
RandChat_Timer = 90000;
+
Phase3 = false;
NeedCheckCube = false;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_OOC_NOT_ATTACKABLE);
m_creature->addUnitState(UNIT_STAT_STUNNED);
m_creature->CastSpell(m_creature, SPELL_SHADOW_CAGE_C, true);
}
+
void JustReachedHome()
{
if (pInstance)
@@ -196,6 +231,7 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
pInstance->SetData(DATA_COLLAPSE, false);
}
}
+
void SetClicker(uint64 cubeGUID, uint64 clickerGUID)
{
// to avoid multiclicks from 1 cube
@@ -204,21 +240,24 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
Cube[cubeGUID] = clickerGUID;
NeedCheckCube = true;
}
+
//function to interrupt channeling and debuff clicker with mind exh(used if second person clicks with same cube or after dispeling/ending shadow grasp DoT)
void DebuffClicker(Unit *clicker)
{
if (!clicker || !clicker->isAlive())
return;
+
clicker->RemoveAurasDueToSpell(SPELL_SHADOW_GRASP); // cannot interrupt triggered spells
clicker->InterruptNonMeleeSpells(false);
clicker->CastSpell(clicker, SPELL_MIND_EXHAUSTION, true);
}
+
void NeedCheckCubeStatus()
{
uint32 ClickerNum = 0;
// now checking if every clicker has debuff from manticron(it is dispelable atm rev 6110 : S)
// if not - apply mind exhaustion and delete from clicker's list
- for (CubeMap::iterator i = Cube.begin(); i != Cube.end(); ++i)
+ for(CubeMap::iterator i = Cube.begin(); i != Cube.end(); ++i)
{
Unit *clicker = Unit::GetUnit(*m_creature, (*i).second);
if (!clicker || !clicker->HasAura(SPELL_SHADOW_GRASP))
@@ -227,6 +266,7 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
(*i).second = 0;
}else ClickerNum++;
}
+
// if 5 clickers from other cubes apply shadow cage
if (ClickerNum >= CLICKERS_COUNT && !m_creature->HasAura(SPELL_SHADOW_CAGE))
{
@@ -235,33 +275,43 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
}
else if (ClickerNum < CLICKERS_COUNT && m_creature->HasAura(SPELL_SHADOW_CAGE))
m_creature->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE);
+
if (!ClickerNum) NeedCheckCube = false;
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(SAY_PLAYER_KILLED, m_creature);
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
pInstance->SetData(DATA_MAGTHERIDON_EVENT, DONE);
+
DoScriptText(SAY_DEATH, m_creature);
}
+
void MoveInLineOfSight(Unit* who) {}
+
void AttackStart(Unit *who)
{
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED))
ScriptedAI::AttackStart(who);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_MAGTHERIDON_EVENT, IN_PROGRESS);
DoZoneInCombat();
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveAurasDueToSpell(SPELL_SHADOW_CAGE_C);
+
DoScriptText(SAY_FREED, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isInCombat())
@@ -272,20 +322,25 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
RandChat_Timer = 90000;
}else RandChat_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
if (NeedCheckCube) NeedCheckCubeStatus();
+
if (Berserk_Timer < diff)
{
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
DoScriptText(EMOTE_BERSERK, m_creature);
Berserk_Timer = 60000;
}else Berserk_Timer -= diff;
+
if (Cleave_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_CLEAVE);
Cleave_Timer = 10000;
}else Cleave_Timer -= diff;
+
if (BlastNova_Timer < diff)
{
// to avoid earthquake interruption
@@ -296,6 +351,7 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
BlastNova_Timer = 60000;
}
}else BlastNova_Timer -= diff;
+
if (Quake_Timer < diff)
{
// to avoid blastnova interruption
@@ -305,6 +361,7 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
Quake_Timer = 50000;
}
}else Quake_Timer -= diff;
+
if (Blaze_Timer < diff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -321,6 +378,7 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
}
Blaze_Timer = 20000 + rand()%20000;
}else Blaze_Timer -= diff;
+
if (!Phase3 && m_creature->GetHealth()*10 < m_creature->GetMaxHealth()*3
&& !m_creature->IsNonMeleeSpellCasted(false) // blast nova
&& !m_creature->hasUnitState(UNIT_STAT_STUNNED)) // shadow cage and earthquake
@@ -329,9 +387,11 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
DoScriptText(SAY_CHAMBER_DESTROY, m_creature);
m_creature->CastSpell(m_creature, SPELL_CAMERA_SHAKE, true);
m_creature->CastSpell(m_creature, SPELL_DEBRIS_KNOCKDOWN, true);
+
if (pInstance)
pInstance->SetData(DATA_COLLAPSE, true);
}
+
if (Phase3)
{
if (Debris_Timer < diff)
@@ -346,86 +406,107 @@ struct TRINITY_DLL_DECL boss_magtheridonAI : public ScriptedAI
Debris_Timer = 10000;
}else Debris_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_hellfire_channelerAI : public ScriptedAI
{
mob_hellfire_channelerAI(Creature *c) : ScriptedAI(c)
{
pInstance =m_creature->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 ShadowBoltVolley_Timer;
uint32 DarkMending_Timer;
uint32 Fear_Timer;
uint32 Infernal_Timer;
+
uint32 Check_Timer;
+
void Reset()
{
ShadowBoltVolley_Timer = 8000 + rand()%2000;
DarkMending_Timer = 10000;
Fear_Timer = 15000 + rand()%5000;
Infernal_Timer = 10000 + rand()%40000;
+
Check_Timer = 5000;
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_CHANNELER_EVENT, IN_PROGRESS);
+
m_creature->InterruptNonMeleeSpells(false);
DoZoneInCombat();
}
+
void JustReachedHome()
{
if (pInstance)
pInstance->SetData(DATA_CHANNELER_EVENT, NOT_STARTED);
+
m_creature->CastSpell(m_creature, SPELL_SHADOW_GRASP_C, false);
}
+
void JustSummoned(Creature *summon)
{
summon->AI()->AttackStart(m_creature->getVictim());
}
+
void DamageTaken(Unit*, uint32 &damage)
{
if (damage >= m_creature->GetHealth())
m_creature->CastSpell(m_creature, SPELL_SOUL_TRANSFER, true);
}
+
void JustDied(Unit* who)
{
if (pInstance)
pInstance->SetData(DATA_CHANNELER_EVENT, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (ShadowBoltVolley_Timer < diff)
{
DoCast(m_creature, SPELL_SHADOW_BOLT_VOLLEY);
ShadowBoltVolley_Timer = 10000 + rand()%10000;
}else ShadowBoltVolley_Timer -= diff;
+
if (DarkMending_Timer < diff)
{
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 50)
DoCast(m_creature, SPELL_DARK_MENDING);
DarkMending_Timer = 10000 +(rand() % 10000);
}else DarkMending_Timer -= diff;
+
if (Fear_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1))
DoCast(target, SPELL_FEAR);
Fear_Timer = 25000 + rand()%15000;
}else Fear_Timer -= diff;
+
if (Infernal_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
m_creature->CastSpell(target, SPELL_BURNING_ABYSSAL, true);
Infernal_Timer = 30000 + rand()%10000;
}else Infernal_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Manticron Cube
bool GOHello_go_Manticron_Cube(Player* pPlayer, GameObject* pGo)
{
@@ -435,27 +516,33 @@ bool GOHello_go_Manticron_Cube(Player* pPlayer, GameObject* pGo)
if (pInstance->GetData(DATA_MAGTHERIDON_EVENT) != IN_PROGRESS) return true;
Creature *Magtheridon =Unit::GetCreature(*pGo, pInstance->GetData64(DATA_MAGTHERIDON));
if (!Magtheridon || !Magtheridon->isAlive()) return true;
+
// if exhausted or already channeling return
if (pPlayer->HasAura(SPELL_MIND_EXHAUSTION) || pPlayer->HasAura(SPELL_SHADOW_GRASP))
return true;
+
pPlayer->InterruptNonMeleeSpells(false);
pPlayer->CastSpell(pPlayer, SPELL_SHADOW_GRASP, true);
pPlayer->CastSpell(pPlayer, SPELL_SHADOW_GRASP_VISUAL, false);
CAST_AI(boss_magtheridonAI, Magtheridon->AI())->SetClicker(pGo->GetGUID(), pPlayer->GetGUID());
return true;
}
+
CreatureAI* GetAI_boss_magtheridon(Creature* pCreature)
{
return new boss_magtheridonAI(pCreature);
}
+
CreatureAI* GetAI_mob_hellfire_channeler(Creature* pCreature)
{
return new mob_hellfire_channelerAI(pCreature);
}
+
CreatureAI* GetAI_mob_abyssalAI(Creature* pCreature)
{
return new mob_abyssalAI(pCreature);
}
+
void AddSC_boss_magtheridon()
{
Script *newscript;
@@ -463,17 +550,21 @@ void AddSC_boss_magtheridon()
newscript->Name = "boss_magtheridon";
newscript->GetAI = &GetAI_boss_magtheridon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_hellfire_channeler";
newscript->GetAI = &GetAI_mob_hellfire_channeler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_manticron_cube";
newscript->pGOHello = &GOHello_go_Manticron_Cube;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_abyssal";
newscript->GetAI = &GetAI_mob_abyssalAI;
newscript->RegisterSelf();
+
}
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h b/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h
index 2fb87069fa9..c5469acaea0 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/def_magtheridons_lair.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_MAGTHERIDONS_LAIR_H
#define DEF_MAGTHERIDONS_LAIR_H
+
#define DATA_MAGTHERIDON_EVENT 1
#define DATA_MAGTHERIDON 3
#define DATA_CHANNELER_EVENT 2
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp
index 2acdae10f37..33946926976 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/magtheridons_lair/instance_magtheridons_lair.cpp
@@ -13,50 +13,65 @@
* along 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"
+
#define SPELL_SOUL_TRANSFER 30531 // core bug, does not support target 7
#define SPELL_BLAZE_TARGET 30541 // core bug, does not support target 7
+
#define CHAMBER_CENTER_X -15.14
#define CHAMBER_CENTER_Y 1.8
#define CHAMBER_CENTER_Z -0.4
+
#define MAX_ENCOUNTER 2
+
#define EMOTE_BONDS_WEAKEN "'s bonds begin to weaken!"
+
struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
{
instance_magtheridons_lair(Map* pMap) : ScriptedInstance(pMap)
{
Initialize();
}
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
uint64 MagtheridonGUID;
std::set<uint64> ChannelerGUID;
uint64 DoorGUID;
std::set<uint64> ColumnGUID;
+
uint32 CageTimer;
uint32 RespawnTimer;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
MagtheridonGUID = 0;
ChannelerGUID.clear();
DoorGUID = 0;
ColumnGUID.clear();
+
CageTimer = 0;
RespawnTimer = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -69,6 +84,7 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
break;
}
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -90,6 +106,7 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
break;
}
}
+
uint64 GetData64(uint32 type)
{
switch(type)
@@ -99,6 +116,7 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -117,7 +135,7 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
if (m_auiEncounter[1] != NOT_STARTED)
{
m_auiEncounter[1] = NOT_STARTED;
- for (std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
+ for(std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
{
if (Creature *Channeler = instance->GetCreature(*i))
{
@@ -135,7 +153,7 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
{
m_auiEncounter[1] = IN_PROGRESS;
// Let all five channelers aggro.
- for (std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
+ for(std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
{
Creature *Channeler = instance->GetCreature(*i);
if (Channeler && Channeler->isAlive())
@@ -151,7 +169,7 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
HandleGameObject(DoorGUID, false);
}break;
case DONE: // Add buff and check if all channelers are dead.
- for (std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
+ for(std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
{
Creature *Channeler = instance->GetCreature(*i);
if (Channeler && Channeler->isAlive())
@@ -166,19 +184,21 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
break;
case DATA_COLLAPSE:
// true - collapse / false - reset
- for (std::set<uint64>::iterator i = ColumnGUID.begin(); i != ColumnGUID.end(); ++i)
+ for(std::set<uint64>::iterator i = ColumnGUID.begin(); i != ColumnGUID.end(); ++i)
DoUseDoorOrButton(*i);
break;
default:
break;
}
}
+
uint32 GetData(uint32 type)
{
if (type == DATA_MAGTHERIDON_EVENT)
return m_auiEncounter[0];
return 0;
}
+
void Update(uint32 diff)
{
if (CageTimer)
@@ -194,11 +214,12 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
CageTimer = 0;
}else CageTimer -= diff;
}
+
if (RespawnTimer)
{
if (RespawnTimer <= diff)
{
- for (std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
+ for(std::set<uint64>::iterator i = ChannelerGUID.begin(); i != ChannelerGUID.end(); ++i)
{
if (Creature *Channeler = instance->GetCreature(*i))
{
@@ -213,10 +234,12 @@ struct TRINITY_DLL_DECL instance_magtheridons_lair : public ScriptedInstance
}
}
};
+
InstanceData* GetInstanceData_instance_magtheridons_lair(Map* pMap)
{
return new instance_magtheridons_lair(pMap);
}
+
void AddSC_instance_magtheridons_lair()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp
index c29269bd7ab..4ae379d988e 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_nethekurse.cpp
@@ -13,23 +13,28 @@
* along 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 completed. 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
{
int32 id;
};
+
static Say PeonAttacked[]=
{
{-1540001},
@@ -44,6 +49,7 @@ static Say PeonDies[]=
{-1540007},
{-1540008},
};
+
#define SAY_INTRO -1540000
#define SAY_TAUNT_1 -1540009
#define SAY_TAUNT_2 -1540010
@@ -54,14 +60,19 @@ static Say PeonDies[]=
#define SAY_SLAY_1 -1540015
#define SAY_SLAY_2 -1540016
#define SAY_DIE -1540017
+
#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 TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
{
boss_grand_warlock_nethekurseAI(Creature *c) : ScriptedAI(c)
@@ -69,49 +80,62 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
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);
+
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;
+
DoScriptText(PeonAttacked[PeonEngagedCount].id, m_creature);
++PeonEngagedCount;
}
+
void DoYellForPeonDeath()
{
if (PeonKilledCount >= 4)
return;
+
DoScriptText(PeonDies[PeonKilledCount].id, m_creature);
++PeonKilledCount;
+
if (PeonKilledCount == 4)
{
IsIntroEvent = false;
@@ -119,9 +143,11 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
}
+
void DoTauntPeons()
{
DoScriptText(RAND(SAY_TAUNT_1,SAY_TAUNT_2,SAY_TAUNT_3), m_creature);
+
//TODO: kill the peons first
IsIntroEvent = false;
PeonEngagedCount = 4;
@@ -129,10 +155,12 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
IsMainEvent = true;
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
void AttackStart(Unit* who)
{
if (IsIntroEvent || !IsMainEvent)
return;
+
if (m_creature->Attack(who, true))
{
if (Phase)
@@ -141,53 +169,67 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
DoStartMovement(who);
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (!IntroOnce && m_creature->IsWithinDistInMap(who, 50.0f))
{
if (who->GetTypeId() != TYPEID_PLAYER)
return;
+
DoScriptText(SAY_INTRO, m_creature);
IntroOnce = true;
IsIntroEvent = true;
+
if (pInstance)
pInstance->SetData(TYPE_NETHEKURSE,IN_PROGRESS);
}
+
if (IsIntroEvent || !IsMainEvent)
return;
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO_1,SAY_AGGRO_2,SAY_AGGRO_3), m_creature);
}
+
void JustSummoned(Creature *summoned)
{
summoned->setFaction(16);
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
summoned->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
//triggered spell of consumption does not properly show it's SpellVisual, wrong spellid?
summoned->CastSpell(summoned,SPELL_TEMPORARY_VISUAL,true);
summoned->CastSpell(summoned,SPELL_CONSUMPTION,false,0,0,m_creature->GetGUID());
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DIE, m_creature);
+
if (!pInstance)
return;
+
pInstance->SetData(TYPE_NETHEKURSE,DONE);
pInstance->HandleGameObject(pInstance->GetData64(DATA_NETHEKURSE_DOOR), true);
}
+
void UpdateAI(const uint32 diff)
{
if (IsIntroEvent)
{
if (!pInstance)
return;
+
if (pInstance->GetData(TYPE_NETHEKURSE) == IN_PROGRESS)
{
if (IntroEvent_Timer < diff)
@@ -196,10 +238,13 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
IntroEvent_Timer -= diff;
}
}
+
if (!UpdateVictim())
return;
+
if (!IsMainEvent)
return;
+
if (Phase)
{
if (!SpinOnce)
@@ -207,6 +252,7 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
DoCast(m_creature->getVictim(),SPELL_DARK_SPIN);
SpinOnce = true;
}
+
if (Cleave_Timer < diff)
{
DoCast(m_creature->getVictim(),HEROIC(SPELL_SHADOW_CLEAVE, H_SPELL_SHADOW_SLAM));
@@ -221,34 +267,42 @@ struct TRINITY_DLL_DECL boss_grand_warlock_nethekurseAI : public ScriptedAI
DoCast(target,SPELL_SHADOW_FISSURE);
ShadowFissure_Timer = urand(7500,15000);
} else ShadowFissure_Timer -= diff;
+
if (DeathCoil_Timer < diff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0))
DoCast(target,SPELL_DEATH_COIL);
DeathCoil_Timer = urand(15000,20000);
} else DeathCoil_Timer -= diff;
+
if ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 20)
Phase = true;
+
DoMeleeAttackIfReady();
}
}
};
+
struct TRINITY_DLL_DECL mob_fel_orc_convertAI : public ScriptedAI
{
mob_fel_orc_convertAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
uint32 Hemorrhage_Timer;
+
void Reset()
{
m_creature->SetNoCallAssistance(true); //we don't want any assistance (WE R HEROZ!)
Hemorrhage_Timer = 3000;
}
+
void MoveInLineOfSight(Unit *who)
{
}
+
void EnterCombat(Unit* who)
{
if (pInstance)
@@ -259,6 +313,7 @@ struct TRINITY_DLL_DECL mob_fel_orc_convertAI : public ScriptedAI
if (pKurse && m_creature->IsWithinDist(pKurse, 45.0f))
{
CAST_AI(boss_grand_warlock_nethekurseAI, pKurse->AI())->DoYellForPeonAggro();
+
if (pInstance->GetData(TYPE_NETHEKURSE) == IN_PROGRESS)
return;
else
@@ -267,6 +322,7 @@ struct TRINITY_DLL_DECL mob_fel_orc_convertAI : public ScriptedAI
}
}
}
+
void JustDied(Unit* Killer)
{
if (pInstance)
@@ -278,50 +334,62 @@ struct TRINITY_DLL_DECL mob_fel_orc_convertAI : public ScriptedAI
CAST_AI(boss_grand_warlock_nethekurseAI, pKurse->AI())->DoYellForPeonDeath();
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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 TRINITY_DLL_DECL mob_lesser_shadow_fissureAI : public ScriptedAI
{
mob_lesser_shadow_fissureAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
void MoveInLineOfSight(Unit *who) { }
void AttackStart(Unit* who) { }
void EnterCombat(Unit* who) { }
};
+
CreatureAI* GetAI_boss_grand_warlock_nethekurse(Creature* pCreature)
{
return new boss_grand_warlock_nethekurseAI (pCreature);
}
+
CreatureAI* GetAI_mob_fel_orc_convert(Creature* pCreature)
{
return new mob_fel_orc_convertAI (pCreature);
}
+
CreatureAI* GetAI_mob_lesser_shadow_fissure(Creature* pCreature)
{
return new mob_lesser_shadow_fissureAI (pCreature);
}
+
void AddSC_boss_grand_warlock_nethekurse()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_grand_warlock_nethekurse";
newscript->GetAI = &GetAI_boss_grand_warlock_nethekurse;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_fel_orc_convert";
newscript->GetAI = &GetAI_mob_fel_orc_convert;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_lesser_shadow_fissure";
newscript->GetAI = &GetAI_mob_lesser_shadow_fissure;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp
index d715b1ea5fc..2fe6fec2684 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warbringer_omrogg.cpp
@@ -13,36 +13,45 @@
* along 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"
+
enum eEnums
{
YELL_DIE_L = -1540039,
YELL_DIE_R = -1540040,
EMOTE_ENRAGE = -1540041,
+
SPELL_BLAST_WAVE = 30600,
SPELL_FEAR = 30584,
SPELL_THUNDERCLAP = 30633,
+
SPELL_BURNING_MAUL = 30598,
H_SPELL_BURNING_MAUL = 36056,
+
NPC_LEFT_HEAD = 19523,
NPC_RIGHT_HEAD = 19524
};
+
struct Yell
{
int32 id;
uint32 creature;
};
+
static Yell GoCombat[]=
{
{-1540018, NPC_LEFT_HEAD},
@@ -55,6 +64,7 @@ static Yell GoCombatDelay[]=
{-1540022, NPC_RIGHT_HEAD},
{-1540023, NPC_RIGHT_HEAD},
};
+
static Yell Threat[]=
{
{-1540024, NPC_LEFT_HEAD},
@@ -76,6 +86,7 @@ static Yell ThreatDelay2[]=
{-1540034, NPC_LEFT_HEAD},
{-1540035, NPC_LEFT_HEAD},
};
+
static Yell Killing[]=
{
{-1540036, NPC_LEFT_HEAD},
@@ -86,25 +97,31 @@ static Yell KillingDelay[]=
{-1540038, NPC_RIGHT_HEAD},
{-1000000, NPC_LEFT_HEAD},
};
+
struct TRINITY_DLL_DECL mob_omrogg_headsAI : public ScriptedAI
{
mob_omrogg_headsAI(Creature *c) : ScriptedAI(c) {}
+
bool DeathYell;
uint32 Death_Timer;
+
void Reset()
{
Death_Timer = 4000;
DeathYell = false;
}
void EnterCombat(Unit* who) { }
+
void DoDeathYell()
{
DeathYell = true;
}
+
void UpdateAI(const uint32 diff)
{
if (!DeathYell)
return;
+
if (Death_Timer < diff)
{
DoScriptText(YELL_DIE_R, m_creature);
@@ -113,6 +130,7 @@ struct TRINITY_DLL_DECL mob_omrogg_headsAI : public ScriptedAI
}else Death_Timer -= diff;
}
};
+
struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
{
boss_warbringer_omroggAI(Creature *c) : ScriptedAI(c)
@@ -122,17 +140,21 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance* pInstance;
bool HeroicMode;
+
uint64 LeftHeadGUID;
uint64 RightHeadGUID;
int iaggro;
int ithreat;
int ikilling;
+
bool AggroYell;
bool ThreatYell;
bool ThreatYell2;
bool KillingYell;
+
uint32 Delay_Timer;
uint32 BlastWave_Timer;
uint32 BlastCount;
@@ -140,6 +162,7 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
uint32 BurningMaul_Timer;
uint32 ThunderClap_Timer;
uint32 ResetThreat_Timer;
+
void Reset()
{
if (Unit* pLeftHead = Unit::GetUnit(*m_creature,LeftHeadGUID))
@@ -147,16 +170,19 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
pLeftHead->setDeathState(JUST_DIED);
LeftHeadGUID = 0;
}
+
if (Unit* pRightHead = Unit::GetUnit(*m_creature,RightHeadGUID))
{
pRightHead->setDeathState(JUST_DIED);
RightHeadGUID = 0;
}
+
AggroYell = false;
ThreatYell = false;
ThreatYell2 = false;
KillingYell = false;
+
Delay_Timer = 4000;
BlastWave_Timer = 0;
BlastCount = 0;
@@ -164,54 +190,74 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
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()
{
Unit *pLeftHead = Unit::GetUnit(*m_creature,LeftHeadGUID);
Unit *pRightHead = Unit::GetUnit(*m_creature,RightHeadGUID);
+
if (!pLeftHead || !pRightHead)
return;
+
ithreat = rand()%4;
+
Unit *source = (pLeftHead->GetEntry() == Threat[ithreat].creature ? pLeftHead : pRightHead);
+
DoScriptText(Threat[ithreat].id, source);
+
Delay_Timer = 3500;
ThreatYell = true;
}
+
void EnterCombat(Unit *who)
{
m_creature->SummonCreature(NPC_LEFT_HEAD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0);
m_creature->SummonCreature(NPC_RIGHT_HEAD, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0);
+
if (Unit *pLeftHead = Unit::GetUnit(*m_creature,LeftHeadGUID))
{
iaggro = rand()%3;
+
DoScriptText(GoCombat[iaggro].id, pLeftHead);
+
Delay_Timer = 3500;
AggroYell = true;
}
+
if (pInstance)
pInstance->SetData(TYPE_OMROGG, IN_PROGRESS);
}
+
void JustSummoned(Creature *summoned)
{
if (summoned->GetEntry() == NPC_LEFT_HEAD)
LeftHeadGUID = summoned->GetGUID();
+
if (summoned->GetEntry() == NPC_RIGHT_HEAD)
RightHeadGUID = 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)
{
Unit* pLeftHead = Unit::GetUnit(*m_creature,LeftHeadGUID);
Unit* pRightHead = Unit::GetUnit(*m_creature,RightHeadGUID);
+
if (!pLeftHead || !pRightHead)
return;
+
ikilling = rand()%2;
+
Unit *source = (pLeftHead->GetEntry() == Killing[ikilling].creature ? pLeftHead : pRightHead);
+
switch(ikilling)
{
case 0:
@@ -225,61 +271,80 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
break;
}
}
+
void JustDied(Unit* Killer)
{
Unit* pLeftHead = Unit::GetUnit(*m_creature,LeftHeadGUID);
Unit* pRightHead = Unit::GetUnit(*m_creature,RightHeadGUID);
+
if (!pLeftHead || !pRightHead)
return;
+
DoScriptText(YELL_DIE_L, pLeftHead);
+
CAST_AI(mob_omrogg_headsAI, CAST_CRE(pRightHead)->AI())->DoDeathYell();
+
if (pInstance)
pInstance->SetData(TYPE_OMROGG, DONE);
}
+
void UpdateAI(const uint32 diff)
{
if (Delay_Timer < diff)
{
Delay_Timer = 3500;
+
Unit* pLeftHead = Unit::GetUnit(*m_creature,LeftHeadGUID);
Unit* pRightHead = Unit::GetUnit(*m_creature,RightHeadGUID);
+
if (!pLeftHead || !pRightHead)
return;
+
if (AggroYell)
{
DoScriptText(GoCombatDelay[iaggro].id, pRightHead);
AggroYell = false;
}
+
if (ThreatYell2)
{
Unit *source = (pLeftHead->GetEntry() == ThreatDelay2[ithreat].creature ? pLeftHead : pRightHead);
+
DoScriptText(ThreatDelay2[ithreat].id, source);
ThreatYell2 = false;
}
+
if (ThreatYell)
{
Unit *source = (pLeftHead->GetEntry() == ThreatDelay1[ithreat].creature ? pLeftHead : pRightHead);
+
DoScriptText(ThreatDelay1[ithreat].id, source);
ThreatYell = false;
ThreatYell2 = true;
}
+
if (KillingYell)
{
Unit *source = (pLeftHead->GetEntry() == KillingDelay[ikilling].creature ? pLeftHead : pRightHead);
+
DoScriptText(KillingDelay[ikilling].id, source);
KillingYell = false;
}
}else Delay_Timer -= diff;
+
if (!UpdateVictim())
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)
{
DoScriptText(EMOTE_ENRAGE, m_creature);
@@ -288,6 +353,7 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
BlastWave_Timer = 16000;
BlastCount = 1;
}else BurningMaul_Timer -= diff;
+
if (ResetThreat_Timer < diff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM,0))
@@ -298,34 +364,42 @@ struct TRINITY_DLL_DECL boss_warbringer_omroggAI : public ScriptedAI
}
ResetThreat_Timer = 25000+rand()%15000;
}else ResetThreat_Timer -= diff;
+
if (Fear_Timer < diff)
{
DoCast(m_creature,SPELL_FEAR);
Fear_Timer = 15000+rand()%20000;
}else Fear_Timer -= diff;
+
if (ThunderClap_Timer < diff)
{
DoCast(m_creature,SPELL_THUNDERCLAP);
ThunderClap_Timer = 15000+rand()%15000;
}else ThunderClap_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_warbringer_omrogg(Creature* pCreature)
{
return new boss_warbringer_omroggAI (pCreature);
}
+
CreatureAI* GetAI_mob_omrogg_heads(Creature* pCreature)
{
return new mob_omrogg_headsAI (pCreature);
}
+
void AddSC_boss_warbringer_omrogg()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_warbringer_omrogg";
newscript->GetAI = &GetAI_boss_warbringer_omrogg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_omrogg_heads";
newscript->GetAI = &GetAI_mob_omrogg_heads;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp
index f01cf7ebad2..98a5cea12c3 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/boss_warchief_kargath_bladefist.cpp
@@ -13,68 +13,89 @@
* along 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_Warchief_Kargath_Bladefist
SD%Complete: 90
SDComment:
SDCategory: Hellfire Citadel, Shattered Halls
EndScriptData */
+
/* ContentData
boss_warchief_kargath_bladefist
EndContentData */
+
#include "precompiled.h"
+
#define SAY_AGGRO1 -1540042
#define SAY_AGGRO2 -1540043
#define SAY_AGGRO3 -1540044
#define SAY_SLAY1 -1540045
#define SAY_SLAY2 -1540046
#define SAY_DEATH -1540047
+
#define SPELL_BLADE_DANCE 30739
#define H_SPELL_CHARGE 25821
+
#define TARGET_NUM 5
+
#define MOB_SHATTERED_ASSASSIN 17695
#define MOB_HEARTHEN_GUARD 17621
#define MOB_SHARPSHOOTER_GUARD 17622
#define MOB_REAVER_GUARD 17623
+
float AssassEntrance[3] = {275.136,-84.29,2.3}; // y -8
float AssassExit[3] = {184.233,-84.29,2.3}; // y -8
float AddsEntrance[3] = {306.036,-84.29,1.93};
+
struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
{
boss_warchief_kargath_bladefistAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
std::vector<uint64> adds;
std::vector<uint64> assassins;
+
uint32 Charge_timer;
uint32 Blade_Dance_Timer;
uint32 Summon_Assistant_Timer;
uint32 resetcheck_timer;
uint32 Wait_Timer;
+
uint32 Assassins_Timer;
+
uint32 summoned;
bool InBlade;
+
uint32 target_num;
+
void Reset()
{
removeAdds();
+
m_creature->SetSpeed(MOVE_RUN,2);
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
summoned = 2;
InBlade = false;
Wait_Timer = 0;
+
Charge_timer = 0;
Blade_Dance_Timer = 45000;
Summon_Assistant_Timer = 30000;
Assassins_Timer = 5000;
resetcheck_timer = 5000;
}
+
void EnterCombat(Unit *who)
{
DoScriptText(RAND(SAY_AGGRO1,SAY_AGGRO2,SAY_AGGRO3), m_creature);
}
+
void JustSummoned(Creature *summoned)
{
switch(summoned->GetEntry())
@@ -90,6 +111,7 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
break;
}
}
+
void KilledUnit(Unit *victim)
{
if (victim->GetTypeId() == TYPEID_PLAYER)
@@ -97,19 +119,23 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
removeAdds();
}
+
void MovementInform(uint32 type, uint32 id)
{
if (InBlade)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id != 1)
return;
+
if (target_num > 0) // to prevent loops
{
Wait_Timer = 1;
@@ -118,9 +144,10 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
}
}
}
+
void removeAdds()
{
- for (std::vector<uint64>::iterator itr = adds.begin(); itr!= adds.end(); ++itr)
+ for(std::vector<uint64>::iterator itr = adds.begin(); itr!= adds.end(); ++itr)
{
Unit* temp = Unit::GetUnit((*m_creature),*itr);
if (temp && temp->isAlive())
@@ -131,7 +158,8 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
}
}
adds.clear();
- for (std::vector<uint64>::iterator itr = assassins.begin(); itr!= assassins.end(); ++itr)
+
+ for(std::vector<uint64>::iterator itr = assassins.begin(); itr!= assassins.end(); ++itr)
{
Unit* temp = Unit::GetUnit((*m_creature),*itr);
if (temp && temp->isAlive())
@@ -150,17 +178,20 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
m_creature->SummonCreature(MOB_SHATTERED_ASSASSIN,AssassExit[0],AssassExit[1]+8, AssassExit[2], 0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,30000);
m_creature->SummonCreature(MOB_SHATTERED_ASSASSIN,AssassExit[0],AssassExit[1]-8, AssassExit[2], 0,TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT,30000);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Assassins_Timer)
if (Assassins_Timer <= diff)
{
SpawnAssassin();
Assassins_Timer = 0;
}else Assassins_Timer -= diff;
+
if (InBlade)
{
if (Wait_Timer)
@@ -202,16 +233,19 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
m_creature->SetSpeed(MOVE_RUN,4);
return;
}else Blade_Dance_Timer -= diff;
+
if (Charge_timer)
if (Charge_timer <= diff)
{
DoCast(SelectUnit(SELECT_TARGET_RANDOM,0),H_SPELL_CHARGE);
Charge_timer = 0;
}else Charge_timer -= diff;
+
if (Summon_Assistant_Timer < diff)
{
Unit* target = NULL;
- for (uint8 i = 0; i < summoned; ++i)
+
+ for(uint8 i = 0; i < summoned; ++i)
{
switch(rand()%3)
{
@@ -223,8 +257,10 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
if (rand()%100 < 20) summoned++;
Summon_Assistant_Timer = 25000 + (rand()%10000) ;
}else Summon_Assistant_Timer -= diff;
+
DoMeleeAttackIfReady();
}
+
if (resetcheck_timer < diff)
{
uint32 tempx,tempy;
@@ -239,10 +275,12 @@ struct TRINITY_DLL_DECL boss_warchief_kargath_bladefistAI : public ScriptedAI
}else resetcheck_timer -= diff;
}
};
+
CreatureAI* GetAI_boss_warchief_kargath_bladefist(Creature* pCreature)
{
return new boss_warchief_kargath_bladefistAI (pCreature);
}
+
void AddSC_boss_warchief_kargath_bladefist()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/def_shattered_halls.h b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/def_shattered_halls.h
index 91815f29de6..cbfa23ec4e0 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/def_shattered_halls.h
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/def_shattered_halls.h
@@ -1,11 +1,14 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef 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/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp
index f8d47c4c3f2..864a49a84ba 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_citadel/shattered_halls/instance_shattered_halls.cpp
@@ -13,28 +13,37 @@
* along 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 MAX_ENCOUNTER 2
+
#define DOOR_NETHEKURSE 1
+
struct TRINITY_DLL_DECL instance_shattered_halls : public ScriptedInstance
{
instance_shattered_halls(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
uint64 nethekurseGUID;
uint64 nethekurseDoorGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
nethekurseGUID = 0;
nethekurseDoorGUID = 0;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -42,6 +51,7 @@ struct TRINITY_DLL_DECL instance_shattered_halls : public ScriptedInstance
case DOOR_NETHEKURSE: nethekurseDoorGUID = pGo->GetGUID(); break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -49,6 +59,7 @@ struct TRINITY_DLL_DECL instance_shattered_halls : public ScriptedInstance
case 16807: nethekurseGUID = pCreature->GetGUID(); break;
}
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -61,6 +72,7 @@ struct TRINITY_DLL_DECL instance_shattered_halls : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -72,6 +84,7 @@ struct TRINITY_DLL_DECL instance_shattered_halls : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -84,10 +97,12 @@ struct TRINITY_DLL_DECL instance_shattered_halls : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_shattered_halls(Map* pMap)
{
return new instance_shattered_halls(pMap);
}
+
void AddSC_instance_shattered_halls()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/hellfire_peninsula.cpp b/src/bindings/scripts/scripts/outland/hellfire_peninsula.cpp
index 077ac13e1de..fe0a52e2c47 100644
--- a/src/bindings/scripts/scripts/outland/hellfire_peninsula.cpp
+++ b/src/bindings/scripts/scripts/outland/hellfire_peninsula.cpp
@@ -13,12 +13,14 @@
* along 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: 9375, 9410, 9418, 10129, 10146, 10162, 10163, 10340, 10346, 10347, 10382 (Special flight paths)
SDCategory: Hellfire Peninsula
EndScriptData */
+
/* ContentData
npc_aeranas
npc_ancestral_wolf
@@ -28,36 +30,48 @@ npc_tracy_proudwell
npc_trollbane
npc_wounded_blood_elf
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_aeranas
######*/
+
enum eAeranas
{
SAY_SUMMON = -1000138,
SAY_FREE = -1000139,
+
FACTION_HOSTILE = 16,
FACTION_FRIENDLY = 35,
+
SPELL_ENVELOPING_WINDS = 15535,
SPELL_SHOCK = 12553,
+
C_AERANAS = 17085
};
+
struct TRINITY_DLL_DECL npc_aeranasAI : public ScriptedAI
{
npc_aeranasAI(Creature* c) : ScriptedAI(c) {}
+
uint32 Faction_Timer;
uint32 EnvelopingWinds_Timer;
uint32 Shock_Timer;
+
void Reset()
{
Faction_Timer = 8000;
EnvelopingWinds_Timer = 9000;
Shock_Timer = 5000;
+
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
m_creature->setFaction(FACTION_FRIENDLY);
+
DoScriptText(SAY_SUMMON, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (Faction_Timer)
@@ -68,8 +82,10 @@ struct TRINITY_DLL_DECL npc_aeranasAI : public ScriptedAI
Faction_Timer = 0;
}else Faction_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
if ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() < 30)
{
m_creature->setFaction(FACTION_FRIENDLY);
@@ -80,34 +96,43 @@ struct TRINITY_DLL_DECL npc_aeranasAI : public ScriptedAI
DoScriptText(SAY_FREE, m_creature);
return;
}
+
if (Shock_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_SHOCK);
Shock_Timer = 10000;
}else Shock_Timer -= diff;
+
if (EnvelopingWinds_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ENVELOPING_WINDS);
EnvelopingWinds_Timer = 25000;
}else EnvelopingWinds_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_aeranas(Creature* pCreature)
{
return new npc_aeranasAI (pCreature);
}
+
/*######
## npc_ancestral_wolf
######*/
+
enum eAncestralWolf
{
EMOTE_WOLF_LIFT_HEAD = -1000496,
EMOTE_WOLF_HOWL = -1000497,
SAY_WOLF_WELCOME = -1000498,
+
SPELL_ANCESTRAL_WOLF_BUFF = 29981,
+
NPC_RYGA = 17123
};
+
struct TRINITY_DLL_DECL npc_ancestral_wolfAI : public npc_escortAI
{
npc_ancestral_wolfAI(Creature* pCreature) : npc_escortAI(pCreature)
@@ -116,21 +141,27 @@ struct TRINITY_DLL_DECL npc_ancestral_wolfAI : public npc_escortAI
Start(false, false, pCreature->GetOwner()->GetGUID());
else
error_log("TRINITY: npc_ancestral_wolf can not obtain owner or owner is not a player.");
+
pCreature->SetSpeed(MOVE_WALK, 1.5f);
Reset();
}
+
Unit* pRyga;
+
void Reset()
{
pRyga = NULL;
m_creature->CastSpell(m_creature, SPELL_ANCESTRAL_WOLF_BUFF, true);
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (!pRyga && pWho->GetTypeId() == TYPEID_UNIT && pWho->GetEntry() == NPC_RYGA && m_creature->IsWithinDistInMap(pWho, 15.0f))
pRyga = pWho;
+
npc_escortAI::MoveInLineOfSight(pWho);
}
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
@@ -148,62 +179,80 @@ struct TRINITY_DLL_DECL npc_ancestral_wolfAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_npc_ancestral_wolf(Creature* pCreature)
{
return new npc_ancestral_wolfAI(pCreature);
}
+
/*######
## go_haaleshi_altar
######*/
+
bool GOHello_go_haaleshi_altar(Player* pPlayer, GameObject* pGo)
{
pGo->SummonCreature(C_AERANAS,-1321.79, 4043.80, 116.24, 1.25, TEMPSUMMON_TIMED_DESPAWN, 180000);
return false;
}
+
/*######
## npc_naladu
######*/
+
#define GOSSIP_NALADU_ITEM1 "Why don't you escape?"
+
enum eNaladu
{
GOSSIP_TEXTID_NALADU1 = 9788
};
+
bool GossipHello_npc_naladu(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_NALADU_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_naladu(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_NALADU1, pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_tracy_proudwell
######*/
+
#define GOSSIP_TEXT_REDEEM_MARKS "I have marks to redeem!"
#define GOSSIP_TRACY_PROUDWELL_ITEM1 "I heard that your dog Fei Fei took Klatu's prayer beads..."
#define GOSSIP_TRACY_PROUDWELL_ITEM2 "<back>"
+
enum eTracy
{
GOSSIP_TEXTID_TRACY_PROUDWELL1 = 10689,
QUEST_DIGGING_FOR_PRAYER_BEADS = 10916
};
+
bool GossipHello_npc_tracy_proudwell(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_REDEEM_MARKS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
if (pPlayer->GetQuestStatus(QUEST_DIGGING_FOR_PRAYER_BEADS) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TRACY_PROUDWELL_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_tracy_proudwell(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -219,29 +268,36 @@ bool GossipSelect_npc_tracy_proudwell(Player* pPlayer, Creature* pCreature, uint
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
break;
}
+
return true;
}
+
/*######
## npc_trollbane
######*/
+
#define GOSSIP_TROLLBANE_ITEM1 "Tell me of the Sons of Lothar."
#define GOSSIP_TROLLBANE_ITEM2 "<more>"
#define GOSSIP_TROLLBANE_ITEM3 "Tell me of your homeland."
+
enum eTrollbane
{
GOSSIP_TEXTID_TROLLBANE1 = 9932,
GOSSIP_TEXTID_TROLLBANE2 = 9933,
GOSSIP_TEXTID_TROLLBANE3 = 8772
};
+
bool GossipHello_npc_trollbane(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TROLLBANE_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TROLLBANE_ITEM3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_trollbane(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -257,11 +313,14 @@ bool GossipSelect_npc_trollbane(Player* pPlayer, Creature* pCreature, uint32 uiS
pPlayer->SEND_GOSSIP_MENU(GOSSIP_TEXTID_TROLLBANE3, pCreature->GetGUID());
break;
}
+
return true;
}
+
/*######
## npc_wounded_blood_elf
######*/
+
enum eWoundedBloodElf
{
SAY_ELF_START = -1000117,
@@ -270,16 +329,21 @@ enum eWoundedBloodElf
SAY_ELF_SUMMON2 = -1000120,
SAY_ELF_COMPLETE = -1000121,
SAY_ELF_AGGRO = -1000122,
+
QUEST_ROAD_TO_FALCON_WATCH = 9375
};
+
struct TRINITY_DLL_DECL npc_wounded_blood_elfAI : public npc_escortAI
{
npc_wounded_blood_elfAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch (i)
{
case 0:
@@ -307,62 +371,77 @@ struct TRINITY_DLL_DECL npc_wounded_blood_elfAI : public npc_escortAI
break;
}
}
+
void Reset() { }
+
void EnterCombat(Unit* who)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
DoScriptText(SAY_ELF_AGGRO, m_creature);
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
};
+
CreatureAI* GetAI_npc_wounded_blood_elf(Creature* pCreature)
{
return new npc_wounded_blood_elfAI(pCreature);
}
+
bool QuestAccept_npc_wounded_blood_elf(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_ROAD_TO_FALCON_WATCH)
{
if (npc_escortAI* pEscortAI = CAST_AI(npc_wounded_blood_elfAI, pCreature->AI()))
pEscortAI->Start(true, false, pPlayer->GetGUID());
+
// Change faction so mobs attack
pCreature->setFaction(775);
}
+
return true;
}
+
void AddSC_hellfire_peninsula()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_aeranas";
newscript->GetAI = &GetAI_npc_aeranas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_ancestral_wolf";
newscript->GetAI = &GetAI_npc_ancestral_wolf;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_haaleshi_altar";
newscript->pGOHello = &GOHello_go_haaleshi_altar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_naladu";
newscript->pGossipHello = &GossipHello_npc_naladu;
newscript->pGossipSelect = &GossipSelect_npc_naladu;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tracy_proudwell";
newscript->pGossipHello = &GossipHello_npc_tracy_proudwell;
newscript->pGossipSelect = &GossipSelect_npc_tracy_proudwell;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_trollbane";
newscript->pGossipHello = &GossipHello_npc_trollbane;
newscript->pGossipSelect = &GossipSelect_npc_trollbane;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_wounded_blood_elf";
newscript->GetAI = &GetAI_npc_wounded_blood_elf;
diff --git a/src/bindings/scripts/scripts/outland/nagrand.cpp b/src/bindings/scripts/scripts/outland/nagrand.cpp
index c589677f1f7..f4f3792283f 100644
--- a/src/bindings/scripts/scripts/outland/nagrand.cpp
+++ b/src/bindings/scripts/scripts/outland/nagrand.cpp
@@ -13,12 +13,14 @@
* along 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, 9868, 9918, 9874, 9991, 10107, 10108, 10044, 10172, 10646, 10085, 10987. TextId's unknown for altruis_the_sufferer and greatmother_geyah (npc_text)
SDCategory: Nagrand
EndScriptData */
+
/* ContentData
mob_shattered_rumbler
mob_lump
@@ -30,20 +32,27 @@ npc_maghar_captive
npc_creditmarker_visit_with_ancestors
mob_sparrowhawk
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## mob_shattered_rumbler - this should be done with ACID
######*/
+
struct TRINITY_DLL_DECL mob_shattered_rumblerAI : public ScriptedAI
{
bool Spawn;
+
mob_shattered_rumblerAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
Spawn = false;
}
+
void EnterCombat(Unit* who) {}
+
void SpellHit(Unit *Hitter, const SpellEntry *Spellkind)
{
if (Spellkind->Id == 32001 && !Spawn)
@@ -51,6 +60,7 @@ struct TRINITY_DLL_DECL mob_shattered_rumblerAI : public ScriptedAI
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);
@@ -64,41 +74,54 @@ CreatureAI* GetAI_mob_shattered_rumbler(Creature* pCreature)
{
return new mob_shattered_rumblerAI (pCreature);
}
+
/*######
## mob_lump
######*/
+
#define SPELL_VISUAL_SLEEP 16093
#define SPELL_SPEAR_THROW 32248
+
#define LUMP_SAY0 -1000293
#define LUMP_SAY1 -1000294
+
#define LUMP_DEFEAT -1000295
+
#define GOSSIP_HL "I need answers, ogre!"
#define GOSSIP_SL1 "Why are Boulderfist out this far? You know that this is Kurenai territory."
#define GOSSIP_SL2 "And you think you can just eat anything you want? You're obviously trying to eat the Broken of Telaar."
#define GOSSIP_SL3 "This means war, Lump! War I say!"
+
struct TRINITY_DLL_DECL mob_lumpAI : public ScriptedAI
{
mob_lumpAI(Creature *c) : ScriptedAI(c)
{
bReset = false;
}
+
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 AttackedBy(Unit* pAttacker)
{
if (m_creature->getVictim())
return;
+
if (m_creature->IsFriendlyTo(pAttacker))
return;
+
AttackStart(pAttacker);
}
+
void DamageTaken(Unit *done_by, uint32 & damage)
{
if (done_by->GetTypeId() == TYPEID_PLAYER && (m_creature->GetHealth() - damage)*100 / m_creature->GetMaxHealth() < 30)
@@ -107,6 +130,7 @@ struct TRINITY_DLL_DECL mob_lumpAI : public ScriptedAI
{
//Take 0 damage
damage = 0;
+
CAST_PLR(done_by)->AttackStop();
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveAllAuras();
@@ -115,18 +139,23 @@ struct TRINITY_DLL_DECL mob_lumpAI : public ScriptedAI
m_creature->setFaction(1080); //friendly
m_creature->SetStandState(UNIT_STAND_STATE_SIT);
DoScriptText(LUMP_DEFEAT, m_creature);
+
bReset = true;
}
}
}
+
void EnterCombat(Unit *who)
{
if (m_creature->HasAura(SPELL_VISUAL_SLEEP))
m_creature->RemoveAura(SPELL_VISUAL_SLEEP);
+
if (!m_creature->IsStandState())
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
DoScriptText(RAND(LUMP_SAY0,LUMP_SAY1), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
//check if we waiting for a reset
@@ -141,29 +170,37 @@ struct TRINITY_DLL_DECL mob_lumpAI : public ScriptedAI
}
else Reset_Timer -= diff;
}
+
//Return since we have no target
if (!UpdateVictim())
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* pCreature)
{
return new mob_lumpAI(pCreature);
}
+
bool GossipHello_mob_lump(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(9918) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(9352, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_mob_lump(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -187,18 +224,23 @@ bool GossipSelect_mob_lump(Player* pPlayer, Creature* pCreature, uint32 uiSender
}
return true;
}
+
/*####
# mob_sunspring_villager - should be done with ACID
####*/
+
struct TRINITY_DLL_DECL mob_sunspring_villagerAI : public ScriptedAI
{
mob_sunspring_villagerAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
m_creature->SetUInt32Value(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_DEAD);
m_creature->SetStandState(UNIT_STAND_STATE_DEAD);
}
+
void EnterCombat(Unit *who) {}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (spell->Id == 32146)
@@ -212,34 +254,44 @@ CreatureAI* GetAI_mob_sunspring_villager(Creature* pCreature)
{
return new mob_sunspring_villagerAI (pCreature);
}
+
/*######
## npc_altruis_the_sufferer
######*/
+
#define GOSSIP_HATS1 "I see twisted steel and smell sundered earth."
#define GOSSIP_HATS2 "Well...?"
#define GOSSIP_HATS3 "[PH] Story about Illidan's Pupil"
+
#define GOSSIP_SATS1 "Legion?"
#define GOSSIP_SATS2 "And now?"
#define GOSSIP_SATS3 "How do you see them now?"
#define GOSSIP_SATS4 "Forge camps?"
#define GOSSIP_SATS5 "Ok."
#define GOSSIP_SATS6 "[PH] Story done"
+
bool GossipHello_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
//gossip before obtaining Survey the Land
if (pPlayer->GetQuestStatus(9991) == QUEST_STATUS_NONE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HATS1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+10);
+
//gossip when Survey the Land is incomplete (technically, after the flight)
if (pPlayer->GetQuestStatus(9991) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HATS2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+20);
+
//wowwiki.com/Varedis
if (pPlayer->GetQuestStatus(10646) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HATS3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+30);
+
pPlayer->SEND_GOSSIP_MENU(9419, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -263,6 +315,7 @@ bool GossipSelect_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature,
case GOSSIP_ACTION_INFO_DEF+14:
pPlayer->SEND_GOSSIP_MENU(9424, pCreature->GetGUID());
break;
+
case GOSSIP_ACTION_INFO_DEF+20:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SATS5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 21);
pPlayer->SEND_GOSSIP_MENU(9427, pCreature->GetGUID());
@@ -271,6 +324,7 @@ bool GossipSelect_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature,
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->AreaExploredOrEventHappens(9991);
break;
+
case GOSSIP_ACTION_INFO_DEF+30:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SATS6, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 31);
pPlayer->SEND_GOSSIP_MENU(384, pCreature->GetGUID());
@@ -282,6 +336,7 @@ bool GossipSelect_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature,
}
return true;
}
+
bool QuestAccept_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if (!pPlayer->GetQuestRewardStatus(9991)) //Survey the Land, q-id 9991
@@ -291,11 +346,14 @@ bool QuestAccept_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature,
}
return true;
}
+
/*######
## npc_greatmother_geyah
######*/
+
#define GOSSIP_HGG1 "Hello, Greatmother. Garrosh told me that you wanted to speak with me."
#define GOSSIP_HGG2 "Garrosh is beyond redemption, Greatmother. I fear that in helping the Mag'har, I have convinced Garrosh that he is unfit to lead."
+
#define GOSSIP_SGG1 "You raised all of the orcs here, Greatmother?"
#define GOSSIP_SGG2 "Do you believe that?"
#define GOSSIP_SGG3 "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."
@@ -307,11 +365,13 @@ bool QuestAccept_npc_altruis_the_sufferer(Player* pPlayer, Creature* pCreature,
#define GOSSIP_SGG9 "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.)"
#define GOSSIP_SGG10 "It is my Warchief, Greatmother. The leader of my people. From my world. He ... He is the son of Durotan. He is your grandchild."
#define GOSSIP_SGG11 "I will return to Azeroth at once, Greatmother."
+
//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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(10044) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HGG1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -323,9 +383,12 @@ bool GossipHello_npc_greatmother_geyah(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
}
else
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_greatmother_geyah(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -358,6 +421,7 @@ bool GossipSelect_npc_greatmother_geyah(Player* pPlayer, Creature* pCreature, ui
pPlayer->AreaExploredOrEventHappens(10044);
pPlayer->CLOSE_GOSSIP_MENU();
break;
+
case GOSSIP_ACTION_INFO_DEF + 10:
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_SGG7, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
@@ -385,9 +449,11 @@ bool GossipSelect_npc_greatmother_geyah(Player* pPlayer, Creature* pCreature, ui
}
return true;
}
+
/*######
## npc_lantresor_of_the_blade
######*/
+
#define GOSSIP_HLB "I have killed many of your ogres, Lantresor. I have no fear."
#define GOSSIP_SLB1 "Should I know? You look like an orc to me."
#define GOSSIP_SLB2 "And the other half?"
@@ -396,15 +462,20 @@ bool GossipSelect_npc_greatmother_geyah(Player* pPlayer, Creature* pCreature, ui
#define GOSSIP_SLB5 "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."
#define GOSSIP_SLB6 "We will fight you until the end, then, Lantresor. We will not stand idly by as you pillage our towns and kill our people."
#define GOSSIP_SLB7 "What do I need to do?"
+
bool GossipHello_npc_lantresor_of_the_blade(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(10107) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(10108) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HLB, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(9361, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_lantresor_of_the_blade(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -447,9 +518,11 @@ bool GossipSelect_npc_lantresor_of_the_blade(Player* pPlayer, Creature* pCreatur
}
return true;
}
+
/*#####
## npc_maghar_captive
#####*/
+
enum eMagharCaptive
{
SAY_MAG_START = -1000482,
@@ -459,77 +532,99 @@ enum eMagharCaptive
SAY_MAG_LIGHTNING = -1000486,
SAY_MAG_SHOCK = -1000487,
SAY_MAG_COMPLETE = -1000488,
+
SPELL_CHAIN_LIGHTNING = 16006,
SPELL_EARTHBIND_TOTEM = 15786,
SPELL_FROST_SHOCK = 12548,
SPELL_HEALING_WAVE = 12491,
+
QUEST_TOTEM_KARDASH_H = 9868,
+
NPC_MURK_RAIDER = 18203,
NPC_MURK_BRUTE = 18211,
NPC_MURK_SCAVENGER = 18207,
NPC_MURK_PUTRIFIER = 18202
};
+
static float m_afAmbushA[]= {-1568.805786, 8533.873047, 1.958};
static float m_afAmbushB[]= {-1491.554321, 8506.483398, 1.248};
+
struct TRINITY_DLL_DECL npc_maghar_captiveAI : public npc_escortAI
{
npc_maghar_captiveAI(Creature* pCreature) : npc_escortAI(pCreature) { Reset(); }
+
uint32 m_uiChainLightningTimer;
uint32 m_uiHealTimer;
uint32 m_uiFrostShockTimer;
+
void Reset()
{
m_uiChainLightningTimer = 1000;
m_uiHealTimer = 0;
m_uiFrostShockTimer = 6000;
}
+
void Aggro(Unit* pWho)
{
m_creature->CastSpell(m_creature, SPELL_EARTHBIND_TOTEM, false);
}
+
void WaypointReached(uint32 uiPointId)
{
switch(uiPointId)
{
case 7:
DoScriptText(SAY_MAG_MORE, m_creature);
+
if (Creature* pTemp = m_creature->SummonCreature(NPC_MURK_PUTRIFIER, m_afAmbushB[0], m_afAmbushB[1], m_afAmbushB[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000))
DoScriptText(SAY_MAG_MORE_REPLY, pTemp);
+
m_creature->SummonCreature(NPC_MURK_PUTRIFIER, m_afAmbushB[0]-2.5f, m_afAmbushB[1]-2.5f, m_afAmbushB[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
+
m_creature->SummonCreature(NPC_MURK_SCAVENGER, m_afAmbushB[0]+2.5f, m_afAmbushB[1]+2.5f, m_afAmbushB[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
m_creature->SummonCreature(NPC_MURK_SCAVENGER, m_afAmbushB[0]+2.5f, m_afAmbushB[1]-2.5f, m_afAmbushB[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
break;
case 16:
DoScriptText(SAY_MAG_COMPLETE, m_creature);
+
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->GroupEventHappens(QUEST_TOTEM_KARDASH_H, m_creature);
+
SetRun();
break;
}
}
+
void JustSummoned(Creature* pSummoned)
{
if (pSummoned->GetEntry() == NPC_MURK_BRUTE)
DoScriptText(SAY_MAG_NO_ESCAPE, pSummoned);
+
if (pSummoned->isTotem())
return;
+
pSummoned->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
pSummoned->GetMotionMaster()->MovePoint(0, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ());
pSummoned->AI()->AttackStart(m_creature);
+
}
+
void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell)
{
if (pSpell->Id == SPELL_CHAIN_LIGHTNING)
{
if (rand()%10)
return;
+
DoScriptText(SAY_MAG_LIGHTNING, m_creature);
}
}
+
void UpdateEscortAI(const uint32 uiDiff)
{
if (/*!m_creature->SelectHostilTarget() ||*/ !m_creature->getVictim())
return;
+
if (m_uiChainLightningTimer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_CHAIN_LIGHTNING);
@@ -537,6 +632,7 @@ struct TRINITY_DLL_DECL npc_maghar_captiveAI : public npc_escortAI
}
else
m_uiChainLightningTimer -= uiDiff;
+
if (m_creature->GetHealth()*100 < m_creature->GetMaxHealth()*30)
{
if (m_uiHealTimer < uiDiff)
@@ -547,6 +643,7 @@ struct TRINITY_DLL_DECL npc_maghar_captiveAI : public npc_escortAI
else
m_uiHealTimer -= uiDiff;
}
+
if (m_uiFrostShockTimer < uiDiff)
{
DoCast(m_creature->getVictim(), SPELL_FROST_SHOCK);
@@ -554,9 +651,11 @@ struct TRINITY_DLL_DECL npc_maghar_captiveAI : public npc_escortAI
}
else
m_uiFrostShockTimer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
bool QuestAccept_npc_maghar_captive(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_TOTEM_KARDASH_H)
@@ -565,8 +664,11 @@ bool QuestAccept_npc_maghar_captive(Player* pPlayer, Creature* pCreature, const
{
pCreature->SetStandState(UNIT_STAND_STATE_STAND);
pCreature->setFaction(232);
+
pEscortAI->Start(true, false, pPlayer->GetGUID(), pQuest);
+
DoScriptText(SAY_MAG_START, pCreature);
+
pCreature->SummonCreature(NPC_MURK_RAIDER, m_afAmbushA[0]+2.5f, m_afAmbushA[1]-2.5f, m_afAmbushA[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
pCreature->SummonCreature(NPC_MURK_PUTRIFIER, m_afAmbushA[0]-2.5f, m_afAmbushA[1]+2.5f, m_afAmbushA[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
pCreature->SummonCreature(NPC_MURK_BRUTE, m_afAmbushA[0], m_afAmbushA[1], m_afAmbushA[2], 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
@@ -574,22 +676,29 @@ bool QuestAccept_npc_maghar_captive(Player* pPlayer, Creature* pCreature, const
}
return true;
}
+
CreatureAI* GetAI_npc_maghar_captive(Creature* pCreature)
{
return new npc_maghar_captiveAI(pCreature);
}
+
/*######
## npc_creditmarker_visist_with_ancestors
######*/
+
struct TRINITY_DLL_DECL npc_creditmarker_visit_with_ancestorsAI : public ScriptedAI
{
npc_creditmarker_visit_with_ancestorsAI(Creature* c) : ScriptedAI(c) {}
+
void Reset() {}
+
void EnterCombat(Unit* who) {}
+
void MoveInLineOfSight(Unit *who)
{
if (!who)
return;
+
if (who->GetTypeId() == TYPEID_PLAYER)
{
if (CAST_PLR(who)->GetQuestStatus(10085) == QUEST_STATUS_INCOMPLETE)
@@ -605,21 +714,28 @@ struct TRINITY_DLL_DECL npc_creditmarker_visit_with_ancestorsAI : public Scripte
}
}
};
+
CreatureAI* GetAI_npc_creditmarker_visit_with_ancestors(Creature* pCreature)
{
return new npc_creditmarker_visit_with_ancestorsAI (pCreature);
}
+
/*######
## mob_sparrowhawk
######*/
+
#define SPELL_SPARROWHAWK_NET 39810
#define SPELL_ITEM_CAPTIVE_SPARROWHAWK 39812
+
struct TRINITY_DLL_DECL mob_sparrowhawkAI : public ScriptedAI
{
+
mob_sparrowhawkAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Check_Timer;
uint64 PlayerGUID;
bool fleeing;
+
void Reset()
{
m_creature->RemoveAurasDueToSpell(SPELL_SPARROWHAWK_NET);
@@ -631,20 +747,26 @@ struct TRINITY_DLL_DECL mob_sparrowhawkAI : public ScriptedAI
{
if (PlayerGUID)
return;
+
ScriptedAI::AttackStart(who);
}
+
void EnterCombat(Unit* who) {}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || PlayerGUID)
return;
+
if (!PlayerGUID && who->GetTypeId() == TYPEID_PLAYER && m_creature->IsWithinDistInMap(who, 30) && CAST_PLR(who)->GetQuestStatus(10987) == QUEST_STATUS_INCOMPLETE)
{
PlayerGUID = who->GetGUID();
return;
}
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void UpdateAI(const uint32 diff)
{
if (Check_Timer < diff)
@@ -653,6 +775,7 @@ struct TRINITY_DLL_DECL mob_sparrowhawkAI : public ScriptedAI
{
if (fleeing && m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() != FLEEING_MOTION_TYPE)
fleeing = false;
+
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
if (pPlayer && m_creature->IsWithinDistInMap(pPlayer, 30))
{
@@ -672,10 +795,13 @@ struct TRINITY_DLL_DECL mob_sparrowhawkAI : public ScriptedAI
}
Check_Timer = 1000;
} else Check_Timer -= diff;
+
if (PlayerGUID)
return;
+
ScriptedAI::UpdateAI(diff);
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (caster->GetTypeId() == TYPEID_PLAYER)
@@ -690,55 +816,67 @@ struct TRINITY_DLL_DECL mob_sparrowhawkAI : public ScriptedAI
return;
}
};
+
CreatureAI* GetAI_mob_sparrowhawk(Creature* pCreature)
{
return new mob_sparrowhawkAI (pCreature);
}
+
/*####
#
####*/
+
void AddSC_nagrand()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_shattered_rumbler";
newscript->GetAI = &GetAI_mob_shattered_rumbler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_lump";
newscript->GetAI = &GetAI_mob_lump;
newscript->pGossipHello = &GossipHello_mob_lump;
newscript->pGossipSelect = &GossipSelect_mob_lump;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_sunspring_villager";
newscript->GetAI = &GetAI_mob_sunspring_villager;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_greatmother_geyah";
newscript->pGossipHello = &GossipHello_npc_greatmother_geyah;
newscript->pGossipSelect = &GossipSelect_npc_greatmother_geyah;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_maghar_captive";
newscript->GetAI = &GetAI_npc_maghar_captive;
newscript->pQuestAccept = &QuestAccept_npc_maghar_captive;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_creditmarker_visit_with_ancestors";
newscript->GetAI = &GetAI_npc_creditmarker_visit_with_ancestors;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_sparrowhawk";
newscript->GetAI = &GetAI_mob_sparrowhawk;
diff --git a/src/bindings/scripts/scripts/outland/netherstorm.cpp b/src/bindings/scripts/scripts/outland/netherstorm.cpp
index 5a8cb6399ec..d648c26b571 100644
--- a/src/bindings/scripts/scripts/outland/netherstorm.cpp
+++ b/src/bindings/scripts/scripts/outland/netherstorm.cpp
@@ -13,23 +13,28 @@
* along 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: 10337, 10438, 10652 (special flight paths), 10299,10321,10322,10323,10329,10330,10338,10365(Shutting Down Manaforge), 10198
SDCategory: Netherstorm
EndScriptData */
+
/* ContentData
npc_manaforge_control_console
go_manaforge_control_console
npc_commander_dawnforge
npc_bessy
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_manaforge_control_console
######*/
+
//used by 20209,20417,20418,20440, signed for 20209
#define EMOTE_START -1000296
#define EMOTE_60 -1000297
@@ -37,21 +42,27 @@ EndContentData */
#define EMOTE_10 -1000299
#define EMOTE_COMPLETE -1000300
#define EMOTE_ABORT -1000301
+
#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 TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
{
npc_manaforge_control_consoleAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Event_Timer;
uint32 Wave_Timer;
uint32 Phase;
@@ -59,6 +70,7 @@ struct TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
uint64 someplayer;
uint64 goConsole;
Creature* add;
+
void Reset()
{
Event_Timer = 3000;
@@ -69,7 +81,9 @@ struct TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
goConsole = 0;
Creature* add = NULL;
}
+
void EnterCombat(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
@@ -77,9 +91,11 @@ struct TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
if (spell->Id == SPELL_INTERRUPT_1)
DoSay("Silence! I kill you!",LANG_UNIVERSAL, NULL);
}*/
+
void JustDied(Unit* killer)
{
DoScriptText(EMOTE_ABORT, m_creature);
+
if (someplayer)
{
Unit* p = Unit::GetUnit((*m_creature),someplayer);
@@ -106,12 +122,14 @@ struct TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
}
}
}
+
if (goConsole)
{
if (GameObject* pGo = GameObject::GetGameObject((*m_creature),goConsole))
pGo->RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
}
}
+
void DoWaveSpawnForCreature(Creature* pCreature)
{
switch(pCreature->GetEntry())
@@ -198,6 +216,7 @@ struct TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
break;
}
}
+
void UpdateAI(const uint32 diff)
{
if (Event_Timer < diff)
@@ -249,6 +268,7 @@ struct TRINITY_DLL_DECL npc_manaforge_control_consoleAI : public ScriptedAI
break;
}
} else Event_Timer -= diff;
+
if (Wave)
{
if (Wave_Timer < diff)
@@ -262,9 +282,11 @@ CreatureAI* GetAI_npc_manaforge_control_console(Creature* pCreature)
{
return new npc_manaforge_control_consoleAI (pCreature);
}
+
/*######
## go_manaforge_control_console
######*/
+
//TODO: clean up this workaround when Trinity adds support to do it properly (with gossip selections instead of instant summon)
bool GOHello_go_manaforge_control_console(Player* pPlayer, GameObject* pGo)
{
@@ -273,8 +295,10 @@ bool GOHello_go_manaforge_control_console(Player* pPlayer, GameObject* pGo)
pPlayer->PrepareQuestMenu(pGo->GetGUID());
pPlayer->SendPreparedQuest(pGo->GetGUID());
}
+
Creature* manaforge;
manaforge = NULL;
+
switch(pGo->GetAreaId())
{
case 3726: //b'naar
@@ -298,6 +322,7 @@ bool GOHello_go_manaforge_control_console(Player* pPlayer, GameObject* pGo)
manaforge = pPlayer->SummonCreature(ENTRY_ARA_C_CONSOLE,4013.71,4028.76,192.10,1.25,TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN,125000);
break;
}
+
if (manaforge)
{
CAST_AI(npc_manaforge_control_consoleAI, manaforge->AI())->someplayer = pPlayer->GetGUID();
@@ -306,9 +331,11 @@ bool GOHello_go_manaforge_control_console(Player* pPlayer, GameObject* pGo)
}
return true;
}
+
/*######
## npc_commander_dawnforge
######*/
+
// The Speech of Dawnforge, Ardonis & Pathaleon
#define SAY_COMMANDER_DAWNFORGE_1 -1000128
#define SAY_ARCANIST_ARDONIS_1 -1000129
@@ -321,8 +348,10 @@ bool GOHello_go_manaforge_control_console(Player* pPlayer, GameObject* pGo)
#define SAY_COMMANDER_DAWNFORGE_4 -1000136
#define SAY_ARCANIST_ARDONIS_2 -1000136
#define SAY_COMMANDER_DAWNFORGE_5 -1000137
+
#define QUEST_INFO_GATHERING 10198
#define SPELL_SUNFURY_DISGUISE 34603
+
// Entries of Arcanist Ardonis, Commander Dawnforge, Pathaleon the Curators Image
const uint32 CreatureEntry[3] =
{
@@ -330,77 +359,96 @@ const uint32 CreatureEntry[3] =
19831, // Dawnforge
21504 // Pathaleon
};
+
struct TRINITY_DLL_DECL npc_commander_dawnforgeAI : public ScriptedAI
{
npc_commander_dawnforgeAI(Creature *c) : ScriptedAI(c) { Reset (); }
+
uint64 PlayerGUID;
uint64 ardonisGUID;
uint64 pathaleonGUID;
+
uint32 Phase;
uint32 PhaseSubphase;
uint32 Phase_Timer;
bool isEvent;
+
float angle_dawnforge;
float angle_ardonis;
+
void Reset()
{
PlayerGUID = 0;
ardonisGUID = 0;
pathaleonGUID = 0;
+
Phase = 1;
PhaseSubphase = 0;
Phase_Timer = 4000;
isEvent = false;
}
+
void EnterCombat(Unit *who) { }
+
void JustSummoned(Creature *summoned)
{
pathaleonGUID = summoned->GetGUID();
}
+
// Emote Ardonis and Pathaleon
void Turn_to_Pathaleons_Image()
{
Creature *ardonis = Unit::GetCreature(*m_creature,ardonisGUID);
Creature *pathaleon = Unit::GetCreature(*m_creature,pathaleonGUID);
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
if (!ardonis || !pathaleon || !pPlayer)
return;
+
//Calculate the angle to Pathaleon
angle_dawnforge = m_creature->GetAngle(pathaleon->GetPositionX(), pathaleon->GetPositionY());
angle_ardonis = ardonis->GetAngle(pathaleon->GetPositionX(), pathaleon->GetPositionY());
+
//Turn Dawnforge and update
m_creature->SetOrientation(angle_dawnforge);
m_creature->SendUpdateToPlayer(pPlayer);
//Turn Ardonis and update
ardonis->SetOrientation(angle_ardonis);
ardonis->SendUpdateToPlayer(pPlayer);
+
//Set them to kneel
m_creature->SetStandState(UNIT_STAND_STATE_KNEEL);
ardonis->SetStandState(UNIT_STAND_STATE_KNEEL);
}
+
//Set them back to each other
void Turn_to_eachother()
{
if (Unit *ardonis = Unit::GetUnit(*m_creature,ardonisGUID))
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
if (!pPlayer)
return;
+
angle_dawnforge = m_creature->GetAngle(ardonis->GetPositionX(), ardonis->GetPositionY());
angle_ardonis = ardonis->GetAngle(m_creature->GetPositionX(), m_creature->GetPositionY());
+
//Turn Dawnforge and update
m_creature->SetOrientation(angle_dawnforge);
m_creature->SendUpdateToPlayer(pPlayer);
//Turn Ardonis and update
ardonis->SetOrientation(angle_ardonis);
ardonis->SendUpdateToPlayer(pPlayer);
+
//Set state
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
ardonis->SetStandState(UNIT_STAND_STATE_STAND);
}
}
+
bool CanStartEvent(Player* pPlayer)
{
if (!isEvent)
@@ -408,39 +456,49 @@ struct TRINITY_DLL_DECL npc_commander_dawnforgeAI : public ScriptedAI
Creature* ardonis = me->FindNearestCreature(CreatureEntry[0], 10.0f);
if (!ardonis)
return false;
+
ardonisGUID = ardonis->GetGUID();
PlayerGUID = pPlayer->GetGUID();
+
isEvent = true;
+
Turn_to_eachother();
return true;
}
+
debug_log("TSCR: npc_commander_dawnforge event already in progress, need to wait.");
return false;
}
+
void UpdateAI(const uint32 diff)
{
//Is event even running?
if (!isEvent)
return;
+
//Phase timing
if (Phase_Timer >= diff)
{
Phase_Timer -= diff;
return;
}
+
Unit *ardonis = Unit::GetUnit(*m_creature,ardonisGUID);
Unit *pathaleon = Unit::GetUnit(*m_creature,pathaleonGUID);
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
if (!ardonis || !pPlayer)
{
Reset();
return;
}
+
if (Phase > 4 && !pathaleon)
{
Reset();
return;
}
+
//Phase 1 Dawnforge say
switch (Phase)
{
@@ -543,42 +601,55 @@ struct TRINITY_DLL_DECL npc_commander_dawnforgeAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_commander_dawnforge(Creature* pCreature)
{
return new npc_commander_dawnforgeAI(pCreature);
}
+
bool AreaTrigger_at_commander_dawnforge(Player* pPlayer, AreaTriggerEntry *at)
{
//if player lost aura or not have at all, we should not try start event.
if (!pPlayer->HasAura(SPELL_SUNFURY_DISGUISE))
return false;
+
if (pPlayer->isAlive() && pPlayer->GetQuestStatus(QUEST_INFO_GATHERING) == QUEST_STATUS_INCOMPLETE)
{
Creature* Dawnforge = pPlayer->FindNearestCreature(CreatureEntry[1], 30.0f);
+
if (!Dawnforge)
return false;
+
if (CAST_AI(npc_commander_dawnforgeAI, Dawnforge->AI())->CanStartEvent(pPlayer))
return true;
}
return false;
}
+
/*######
## npc_professor_dabiri
######*/
+
#define SPELL_PHASE_DISTRUPTOR 35780
#define GOSSIP_ITEM "I need a new phase distruptor, Professor"
#define WHISPER_DABIRI -1000302
+
#define QUEST_DIMENSIUS 10439
#define QUEST_ON_NETHERY_WINGS 10438
+
bool GossipHello_npc_professor_dabiri(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_ON_NETHERY_WINGS) == QUEST_STATUS_INCOMPLETE && !pPlayer->HasItemCount(29778, 1))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_professor_dabiri(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -586,54 +657,69 @@ bool GossipSelect_npc_professor_dabiri(Player* pPlayer, Creature* pCreature, uin
pCreature->CastSpell(pPlayer, SPELL_PHASE_DISTRUPTOR, false);
pPlayer->CLOSE_GOSSIP_MENU();
}
+
return true;
}
+
bool QuestAccept_npc_professor_dabiri(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if (quest->GetQuestId() == QUEST_DIMENSIUS)
DoScriptText(WHISPER_DABIRI, pCreature, pPlayer);
+
return true;
}
+
/*######
## mob_phase_hunter
######*/
+
#define SUMMONED_MOB 19595
#define EMOTE_WEAK -1000303
+
// Spells
#define SPELL_PHASE_SLIP 36574
#define SPELL_MANA_BURN 13321
#define SPELL_MATERIALIZE 34804
#define SPELL_DE_MATERIALIZE 34804
+
struct TRINITY_DLL_DECL mob_phase_hunterAI : public ScriptedAI
{
+
mob_phase_hunterAI(Creature *c) : ScriptedAI(c) {}
+
bool Weak;
bool Materialize;
bool Drained;
+
int WeakPercent;
uint64 PlayerGUID;
uint32 Health;
uint32 Level;
uint32 PhaseSlipVulnerabilityTimer;
uint32 ManaBurnTimer;
+
void Reset()
{
Weak = false;
Materialize = false;
Drained = false;
+
WeakPercent = 25 + (rand()%16); // 25-40
PlayerGUID = 0;
ManaBurnTimer = 5000 + (rand()%3 * 1000); // 5-8 sec cd
}
+
void EnterCombat(Unit *who)
{
if (Player* pPlayer = who->GetCharmerOrOwnerPlayerOrPlayerItself())
PlayerGUID = pPlayer->GetGUID();
}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
DoCast(m_creature, SPELL_DE_MATERIALIZE);
}
+
void UpdateAI(const uint32 diff)
{
if (!Materialize)
@@ -641,10 +727,13 @@ struct TRINITY_DLL_DECL mob_phase_hunterAI : public ScriptedAI
DoCast(m_creature, SPELL_MATERIALIZE);
Materialize = true;
}
+
if (m_creature->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED) || m_creature->hasUnitState(UNIT_STAT_ROOT)) // if the mob is rooted/slowed by spells eg.: Entangling Roots, Frost Nova, Hamstring, Crippling Poison, etc. => remove it
DoCast(m_creature, SPELL_PHASE_SLIP);
+
if (!UpdateVictim())
return;
+
if (ManaBurnTimer < diff) // cast Mana Burn
{
if (m_creature->getVictim()->GetCreateMana() > 0)
@@ -653,9 +742,11 @@ struct TRINITY_DLL_DECL mob_phase_hunterAI : public ScriptedAI
ManaBurnTimer = 8000 + (rand()%10 * 1000); // 8-18 sec cd
}
}else ManaBurnTimer -= diff;
+
if (PlayerGUID) // start: support for quest 10190
{
Unit* target = Unit::GetUnit((*m_creature), PlayerGUID);
+
if (target && !Weak && m_creature->GetHealth() < (m_creature->GetMaxHealth() / 100 * WeakPercent)
&& CAST_PLR(target)->GetQuestStatus(10190) == QUEST_STATUS_INCOMPLETE)
{
@@ -665,14 +756,19 @@ struct TRINITY_DLL_DECL mob_phase_hunterAI : public ScriptedAI
if (Weak && !Drained && m_creature->HasAura(34219))
{
Drained = true;
+
Health = m_creature->GetHealth(); // get the normal mob's data
Level = m_creature->getLevel();
+
m_creature->AttackStop(); // delete the normal mob
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
m_creature->RemoveCorpse();
+
Creature* DrainedPhaseHunter = NULL;
+
if (!DrainedPhaseHunter)
DrainedPhaseHunter = m_creature->SummonCreature(SUMMONED_MOB, m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(), m_creature->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000); // summon the mob
+
if (DrainedPhaseHunter)
{
DrainedPhaseHunter->SetLevel(Level); // set the summoned mob's data
@@ -682,35 +778,45 @@ struct TRINITY_DLL_DECL mob_phase_hunterAI : public ScriptedAI
}
}
}// end: support for quest 10190
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_phase_hunter(Creature* pCreature)
{
return new mob_phase_hunterAI (pCreature);
}
+
/*######
## npc_bessy
######*/
+
#define Q_ALMABTRIEB 10337
#define N_THADELL 20464
#define SPAWN_FIRST 20512
#define SPAWN_SECOND 19881
#define SAY_THADELL_1 -1000304
#define SAY_THADELL_2 -1000305
+
struct TRINITY_DLL_DECL npc_bessyAI : public npc_escortAI
{
+
npc_bessyAI(Creature *c) : npc_escortAI(c) {}
+
void JustDied(Unit* killer)
{
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->FailQuest(Q_ALMABTRIEB);
}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 3: //first spawn
@@ -718,10 +824,12 @@ struct TRINITY_DLL_DECL npc_bessyAI : public npc_escortAI
m_creature->SummonCreature(SPAWN_FIRST, 2449.53, 2184.43, 96.36, 6.27, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
m_creature->SummonCreature(SPAWN_FIRST, 2449.85, 2186.34, 97.57, 6.08, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
break;
+
case 7:
m_creature->SummonCreature(SPAWN_SECOND, 2309.64, 2186.24, 92.25, 6.06, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
m_creature->SummonCreature(SPAWN_SECOND, 2309.25, 2183.46, 91.75, 6.22, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
break;
+
case 12:
if (pPlayer)
pPlayer->GroupEventHappens(Q_ALMABTRIEB, m_creature);
@@ -732,15 +840,19 @@ struct TRINITY_DLL_DECL npc_bessyAI : public npc_escortAI
DoScriptText(SAY_THADELL_2, m_creature, pPlayer); break;
}
}
+
void JustSummoned(Creature* summoned)
{
summoned->AI()->AttackStart(m_creature);
}
+
void Reset()
{
me->RestoreFaction();
}
+
};
+
bool QuestAccept_npc_bessy(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == Q_ALMABTRIEB)
@@ -751,42 +863,52 @@ bool QuestAccept_npc_bessy(Player* pPlayer, Creature* pCreature, Quest const* qu
}
return true;
}
+
CreatureAI* GetAI_npc_bessy(Creature* pCreature)
{
return new npc_bessyAI(pCreature);
}
+
/*######
##
######*/
+
void AddSC_netherstorm()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_manaforge_control_console";
newscript->pGOHello = &GOHello_go_manaforge_control_console;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_manaforge_control_console";
newscript->GetAI = &GetAI_npc_manaforge_control_console;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_commander_dawnforge";
newscript->GetAI = &GetAI_npc_commander_dawnforge;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "at_commander_dawnforge";
newscript->pAreaTrigger = &AreaTrigger_at_commander_dawnforge;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_professor_dabiri";
newscript->pGossipHello = &GossipHello_npc_professor_dabiri;
newscript->pGossipSelect = &GossipSelect_npc_professor_dabiri;
newscript->pQuestAccept = &QuestAccept_npc_professor_dabiri;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_phase_hunter";
newscript->GetAI = &GetAI_mob_phase_hunter;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_bessy";
newscript->GetAI = &GetAI_npc_bessy;
diff --git a/src/bindings/scripts/scripts/outland/shadowmoon_valley.cpp b/src/bindings/scripts/scripts/outland/shadowmoon_valley.cpp
index a0424c5ac7d..3a6a121ff4d 100644
--- a/src/bindings/scripts/scripts/outland/shadowmoon_valley.cpp
+++ b/src/bindings/scripts/scripts/outland/shadowmoon_valley.cpp
@@ -13,12 +13,14 @@
* along 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, 10458, 10481, 10480, 11082, 10781, 10451. Vendor Drake Dealer Hurlunk.
SDCategory: Shadowmoon Valley
EndScriptData */
+
/* ContentData
mob_mature_netherwing_drake
mob_enslaved_netherwing_drake
@@ -36,53 +38,70 @@ npc_lord_illidan_stormrage
go_crystal_prison
npc_enraged_spirit
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*#####
# mob_mature_netherwing_drake
#####*/
+
enum eMatureNetherwing
{
SAY_JUST_EATEN = -1000222,
+
SPELL_PLACE_CARCASS = 38439,
SPELL_JUST_EATEN = 38502,
SPELL_NETHER_BREATH = 38467,
POINT_ID = 1,
+
GO_CARCASS = 185155,
+
QUEST_KINDNESS = 10804,
NPC_EVENT_PINGER = 22131
};
+
struct TRINITY_DLL_DECL mob_mature_netherwing_drakeAI : public ScriptedAI
{
mob_mature_netherwing_drakeAI(Creature* c) : ScriptedAI(c) { }
+
uint64 uiPlayerGUID;
+
bool bCanEat;
bool bIsEating;
+
uint32 EatTimer;
uint32 CastTimer;
+
void Reset()
{
uiPlayerGUID = 0;
+
bCanEat = false;
bIsEating = false;
+
EatTimer = 5000;
CastTimer = 5000;
}
+
void SpellHit(Unit* pCaster, SpellEntry const* pSpell)
{
if (bCanEat || bIsEating)
return;
+
if (pCaster->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_PLACE_CARCASS && !m_creature->HasAura(SPELL_JUST_EATEN))
{
uiPlayerGUID = pCaster->GetGUID();
bCanEat = true;
}
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type != POINT_MOTION_TYPE)
return;
+
if (id == POINT_ID)
{
bIsEating = true;
@@ -90,6 +109,7 @@ struct TRINITY_DLL_DECL mob_mature_netherwing_drakeAI : public ScriptedAI
m_creature->HandleEmoteCommand(EMOTE_ONESHOT_ATTACKUNARMED);
}
}
+
void UpdateAI(const uint32 diff)
{
if (bCanEat || bIsEating)
@@ -104,8 +124,10 @@ struct TRINITY_DLL_DECL mob_mature_netherwing_drakeAI : public ScriptedAI
{
if (m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
m_creature->GetMotionMaster()->MovementExpired();
+
m_creature->GetMotionMaster()->MoveIdle();
m_creature->StopMoving();
+
m_creature->GetMotionMaster()->MovePoint(POINT_ID, pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ());
}
}
@@ -115,43 +137,56 @@ struct TRINITY_DLL_DECL mob_mature_netherwing_drakeAI : public ScriptedAI
{
DoCast(m_creature, SPELL_JUST_EATEN);
DoScriptText(SAY_JUST_EATEN, m_creature);
+
if (Player* pPlr = Unit::GetPlayer(uiPlayerGUID))
{
pPlr->KilledMonsterCredit(NPC_EVENT_PINGER, m_creature->GetGUID());
+
if (GameObject* pGo = pPlr->FindNearestGameObject(GO_CARCASS, 10))
pGo->Delete();
}
+
Reset();
m_creature->GetMotionMaster()->Clear();
}
}
else
EatTimer -= diff;
+
return;
}
+
if (!UpdateVictim())
return;
+
if (CastTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_NETHER_BREATH);
CastTimer = 5000;
}else CastTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_mature_netherwing_drake(Creature* pCreature)
{
return new mob_mature_netherwing_drakeAI(pCreature);
}
+
/*###
# 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 TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
{
mob_enslaved_netherwing_drakeAI(Creature* c) : ScriptedAI(c)
@@ -160,42 +195,53 @@ struct TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
Tapped = false;
Reset();
}
+
uint64 PlayerGUID;
uint32 FlyTimer;
bool Tapped;
+
void Reset()
{
if (!Tapped)
m_creature->setFaction(FACTION_DEFAULT);
+
FlyTimer = 10000;
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
m_creature->SetVisibility(VISIBILITY_ON);
}
+
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);
+
Unit* Dragonmaw = me->FindNearestCreature(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)
@@ -203,6 +249,7 @@ struct TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
Unit* plr = Unit::GetUnit((*m_creature), PlayerGUID);
if (plr)
DoCast(plr, SPELL_FORCE_OF_NELTHARAKU, true);
+
PlayerGUID = 0;
}
m_creature->SetVisibility(VISIBILITY_OFF);
@@ -211,6 +258,7 @@ struct TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
m_creature->RemoveCorpse();
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -228,9 +276,11 @@ struct TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
/*
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*/
+
Position pos;
if(Unit* EscapeDummy = me->FindNearestCreature(CREATURE_ESCAPE_DUMMY, 30))
EscapeDummy->GetPosition(&pos);
@@ -239,6 +289,7 @@ struct TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
m_creature->GetRandomNearPosition(pos, 20);
pos.m_positionZ += 25;
}
+
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
m_creature->GetMotionMaster()->MovePoint(1, pos);
}
@@ -246,52 +297,65 @@ struct TRINITY_DLL_DECL mob_enslaved_netherwing_drakeAI : public ScriptedAI
}else FlyTimer -= diff;
return;
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_enslaved_netherwing_drake(Creature* pCreature)
{
return new mob_enslaved_netherwing_drakeAI(pCreature);
}
+
/*#####
# mob_dragonmaw_peon
#####*/
+
struct TRINITY_DLL_DECL mob_dragonmaw_peonAI : public ScriptedAI
{
mob_dragonmaw_peonAI(Creature* c) : ScriptedAI(c) {}
+
uint64 PlayerGUID;
bool Tapped;
uint32 PoisonTimer;
+
void Reset()
{
PlayerGUID = 0;
Tapped = false;
PoisonTimer = 0;
}
+
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)
@@ -308,40 +372,53 @@ struct TRINITY_DLL_DECL mob_dragonmaw_peonAI : public ScriptedAI
}else PoisonTimer -= diff;
}
};
+
CreatureAI* GetAI_mob_dragonmaw_peon(Creature* pCreature)
{
return new mob_dragonmaw_peonAI(pCreature);
}
+
/*######
## npc_drake_dealer_hurlunk
######*/
+
bool GossipHello_npc_drake_dealer_hurlunk(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isVendor() && pPlayer->GetReputationRank(1015) == REP_EXALTED)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_drake_dealer_hurlunk(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_flanis_swiftwing_and_kagrosh
######*/
+
#define GOSSIP_HSK1 "Take Flanis's Pack"
#define GOSSIP_HSK2 "Take Kagrosh's Pack"
+
bool GossipHello_npcs_flanis_swiftwing_and_kagrosh(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(10583) == QUEST_STATUS_INCOMPLETE && !pPlayer->HasItemCount(30658,1,true))
pPlayer->ADD_GOSSIP_ITEM(0, GOSSIP_HSK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
if (pPlayer->GetQuestStatus(10601) == QUEST_STATUS_INCOMPLETE && !pPlayer->HasItemCount(30659,1,true))
pPlayer->ADD_GOSSIP_ITEM(0, GOSSIP_HSK2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npcs_flanis_swiftwing_and_kagrosh(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -366,23 +443,29 @@ bool GossipSelect_npcs_flanis_swiftwing_and_kagrosh(Player* pPlayer, Creature* p
}
return true;
}
+
/*######
## npc_murkblood_overseer
######*/
+
#define QUEST_11082 11082
+
#define GOSSIP_HMO "I am here for you, overseer."
#define GOSSIP_SMO1 "How dare you question an overseer of the Dragonmaw!"
#define GOSSIP_SMO2 "Who speaks of me? What are you talking about, broken?"
#define GOSSIP_SMO3 "Continue please."
#define GOSSIP_SMO4 "Who are these bidders?"
#define GOSSIP_SMO5 "Well... yes."
+
bool GossipHello_npc_murkblood_overseer(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_11082) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(0, GOSSIP_HMO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(10940, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_murkblood_overseer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -421,22 +504,29 @@ bool GossipSelect_npc_murkblood_overseer(Player* pPlayer, Creature* pCreature, u
}
return true;
}
+
/*######
## npc_neltharaku
######*/
+
#define GOSSIP_HN "I am listening, dragon"
#define GOSSIP_SN1 "But you are dragons! How could orcs do this to you?"
#define GOSSIP_SN2 "Your mate?"
#define GOSSIP_SN3 "I have battled many beasts, dragon. I will help you."
+
bool GossipHello_npc_neltharaku(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(10814) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(0, GOSSIP_HN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(10613, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_neltharaku(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -460,9 +550,11 @@ bool GossipSelect_npc_neltharaku(Player* pPlayer, Creature* pCreature, uint32 ui
}
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?"
@@ -470,20 +562,24 @@ bool GossipSelect_npc_neltharaku(Player* pPlayer, Creature* pCreature, uint32 ui
#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* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
if (pCreature->isVendor())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
if (pPlayer->GetQuestStatus(10519) == QUEST_STATUS_INCOMPLETE)
{
pPlayer->ADD_GOSSIP_ITEM(0, GOSSIP_ORONOK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->SEND_GOSSIP_MENU(10312, pCreature->GetGUID());
}else
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_oronok_tornheart(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -522,38 +618,50 @@ bool GossipSelect_npc_oronok_tornheart(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
/*####
# npc_karynaku
####*/
+
enum eKarynaku
{
QUEST_ALLY_OF_NETHER = 10870,
+
TAXI_PATH_ID = 649
};
+
bool QuestAccept_npc_karynaku(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_ALLY_OF_NETHER)
pPlayer->ActivateTaxiPathTo(TAXI_PATH_ID); //pPlayer->ActivateTaxiPathTo(649);
+
return true;
}
+
/*####
# npc_overlord_morghor
####*/
+
#define QUEST_LORD_ILLIDAN_STORMRAGE 11108
+
#define C_ILLIDAN 22083
#define C_YARZILL 23141
+
#define SPELL_ONE 39990 // Red Lightning Bolt
#define SPELL_TWO 41528 // Mark of Stormrage
#define SPELL_THREE 40216 // Dragonaw Faction
#define SPELL_FOUR 42016 // Dragonaw Trasform
+
#define OVERLORD_SAY_1 -1000206
#define OVERLORD_SAY_2 -1000207
#define OVERLORD_SAY_3 -1000208 //signed for 28315
#define OVERLORD_SAY_4 -1000209
#define OVERLORD_SAY_5 -1000210
#define OVERLORD_SAY_6 -1000211
+
#define OVERLORD_YELL_1 -1000212
#define OVERLORD_YELL_2 -1000213
+
#define LORD_ILLIDAN_SAY_1 -1000214
#define LORD_ILLIDAN_SAY_2 -1000215
#define LORD_ILLIDAN_SAY_3 -1000216
@@ -561,23 +669,32 @@ bool QuestAccept_npc_karynaku(Player* pPlayer, Creature* pCreature, Quest const*
#define LORD_ILLIDAN_SAY_5 -1000218
#define LORD_ILLIDAN_SAY_6 -1000219
#define LORD_ILLIDAN_SAY_7 -1000220
+
#define YARZILL_THE_MERC_SAY -1000221
+
struct TRINITY_DLL_DECL npc_overlord_morghorAI : public ScriptedAI
{
npc_overlord_morghorAI(Creature *c) : ScriptedAI(c) {}
+
uint64 PlayerGUID;
uint64 IllidanGUID;
+
uint32 ConversationTimer;
uint32 Step;
+
bool Event;
+
void Reset()
{
PlayerGUID = 0;
IllidanGUID = 0;
+
ConversationTimer = 0;
Step = 0;
+
Event = false;
}
+
void StartEvent()
{
m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0);
@@ -598,15 +715,19 @@ struct TRINITY_DLL_DECL npc_overlord_morghorAI : public ScriptedAI
Step = 0;
Event = true;
}
+
uint32 NextStep(uint32 Step)
{
Unit* plr = Unit::GetUnit((*m_creature), PlayerGUID);
+
Unit* Illi = Unit::GetUnit((*m_creature), IllidanGUID);
+
if (!plr || !Illi)
{
EnterEvadeMode();
return 0;
}
+
switch(Step)
{
case 0: return 0; break;
@@ -637,9 +758,7 @@ struct TRINITY_DLL_DECL npc_overlord_morghorAI : public ScriptedAI
plr->RemoveAurasDueToSpell(SPELL_THREE);
plr->RemoveAurasDueToSpell(SPELL_FOUR);
return 5000;
- }
- else
- {
+ }else{
CAST_PLR(plr)->FailQuest(QUEST_LORD_ILLIDAN_STORMRAGE); Step = 30; return 100;
}break;
case 17: DoScriptText(LORD_ILLIDAN_SAY_5, Illi); return 5000; break;
@@ -693,13 +812,16 @@ struct TRINITY_DLL_DECL npc_overlord_morghorAI : public ScriptedAI
return 1000;}break;
case 32: m_creature->GetMotionMaster()->MovePoint(0, -5085.77, 577.231, 86.6719); return 5000; break;
case 33: Reset(); return 100; break;
+
default : return 0;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!ConversationTimer)
return;
+
if (ConversationTimer <= diff)
{
if (Event && IllidanGUID && PlayerGUID)
@@ -709,10 +831,12 @@ struct TRINITY_DLL_DECL npc_overlord_morghorAI : public ScriptedAI
}else ConversationTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_overlord_morghor(Creature* pCreature)
{
return new npc_overlord_morghorAI(pCreature);
}
+
bool QuestAccept_npc_overlord_morghor(Player* pPlayer, Creature* pCreature, const Quest *_Quest)
{
if (_Quest->GetQuestId() == QUEST_LORD_ILLIDAN_STORMRAGE)
@@ -723,9 +847,11 @@ bool QuestAccept_npc_overlord_morghor(Player* pPlayer, Creature* pCreature, cons
}
return false;
}
+
/*####
# npc_earthmender_wilda
####*/
+
enum eEarthmender
{
SAY_WIL_START = -1000381,
@@ -738,27 +864,35 @@ enum eEarthmender
SAY_WIL_PROGRESS5 = -1000388,
SAY_WIL_JUST_AHEAD = -1000389,
SAY_WIL_END = -1000390,
+
SPELL_CHAIN_LIGHTNING = 16006,
SPELL_EARTHBING_TOTEM = 15786,
SPELL_FROST_SHOCK = 12548,
SPELL_HEALING_WAVE = 12491,
+
QUEST_ESCAPE_COILSCAR = 10451,
NPC_COILSKAR_ASSASSIN = 21044,
FACTION_EARTHEN = 1726 //guessed
};
+
struct TRINITY_DLL_DECL npc_earthmender_wildaAI : public npc_escortAI
{
npc_earthmender_wildaAI(Creature* pCreature) : npc_escortAI(pCreature) { }
+
uint32 m_uiHealingTimer;
+
void Reset()
{
m_uiHealingTimer = 0;
}
+
void WaypointReached(uint32 uiPointId)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(uiPointId)
{
case 13:
@@ -806,31 +940,37 @@ struct TRINITY_DLL_DECL npc_earthmender_wildaAI : public npc_escortAI
break;
case 50:
DoScriptText(SAY_WIL_END, m_creature, pPlayer);
+
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->GroupEventHappens(QUEST_ESCAPE_COILSCAR, m_creature);
break;
}
}
+
void JustSummoned(Creature* pSummoned)
{
if (pSummoned->GetEntry() == NPC_COILSKAR_ASSASSIN)
pSummoned->AI()->AttackStart(m_creature);
}
+
//this is very unclear, random say without no real relevance to script/event
void DoRandomSay()
{
DoScriptText(RAND(SAY_WIL_PROGRESS2,SAY_WIL_PROGRESS4,SAY_WIL_PROGRESS5), m_creature);
}
+
void DoSpawnAssassin()
{
//unknown where they actually appear
DoSummon(NPC_COILSKAR_ASSASSIN, me, 15.0f, 5000, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT);
}
+
void Aggro(Unit* pWho)
{
//don't always use
if (rand()%5)
return;
+
//only aggro text if not player
if (pWho->GetTypeId() != TYPEID_PLAYER)
{
@@ -839,11 +979,14 @@ struct TRINITY_DLL_DECL npc_earthmender_wildaAI : public npc_escortAI
DoScriptText(RAND(SAY_WIL_AGGRO1, SAY_WIL_AGGRO2), pWho);
}
}
+
void UpdateAI(const uint32 uiDiff)
{
npc_escortAI::UpdateAI(uiDiff);
+
if (!UpdateVictim())
return;
+
//TODO: add more abilities
if (m_creature->GetHealth()*100 / m_creature->GetMaxHealth() <= 30)
{
@@ -857,24 +1000,29 @@ struct TRINITY_DLL_DECL npc_earthmender_wildaAI : public npc_escortAI
}
}
};
+
CreatureAI* GetAI_npc_earthmender_wilda(Creature* pCreature)
{
return new npc_earthmender_wildaAI(pCreature);
}
+
bool QuestAccept_npc_earthmender_wilda(Player* pPlayer, Creature* pCreature, const Quest* pQuest)
{
if (pQuest->GetQuestId() == QUEST_ESCAPE_COILSCAR)
{
DoScriptText(SAY_WIL_START, pCreature, pPlayer);
pCreature->setFaction(FACTION_EARTHEN);
+
if (npc_earthmender_wildaAI* pEscortAI = CAST_AI(npc_earthmender_wildaAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID(), pQuest);
}
return true;
}
+
/*#####
# Quest: Battle of the crimson watch
#####*/
+
/* ContentData
Battle of the crimson watch - creatures, gameobjects and defines
mob_illidari_spawn : Adds that are summoned in the Crimson Watch battle.
@@ -882,15 +1030,19 @@ mob_torloth_the_magnificent : Final Creature that players have to face before qu
npc_lord_illidan_stormrage : Creature that controls the event.
go_crystal_prison : GameObject that begins the event and hands out quest
EndContentData */
+
#define END_TEXT -1000366 //signed for 10646
+
#define QUEST_BATTLE_OF_THE_CRIMSON_WATCH 10781
#define EVENT_AREA_RADIUS 65 //65yds
#define EVENT_COOLDOWN 30000 //in ms. appear after event completed or failed (should be = Adds despawn time)
+
struct TorlothCinematic
{
int32 TextId;
uint32 pCreature, Timer;
};
+
// Creature 0 - Torloth, 1 - Illidan
static TorlothCinematic TorlothAnim[]=
{
@@ -902,10 +1054,12 @@ static TorlothCinematic TorlothAnim[]=
{NULL, 0, 3000},
{NULL, 0, NULL}
};
+
struct Location
{
float x, y, z, o;
};
+
//Cordinates for Spawns
static Location SpawnLocation[]=
{
@@ -927,12 +1081,14 @@ static Location SpawnLocation[]=
{-4616.4736, 1384.2170, 139.9, 4.971},//Illidari Highlord
{-4627.1240, 1378.8752, 139.9, 2.544} //Torloth The Magnificent
};
+
struct WaveData
{
uint8 SpawnCount, UsedSpawnPoint;
uint32 CreatureId, SpawnTimer,YellTimer;
int32 WaveTextId;
};
+
static WaveData WavesInfo[]=
{
{9, 0, 22075, 10000, 7000, -1000371}, //Illidari Soldier
@@ -940,10 +1096,12 @@ static WaveData WavesInfo[]=
{4, 11, 19797, 10000, 7000, -1000373}, //Illidari Highlord
{1, 15, 22076, 10000, 7000, -1000374} //Torloth The Magnificent
};
+
struct SpawnSpells
{
uint32 Timer1, Timer2, SpellId;
};
+
static SpawnSpells SpawnCast[]=
{
{10000, 15000, 35871}, // Illidari Soldier Cast - Spellbreaker
@@ -956,39 +1114,46 @@ static SpawnSpells SpawnCast[]=
{18000, 20000, 39082}, // Torloth the Magnificent Cast - Shadowfury
{25000, 28000, 33961} // Torloth the Magnificent Cast - Spell Reflection
};
+
/*######
# mob_illidari_spawn
######*/
+
struct TRINITY_DLL_DECL mob_illidari_spawnAI : public ScriptedAI
{
mob_illidari_spawnAI(Creature* c) : ScriptedAI(c) {}
+
uint64 LordIllidanGUID;
uint32 SpellTimer1, SpellTimer2, SpellTimer3;
bool Timers;
+
void Reset()
{
LordIllidanGUID = 0;
Timers = false;
}
+
void EnterCombat(Unit* who) {}
void JustDied(Unit* slayer);
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (!Timers)
{
- if (m_creature->GetEntry() == 22075) //Illidari Soldier
+ if (m_creature->GetEntry() == 22075)//Illidari Soldier
{
SpellTimer1 = SpawnCast[0].Timer1 + (rand()%4 * 1000);
}
- if (m_creature->GetEntry() == 22074) //Illidari Mind Breaker
+ if (m_creature->GetEntry() == 22074)//Illidari Mind Breaker
{
SpellTimer1 = SpawnCast[1].Timer1 + (rand()%10 * 1000);
SpellTimer2 = SpawnCast[2].Timer1 + (rand()%4 * 1000);
SpellTimer3 = SpawnCast[3].Timer1 + (rand()%4 * 1000);
}
- if (m_creature->GetEntry() == 19797) // Illidari Highlord
+ if (m_creature->GetEntry() == 19797)// Illidari Highlord
{
SpellTimer1 = SpawnCast[4].Timer1 + (rand()%4 * 1000);
SpellTimer2 = SpawnCast[5].Timer1 + (rand()%4 * 1000);
@@ -1018,11 +1183,13 @@ struct TRINITY_DLL_DECL mob_illidari_spawnAI : public ScriptedAI
}else SpellTimer1 = 2000;
}
}else SpellTimer1 -= diff;
+
if (SpellTimer2 < diff)
{
DoCast(m_creature->getVictim(), SpawnCast[2].SpellId);//Psychic Scream
SpellTimer2 = SpawnCast[2].Timer2 + (rand()%13 * 1000);
}else SpellTimer2 -= diff;
+
if (SpellTimer3 < diff)
{
DoCast(m_creature->getVictim(), SpawnCast[3].SpellId);//Mind Blast
@@ -1037,26 +1204,35 @@ struct TRINITY_DLL_DECL mob_illidari_spawnAI : public ScriptedAI
DoCast(m_creature->getVictim(), SpawnCast[4].SpellId);//Curse Of Flames
SpellTimer1 = SpawnCast[4].Timer2 + (rand()%10 * 1000);
}else SpellTimer1 -= diff;
+
if (SpellTimer2 < diff)
{
DoCast(m_creature->getVictim(), SpawnCast[5].SpellId);//Flamestrike
SpellTimer2 = SpawnCast[5].Timer2 + (rand()%7 * 13000);
}else SpellTimer2 -= diff;
}
+
DoMeleeAttackIfReady();
}
};
+
/*######
# mob_torloth_the_magnificent
#####*/
+
struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
{
mob_torloth_the_magnificentAI(Creature* c) : ScriptedAI(c) {}
+
uint32 AnimationTimer, SpellTimer1, SpellTimer2, SpellTimer3;
+
uint8 AnimationCount;
+
uint64 LordIllidanGUID;
uint64 AggroTargetGUID;
+
bool Timers;
+
void Reset()
{
AnimationTimer = 4000;
@@ -1064,23 +1240,31 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
LordIllidanGUID = 0;
AggroTargetGUID = 0;
Timers = false;
+
m_creature->addUnitState(UNIT_STAT_ROOT);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->SetUInt64Value(UNIT_FIELD_TARGET, 0);
}
+
void EnterCombat(Unit* who){}
+
void HandleAnimation()
{
Creature* pCreature = m_creature;
+
if (TorlothAnim[AnimationCount].pCreature == 1)
{
pCreature = (Unit::GetCreature(*m_creature, LordIllidanGUID));
+
if (!pCreature)
return;
}
+
if (TorlothAnim[AnimationCount].TextId)
DoScriptText(TorlothAnim[AnimationCount].TextId, pCreature);
+
AnimationTimer = TorlothAnim[AnimationCount].Timer;
+
switch(AnimationCount)
{
case 0:
@@ -1102,6 +1286,7 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->clearUnitState(UNIT_STAT_ROOT);
+
float x, y, z;
AggroTarget->GetPosition(x,y,z);
m_creature->GetMotionMaster()->MovePoint(0,x,y,z);
@@ -1110,6 +1295,7 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
}
++AnimationCount;
}
+
void UpdateAI(const uint32 diff)
{
if (AnimationTimer)
@@ -1119,16 +1305,19 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
HandleAnimation();
}else AnimationTimer -= diff;
}
+
if (AnimationCount < 6)
{
m_creature->CombatStop();
}else if (!Timers)
{
+
SpellTimer1 = SpawnCast[6].Timer1;
SpellTimer2 = SpawnCast[7].Timer1;
SpellTimer3 = SpawnCast[8].Timer1;
Timers = true;
}
+
if (Timers)
{
if (SpellTimer1 < diff)
@@ -1136,19 +1325,23 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
DoCast(m_creature->getVictim(), SpawnCast[6].SpellId);//Cleave
SpellTimer1 = SpawnCast[6].Timer2 + (rand()%10 * 1000);
}else SpellTimer1 -= diff;
+
if (SpellTimer2 < diff)
{
DoCast(m_creature->getVictim(), SpawnCast[7].SpellId);//Shadowfury
SpellTimer2 = SpawnCast[7].Timer2 + (rand()%5 * 1000);
}else SpellTimer2 -= diff;
+
if (SpellTimer3 < diff)
{
DoCast(m_creature, SpawnCast[8].SpellId);
SpellTimer3 = SpawnCast[8].Timer2 + (rand()%7 * 1000);//Spell Reflection
}else SpellTimer3 -= diff;
}
+
DoMeleeAttackIfReady();
}
+
void JustDied(Unit* slayer)
{
if (slayer)
@@ -1159,10 +1352,12 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
if (owner->GetTypeId() == TYPEID_PLAYER)
CAST_PLR(owner)->GroupEventHappens(QUEST_BATTLE_OF_THE_CRIMSON_WATCH, m_creature);
break;
+
case TYPEID_PLAYER:
CAST_PLR(slayer)->GroupEventHappens(QUEST_BATTLE_OF_THE_CRIMSON_WATCH, m_creature);
break;
}
+
if (Creature* LordIllidan = (Unit::GetCreature(*m_creature, LordIllidanGUID)))
{
DoScriptText(END_TEXT, LordIllidan, slayer);
@@ -1170,35 +1365,47 @@ struct TRINITY_DLL_DECL mob_torloth_the_magnificentAI : public ScriptedAI
}
}
};
+
/*#####
# npc_lord_illidan_stormrage
#####*/
+
struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
{
npc_lord_illidan_stormrageAI(Creature* c) : ScriptedAI(c) {}
+
uint64 PlayerGUID;
+
uint32 WaveTimer;
uint32 AnnounceTimer;
+
int8 LiveCount;
uint8 WaveCount;
+
bool EventStarted;
bool Announced;
bool Failed;
+
void Reset()
{
PlayerGUID = 0;
+
WaveTimer = 10000;
AnnounceTimer = 7000;
LiveCount = 0;
WaveCount = 0;
+
EventStarted = false;
Announced = false;
Failed = false;
+
m_creature->SetVisibility(VISIBILITY_OFF);
}
+
void EnterCombat(Unit* who) {}
void MoveInLineOfSight(Unit* who) {}
void AttackStart(Unit* who) {}
+
void SummonNextWave()
{
uint8 count = WavesInfo[WaveCount].SpawnCount;
@@ -1206,7 +1413,8 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
srand(time(NULL));//initializing random seed
uint8 FelguardCount = 0;
uint8 DreadlordCount = 0;
- for (uint8 i = 0; i < count; ++i)
+
+ for(uint8 i = 0; i < count; ++i)
{
Creature* Spawn = NULL;
float X = SpawnLocation[locIndex + i].x;
@@ -1215,10 +1423,12 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
float O = SpawnLocation[locIndex + i].o;
Spawn = m_creature->SummonCreature(WavesInfo[WaveCount].CreatureId, X, Y, Z, O, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 60000);
++LiveCount;
+
if (Spawn)
{
Spawn->LoadCreaturesAddon();
- if (WaveCount == 0) //1 Wave
+
+ if (WaveCount == 0)//1 Wave
{
if (rand()%3 == 1 && FelguardCount<2)
{
@@ -1236,7 +1446,8 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
++FelguardCount;
}
}
- if (WaveCount < 3) //1-3 Wave
+
+ if (WaveCount < 3)//1-3 Wave
{
if (PlayerGUID)
{
@@ -1249,6 +1460,7 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
}
CAST_AI(mob_illidari_spawnAI, Spawn->AI())->LordIllidanGUID = m_creature->GetGUID();
}
+
if (WavesInfo[WaveCount].CreatureId == 22076) // Torloth
{
CAST_AI(mob_torloth_the_magnificentAI, Spawn->AI())->LordIllidanGUID = m_creature->GetGUID();
@@ -1261,19 +1473,25 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
WaveTimer = WavesInfo[WaveCount].SpawnTimer;
AnnounceTimer = WavesInfo[WaveCount].YellTimer;
}
+
void CheckEventFail()
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
if (!pPlayer)
return;
+
if (Group *EventGroup = pPlayer->GetGroup())
{
Player* GroupMember;
+
uint8 GroupMemberCount = 0;
uint8 DeadMemberCount = 0;
uint8 FailedMemberCount = 0;
+
const Group::MemberSlotList members = EventGroup->GetMemberSlots();
- for (Group::member_citerator itr = members.begin(); itr!= members.end(); itr++)
+
+ for(Group::member_citerator itr = members.begin(); itr!= members.end(); itr++)
{
GroupMember = (Unit::GetPlayer(itr->guid));
if (!GroupMember)
@@ -1285,20 +1503,24 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
++FailedMemberCount;
}
++GroupMemberCount;
+
if (GroupMember->isDead())
{
++DeadMemberCount;
}
}
+
if (GroupMemberCount == FailedMemberCount)
{
Failed = true;
}
+
if (GroupMemberCount == DeadMemberCount)
{
- for (Group::member_citerator itr = members.begin(); itr!= members.end(); itr++)
+ for(Group::member_citerator itr = members.begin(); itr!= members.end(); itr++)
{
GroupMember = Unit::GetPlayer(itr->guid);
+
if (GroupMember && GroupMember->GetQuestStatus(QUEST_BATTLE_OF_THE_CRIMSON_WATCH) == QUEST_STATUS_INCOMPLETE)
{
GroupMember->FailQuest(QUEST_BATTLE_OF_THE_CRIMSON_WATCH);
@@ -1313,16 +1535,19 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
Failed = true;
}
}
+
void LiveCounter()
{
--LiveCount;
if (!LiveCount)
Announced = false;
}
+
void UpdateAI(const uint32 diff)
{
if (!PlayerGUID || !EventStarted)
return;
+
if (!LiveCount && WaveCount < 4)
{
if (!Announced && AnnounceTimer < diff)
@@ -1330,16 +1555,19 @@ struct TRINITY_DLL_DECL npc_lord_illidan_stormrageAI : public ScriptedAI
DoScriptText(WavesInfo[WaveCount].WaveTextId, m_creature);
Announced = true;
}else AnnounceTimer -= diff;
+
if (WaveTimer < diff)
{
SummonNextWave();
}else WaveTimer -= diff;
}
CheckEventFail();
+
if (Failed)
EnterEvadeMode();
}
};
+
void mob_illidari_spawnAI::JustDied(Unit *slayer)
{
m_creature->RemoveCorpse();
@@ -1347,14 +1575,17 @@ void mob_illidari_spawnAI::JustDied(Unit *slayer)
if (LordIllidan)
CAST_AI(npc_lord_illidan_stormrageAI, LordIllidan->AI())->LiveCounter();
}
+
/*#####
# go_crystal_prison
######*/
+
bool GOQuestAccept_GO_crystal_prison(Player* plr, GameObject* go, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_BATTLE_OF_THE_CRIMSON_WATCH)
{
Creature* Illidan = plr->FindNearestCreature(22083, 50);
+
if (Illidan && !CAST_AI(npc_lord_illidan_stormrageAI, Illidan->AI())->EventStarted)
{
CAST_AI(npc_lord_illidan_stormrageAI, Illidan->AI())->PlayerGUID = plr->GetGUID();
@@ -1364,58 +1595,74 @@ bool GOQuestAccept_GO_crystal_prison(Player* plr, GameObject* go, Quest const* q
}
return true;
}
+
CreatureAI* GetAI_npc_lord_illidan_stormrage(Creature* c)
{
return new npc_lord_illidan_stormrageAI(c);
}
+
CreatureAI* GetAI_mob_illidari_spawn(Creature* c)
{
return new mob_illidari_spawnAI(c);
}
+
CreatureAI* GetAI_mob_torloth_the_magnificent(Creature* c)
{
return new mob_torloth_the_magnificentAI(c);
}
+
/*####
# npc_enraged_spirits
####*/
+
/* QUESTS */
#define QUEST_ENRAGED_SPIRITS_FIRE_EARTH 10458
#define QUEST_ENRAGED_SPIRITS_AIR 10481
#define QUEST_ENRAGED_SPIRITS_WATER 10480
+
/* Totem */
#define ENTRY_TOTEM_OF_SPIRITS 21071
#define RADIUS_TOTEM_OF_SPIRITS 15
+
/* SPIRITS */
#define ENTRY_ENRAGED_EARTH_SPIRIT 21050
#define ENTRY_ENRAGED_FIRE_SPIRIT 21061
#define ENTRY_ENRAGED_AIR_SPIRIT 21060
#define ENTRY_ENRAGED_WATER_SPIRIT 21059
+
/* SOULS */
#define ENTRY_EARTHEN_SOUL 21073
#define ENTRY_FIERY_SOUL 21097
#define ENTRY_ENRAGED_AIRY_SOUL 21116
#define ENTRY_ENRAGED_WATERY_SOUL 21109 // wrong model
+
/* SPELL KILLCREDIT - not working!?! - using KilledMonsterCredit */
#define SPELL_EARTHEN_SOUL_CAPTURED_CREDIT 36108
#define SPELL_FIERY_SOUL_CAPTURED_CREDIT 36117
#define SPELL_AIRY_SOUL_CAPTURED_CREDIT 36182
#define SPELL_WATERY_SOUL_CAPTURED_CREDIT 36171
+
/* KilledMonsterCredit Workaround */
#define CREDIT_FIRE 21094
#define CREDIT_WATER 21095
#define CREDIT_AIR 21096
#define CREDIT_EARTH 21092
+
/* Captured Spell/Buff */
#define SPELL_SOUL_CAPTURED 36115
+
/* Factions */
#define ENRAGED_SOUL_FRIENDLY 35
#define ENRAGED_SOUL_HOSTILE 14
+
struct TRINITY_DLL_DECL npc_enraged_spiritAI : public ScriptedAI
{
npc_enraged_spiritAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
+
void EnterCombat(Unit *who){}
+
void JustDied(Unit* killer)
{
// always spawn spirit on death
@@ -1423,6 +1670,7 @@ struct TRINITY_DLL_DECL npc_enraged_spiritAI : public ScriptedAI
// move spirit to totem and cast kill count
uint32 entry = 0;
uint32 credit = 0;
+
switch(m_creature->GetEntry()) {
case ENTRY_ENRAGED_FIRE_SPIRIT:
entry = ENTRY_FIERY_SOUL;
@@ -1445,11 +1693,14 @@ struct TRINITY_DLL_DECL npc_enraged_spiritAI : public ScriptedAI
credit = CREDIT_WATER;
break;
}
+
// Spawn Soul on Kill ALWAYS!
Creature* Summoned = NULL;
Unit* totemOspirits = NULL;
+
if (entry != 0)
Summoned = DoSpawnCreature(entry, 0, 0, 1, 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 5000);
+
// FIND TOTEM, PROCESS QUEST
if (Summoned)
{
@@ -1458,6 +1709,7 @@ struct TRINITY_DLL_DECL npc_enraged_spiritAI : public ScriptedAI
{
Summoned->setFaction(ENRAGED_SOUL_FRIENDLY);
Summoned->GetMotionMaster()->MovePoint(0,totemOspirits->GetPositionX(), totemOspirits->GetPositionY(), Summoned->GetPositionZ());
+
Unit* Owner = totemOspirits->GetOwner();
if (Owner && Owner->GetTypeId() == TYPEID_PLAYER)
// DoCast(Owner, credit); -- not working!
@@ -1467,83 +1719,102 @@ struct TRINITY_DLL_DECL npc_enraged_spiritAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_enraged_spirit(Creature* pCreature)
{
return new npc_enraged_spiritAI(pCreature);
}
+
/*#####
#
######*/
+
void AddSC_shadowmoon_valley()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_mature_netherwing_drake";
newscript->GetAI = &GetAI_mob_mature_netherwing_drake;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_enslaved_netherwing_drake";
newscript->GetAI = &GetAI_mob_enslaved_netherwing_drake;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_dragonmaw_peon";
newscript->GetAI = &GetAI_mob_dragonmaw_peon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_drake_dealer_hurlunk";
newscript->pGossipHello = &GossipHello_npc_drake_dealer_hurlunk;
newscript->pGossipSelect = &GossipSelect_npc_drake_dealer_hurlunk;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_murkblood_overseer";
newscript->pGossipHello = &GossipHello_npc_murkblood_overseer;
newscript->pGossipSelect = &GossipSelect_npc_murkblood_overseer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_neltharaku";
newscript->pGossipHello = &GossipHello_npc_neltharaku;
newscript->pGossipSelect = &GossipSelect_npc_neltharaku;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_karynaku";
newscript->pQuestAccept = &QuestAccept_npc_karynaku;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_oronok_tornheart";
newscript->pGossipHello = &GossipHello_npc_oronok_tornheart;
newscript->pGossipSelect = &GossipSelect_npc_oronok_tornheart;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_overlord_morghor";
newscript->GetAI = &GetAI_npc_overlord_morghor;
newscript->pQuestAccept = &QuestAccept_npc_overlord_morghor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_earthmender_wilda";
newscript->GetAI = &GetAI_npc_earthmender_wilda;
newscript->pQuestAccept = &QuestAccept_npc_earthmender_wilda;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lord_illidan_stormrage";
newscript->GetAI = &GetAI_npc_lord_illidan_stormrage;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_crystal_prison";
newscript->pGOQuestAccept = &GOQuestAccept_GO_crystal_prison;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_illidari_spawn";
newscript->GetAI = &GetAI_mob_illidari_spawn;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_torloth_the_magnificent";
newscript->GetAI = &GetAI_mob_torloth_the_magnificent;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_enraged_spirit";
newscript->GetAI = &GetAI_npc_enraged_spirit;
diff --git a/src/bindings/scripts/scripts/outland/shattrath_city.cpp b/src/bindings/scripts/scripts/outland/shattrath_city.cpp
index 54e29840957..cd25653fc61 100644
--- a/src/bindings/scripts/scripts/outland/shattrath_city.cpp
+++ b/src/bindings/scripts/scripts/outland/shattrath_city.cpp
@@ -13,12 +13,14 @@
* along 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, 10211, 10231. Flask vendors, Teleport to Caverns of Time
SDCategory: Shattrath City
EndScriptData */
+
/* ContentData
npc_raliq_the_drunk
npc_salsalabim
@@ -29,54 +31,68 @@ npc_dirty_larry
npc_ishanah
npc_khadgar
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## npc_raliq_the_drunk
######*/
+
#define GOSSIP_RALIQ "You owe Sim'salabim money. Hand them over or die!"
+
enum eRaliq
{
SPELL_UPPERCUT = 10966,
QUEST_CRACK_SKULLS = 10009,
FACTION_HOSTILE_RD = 45
};
+
struct TRINITY_DLL_DECL npc_raliq_the_drunkAI : public ScriptedAI
{
npc_raliq_the_drunkAI(Creature* c) : ScriptedAI(c)
{
m_uiNormFaction = c->getFaction();
}
+
uint32 m_uiNormFaction;
uint32 Uppercut_Timer;
+
void Reset()
{
Uppercut_Timer = 5000;
me->RestoreFaction();
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new npc_raliq_the_drunkAI (pCreature);
}
+
bool GossipHello_npc_raliq_the_drunk(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_CRACK_SKULLS) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_RALIQ, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(9440, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_raliq_the_drunk(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -87,22 +103,29 @@ bool GossipSelect_npc_raliq_the_drunk(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
/*######
# npc_salsalabim
######*/
+
#define FACTION_HOSTILE_SA 90
#define FACTION_FRIENDLY_SA 35
#define QUEST_10004 10004
+
#define SPELL_MAGNETIC_PULL 31705
+
struct TRINITY_DLL_DECL npc_salsalabimAI : public ScriptedAI
{
npc_salsalabimAI(Creature* c) : ScriptedAI(c) {}
+
uint32 MagneticPull_Timer;
+
void Reset()
{
MagneticPull_Timer = 15000;
me->RestoreFaction();
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetTypeId() == TYPEID_PLAYER)
@@ -113,15 +136,18 @@ struct TRINITY_DLL_DECL npc_salsalabimAI : public ScriptedAI
EnterEvadeMode();
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (MagneticPull_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_MAGNETIC_PULL);
MagneticPull_Timer = 15000;
}else MagneticPull_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -129,6 +155,7 @@ CreatureAI* GetAI_npc_salsalabim(Creature* pCreature)
{
return new npc_salsalabimAI (pCreature);
}
+
bool GossipHello_npc_salsalabim(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_10004) == QUEST_STATUS_INCOMPLETE)
@@ -144,6 +171,7 @@ bool GossipHello_npc_salsalabim(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
/*
##################################################
Shattrath City Flask Vendors provides flasks to people exalted with 3 factions:
@@ -154,6 +182,7 @@ 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* pPlayer, Creature* pCreature)
{
if (pCreature->GetEntry() == 23484)
@@ -169,6 +198,7 @@ bool GossipHello_npc_shattrathflaskvendors(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(11083, pCreature->GetGUID());
}
}
+
if (pCreature->GetEntry() == 23483)
{
// Scryers vendor
@@ -182,34 +212,46 @@ bool GossipHello_npc_shattrathflaskvendors(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(11084, pCreature->GetGUID());
}
}
+
return true;
}
+
bool GossipSelect_npc_shattrathflaskvendors(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*######
# npc_zephyr
######*/
+
#define GOSSIP_HZ "Take me to the Caverns of Time."
+
bool GossipHello_npc_zephyr(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetReputationRank(989) >= REP_REVERED)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HZ, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_zephyr(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
pPlayer->CastSpell(pPlayer,37778,false);
+
return true;
}
+
/*######
# npc_kservant
######*/
+
#define SAY1 -1000306
#define WHISP1 -1000307
#define WHISP2 -1000308
@@ -232,16 +274,20 @@ bool GossipSelect_npc_zephyr(Player* pPlayer, Creature* pCreature, uint32 uiSend
#define WHISP19 -1000325
#define WHISP20 -1000326
#define WHISP21 -1000327
+
struct TRINITY_DLL_DECL npc_kservantAI : public npc_escortAI
{
public:
npc_kservantAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 0: DoScriptText(SAY1, m_creature, pPlayer); break;
@@ -271,10 +317,12 @@ public:
break;
}
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (pWho->GetTypeId() == TYPEID_PLAYER)
{
if (CAST_PLR(pWho)->GetQuestStatus(10211) == QUEST_STATUS_INCOMPLETE)
@@ -287,42 +335,54 @@ public:
}
}
}
+
void Reset() {}
};
CreatureAI* GetAI_npc_kservantAI(Creature* pCreature)
{
return new npc_kservantAI(pCreature);
}
+
/*######
# npc_dirty_larry
######*/
+
#define GOSSIP_BOOK "Ezekiel said that you might have a certain book..."
+
#define SAY_1 -1000328
#define SAY_2 -1000329
#define SAY_3 -1000330
#define SAY_4 -1000331
#define SAY_5 -1000332
#define SAY_GIVEUP -1000333
+
#define QUEST_WBI 10231
#define NPC_CREEPJACK 19726
#define NPC_MALONE 19725
+
struct TRINITY_DLL_DECL npc_dirty_larryAI : public ScriptedAI
{
npc_dirty_larryAI(Creature* c) : ScriptedAI(c) {}
+
bool Event;
bool Attack;
bool Done;
+
uint64 PlayerGUID;
+
uint32 SayTimer;
uint32 Step;
+
void Reset()
{
Event = false;
Attack = false;
Done = false;
+
PlayerGUID = 0;
SayTimer = 0;
Step = 0;
+
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->setFaction(1194);
Unit* Creepjack = me->FindNearestCreature(NPC_CREEPJACK, 20);
@@ -340,9 +400,11 @@ struct TRINITY_DLL_DECL npc_dirty_larryAI : public ScriptedAI
Malone->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
}
+
uint32 NextStep(uint32 Step)
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
+
switch(Step)
{
case 0:{ m_creature->SetInFront(pPlayer);
@@ -362,7 +424,9 @@ struct TRINITY_DLL_DECL npc_dirty_larryAI : public ScriptedAI
default: return 0;
}
}
+
void EnterCombat(Unit* who){}
+
void UpdateAI(const uint32 diff)
{
if (SayTimer < diff)
@@ -370,6 +434,7 @@ struct TRINITY_DLL_DECL npc_dirty_larryAI : public ScriptedAI
if (Event)
SayTimer = NextStep(++Step);
}else SayTimer -= diff;
+
if (Attack)
{
Player* pPlayer = Unit::GetPlayer(PlayerGUID);
@@ -398,6 +463,7 @@ struct TRINITY_DLL_DECL npc_dirty_larryAI : public ScriptedAI
}
Attack = false;
}
+
if ((m_creature->GetHealth()*100)/m_creature->GetMaxHealth() < 1 && !Done)
{
Unit* Creepjack = me->FindNearestCreature(NPC_CREEPJACK, 20);
@@ -430,15 +496,19 @@ struct TRINITY_DLL_DECL npc_dirty_larryAI : public ScriptedAI
DoMeleeAttackIfReady();
}
};
+
bool GossipHello_npc_dirty_larry(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->GetQuestStatus(QUEST_WBI) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BOOK, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_dirty_larry(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -447,52 +517,69 @@ bool GossipSelect_npc_dirty_larry(Player* pPlayer, Creature* pCreature, uint32 u
CAST_AI(npc_dirty_larryAI, pCreature->AI())->PlayerGUID = pPlayer->GetGUID();
pPlayer->CLOSE_GOSSIP_MENU();
}
+
return true;
}
+
CreatureAI* GetAI_npc_dirty_larryAI(Creature* pCreature)
{
return new npc_dirty_larryAI (pCreature);
}
+
/*######
# npc_ishanah
######*/
+
#define ISANAH_GOSSIP_1 "Who are the Sha'tar?"
#define ISANAH_GOSSIP_2 "Isn't Shattrath a draenei city? Why do you allow others here?"
+
bool GossipHello_npc_ishanah(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, ISANAH_GOSSIP_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, ISANAH_GOSSIP_2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+2);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_ishanah(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
pPlayer->SEND_GOSSIP_MENU(9458, pCreature->GetGUID());
else if (uiAction == GOSSIP_ACTION_INFO_DEF+2)
pPlayer->SEND_GOSSIP_MENU(9459, pCreature->GetGUID());
+
return true;
}
+
/*######
# npc_khadgar
######*/
+
#define KHADGAR_GOSSIP_1 "I've heard your name spoken only in whispers, mage. Who are you?"
#define KHADGAR_GOSSIP_2 "Go on, please."
#define KHADGAR_GOSSIP_3 "I see." //6th too this
#define KHADGAR_GOSSIP_4 "What did you do then?"
#define KHADGAR_GOSSIP_5 "What happened next?"
#define KHADGAR_GOSSIP_7 "There was something else I wanted to ask you."
+
bool GossipHello_npc_khadgar(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (!pPlayer->hasQuest(10211))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, KHADGAR_GOSSIP_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(9243, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_khadgar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -528,45 +615,54 @@ bool GossipSelect_npc_khadgar(Player* pPlayer, Creature* pCreature, uint32 uiSen
}
return true;
}
+
void AddSC_shattrath_city()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_raliq_the_drunk";
newscript->GetAI = &GetAI_npc_raliq_the_drunk;
newscript->pGossipHello = &GossipHello_npc_raliq_the_drunk;
newscript->pGossipSelect = &GossipSelect_npc_raliq_the_drunk;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_salsalabim";
newscript->GetAI = &GetAI_npc_salsalabim;
newscript->pGossipHello = &GossipHello_npc_salsalabim;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_shattrathflaskvendors";
newscript->pGossipHello = &GossipHello_npc_shattrathflaskvendors;
newscript->pGossipSelect = &GossipSelect_npc_shattrathflaskvendors;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_zephyr";
newscript->pGossipHello = &GossipHello_npc_zephyr;
newscript->pGossipSelect = &GossipSelect_npc_zephyr;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_kservant";
newscript->GetAI = &GetAI_npc_kservantAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_dirty_larry";
newscript->GetAI = &GetAI_npc_dirty_larryAI;
newscript->pGossipHello = &GossipHello_npc_dirty_larry;
newscript->pGossipSelect = &GossipSelect_npc_dirty_larry;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_ishanah";
newscript->pGossipHello = &GossipHello_npc_ishanah;
newscript->pGossipSelect = &GossipSelect_npc_ishanah;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_khadgar";
newscript->pGossipHello = &GossipHello_npc_khadgar;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp
index b329e1a2f3b..6c5460e1d83 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/arcatraz.cpp
@@ -13,22 +13,27 @@
* along 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 -1552010
#define SAY_INTRO_2 -1552011
#define SAY_WATER -1552012
@@ -42,46 +47,57 @@ EndContentData */
#define SAY_LOWHP -1552020
#define SAY_DEATH -1552021
#define SAY_COMPLETE -1552022
+
#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 TRINITY_DLL_DECL npc_millhouse_manastormAI : public ScriptedAI
{
npc_millhouse_manastormAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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)
{
DoScriptText(SAY_COMPLETE, m_creature);
}
}
}
+
void AttackStart(Unit* pWho)
{
if (m_creature->Attack(pWho, true))
@@ -89,23 +105,29 @@ struct TRINITY_DLL_DECL npc_millhouse_manastormAI : public ScriptedAI
m_creature->AddThreat(pWho, 0.0f);
m_creature->SetInCombatWith(pWho);
pWho->SetInCombatWith(m_creature);
+
m_creature->GetMotionMaster()->MoveChase(pWho, 25.0f);
}
}
+
void EnterCombat(Unit *who)
{
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
+
/*for questId 10886 (heroic mode only)
if (pInstance && pInstance->GetData(TYPE_HARBINGERSKYRISS) != DONE)
->FailQuest();*/
}
+
void UpdateAI(const uint32 diff)
{
if (!Init)
@@ -153,36 +175,46 @@ struct TRINITY_DLL_DECL npc_millhouse_manastormAI : public ScriptedAI
}
} else EventProgress_Timer -= diff;
}
+
if (!UpdateVictim())
return;
+
if (!LowHp && ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) < 20))
{
DoScriptText(SAY_LOWHP, m_creature);
LowHp = true;
}
+
if (Pyroblast_Timer < diff)
{
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
DoScriptText(SAY_PYRO, m_creature);
+
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* pCreature)
{
return new npc_millhouse_manastormAI (pCreature);
}
+
/*#####
# npc_warden_mellichar
#####*/
+
#define YELL_INTRO1 -1552023
#define YELL_INTRO2 -1552024
#define YELL_RELEASE1 -1552025
@@ -191,6 +223,7 @@ CreatureAI* GetAI_npc_millhouse_manastorm(Creature* pCreature)
#define YELL_RELEASE3 -1552028
#define YELL_RELEASE4 -1552029
#define YELL_WELCOME -1552030
+
//phase 2(acid mobs)
#define ENTRY_TRICKSTER 20905
#define ENTRY_PH_HUNTER 20906
@@ -204,6 +237,7 @@ CreatureAI* GetAI_npc_millhouse_manastorm(Creature* pCreature)
#define ENTRY_BL_DRAK 20911
//phase 6
#define ENTRY_SKYRISS 20912
+
//TARGET_SCRIPT
#define SPELL_TARGET_ALPHA 36856
#define SPELL_TARGET_BETA 36854
@@ -211,48 +245,62 @@ CreatureAI* GetAI_npc_millhouse_manastorm(Creature* pCreature)
#define SPELL_TARGET_GAMMA 36858
#define SPELL_TARGET_OMEGA 36852
#define SPELL_BUBBLE_VISUAL 36849
+
struct TRINITY_DLL_DECL npc_warden_mellicharAI : public ScriptedAI
{
npc_warden_mellicharAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
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->isInAccessiblePlaceFor(m_creature))
{
if (!m_creature->canFly() && 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))
EnterCombat(who);
}
}
+
void EnterCombat(Unit *who)
{
DoScriptText(YELL_INTRO1, m_creature);
DoCast(m_creature,SPELL_BUBBLE_VISUAL);
+
if (pInstance)
{
pInstance->SetData(TYPE_HARBINGERSKYRISS,IN_PROGRESS);
@@ -260,6 +308,7 @@ struct TRINITY_DLL_DECL npc_warden_mellicharAI : public ScriptedAI
IsRunning = true;
}
}
+
bool CanProgress()
{
if (pInstance)
@@ -282,12 +331,14 @@ struct TRINITY_DLL_DECL npc_warden_mellicharAI : public ScriptedAI
}
return false;
}
+
void DoPrepareForPhase()
{
if (pInstance)
{
m_creature->InterruptNonMeleeSpells(true);
m_creature->RemoveAurasByType(SPELL_AURA_DUMMY);
+
switch(Phase)
{
case 2:
@@ -314,10 +365,12 @@ struct TRINITY_DLL_DECL npc_warden_mellicharAI : public ScriptedAI
CanSpawn = true;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!IsRunning)
return;
+
if (EventProgress_Timer < diff)
{
if (pInstance)
@@ -328,11 +381,13 @@ struct TRINITY_DLL_DECL npc_warden_mellicharAI : public ScriptedAI
return;
}
}
+
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:
@@ -416,37 +471,47 @@ CreatureAI* GetAI_npc_warden_mellichar(Creature* pCreature)
{
return new npc_warden_mellicharAI (pCreature);
}
+
/*#####
# mob_zerekethvoidzone (this script probably not needed in future -> `creature_template_addon`.`auras`='36120 0')
#####*/
+
#define SPELL_VOID_ZONE_DAMAGE 36120
+
struct TRINITY_DLL_DECL mob_zerekethvoidzoneAI : public ScriptedAI
{
mob_zerekethvoidzoneAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit* who) {}
};
CreatureAI* GetAI_mob_zerekethvoidzoneAI(Creature* pCreature)
{
return new mob_zerekethvoidzoneAI (pCreature);
}
+
void AddSC_arcatraz()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_millhouse_manastorm";
newscript->GetAI = &GetAI_npc_millhouse_manastorm;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_warden_mellichar";
newscript->GetAI = &GetAI_npc_warden_mellichar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_zerekethvoidzone";
newscript->GetAI = &GetAI_mob_zerekethvoidzoneAI;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp
index ba7851b7a47..e5620e908c2 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/boss_harbinger_skyriss.cpp
@@ -13,18 +13,22 @@
* along 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 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 -1552000
#define SAY_AGGRO -1552001
#define SAY_KILL_1 -1552002
@@ -35,14 +39,20 @@ EndContentData */
#define SAY_FEAR_2 -1552007
#define SAY_IMAGE -1552008
#define SAY_DEATH -1552009
+
#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 TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
{
boss_harbinger_skyrissAI(Creature *c) : ScriptedAI(c)
@@ -51,23 +61,29 @@ struct TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
HeroicMode = c->GetMap()->IsHeroic();
Intro = false;
}
+
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()
{
if (!Intro)
m_creature->SetFlag(UNIT_FIELD_FLAGS,UNIT_FLAG_OOC_NOT_ATTACKABLE);
+
IsImage33 = false;
IsImage66 = false;
+
Intro_Phase = 1;
Intro_Timer = 5000;
MindRend_Timer = 3000;
@@ -75,6 +91,7 @@ struct TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
Domination_Timer = 30000;
ManaBurn_Timer = 25000;
}
+
void MoveInLineOfSight(Unit *who)
{
if (!Intro)
@@ -83,13 +100,16 @@ struct TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
}
ScriptedAI::MoveInLineOfSight(who);
}
+
void EnterCombat(Unit *who) {}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
if (pInstance)
pInstance->SetData(TYPE_HARBINGERSKYRISS,DONE);
}
+
void JustSummoned(Creature *summon)
{
if (!summon)
@@ -102,29 +122,36 @@ struct TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
summon->AI()->AttackStart(target);
}
+
void KilledUnit(Unit* victim)
{
//won't yell killing pet/other unit
if (victim->GetEntry() == 21436)
return;
+
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void DoSplit(uint32 val)
{
if (m_creature->IsNonMeleeSpellCasted(false))
m_creature->InterruptNonMeleeSpells(false);
+
DoScriptText(SAY_IMAGE, m_creature);
+
if (val == 66)
DoCast(m_creature, SPELL_66_ILLUSION);
else
DoCast(m_creature, SPELL_33_ILLUSION);
}
+
void UpdateAI(const uint32 diff)
{
if (!Intro)
{
if (!pInstance)
return;
+
if (Intro_Timer < diff)
{
switch(Intro_Phase)
@@ -154,8 +181,10 @@ struct TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
}
}else Intro_Timer -=diff;
}
+
if (!UpdateVictim())
return;
+
if (!IsImage66 && ((m_creature->GetHealth()*100) / m_creature->GetMaxHealth() <= 66))
{
DoSplit(66);
@@ -166,77 +195,101 @@ struct TRINITY_DLL_DECL boss_harbinger_skyrissAI : public ScriptedAI
DoSplit(33);
IsImage33 = true;
}
+
if (MindRend_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1))
DoCast(target,HEROIC(SPELL_MIND_REND, H_SPELL_MIND_REND));
else
DoCast(m_creature->getVictim(),HEROIC(SPELL_MIND_REND, H_SPELL_MIND_REND));
+
MindRend_Timer = 8000;
}else MindRend_Timer -=diff;
+
if (Fear_Timer < diff)
{
if (m_creature->IsNonMeleeSpellCasted(false))
return;
+
DoScriptText(RAND(SAY_FEAR_1,SAY_FEAR_2), m_creature);
+
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;
+
DoScriptText(RAND(SAY_MIND_1,SAY_MIND_2), m_creature);
+
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1))
DoCast(target,HEROIC(SPELL_DOMINATION, H_SPELL_DOMINATION));
else
DoCast(m_creature->getVictim(),HEROIC(SPELL_DOMINATION, H_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* pCreature)
{
return new boss_harbinger_skyrissAI (pCreature);
}
+
#define SPELL_MIND_REND_IMAGE 36929
#define H_SPELL_MIND_REND_IMAGE 39021
+
struct TRINITY_DLL_DECL boss_harbinger_skyriss_illusionAI : public ScriptedAI
{
boss_harbinger_skyriss_illusionAI(Creature *c) : ScriptedAI(c)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
void Reset() { }
+
void EnterCombat(Unit *who) { }
};
+
CreatureAI* GetAI_boss_harbinger_skyriss_illusion(Creature* pCreature)
{
return new boss_harbinger_skyriss_illusionAI (pCreature);
}
+
void AddSC_boss_harbinger_skyriss()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_harbinger_skyriss";
newscript->GetAI = &GetAI_boss_harbinger_skyriss;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_harbinger_skyriss_illusion";
newscript->GetAI = &GetAI_boss_harbinger_skyriss_illusion;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/def_arcatraz.h b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/def_arcatraz.h
index 56810237064..3f8dee8bbd0 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/def_arcatraz.h
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/def_arcatraz.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_ARCATRAZ_H
#define DEF_ARCATRAZ_H
+
#define TYPE_ZEREKETH 1
#define TYPE_DALLIAH 2
#define TYPE_SOCCOTHRATES 3
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp
index 44c13e4c3a1..ebf5484671c 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/arcatraz/instance_arcatraz.cpp
@@ -13,15 +13,19 @@
* along 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 MAX_ENCOUNTER 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
@@ -31,18 +35,23 @@ EndScriptData */
#define POD_OMEGA 183965 //pod fifth boss wave
#define WARDENS_SHIELD 184802 // warden shield
#define SEAL_SPHERE 184802 //shield 'protecting' mellichar
+
#define MELLICHAR 20904 //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 TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
{
instance_arcatraz(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
GameObject *Containment_Core_Security_Field_Alpha;
GameObject *Containment_Core_Security_Field_Beta;
GameObject *Pod_Alpha;
@@ -51,11 +60,14 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
GameObject *Pod_Delta;
GameObject *Pod_Omega;
GameObject *Wardens_Shield;
+
uint64 GoSphereGUID;
uint64 MellicharGUID;
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
Containment_Core_Security_Field_Alpha = NULL;
Containment_Core_Security_Field_Beta = NULL;
Pod_Alpha = NULL;
@@ -64,15 +76,19 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
Pod_Gamma = NULL;
Pod_Omega = NULL;
Wardens_Shield = NULL;
+
GoSphereGUID = 0;
MellicharGUID = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnGameObjectCreate(GameObject* pGo, bool add)
{
switch(pGo->GetEntry())
@@ -88,11 +104,13 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
//case WARDENS_SHIELD: Wardens_Shield = pGo; break;
}
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
if (pCreature->GetEntry() == MELLICHAR)
MellicharGUID = pCreature->GetGUID();
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -100,18 +118,21 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
case TYPE_ZEREKETH:
m_auiEncounter[0] = data;
break;
+
case TYPE_DALLIAH:
if (data == DONE)
if (Containment_Core_Security_Field_Beta)
Containment_Core_Security_Field_Beta->UseDoorOrButton();
m_auiEncounter[1] = data;
break;
+
case TYPE_SOCCOTHRATES:
if (data == DONE)
if (Containment_Core_Security_Field_Alpha)
Containment_Core_Security_Field_Alpha->UseDoorOrButton();
m_auiEncounter[2] = data;
break;
+
case TYPE_HARBINGERSKYRISS:
if (data == NOT_STARTED || data == FAIL)
{
@@ -123,36 +144,42 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
}
m_auiEncounter[3] = data;
break;
+
case TYPE_WARDEN_1:
if (data == IN_PROGRESS)
if (Pod_Alpha)
Pod_Alpha->UseDoorOrButton();
m_auiEncounter[4] = data;
break;
+
case TYPE_WARDEN_2:
if (data == IN_PROGRESS)
if (Pod_Beta)
Pod_Beta->UseDoorOrButton();
m_auiEncounter[5] = data;
break;
+
case TYPE_WARDEN_3:
if (data == IN_PROGRESS)
if (Pod_Delta)
Pod_Delta->UseDoorOrButton();
m_auiEncounter[6] = data;
break;
+
case TYPE_WARDEN_4:
if (data == IN_PROGRESS)
if (Pod_Gamma)
Pod_Gamma->UseDoorOrButton();
m_auiEncounter[7] = data;
break;
+
case TYPE_WARDEN_5:
if (data == IN_PROGRESS)
if (Pod_Omega)
Pod_Omega->UseDoorOrButton();
m_auiEncounter[8] = data;
break;
+
case TYPE_SHIELD_OPEN:
if (data == IN_PROGRESS)
if (Wardens_Shield)
@@ -160,6 +187,7 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
break;
}
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -179,6 +207,7 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
}
return 0;
}
+
uint64 GetData64(uint32 data)
{
switch(data)
@@ -191,10 +220,12 @@ struct TRINITY_DLL_DECL instance_arcatraz : public ScriptedInstance
return 0;
}
};
+
InstanceData* GetInstanceData_instance_arcatraz(Map* pMap)
{
return new instance_arcatraz(pMap);
}
+
void AddSC_instance_arcatraz()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp
index e3868d56115..2e48e5147a3 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_high_botanist_freywinn.cpp
@@ -13,54 +13,68 @@
* along 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 -1553000
#define SAY_KILL_1 -1553001
#define SAY_KILL_2 -1553002
#define SAY_TREE_1 -1553003
#define SAY_TREE_2 -1553004
#define SAY_DEATH -1553005
+
#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 TRINITY_DLL_DECL boss_high_botanist_freywinnAI : public ScriptedAI
{
boss_high_botanist_freywinnAI(Creature *c) : ScriptedAI(c) {}
+
std::list<uint64> 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 EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustSummoned(Creature *summoned)
{
if (summoned->GetEntry() == ENTRY_FRAYER)
Adds_List.push_back(summoned->GetGUID());
}
+
void DoSummonSeedling()
{
switch(rand()%4)
@@ -71,38 +85,48 @@ struct TRINITY_DLL_DECL boss_high_botanist_freywinnAI : public ScriptedAI
case 3: DoCast(m_creature,SPELL_PLANT_RED); break;
}
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_KILL_1,SAY_KILL_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (TreeForm_Timer < diff)
{
DoScriptText(RAND(SAY_TREE_1,SAY_TREE_2), m_creature);
+
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<uint64>::iterator itr = Adds_List.begin(); itr != Adds_List.end(); ++itr)
+ for(std::list<uint64>::iterator itr = Adds_List.begin(); itr != Adds_List.end(); ++itr)
{
if (Unit *temp = Unit::GetUnit(*m_creature,*itr))
{
@@ -115,12 +139,15 @@ struct TRINITY_DLL_DECL boss_high_botanist_freywinnAI : public ScriptedAI
}
}
}
+
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());
@@ -129,26 +156,33 @@ struct TRINITY_DLL_DECL boss_high_botanist_freywinnAI : public ScriptedAI
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* pCreature)
{
return new boss_high_botanist_freywinnAI (pCreature);
}
+
void AddSC_boss_high_botanist_freywinn()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_high_botanist_freywinn";
newscript->GetAI = &GetAI_boss_high_botanist_freywinn;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_laj.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_laj.cpp
index 1788cf749a4..58ff64a9493 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_laj.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_laj.cpp
@@ -13,16 +13,21 @@
* along 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: 90
SDComment: Immunities are wrong, must be adjusted to use resistance from creature_templates. Most spells require database support.
SDCategory: Tempest Keep, The Botanica
EndScriptData */
+
#include "precompiled.h"
+
#define EMOTE_SUMMON -1553006
+
#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
@@ -31,19 +36,23 @@ EndScriptData */
#define SPELL_SUMMON_FLAYER_4 34687
#define SPELL_SUMMON_LASHER_4 34688
#define SPELL_SUMMON_FLAYER_3 34690
+
#define MODEL_DEFAULT 13109
#define MODEL_ARCANE 14213
#define MODEL_FIRE 13110
#define MODEL_FROST 14112
#define MODEL_NATURE 14214
+
struct TRINITY_DLL_DECL boss_lajAI : public ScriptedAI
{
boss_lajAI(Creature *c) : ScriptedAI(c) {}
+
bool CanSummon;
uint32 Teleport_Timer;
uint32 Summon_Timer;
uint32 Transform_Timer;
uint32 Allergic_Timer;
+
void Reset()
{
m_creature->SetDisplayId(MODEL_DEFAULT);
@@ -52,12 +61,14 @@ struct TRINITY_DLL_DECL boss_lajAI : public ScriptedAI
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)
@@ -104,6 +115,7 @@ struct TRINITY_DLL_DECL boss_lajAI : public ScriptedAI
break;
}
}
+
void DoSummons()
{
switch(rand()%4)
@@ -127,18 +139,22 @@ struct TRINITY_DLL_DECL boss_lajAI : public ScriptedAI
}
CanSummon = false;
}
+
void EnterCombat(Unit *who)
{
}
+
void JustSummoned(Creature *summon)
{
if (summon && m_creature->getVictim())
summon->AI()->AttackStart(SelectUnit(SELECT_TARGET_RANDOM, 0));
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (CanSummon)
{
if (Summon_Timer < diff)
@@ -148,32 +164,39 @@ struct TRINITY_DLL_DECL boss_lajAI : public ScriptedAI
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* pCreature)
{
return new boss_lajAI (pCreature);
}
+
void AddSC_boss_laj()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_laj";
newscript->GetAI = &GetAI_boss_laj;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp
index b50d15cd3a2..e06b1b03a41 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/botanica/boss_warp_splinter.cpp
@@ -13,31 +13,41 @@
* along 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).
SDCategory: Tempest Keep, The Botanica
EndScriptData */
+
#include "precompiled.h"
+
/*#####
# mob_treant (Sapling)
#####*/
+
#define SPELL_HEAL_FATHER 6262
+
struct TRINITY_DLL_DECL mob_treantAI : public ScriptedAI
{
mob_treantAI (Creature *c) : ScriptedAI(c)
{
WarpGuid = 0;
}
+
uint64 WarpGuid;
uint32 check_Timer;
+
void Reset()
{
check_Timer = 0;
}
+
void EnterCombat(Unit *who) {}
+
void MoveInLineOfSight(Unit* who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -59,24 +69,31 @@ struct TRINITY_DLL_DECL mob_treantAI : public ScriptedAI
}else check_Timer -= diff;
return;
}
+
if (m_creature->getVictim()->GetGUID() != WarpGuid)
DoMeleeAttackIfReady();
}
};
+
/*#####
# boss_warp_splinter
#####*/
+
#define SAY_AGGRO -1553007
#define SAY_SLAY_1 -1553008
#define SAY_SLAY_2 -1553009
#define SAY_SUMMON_1 -1553010
#define SAY_SUMMON_2 -1553011
#define SAY_DEATH -1553012
+
#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 (HeroicMode?39133:36705)
+
#define CREATURE_TREANT 19949
+
#define TREANT_SPAWN_DIST 50 //50 yards from Warp Splinter's spawn point
+
float treant_pos[6][3] =
{
{24.301233, 427.221100, -27.060635},
@@ -86,6 +103,7 @@ float treant_pos[6][3] =
{109.861877, 423.201630, -27.356019},
{106.780159, 355.582581, -27.593357}
};
+
struct TRINITY_DLL_DECL boss_warp_splinterAI : public ScriptedAI
{
boss_warp_splinterAI(Creature *c) : ScriptedAI(c)
@@ -94,84 +112,104 @@ struct TRINITY_DLL_DECL boss_warp_splinterAI : public ScriptedAI
Treant_Spawn_Pos_X = c->GetPositionX();
Treant_Spawn_Pos_Y = c->GetPositionY();
}
+
uint32 War_Stomp_Timer;
uint32 Summon_Treants_Timer;
uint32 Arcane_Volley_Timer;
bool HeroicMode;
+
float Treant_Spawn_Pos_X;
float Treant_Spawn_Pos_Y;
+
void Reset()
{
War_Stomp_Timer = 25000 + rand()%15000;
Summon_Treants_Timer = 45000;
Arcane_Volley_Timer = 8000 + rand()%12000;
+
m_creature->SetSpeed(MOVE_RUN, 0.7f, true);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
}
+
void SummonTreants()
{
- for (uint8 i = 0; i < 6; ++i)
+ for(uint8 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 O = - m_creature->GetAngle(X,Y);
+
if (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,25000))
CAST_AI(mob_treantAI, pTreant->AI())->WarpGuid = m_creature->GetGUID();
}
DoScriptText(RAND(SAY_SUMMON_1,SAY_SUMMON_2), m_creature);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
//Check for War Stomp
if (War_Stomp_Timer < diff)
{
DoCast(m_creature->getVictim(),WAR_STOMP);
War_Stomp_Timer = 25000 + rand()%15000;
}else War_Stomp_Timer -= diff;
+
//Check for Arcane Volley
if (Arcane_Volley_Timer < diff)
{
DoCast(m_creature->getVictim(),ARCANE_VOLLEY);
Arcane_Volley_Timer = 20000 + rand()%15000;
}else Arcane_Volley_Timer -= diff;
+
//Check for Summon Treants
if (Summon_Treants_Timer < diff)
{
SummonTreants();
Summon_Treants_Timer = 45000;
}else Summon_Treants_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_warp_splinter(Creature* pCreature)
{
return new boss_warp_splinterAI (pCreature);
}
+
CreatureAI* GetAI_mob_treant(Creature* pCreature)
{
return new mob_treantAI (pCreature);
}
+
void AddSC_boss_warp_splinter()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_warp_splinter";
newscript->GetAI = &GetAI_boss_warp_splinter;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_warp_splinter_treant";
newscript->GetAI = &GetAI_mob_treant;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_alar.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_alar.cpp
index 13b572aa865..b96ca667835 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_alar.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_alar.cpp
@@ -13,14 +13,17 @@
* along 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_alar
SD%Complete: 95
SDComment:
SDCategory: Tempest Keep, The Eye
EndScriptData */
+
#include "precompiled.h"
#include "def_the_eye.h"
+
#define SPELL_FLAME_BUFFET 34121 // Flame Buffet - every 1,5 secs in phase 1 if there is no victim in melee range and after Dive Bomb in phase 2 with same conditions
#define SPELL_FLAME_QUILLS 34229 // Randomly after changing position in phase after watching tonns of movies, set probability 20%
#define SPELL_REBIRTH 34342 // Rebirth - beginning of second phase(after loose all health in phase 1)
@@ -30,10 +33,13 @@ EndScriptData */
#define SPELL_DIVE_BOMB_VISUAL 35367 // Bosskillers says 30 sec cooldown, wowwiki says 30 sec colldown, DBM and BigWigs addons says ~47 sec
#define SPELL_DIVE_BOMB 35181 // after watching tonns of movies, set cooldown to 40+rand()%5.
#define SPELL_BERSERK 45078 // 10 minutes after phase 2 starts(id is wrong, but proper id is unknown)
+
#define CREATURE_EMBER_OF_ALAR 19551 // Al'ar summons one Ember of Al'ar every position change in phase 1 and two after Dive Bomb. Also in phase 2 when Ember of Al'ar dies, boss loose 3% health.
#define SPELL_EMBER_BLAST 34133 // When Ember of Al'ar dies, it casts Ember Blast
+
#define CREATURE_FLAME_PATCH_ALAR 20602 // Flame Patch - every 30 sec in phase 2
#define SPELL_FLAME_PATCH 35380 //
+
static float waypoint[6][3] =
{
{340.15, 58.65, 17.71},
@@ -43,6 +49,7 @@ static float waypoint[6][3] =
{332, 0.01, 39}, // better not use the same xy coord
{331, 0.01, -2.39}
};
+
enum WaitEventType
{
WE_NONE = 0,
@@ -57,6 +64,7 @@ enum WaitEventType
WE_LAND = 9,
WE_SUMMON = 10
};
+
struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
{
boss_alarAI(Creature *c) : ScriptedAI(c)
@@ -64,34 +72,46 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
pInstance =c->GetInstanceData();
DefaultMoveSpeedRate = c->GetSpeedRate(MOVE_RUN);
}
+
ScriptedInstance *pInstance;
+
WaitEventType WaitEvent;
uint32 WaitTimer;
+
bool AfterMoving;
+
uint32 Platforms_Move_Timer;
uint32 DiveBomb_Timer;
uint32 MeltArmor_Timer;
uint32 Charge_Timer;
uint32 FlamePatch_Timer;
uint32 Berserk_Timer;
+
float DefaultMoveSpeedRate;
+
bool Phase1;
bool ForceMove;
uint32 ForceTimer;
+
int8 cur_wp;
+
void Reset()
{
if (pInstance)
pInstance->SetData(DATA_ALAREVENT, NOT_STARTED);
+
Berserk_Timer = 1200000;
Platforms_Move_Timer = 0;
+
Phase1 = true;
WaitEvent = WE_NONE;
WaitTimer = 0;
AfterMoving = false;
ForceMove = false;
ForceTimer = 5000;
+
cur_wp = 4;
+
m_creature->SetDisplayId(m_creature->GetNativeDisplayId());
m_creature->SetSpeed(MOVE_RUN, DefaultMoveSpeedRate);
//m_creature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 10);
@@ -101,26 +121,32 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->setActive(false);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_ALAREVENT, IN_PROGRESS);
+
m_creature->SetUnitMovementFlags(MOVEMENTFLAG_LEVITATING); // after enterevademode will be set walk movement
DoZoneInCombat();
m_creature->setActive(true);
}
+
void JustDied(Unit *victim)
{
if (pInstance)
pInstance->SetData(DATA_ALAREVENT, DONE);
}
+
void JustSummoned(Creature *summon)
{
if (summon->GetEntry() == CREATURE_EMBER_OF_ALAR)
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
summon->AI()->AttackStart(target);
}
+
void MoveInLineOfSight(Unit *who) {}
+
void AttackStart(Unit* who)
{
if (Phase1)
@@ -128,6 +154,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
else
ScriptedAI::AttackStart(who);
}
+
void DamageTaken(Unit* pKiller, uint32 &damage)
{
if (damage >= m_creature->GetHealth() && Phase1)
@@ -149,6 +176,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
}
}
}
+
void SpellHit(Unit*, const SpellEntry *spell)
{
if (spell->Id == SPELL_DIVE_BOMB_VISUAL)
@@ -158,6 +186,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
//m_creature->SendUpdateObjectToAllExcept(NULL);
}
}
+
void MovementInform(uint32 type, uint32 id)
{
if (type == POINT_MOTION_TYPE)
@@ -167,15 +196,18 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
ForceMove = false;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!m_creature->isInCombat()) // sometimes isincombat but !incombat, faction bug?
return;
+
if (Berserk_Timer < diff)
{
m_creature->CastSpell(m_creature, SPELL_BERSERK, true);
Berserk_Timer = 60000;
}else Berserk_Timer -= diff;
+
if (ForceMove)
{
if (ForceTimer < diff)
@@ -183,6 +215,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
m_creature->GetMotionMaster()->MovePoint(0, waypoint[cur_wp][0], waypoint[cur_wp][1], waypoint[cur_wp][2]);
ForceTimer = 5000;
}else ForceTimer -= diff;
+
}
if (WaitEvent)
{
@@ -195,6 +228,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
m_creature->GetMotionMaster()->MoveIdle();
AfterMoving = false;
}
+
switch(WaitEvent)
{
case WE_PLATFORM:
@@ -254,7 +288,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
WaitTimer = 2000;
return;
case WE_SUMMON:
- for (uint8 i = 0; i < 2; ++i)
+ for(uint8 i = 0; i < 2; ++i)
DoSpawnCreature(CREATURE_EMBER_OF_ALAR, 0, 0, 0, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000);
m_creature->SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, 10);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -265,12 +299,14 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
default:
break;
}
+
WaitEvent = WE_NONE;
WaitTimer = 0;
}else WaitTimer -= diff;
}
return;
}
+
if (Phase1)
{
if (m_creature->getThreatManager().getThreatList().empty())
@@ -278,6 +314,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
EnterEvadeMode();
return;
}
+
if (Platforms_Move_Timer < diff)
{
if (cur_wp == 4)
@@ -318,11 +355,13 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
DoCast(target, SPELL_CHARGE);
Charge_Timer = 30000;
}else Charge_Timer -= diff;
+
if (MeltArmor_Timer < diff)
{
DoCast(m_creature->getVictim(), SPELL_MELT_ARMOR);
MeltArmor_Timer = 60000;
}else MeltArmor_Timer -= diff;
+
if (DiveBomb_Timer < diff)
{
m_creature->AttackStop();
@@ -334,6 +373,7 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
DiveBomb_Timer = 40000+rand()%5000;
return;
}else DiveBomb_Timer -= diff;
+
if (FlamePatch_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
@@ -352,8 +392,10 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
FlamePatch_Timer = 30000;
}else FlamePatch_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
void DoMeleeAttackIfReady()
{
if (m_creature->isAttackReady() && !m_creature->IsNonMeleeSpellCasted(false))
@@ -378,10 +420,12 @@ struct TRINITY_DLL_DECL boss_alarAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_boss_alar(Creature* pCreature)
{
return new boss_alarAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_ember_of_alarAI : public ScriptedAI
{
mob_ember_of_alarAI(Creature *c) : ScriptedAI(c)
@@ -390,11 +434,14 @@ struct TRINITY_DLL_DECL mob_ember_of_alarAI : public ScriptedAI
c->SetUnitMovementFlags(MOVEMENTFLAG_LEVITATING);
c->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_FIRE, true);
}
+
ScriptedInstance *pInstance;
bool toDie;
+
void Reset() {toDie = false;}
void EnterCombat(Unit *who) {DoZoneInCombat();}
void EnterEvadeMode() {m_creature->setDeathState(JUST_DIED);}
+
void DamageTaken(Unit* pKiller, uint32 &damage)
{
if (damage >= m_creature->GetHealth() && pKiller != m_creature && !toDie)
@@ -417,22 +464,28 @@ struct TRINITY_DLL_DECL mob_ember_of_alarAI : public ScriptedAI
toDie = true;
}
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (toDie)
{
m_creature->DealDamage(m_creature, m_creature->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
//m_creature->SetVisibility(VISIBILITY_OFF);
}
+
DoMeleeAttackIfReady();
}
+
};
+
CreatureAI* GetAI_mob_ember_of_alar(Creature* pCreature)
{
return new mob_ember_of_alarAI(pCreature);
}
+
struct TRINITY_DLL_DECL mob_flame_patch_alarAI : public ScriptedAI
{
mob_flame_patch_alarAI(Creature *c) : ScriptedAI(c) {}
@@ -442,21 +495,26 @@ struct TRINITY_DLL_DECL mob_flame_patch_alarAI : public ScriptedAI
void MoveInLineOfSight(Unit* who) {}
void UpdateAI(const uint32 diff) {}
};
+
CreatureAI* GetAI_mob_flame_patch_alar(Creature* pCreature)
{
return new mob_flame_patch_alarAI(pCreature);
}
+
void AddSC_boss_alar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_alar";
newscript->GetAI = &GetAI_boss_alar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ember_of_alar";
newscript->GetAI = &GetAI_mob_ember_of_alar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_flame_patch_alar";
newscript->GetAI = &GetAI_mob_flame_patch_alar;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp
index e8ba7e3a197..7cf9f3b1c62 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_astromancer.cpp
@@ -13,14 +13,17 @@
* along 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: 80
SDComment:
SDCategory: Tempest Keep, The Eye
EndScriptData */
+
#include "precompiled.h"
#include "def_the_eye.h"
+
enum eEnums
{
SAY_AGGRO = -1550007,
@@ -32,22 +35,29 @@ enum eEnums
SAY_DEATH = -1550013,
SAY_VOIDA = -1550014,
SAY_VOIDB = -1550015,
+
SPELL_ARCANE_MISSILES = 33031,
SPELL_WRATH_OF_THE_ASTROMANCER = 42783,
SPELL_BLINDING_LIGHT = 33009,
SPELL_FEAR = 34322,
SPELL_VOID_BOLT = 39329,
+
SPELL_SPOTLIGHT = 25824,
NPC_ASTROMANCER_SOLARIAN_SPOTLIGHT = 18928,
+
NPC_SOLARIUM_AGENT = 18925,
NPC_SOLARIUM_PRIEST = 18806,
+
MODEL_HUMAN = 18239,
MODEL_VOIDWALKER = 18988,
+
SPELL_SOLARIUM_GREAT_HEAL = 33387,
SPELL_SOLARIUM_HOLY_SMITE = 25054,
SPELL_SOLARIUM_ARCANE_TORRENT = 33390,
+
WV_ARMOR = 31000
};
+
const float CENTER_X = 432.909f;
const float CENTER_Y = -373.424f;
const float CENTER_Z = 17.9608f;
@@ -55,19 +65,25 @@ const float CENTER_O = 1.06421f;
const float SMALL_PORTAL_RADIUS = 12.6f;
const float LARGE_PORTAL_RADIUS = 26.0f;
const float PORTAL_Z = 17.005f;
+
// x, y, z, o
static float SolarianPos[4] = {432.909, -373.424, 17.9608, 1.06421};
+
struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
{
boss_high_astromancer_solarianAI(Creature *c) : ScriptedAI(c), Summons(m_creature)
{
pInstance = c->GetInstanceData();
+
defaultarmor = c->GetArmor();
defaultsize = c->GetFloatValue(OBJECT_FIELD_SCALE_X);
}
+
ScriptedInstance *pInstance;
SummonList Summons;
+
uint8 Phase;
+
uint32 ArcaneMissiles_Timer;
uint32 m_uiWrathOfTheAstromancer_Timer;
uint32 BlindingLight_Timer;
@@ -79,10 +95,13 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
uint32 AppearDelay_Timer;
uint32 defaultarmor;
uint32 Wrath_Timer;
+
float defaultsize;
float Portals[3][3];
+
bool AppearDelay;
bool BlindingLight;
+
void Reset()
{
ArcaneMissiles_Timer = 2000;
@@ -99,34 +118,43 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
Wrath_Timer = 20000+rand()%5000;//twice in phase one
Phase = 1;
Wrath_Timer = 20000+rand()%5000;//twice in phase one
+
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->SetDisplayId(MODEL_HUMAN);
+
Summons.DespawnAll();
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_KILL1,SAY_KILL2,SAY_KILL3), m_creature);
}
+
void JustDied(Unit *victim)
{
m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, defaultsize);
m_creature->SetDisplayId(MODEL_HUMAN);
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
DoZoneInCombat();
+
if (pInstance)
pInstance->SetData(DATA_HIGHASTROMANCERSOLARIANEVENT, IN_PROGRESS);
}
+
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);
@@ -134,18 +162,23 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
Summoned->AI()->AttackStart(target);
+
Summons.Summon(Summoned);
}
}
+
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 = 0.0f;
+
switch(rand()%2)
{
case 0: z = 1; break;
@@ -153,10 +186,12 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
}
return (z*sqrt(radius*radius - (x - CENTER_X)*(x - CENTER_X)) + CENTER_Y);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (AppearDelay)
{
m_creature->StopMoving();
@@ -172,38 +207,43 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
AppearDelay_Timer = 2000;
}else AppearDelay_Timer -= diff;
}
+
if (Phase == 1)
{
if (BlindingLight_Timer < diff){
BlindingLight = true;
BlindingLight_Timer = 45000;
}else BlindingLight_Timer -= diff;
+
if (Wrath_Timer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
DoCast(SelectTarget(SELECT_TARGET_RANDOM,1,100,true), SPELL_WRATH_OF_THE_ASTROMANCER, true);
Wrath_Timer = 20000+rand()%5000;
}else Wrath_Timer -= diff;
+
if (ArcaneMissiles_Timer < diff)
{
if (BlindingLight)
{
DoCast(m_creature->getVictim(), SPELL_BLINDING_LIGHT);
BlindingLight = false;
- }
- else
- {
+ }else{
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;
+
if (m_uiWrathOfTheAstromancer_Timer < diff)
{
m_creature->InterruptNonMeleeSpells(false);
+
//Target the tank ?
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 1))
if (pTarget->GetTypeId() == TYPEID_PLAYER)
@@ -214,6 +254,7 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
else
m_uiWrathOfTheAstromancer_Timer = 1000;
}else m_uiWrathOfTheAstromancer_Timer -= diff;
+
//Phase1_Timer
if (Phase1_Timer < diff)
{
@@ -222,7 +263,7 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
//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->GetMap()->CreatureRelocation(m_creature, CENTER_X, CENTER_Y, CENTER_Z, CENTER_O);
- for (uint8 i=0; i<=2; ++i)
+ for(uint8 i=0; i<=2; ++i)
{
if (!i)
{
@@ -267,6 +308,7 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
for (int i=0; i<=2; ++i)
for (int j=1; j<=4; j++)
SummonMinion(NPC_SOLARIUM_AGENT, Portals[i][0], Portals[i][1], Portals[i][2]);
+
DoScriptText(SAY_SUMMON1, m_creature);
Phase2_Timer = 10000;
} else Phase2_Timer -= diff;
@@ -275,19 +317,24 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
{
m_creature->AttackStop();
m_creature->StopMoving();
+
//Check Phase3_Timer
if (Phase3_Timer < diff)
{
Phase = 1;
+
//15 seconds later Solarian reappears out of one of the 3 portals. Simultaneously, 2 healers appear in the two other portals.
int i = rand()%3;
m_creature->GetMotionMaster()->Clear();
m_creature->GetMap()->CreatureRelocation(m_creature, Portals[i][0], Portals[i][1], Portals[i][2], CENTER_O);
+
for (int j=0; j<=2; j++)
if (j!=i)
SummonMinion(NPC_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);
+
DoScriptText(SAY_SUMMON2, m_creature);
AppearDelay = true;
Phase3_Timer = 15000;
@@ -301,6 +348,7 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
DoCast(m_creature, SPELL_FEAR);
Fear_Timer = 20000;
}else Fear_Timer -= diff;
+
//VoidBolt_Timer
if (VoidBolt_Timer < diff)
{
@@ -308,10 +356,12 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
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);
@@ -321,35 +371,44 @@ struct TRINITY_DLL_DECL boss_high_astromancer_solarianAI : public ScriptedAI
m_creature->SetDisplayId(MODEL_VOIDWALKER);
m_creature->SetFloatValue(OBJECT_FIELD_SCALE_X, defaultsize*2.5f);
}
+
DoMeleeAttackIfReady();
}
};
+
struct TRINITY_DLL_DECL mob_solarium_priestAI : public ScriptedAI
{
mob_solarium_priestAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance *pInstance;
+
uint32 healTimer;
uint32 holysmiteTimer;
uint32 aoesilenceTimer;
+
void Reset()
{
healTimer = 9000;
holysmiteTimer = 1;
aoesilenceTimer = 15000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (healTimer < diff)
{
Unit* target = NULL;
+
switch(rand()%2)
{
case 0:
@@ -360,33 +419,40 @@ struct TRINITY_DLL_DECL mob_solarium_priestAI : public ScriptedAI
target = m_creature;
break;
}
+
if (target)
{
DoCast(target,SPELL_SOLARIUM_GREAT_HEAL);
healTimer = 9000;
}
} else healTimer -= diff;
+
if (holysmiteTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SOLARIUM_HOLY_SMITE);
holysmiteTimer = 4000;
} else holysmiteTimer -= diff;
+
if (aoesilenceTimer < diff)
{
DoCast(m_creature->getVictim(), SPELL_SOLARIUM_ARCANE_TORRENT);
aoesilenceTimer = 13000;
} else aoesilenceTimer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_mob_solarium_priest(Creature* pCreature)
{
return new mob_solarium_priestAI (pCreature);
}
+
CreatureAI* GetAI_boss_high_astromancer_solarian(Creature* pCreature)
{
return new boss_high_astromancer_solarianAI (pCreature);
}
+
void AddSC_boss_high_astromancer_solarian()
{
Script *newscript;
@@ -394,6 +460,7 @@ void AddSC_boss_high_astromancer_solarian()
newscript->Name = "boss_high_astromancer_solarian";
newscript->GetAI = &GetAI_boss_high_astromancer_solarian;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_solarium_priest";
newscript->GetAI = &GetAI_mob_solarium_priest;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp
index b5a950e8dee..2341f78ebe3 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_kaelthas.cpp
@@ -13,15 +13,18 @@
* along 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, weapon scripts, mind control, need correct spells(interruptible/uninterruptible), phoenix spawn location & animation, phoenix behaviour & spawn during gravity lapse
SDCategory: Tempest Keep, The Eye
EndScriptData */
+
#include "precompiled.h"
#include "def_the_eye.h"
#include "WorldPacket.h"
+
enum eEnums
{
//kael'thas Speech
@@ -44,19 +47,24 @@ enum eEnums
SAY_SUMMON_PHOENIX1 = -1550032,
SAY_SUMMON_PHOENIX2 = -1550033,
SAY_DEATH = -1550034,
+
//Thaladred the Darkener speech
SAY_THALADRED_AGGRO = -1550035,
SAY_THALADRED_DEATH = -1550036,
EMOTE_THALADRED_GAZE = -1550037,
+
//Lord Sanguinar speech
SAY_SANGUINAR_AGGRO = -1550038,
SAY_SANGUINAR_DEATH = -1550039,
+
//Grand Astromancer Capernian speech
SAY_CAPERNIAN_AGGRO = -1550040,
SAY_CAPERNIAN_DEATH = -1550041,
+
//Master Engineer Telonicus speech
SAY_TELONICUS_AGGRO = -1550042,
SAY_TELONICUS_DEATH = -1550043,
+
//Phase 2 spells
SPELL_SUMMON_WEAPONS = 36976,
SPELL_SUMMON_WEAPONA = 36958,
@@ -67,6 +75,7 @@ enum eEnums
SPELL_SUMMON_WEAPONF = 36963,
SPELL_SUMMON_WEAPONG = 36964,
SPELL_RES_VISUAL = 24171,
+
//Phase 4 spells
SPELL_FIREBALL = 22088, //wrong but works with CastCustomSpell
SPELL_PYROBLAST = 36819,
@@ -77,6 +86,7 @@ enum eEnums
SPELL_SHOCK_BARRIER = 36815,
SPELL_PHOENIX_ANIMATION = 36723,
SPELL_MIND_CONTROL = 32830,
+
//Phase 5 spells
SPELL_EXPLODE = 36092,
SPELL_FULLPOWER = 36187,
@@ -84,12 +94,14 @@ enum eEnums
SPELL_GRAVITY_LAPSE = 34480,
SPELL_GRAVITY_LAPSE_AURA = 39432,
SPELL_NETHER_BEAM = 35873,
+
//Thaladred the Darkener spells
SPELL_PSYCHIC_BLOW = 10689,
SPELL_SILENCE = 30225,
//Lord Sanguinar spells
SPELL_BELLOWING_ROAR = 40636,
//Grand Astromancer Capernian spells
+
SPELL_CAPERNIAN_FIREBALL = 36971,
SPELL_CONFLAGRATION = 37018,
SPELL_ARCANE_EXPLOSION = 36970,
@@ -102,24 +114,32 @@ enum eEnums
SPELL_BURN = 36720,
SPELL_EMBER_BLAST = 34341,
SPELL_REBIRTH = 41587,
+
//Creature IDs
NPC_PHOENIX = 21362,
NPC_PHOENIX_EGG = 21364,
+
//Phoenix egg and phoenix model
MODEL_ID_PHOENIX = 19682,
MODEL_ID_PHOENIX_EGG = 20245,
+
MAX_ADVISORS = 4
};
+
uint32 m_auiSpellSummonWeapon[]=
{
SPELL_SUMMON_WEAPONA, SPELL_SUMMON_WEAPONB, SPELL_SUMMON_WEAPONC, SPELL_SUMMON_WEAPOND,
SPELL_SUMMON_WEAPONE, SPELL_SUMMON_WEAPONF, SPELL_SUMMON_WEAPONG
};
+
const float CAPERNIAN_DISTANCE = 20.0f; //she casts away from the target
const float KAEL_VISIBLE_RANGE = 50.0f;
+
const float afGravityPos[3] = {795.0f, 0.0f, 70.0f};
+
#define TIME_PHASE_2_3 120000
#define TIME_PHASE_3_4 180000
+
//Base AI for Advisors
struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
{
@@ -128,11 +148,13 @@ struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
m_bDoubled_Health = false;
}
+
ScriptedInstance* m_pInstance;
bool FakeDeath;
bool m_bDoubled_Health;
uint32 DelayRes_Timer;
uint64 DelayRes_Target;
+
void Reset()
{
if (m_bDoubled_Health)
@@ -140,29 +162,37 @@ struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
m_creature->SetMaxHealth(m_creature->GetMaxHealth() / 2);
m_bDoubled_Health = false;
}
+
FakeDeath = false;
DelayRes_Timer = 0;
DelayRes_Target = 0;
+
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
//reset encounter
if (m_pInstance && (m_pInstance->GetData(DATA_KAELTHASEVENT) == 1 || m_pInstance->GetData(DATA_KAELTHASEVENT) == 3))
if (Creature *Kaelthas = Unit::GetCreature((*m_creature), m_pInstance->GetData64(DATA_KAELTHAS)))
Kaelthas->AI()->EnterEvadeMode();
}
+
void MoveInLineOfSight(Unit *who)
{
if (!who || FakeDeath || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
ScriptedAI::MoveInLineOfSight(who);
}
+
void AttackStart(Unit* who)
{
if (!who || FakeDeath || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
ScriptedAI::AttackStart(who);
}
+
void Revive(Unit* Target)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
@@ -171,25 +201,30 @@ struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
m_bDoubled_Health = true;
m_creature->SetHealth(m_creature->GetMaxHealth());
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
+
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 && m_pInstance && m_pInstance->GetData(DATA_KAELTHASEVENT) != 0)
{
damage = 0;
return;
}
+
//Don't really die in phase 1 & 3, only die after that
if (m_pInstance && m_pInstance->GetData(DATA_KAELTHASEVENT) != 0)
{
//prevent death
damage = 0;
FakeDeath = true;
+
m_creature->InterruptNonMeleeSpells(false);
m_creature->SetHealth(0);
m_creature->StopMoving();
@@ -206,6 +241,7 @@ struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
JustDied(pKiller);
}
}
+
void UpdateAI(const uint32 diff)
{
if (DelayRes_Timer)
@@ -214,9 +250,11 @@ struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
{
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();
@@ -225,7 +263,9 @@ struct TRINITY_DLL_DECL advisorbase_ai : public ScriptedAI
} else DelayRes_Timer -= diff;
}
}
+
};
+
//Kael'thas AI
struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
{
@@ -234,7 +274,9 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
m_pInstance = pCreature->GetInstanceData();
memset(&m_auiAdvisorGuid, 0, sizeof(m_auiAdvisorGuid));
}
+
ScriptedInstance* m_pInstance;
+
uint32 Fireball_Timer;
uint32 ArcaneDisruption_Timer;
uint32 Phoenix_Timer;
@@ -249,11 +291,15 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
uint32 PhaseSubphase; //generic
uint32 Phase_Timer; //generic timer
uint32 PyrosCasted;
+
bool InGravityLapse;
bool IsCastingFireball;
bool ChainPyros;
+
SummonList summons;
+
uint64 m_auiAdvisorGuid[MAX_ADVISORS];
+
void Reset()
{
Fireball_Timer = 5000+rand()%10000;
@@ -271,17 +317,22 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
InGravityLapse = false;
IsCastingFireball = false;
ChainPyros = false;
+
if (m_creature->isInCombat())
PrepareAdvisors();
+
summons.DespawnAll();
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
if (m_pInstance)
m_pInstance->SetData(DATA_KAELTHASEVENT, 0);
}
+
void PrepareAdvisors()
{
- for (uint8 i = 0; i < MAX_ADVISORS; ++i)
+ for(uint8 i = 0; i < MAX_ADVISORS; ++i)
{
if (Creature *pCreature = Unit::GetCreature((*m_creature), m_auiAdvisorGuid[i]))
{
@@ -292,36 +343,49 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
}
}
}
+
void StartEvent()
{
if (!m_pInstance)
return;
+
m_auiAdvisorGuid[0] = m_pInstance->GetData64(DATA_THALADREDTHEDARKENER);
m_auiAdvisorGuid[1] = m_pInstance->GetData64(DATA_LORDSANGUINAR);
m_auiAdvisorGuid[2] = m_pInstance->GetData64(DATA_GRANDASTROMANCERCAPERNIAN);
m_auiAdvisorGuid[3] = m_pInstance->GetData64(DATA_MASTERENGINEERTELONICUS);
+
if (!m_auiAdvisorGuid[0] || !m_auiAdvisorGuid[1] || !m_auiAdvisorGuid[2] || !m_auiAdvisorGuid[3])
{
error_log("TSCR: Kael'Thas One or more advisors missing, Skipping Phases 1-3");
+
DoScriptText(SAY_PHASE4_INTRO2, m_creature);
+
Phase = 4;
+
m_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);
+
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
AttackStart(target);
+
}
else
{
PrepareAdvisors();
+
DoScriptText(SAY_INTRO, m_creature);
+
m_pInstance->SetData(DATA_KAELTHASEVENT, 1);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
PhaseSubphase = 0;
Phase_Timer = 23000;
Phase = 1;
}
}
+
void MoveInLineOfSight(Unit *who)
{
if (!m_creature->hasUnitState(UNIT_STAT_STUNNED) && who->isTargetableForAttack() &&
@@ -329,6 +393,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
{
if (!m_creature->canFly() && m_creature->GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE)
return;
+
float attackRadius = m_creature->GetAttackDistance(who);
if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->IsWithinLOSInMap(who))
{
@@ -341,21 +406,25 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
{
if (m_pInstance && !m_pInstance->GetData(DATA_KAELTHASEVENT) && !Phase)
StartEvent();
+
who->SetInCombatWith(m_creature);
m_creature->AddThreat(who, 0.0f);
}
}
}
}
+
void Aggro(Unit *who)
{
if (m_pInstance && !m_pInstance->GetData(DATA_KAELTHASEVENT) && !Phase)
StartEvent();
}
+
void KilledUnit()
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustSummoned(Creature* pSummoned)
{
// if not phoenix, then it's one of the 7 weapons
@@ -363,24 +432,32 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
pSummoned->AI()->AttackStart(pTarget);
+
summons.Summon(pSummoned);
}
}
+
void SummonedCreatureDespawn(Creature *summon) {summons.Despawn(summon);}
+
void JustDied(Unit* Killer)
{
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
DoScriptText(SAY_DEATH, m_creature);
+
summons.DespawnAll();
+
if (m_pInstance)
m_pInstance->SetData(DATA_KAELTHASEVENT, 0);
- for (uint8 i = 0; i < MAX_ADVISORS; ++i)
+
+ for(uint8 i = 0; i < MAX_ADVISORS; ++i)
{
if (Unit* pAdvisor = Unit::GetUnit((*m_creature), m_auiAdvisorGuid[i]))
pAdvisor->Kill(pAdvisor);
}
}
+
void UpdateAI(const uint32 diff)
{
//Phase 1
@@ -390,6 +467,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
{
Unit *target = NULL;
Creature* Advisor = NULL;
+
//Subphase switch
switch(PhaseSubphase)
{
@@ -398,118 +476,148 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
if (Phase_Timer < diff)
{
DoScriptText(SAY_INTRO_THALADRED, m_creature);
+
//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 = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[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 = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[0]));
+
if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD))
{
DoScriptText(SAY_INTRO_SANGUINAR, m_creature);
+
//start advisor within 12.5 seconds
Phase_Timer = 12500;
++PhaseSubphase;
}
break;
+
//Subphase 2 - Unlock advisor
case 3:
if (Phase_Timer < diff)
{
Advisor = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[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 = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[1]));
+
if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD))
{
DoScriptText(SAY_INTRO_CAPERNIAN, m_creature);
+
//start advisor within 7 seconds
Phase_Timer = 7000;
++PhaseSubphase;
}
break;
+
//Subphase 3 - Unlock advisor
case 5:
if (Phase_Timer < diff)
{
Advisor = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[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 = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[2]));
+
if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD))
{
DoScriptText(SAY_INTRO_TELONICUS, m_creature);
+
//start advisor within 8.4 seconds
Phase_Timer = 8400;
++PhaseSubphase;
}
break;
+
//Subphase 4 - Unlock advisor
case 7:
if (Phase_Timer < diff)
{
Advisor = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[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 = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[3]));
+
if (Advisor && (Advisor->getStandState() == UNIT_STAND_STATE_DEAD))
{
Phase = 2;
if(m_pInstance)
m_pInstance->SetData(DATA_KAELTHASEVENT, 2);
+
DoScriptText(SAY_PHASE2_WEAPON, m_creature);
+
PhaseSubphase = 0;
Phase_Timer = 3500;
DoCast(m_creature, SPELL_SUMMON_WEAPONS);
@@ -517,6 +625,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
break;
}
}break;
+
case 2:
{
if (PhaseSubphase == 0)
@@ -526,16 +635,21 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
PhaseSubphase = 1;
}else Phase_Timer -= diff;
}
+
//Spawn weapons
if (PhaseSubphase == 1)
{
m_creature->CastSpell(m_creature, SPELL_SUMMON_WEAPONS, false);
+
uint8 uiMaxWeapon = sizeof(m_auiSpellSummonWeapon)/sizeof(uint32);
+
for (uint32 i = 0; i < uiMaxWeapon; ++i)
m_creature->CastSpell(m_creature,m_auiSpellSummonWeapon[i],true);
+
PhaseSubphase = 2;
Phase_Timer = TIME_PHASE_2_3;
}
+
if (PhaseSubphase == 2)
{
if (Phase_Timer < diff)
@@ -548,40 +662,51 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
}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 < MAX_ADVISORS; ++i)
{
Advisor = (Unit::GetCreature((*m_creature), m_auiAdvisorGuid[i]));
+
if (!Advisor)
error_log("SD2: Kael'Thas Advisor %u does not exist. Possibly despawned? Incorrectly Killed?", i);
else
CAST_AI(advisorbase_ai, Advisor->AI())->Revive(Target);
}
+
PhaseSubphase = 1;
Phase_Timer = TIME_PHASE_3_4;
}
+
if (Phase_Timer < diff)
{
DoScriptText(SAY_PHASE4_INTRO2, m_creature);
Phase = 4;
+
if(m_pInstance)
m_pInstance->SetData(DATA_KAELTHASEVENT, 4);
+
// Sometimes people can collect Aggro in Phase 1-3. Reset threat before releasing Kael.
DoResetThreat();
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
AttackStart(target);
+
Phase_Timer = 30000;
} else Phase_Timer -= diff;
}
break;
+
case 4:
case 5:
case 6:
@@ -589,6 +714,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
//Return since we have no target
if (!UpdateVictim())
return;
+
//Fireball_Timer
if (!InGravityLapse && !ChainPyros && Phase != 5)
{
@@ -614,18 +740,22 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
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)
{
if (Unit* pUnit = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pUnit, SPELL_FLAME_STRIKE);
+
FlameStrike_Timer = 30000;
} else FlameStrike_Timer -= diff;
+
if (MindControl_Timer < diff)
{
if (m_creature->getThreatManager().getThreatList().size() >= 2)
@@ -634,16 +764,20 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
debug_log("SD2: Kael'Thas mind control not supported.");
//DoCast(pUnit, SPELL_MIND_CONTROL);
}
+
MindControl_Timer = 60000;
} else MindControl_Timer -= diff;
}
+
//Phoenix_Timer
if (Phoenix_Timer < diff)
{
DoCast(m_creature, SPELL_PHOENIX_ANIMATION);
DoScriptText(RAND(SAY_SUMMON_PHOENIX1,SAY_SUMMON_PHOENIX2), m_creature);
+
Phoenix_Timer = 60000;
} else Phoenix_Timer -= diff;
+
//Phase 4 specific spells
if (Phase == 4)
{
@@ -653,16 +787,20 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
m_pInstance->SetData(DATA_KAELTHASEVENT, 4);
Phase = 5;
Phase_Timer = 10000;
+
DoScriptText(SAY_PHASE5_NUTS, m_creature);
+
m_creature->StopMoving();
m_creature->GetMotionMaster()->Clear();
m_creature->GetMotionMaster()->MoveIdle();
m_creature->GetMap()->CreatureRelocation(m_creature, afGravityPos[0], afGravityPos[1], afGravityPos[2], 0);
m_creature->SendMonsterMove(afGravityPos[0], afGravityPos[1], afGravityPos[2], 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)
{
@@ -671,6 +809,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
PyrosCasted = 0;
ShockBarrier_Timer = 60000;
}else ShockBarrier_Timer -= diff;
+
//Chain Pyros (3 of them max)
if (ChainPyros && !m_creature->IsNonMeleeSpellCasted(false))
{
@@ -687,21 +826,26 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
}
}
}
+
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;
AttackStart(m_creature->getVictim());
} else Phase_Timer -= diff;
}
+
//Phase 5
if (Phase == 6)
{
+
//GravityLapse_Timer
if (GravityLapse_Timer < diff)
{
@@ -714,6 +858,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
m_creature->GetMotionMaster()->MoveIdle();
m_creature->GetMap()->CreatureRelocation(m_creature, afGravityPos[0], afGravityPos[1], afGravityPos[2], 0);
m_creature->SendMonsterMove(afGravityPos[0], afGravityPos[1], afGravityPos[2], 0, MOVEMENTFLAG_NONE, 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)
{
@@ -724,14 +869,17 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
DoTeleportPlayer(pUnit, afGravityPos[0], afGravityPos[1], afGravityPos[2], pUnit->GetOrientation());
}
}
+
GravityLapse_Timer = 500;
++GravityLapse_Phase;
InGravityLapse = true;
ShockBarrier_Timer = 1000;
NetherBeam_Timer = 5000;
break;
+
case 1:
DoScriptText(RAND(SAY_GRAVITYLAPSE1,SAY_GRAVITYLAPSE2), m_creature);
+
// 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)
{
@@ -739,8 +887,10 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
{
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);
@@ -752,13 +902,16 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
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)
@@ -773,6 +926,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
pUnit->SendMessageToSet(&data, true);
}
}
+
m_creature->RemoveAurasDueToSpell(SPELL_NETHER_VAPOR);
InGravityLapse = false;
GravityLapse_Timer = 60000;
@@ -781,6 +935,7 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
break;
}
}else GravityLapse_Timer -= diff;
+
if (InGravityLapse)
{
//ShockBarrier_Timer
@@ -789,58 +944,73 @@ struct TRINITY_DLL_DECL boss_kaelthasAI : public ScriptedAI
DoCast(m_creature, SPELL_SHOCK_BARRIER);
ShockBarrier_Timer = 20000;
} else ShockBarrier_Timer -= diff;
+
//NetherBeam_Timer
if (NetherBeam_Timer < diff)
{
if (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 TRINITY_DLL_DECL boss_thaladred_the_darkenerAI : public advisorbase_ai
{
boss_thaladred_the_darkenerAI(Creature* pCreature) : advisorbase_ai(pCreature) {}
+
uint32 Gaze_Timer;
uint32 Silence_Timer;
uint32 PsychicBlow_Timer;
+
void Reset()
{
Gaze_Timer = 100;
Silence_Timer = 20000;
PsychicBlow_Timer = 10000;
+
advisorbase_ai::Reset();
}
+
void Aggro(Unit *who)
{
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (!who || FakeDeath)
return;
+
DoScriptText(SAY_THALADRED_AGGRO, m_creature);
m_creature->AddThreat(who, 5000000.0f);
}
+
void JustDied(Unit* pKiller)
{
if (m_pInstance && m_pInstance->GetData(DATA_KAELTHASEVENT) == 3)
DoScriptText(SAY_THALADRED_DEATH, m_creature);
}
+
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 (!UpdateVictim())
return;
+
//Gaze_Timer
if (Gaze_Timer < diff)
{
@@ -852,71 +1022,89 @@ struct TRINITY_DLL_DECL boss_thaladred_the_darkenerAI : public advisorbase_ai
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 TRINITY_DLL_DECL boss_lord_sanguinarAI : public advisorbase_ai
{
boss_lord_sanguinarAI(Creature* pCreature) : advisorbase_ai(pCreature) {}
+
uint32 Fear_Timer;
+
void Reset()
{
Fear_Timer = 20000;
advisorbase_ai::Reset();
}
+
void Aggro(Unit *who)
{
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (!who || FakeDeath)
return;
+
DoScriptText(SAY_SANGUINAR_AGGRO, m_creature);
}
+
void JustDied(Unit* Killer)
{
if (m_pInstance && m_pInstance->GetData(DATA_KAELTHASEVENT) == 3)
DoScriptText(SAY_SANGUINAR_DEATH, m_creature);
}
+
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 (!UpdateVictim())
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 TRINITY_DLL_DECL boss_grand_astromancer_capernianAI : public advisorbase_ai
{
boss_grand_astromancer_capernianAI(Creature* pCreature) : advisorbase_ai(pCreature) {}
+
uint32 Fireball_Timer;
uint32 Conflagration_Timer;
uint32 ArcaneExplosion_Timer;
uint32 Yell_Timer;
bool Yell;
+
void Reset()
{
Fireball_Timer = 2000;
@@ -924,41 +1112,52 @@ struct TRINITY_DLL_DECL boss_grand_astromancer_capernianAI : public advisorbase_
ArcaneExplosion_Timer = 5000;
Yell_Timer = 2000;
Yell = false;
+
advisorbase_ai::Reset();
}
+
void JustDied(Unit* pKiller)
{
if (m_pInstance && m_pInstance->GetData(DATA_KAELTHASEVENT) == 3)
DoScriptText(SAY_CAPERNIAN_DEATH, m_creature);
}
+
void AttackStart(Unit* who)
{
if (!who || FakeDeath || m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (m_creature->Attack(who, true))
{
m_creature->AddThreat(who, 0.0f);
m_creature->SetInCombatWith(who);
who->SetInCombatWith(m_creature);
+
m_creature->GetMotionMaster()->MoveChase(who, CAPERNIAN_DISTANCE);
}
}
+
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 (!UpdateVictim())
return;
+
//Yell_Timer
if (!Yell)
{
@@ -968,23 +1167,28 @@ struct TRINITY_DLL_DECL boss_grand_astromancer_capernianAI : public advisorbase_
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)
{
@@ -1002,79 +1206,103 @@ struct TRINITY_DLL_DECL boss_grand_astromancer_capernianAI : public advisorbase_
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 TRINITY_DLL_DECL boss_master_engineer_telonicusAI : public advisorbase_ai
{
boss_master_engineer_telonicusAI(Creature* pCreature) : advisorbase_ai(pCreature) {}
+
uint32 Bomb_Timer;
uint32 RemoteToy_Timer;
+
void Reset()
{
Bomb_Timer = 10000;
RemoteToy_Timer = 5000;
+
advisorbase_ai::Reset();
}
+
void JustDied(Unit* pKiller)
{
if (m_pInstance && m_pInstance->GetData(DATA_KAELTHASEVENT) == 3)
DoScriptText(SAY_TELONICUS_DEATH, m_creature);
}
+
void Aggro(Unit *who)
{
if (m_creature->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return;
+
if (!who || FakeDeath)
return;
+
DoScriptText(SAY_TELONICUS_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 diff)
{
advisorbase_ai::UpdateAI(diff);
+
//Faking Death, do nothing
if (FakeDeath)
return;
+
//Return since we have no target
if (!UpdateVictim())
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)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_REMOTE_TOY);
+
RemoteToy_Timer = 10000+rand()%5000;
} else RemoteToy_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Flame Strike AI
struct TRINITY_DLL_DECL mob_kael_flamestrikeAI : public ScriptedAI
{
mob_kael_flamestrikeAI(Creature* pCreature) : ScriptedAI(pCreature) {}
+
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 MoveInLineOfSight(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!Casting)
@@ -1082,6 +1310,7 @@ struct TRINITY_DLL_DECL mob_kael_flamestrikeAI : public ScriptedAI
DoCast(m_creature, SPELL_FLAME_STRIKE_VIS);
Casting = true;
}
+
//Timer
if (Timer < diff)
{
@@ -1090,31 +1319,38 @@ struct TRINITY_DLL_DECL mob_kael_flamestrikeAI : public ScriptedAI
m_creature->InterruptNonMeleeSpells(false);
DoCast(m_creature, SPELL_FLAME_STRIKE_DMG);
} else m_creature->Kill(m_creature);
+
KillSelf = true;
Timer = 1000;
} else Timer -= diff;
}
};
+
//Phoenix AI
struct TRINITY_DLL_DECL mob_phoenix_tkAI : public ScriptedAI
{
mob_phoenix_tkAI(Creature* pCreature) : ScriptedAI(pCreature) {}
+
uint32 Cycle_Timer;
+
void Reset()
{
Cycle_Timer = 2000;
m_creature->CastSpell(m_creature,SPELL_BURN,true);
}
+
void JustDied(Unit* killer)
{
//is this spell in use anylonger?
//m_creature->CastSpell(m_creature,SPELL_EMBER_BLAST,true);
m_creature->SummonCreature(NPC_PHOENIX_EGG,m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ(),m_creature->GetOrientation(),TEMPSUMMON_TIMED_DESPAWN,16000);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (Cycle_Timer < diff)
{
//spell Burn should possible do this, but it doesn't, so do this for now.
@@ -1123,38 +1359,48 @@ struct TRINITY_DLL_DECL mob_phoenix_tkAI : public ScriptedAI
m_creature->SetHealth(uint32(m_creature->GetHealth()-dmg));
Cycle_Timer = 2000;
} else Cycle_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
//Phoenix Egg AI
struct TRINITY_DLL_DECL mob_phoenix_egg_tkAI : public ScriptedAI
{
mob_phoenix_egg_tkAI(Creature* pCreature) : ScriptedAI(pCreature) {}
+
uint32 Rebirth_Timer;
+
void Reset()
{
Rebirth_Timer = 15000;
}
+
//ignore any
void MoveInLineOfSight(Unit* who) { return; }
+
void AttackStart(Unit* who)
{
if (m_creature->Attack(who, false))
{
m_creature->SetInCombatWith(who);
who->SetInCombatWith(m_creature);
+
DoStartNoMovement(who);
}
}
+
void JustSummoned(Creature* summoned)
{
summoned->AddThreat(m_creature->getVictim(), 0.0f);
summoned->CastSpell(summoned,SPELL_REBIRTH,false);
}
+
void UpdateAI(const uint32 diff)
{
if (!Rebirth_Timer)
return;
+
if (Rebirth_Timer <= diff)
{
m_creature->SummonCreature(NPC_PHOENIX,m_creature->GetPositionX(),m_creature->GetPositionY(),m_creature->GetPositionZ(),m_creature->GetOrientation(),TEMPSUMMON_CORPSE_DESPAWN,5000);
@@ -1162,38 +1408,47 @@ struct TRINITY_DLL_DECL mob_phoenix_egg_tkAI : public ScriptedAI
} else Rebirth_Timer -= diff;
}
};
+
CreatureAI* GetAI_boss_kaelthas(Creature* pCreature)
{
return new boss_kaelthasAI(pCreature);
}
+
CreatureAI* GetAI_boss_thaladred_the_darkener(Creature* pCreature)
{
return new boss_thaladred_the_darkenerAI(pCreature);
}
+
CreatureAI* GetAI_boss_lord_sanguinar(Creature* pCreature)
{
return new boss_lord_sanguinarAI(pCreature);
}
+
CreatureAI* GetAI_boss_grand_astromancer_capernian(Creature* pCreature)
{
return new boss_grand_astromancer_capernianAI(pCreature);
}
+
CreatureAI* GetAI_boss_master_engineer_telonicus(Creature* pCreature)
{
return new boss_master_engineer_telonicusAI(pCreature);
}
+
CreatureAI* GetAI_mob_kael_flamestrike(Creature* pCreature)
{
return new mob_kael_flamestrikeAI(pCreature);
}
+
CreatureAI* GetAI_mob_phoenix_tk(Creature* pCreature)
{
return new mob_phoenix_tkAI(pCreature);
}
+
CreatureAI* GetAI_mob_phoenix_egg_tk(Creature* pCreature)
{
return new mob_phoenix_egg_tkAI(pCreature);
}
+
void AddSC_boss_kaelthas()
{
Script *newscript;
@@ -1201,30 +1456,37 @@ void AddSC_boss_kaelthas()
newscript->Name = "boss_kaelthas";
newscript->GetAI = &GetAI_boss_kaelthas;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_thaladred_the_darkener";
newscript->GetAI = &GetAI_boss_thaladred_the_darkener;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_lord_sanguinar";
newscript->GetAI = &GetAI_boss_lord_sanguinar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_grand_astromancer_capernian";
newscript->GetAI = &GetAI_boss_grand_astromancer_capernian;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_master_engineer_telonicus";
newscript->GetAI = &GetAI_boss_master_engineer_telonicus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_kael_flamestrike";
newscript->GetAI = &GetAI_mob_kael_flamestrike;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_phoenix_tk";
newscript->GetAI = &GetAI_mob_phoenix_tk;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_phoenix_egg_tk";
newscript->GetAI = &GetAI_mob_phoenix_egg_tk;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp
index afd513b4a30..c1de7772120 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/boss_void_reaver.cpp
@@ -13,14 +13,17 @@
* along 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: 90
SDComment: Should reset if raid are out of room.
SDCategory: Tempest Keep, The Eye
EndScriptData */
+
#include "precompiled.h"
#include "def_the_eye.h"
+
enum eEnums
{
SAY_AGGRO = -1550000,
@@ -30,111 +33,141 @@ enum eEnums
SAY_DEATH = -1550004,
SAY_POUNDING1 = -1550005,
SAY_POUNDING2 = -1550006,
+
SPELL_POUNDING = 34162,
SPELL_ARCANE_ORB = 34172,
SPELL_KNOCK_AWAY = 25778,
SPELL_BERSERK = 27680
};
+
struct TRINITY_DLL_DECL boss_void_reaverAI : public ScriptedAI
{
boss_void_reaverAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData();
}
+
ScriptedInstance* pInstance;
+
uint32 Pounding_Timer;
uint32 ArcaneOrb_Timer;
uint32 KnockAway_Timer;
uint32 Berserk_Timer;
+
bool Enraged;
+
void Reset()
{
Pounding_Timer = 15000;
ArcaneOrb_Timer = 3000;
KnockAway_Timer = 30000;
Berserk_Timer = 600000;
+
Enraged = false;
+
if (pInstance && m_creature->isAlive())
pInstance->SetData(DATA_VOIDREAVEREVENT, NOT_STARTED);
}
+
void KilledUnit(Unit *victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2,SAY_SLAY3), m_creature);
}
+
void JustDied(Unit *victim)
{
DoScriptText(SAY_DEATH, m_creature);
DoZoneInCombat();
+
if (pInstance)
pInstance->SetData(DATA_VOIDREAVEREVENT, DONE);
}
+
void EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_VOIDREAVEREVENT, IN_PROGRESS);
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
// Pounding
if (Pounding_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_POUNDING);
+
DoScriptText(RAND(SAY_POUNDING1,SAY_POUNDING2), m_creature);
Pounding_Timer = 15000; //cast time(3000) + cooldown time(12000)
}else Pounding_Timer -= diff;
+
// Arcane Orb
if (ArcaneOrb_Timer < diff)
{
Unit *target = NULL;
std::list<HostilReference *> t_list = m_creature->getThreatManager().getThreatList();
std::vector<Unit *> target_list;
- for (std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
+ for(std::list<HostilReference *>::iterator itr = t_list.begin(); itr!= t_list.end(); ++itr)
{
target = Unit::GetUnit(*m_creature, (*itr)->getUnitGuid());
if (!target)
continue;
+
// exclude pets & totems
if (target->GetTypeId() != TYPEID_PLAYER)
continue;
+
//18 yard radius minimum
if (target && target->GetTypeId() == TYPEID_PLAYER && target->isAlive() && !target->IsWithinDist(m_creature, 18, false))
target_list.push_back(target);
target = NULL;
}
+
if (target_list.size())
target = *(target_list.begin()+rand()%target_list.size());
else
target = m_creature->getVictim();
+
if (target)
m_creature->CastSpell(target->GetPositionX(),target->GetPositionY(),target->GetPositionZ(), SPELL_ARCANE_ORB, false, NULL, NULL, NULL, target);
+
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 (DoGetThreat(m_creature->getVictim()))
DoModifyThreatPercent(m_creature->getVictim(),-25);
+
KnockAway_Timer = 30000;
}else KnockAway_Timer -= diff;
+
//Berserk
if (Berserk_Timer < diff && !Enraged)
{
DoCast(m_creature,SPELL_BERSERK);
Enraged = true;
}else Berserk_Timer -= diff;
+
DoMeleeAttackIfReady();
+
EnterEvadeIfOutOfCombatArea(diff);
}
};
+
CreatureAI* GetAI_boss_void_reaver(Creature* pCreature)
{
return new boss_void_reaverAI (pCreature);
}
+
void AddSC_boss_void_reaver()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/def_the_eye.h b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/def_the_eye.h
index 3fe81530bc4..d0d3ea09061 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/def_the_eye.h
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/def_the_eye.h
@@ -1,8 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef DEF_THE_EYE_H
#define DEF_THE_EYE_H
+
#define DATA_ALAREVENT 1
#define DATA_ASTROMANCER 2
#define DATA_GRANDASTROMANCERCAPERNIAN 3
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp
index 79183547c50..f973f96c1ff 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/instance_the_eye.cpp
@@ -13,24 +13,30 @@
* along 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 MAX_ENCOUNTER 5
+
/* The Eye encounters:
0 - Kael'thas event
1 - Al' ar event
2 - Solarian Event
3 - Void Reaver event
*/
+
struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
{
instance_the_eye(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint64 ThaladredTheDarkener;
uint64 LordSanguinar;
uint64 GrandAstromancerCapernian;
@@ -38,12 +44,16 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
uint64 Kaelthas;
uint64 Astromancer;
uint64 Alar;
+
uint8 KaelthasEventPhase;
uint8 AlarEventPhase;
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
+
ThaladredTheDarkener = 0;
LordSanguinar = 0;
GrandAstromancerCapernian = 0;
@@ -51,15 +61,19 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
Kaelthas = 0;
Astromancer = 0;
Alar = 0;
+
KaelthasEventPhase = 0;
AlarEventPhase = 0;
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) return true;
+
return false;
}
+
void OnCreatureCreate(Creature* pCreature, bool add)
{
switch(pCreature->GetEntry())
@@ -73,6 +87,7 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
case 19514: Alar = pCreature->GetGUID(); break;
}
}
+
uint64 GetData64(uint32 identifier)
{
switch(identifier)
@@ -87,6 +102,7 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
}
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -99,6 +115,7 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
if (data == DONE)
SaveToDB();
}
+
uint32 GetData(uint32 type)
{
switch(type)
@@ -110,6 +127,7 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
}
return 0;
}
+
std::string GetSaveData()
{
OUT_SAVE_INST_DATA;
@@ -124,6 +142,7 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
}
return NULL;
}
+
void Load(const char* in)
{
if (!in)
@@ -134,16 +153,18 @@ struct TRINITY_DLL_DECL instance_the_eye : public ScriptedInstance
OUT_LOAD_INST_DATA(in);
std::istringstream stream(in);
stream >> m_auiEncounter[0] >> m_auiEncounter[1] >> m_auiEncounter[2] >> m_auiEncounter[3];
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS) // Do not load an encounter as "In Progress" - reset it instead.
m_auiEncounter[i] = NOT_STARTED;
OUT_LOAD_INST_DATA_COMPLETE;
}
};
+
InstanceData* GetInstanceData_instance_the_eye(Map* pMap)
{
return new instance_the_eye(pMap);
}
+
void AddSC_instance_the_eye()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/the_eye.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/the_eye.cpp
index e39457fedf5..a7e47718017 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/the_eye.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_eye/the_eye.cpp
@@ -13,63 +13,81 @@
* along 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 TRINITY_DLL_DECL mob_crystalcore_devastatorAI : public ScriptedAI
{
mob_crystalcore_devastatorAI(Creature *c) : ScriptedAI(c) {}
+
uint32 Knockaway_Timer;
uint32 Countercharge_Timer;
+
void Reset()
{
Countercharge_Timer = 9000;
Knockaway_Timer = 25000;
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new mob_crystalcore_devastatorAI (pCreature);
}
+
void AddSC_the_eye()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp
index 16b3e443769..90f17e299b7 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_gyrokill.cpp
@@ -13,13 +13,16 @@
* along 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"
+
//not used
#define SAY_AGGRO -1554000
#define SAY_SAW_ATTACK1 -1554001
@@ -27,6 +30,7 @@ EndScriptData */
#define SAY_SLAY1 -1554003
#define SAY_SLAY2 -1554004
#define SAY_DEATH -1554005
+
#define SPELL_STREAM_OF_MACHINE_FLUID 35311
#define SPELL_SAW_BLADE 35318
#define H_SPELL_SAW_BLADE 39192
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp
index 66e1580f3d8..946108a72f0 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_gatewatcher_ironhand.cpp
@@ -13,13 +13,16 @@
* along 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: 75
SDComment:
SDCategory: Tempest Keep, The Mechanar
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO_1 -1554006
#define SAY_HAMMER_1 -1554007
#define SAY_HAMMER_2 -1554008
@@ -27,6 +30,7 @@ EndScriptData */
#define SAY_SLAY_2 -1554010
#define SAY_DEATH_1 -1554011
#define EMOTE_HAMMER -1554012
+
// Spells to be casted
#define SPELL_SHADOW_POWER 35322
#define H_SPELL_SHADOW_POWER 39193
@@ -34,6 +38,7 @@ EndScriptData */
#define SPELL_JACKHAMMER 35327
#define H_SPELL_JACKHAMMER 39194
#define SPELL_STREAM_OF_MACHINE_FLUID 35311
+
// Gatewatcher Iron-Hand AI
struct TRINITY_DLL_DECL boss_gatewatcher_iron_handAI : public ScriptedAI
{
@@ -41,60 +46,74 @@ struct TRINITY_DLL_DECL boss_gatewatcher_iron_handAI : public ScriptedAI
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
bool HeroicMode;
+
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 EnterCombat(Unit *who)
{
DoScriptText(SAY_AGGRO_1, m_creature);
}
+
void KilledUnit(Unit* victim)
{
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH_1, m_creature);
//TODO: Add door check/open code
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Shadow Power
if (Shadow_Power_Timer < diff)
{
DoCast(m_creature,HEROIC(SPELL_SHADOW_POWER, H_SPELL_SHADOW_POWER));
Shadow_Power_Timer = 20000 + rand()%8000;
}else Shadow_Power_Timer -= diff;
+
//Jack Hammer
if (Jackhammer_Timer < diff)
{
//TODO: expect cast this about 5 times in a row (?), announce it by emote only once
DoScriptText(EMOTE_HAMMER, m_creature);
DoCast(m_creature->getVictim(),HEROIC(SPELL_JACKHAMMER, H_SPELL_JACKHAMMER));
+
//chance to yell, but not same time as emote (after spell in fact casted)
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_HAMMER_1,SAY_HAMMER_2), m_creature);
Jackhammer_Timer = 30000;
}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 = 35000 + rand()%15000;
}else Stream_of_Machine_Fluid_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
@@ -102,6 +121,7 @@ CreatureAI* GetAI_boss_gatewatcher_iron_hand(Creature* pCreature)
{
return new boss_gatewatcher_iron_handAI (pCreature);
}
+
void AddSC_boss_gatewatcher_iron_hand()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp
index e7d861dbee7..aef118ff42f 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_nethermancer_sepethrea.cpp
@@ -13,14 +13,17 @@
* along 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: 90
SDComment: Need adjustments to initial summons
SDCategory: Tempest Keep, The Mechanar
EndScriptData */
+
#include "precompiled.h"
#include "def_mechanar.h"
+
#define SAY_AGGRO -1554013
#define SAY_SUMMON -1554014
#define SAY_DRAGONS_BREATH_1 -1554015
@@ -28,13 +31,16 @@ EndScriptData */
#define SAY_SLAY1 -1554017
#define SAY_SLAY2 -1554018
#define SAY_DEATH -1554019
+
#define SPELL_SUMMON_RAGIN_FLAMES 35275
#define H_SPELL_SUMMON_RAGIN_FLAMES 39084
+
#define SPELL_FROST_ATTACK 35263
#define SPELL_ARCANE_BLAST 35314
#define SPELL_DRAGONS_BREATH 35250
#define SPELL_KNOCKBACK 37317
#define SPELL_SOLARBURN 35267
+
struct TRINITY_DLL_DECL boss_nethermancer_sepethreaAI : public ScriptedAI
{
boss_nethermancer_sepethreaAI(Creature *c) : ScriptedAI(c)
@@ -42,13 +48,17 @@ struct TRINITY_DLL_DECL boss_nethermancer_sepethreaAI : public ScriptedAI
pInstance = c->GetInstanceData();
HeroicMode = c->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
+
bool HeroicMode;
+
uint32 frost_attack_Timer;
uint32 arcane_blast_Timer;
uint32 dragons_breath_Timer;
uint32 knockback_Timer;
uint32 solarburn_Timer;
+
void Reset()
{
frost_attack_Timer = 7000 + rand()%3000;
@@ -56,44 +66,54 @@ struct TRINITY_DLL_DECL boss_nethermancer_sepethreaAI : public ScriptedAI
dragons_breath_Timer = 18000 + rand()%4000;
knockback_Timer = 22000 + rand()%6000;
solarburn_Timer = 30000;
+
if (pInstance)
pInstance->SetData(DATA_NETHERMANCER_EVENT, NOT_STARTED);
}
+
void EnterCombat(Unit *who)
{
if (pInstance)
pInstance->SetData(DATA_NETHERMANCER_EVENT, IN_PROGRESS);
+
DoScriptText(SAY_AGGRO, m_creature);
DoCast(who, HEROIC(SPELL_SUMMON_RAGIN_FLAMES, H_SPELL_SUMMON_RAGIN_FLAMES));
DoScriptText(SAY_SUMMON, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY1,SAY_SLAY2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
if (pInstance)
pInstance->SetData(DATA_NETHERMANCER_EVENT, DONE);
}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Frost Attack
if (frost_attack_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_FROST_ATTACK);
frost_attack_Timer = 7000 + rand()%3000;
}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)
{
@@ -101,44 +121,56 @@ struct TRINITY_DLL_DECL boss_nethermancer_sepethreaAI : public ScriptedAI
{
if (rand()%2)
return;
+
DoScriptText(RAND(SAY_DRAGONS_BREATH_1,SAY_DRAGONS_BREATH_2), m_creature);
}
dragons_breath_Timer = 12000 + rand()%10000;
}else dragons_breath_Timer -= diff;
+
//Knockback
if (knockback_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_KNOCKBACK);
knockback_Timer = 15000 + rand()%10000;
}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* pCreature)
{
return new boss_nethermancer_sepethreaAI (pCreature);
}
+
#define SPELL_INFERNO 35268
#define H_SPELL_INFERNO 39346
#define SPELL_FIRE_TAIL 35278
+
struct TRINITY_DLL_DECL mob_ragin_flamesAI : public ScriptedAI
{
mob_ragin_flamesAI(Creature *c) : ScriptedAI(c)
{
pInstance = c->GetInstanceData(); HeroicMode = m_creature->GetMap()->IsHeroic();
}
+
ScriptedInstance *pInstance;
+
bool HeroicMode;
+
uint32 inferno_Timer;
uint32 flame_timer;
uint32 Check_Timer;
+
bool onlyonce;
+
void Reset()
{
inferno_Timer = 10000;
@@ -149,9 +181,11 @@ struct TRINITY_DLL_DECL mob_ragin_flamesAI : public ScriptedAI
m_creature->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true);
m_creature->SetSpeed(MOVE_RUN, HeroicMode ? 0.7f : 0.5f);
}
+
void EnterCombat(Unit* who)
{
}
+
void UpdateAI(const uint32 diff)
{
//Check_Timer
@@ -168,27 +202,33 @@ struct TRINITY_DLL_DECL mob_ragin_flamesAI : public ScriptedAI
}
Check_Timer = 1000;
}else Check_Timer -= diff;
+
if (!UpdateVictim())
return;
+
if (!onlyonce)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0))
m_creature->GetMotionMaster()->MoveChase(target);
onlyonce = true;
}
+
if (inferno_Timer < diff)
{
DoCast(m_creature->getVictim(),HEROIC(SPELL_INFERNO, H_SPELL_INFERNO));
m_creature->TauntApply(m_creature->getVictim());
inferno_Timer = 10000;
}else inferno_Timer -= diff;
+
if (flame_timer < diff)
{
DoCast(m_creature,SPELL_FIRE_TAIL);
flame_timer = 500;
}else flame_timer -=diff;
+
DoMeleeAttackIfReady();
}
+
};
CreatureAI* GetAI_mob_ragin_flames(Creature* pCreature)
{
@@ -201,6 +241,7 @@ void AddSC_boss_nethermancer_sepethrea()
newscript->Name = "boss_nethermancer_sepethrea";
newscript->GetAI = &GetAI_boss_nethermancer_sepethrea;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_ragin_flames";
newscript->GetAI = &GetAI_mob_ragin_flames;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp
index 077cb0e0ecc..02e44616b52 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/boss_pathaleon_the_calculator.cpp
@@ -13,13 +13,16 @@
* along 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 Pathaleon the Calculator
SD%Complete: 50
SDComment: Event missing. Script for himself 99% blizzlike.
SDCategory: Tempest Keep, The Mechanar
EndScriptData */
+
#include "precompiled.h"
+
#define SAY_AGGRO -1554020
#define SAY_DOMINATION_1 -1554021
#define SAY_DOMINATION_2 -1554022
@@ -28,25 +31,30 @@ EndScriptData */
#define SAY_SLAY_1 -1554025
#define SAY_SLAY_2 -1554026
#define SAY_DEATH -1554027
+
// Spells to be casted
#define SPELL_MANA_TAP 36021
#define SPELL_ARCANE_TORRENT 36022
#define SPELL_DOMINATION 35280
#define H_SPELL_ARCANE_EXPLOSION 15453
#define SPELL_FRENZY 36992
+
#define SPELL_SUMMON_NETHER_WRAITH_1 35285 //Spells work, but not implemented
#define SPELL_SUMMON_NETHER_WRAITH_2 35286
#define SPELL_SUMMON_NETHER_WRAITH_3 35287
#define SPELL_SUMMON_NETHER_WRAITH_4 35288
+
// Add Spells
#define SPELL_DETONATION 35058
#define SPELL_ARCANE_MISSILES 35034
+
struct TRINITY_DLL_DECL boss_pathaleon_the_calculatorAI : public ScriptedAI
{
boss_pathaleon_the_calculatorAI(Creature *c) : ScriptedAI(c), summons(m_creature)
{
HeroicMode = c->GetMap()->IsHeroic();
}
+
uint32 Summon_Timer;
SummonList summons;
uint32 ManaTap_Timer;
@@ -55,7 +63,9 @@ struct TRINITY_DLL_DECL boss_pathaleon_the_calculatorAI : public ScriptedAI
uint32 ArcaneExplosion_Timer;
bool HeroicMode;
bool Enraged;
+
uint32 Counter;
+
void Reset()
{
Summon_Timer = 30000;
@@ -63,7 +73,9 @@ struct TRINITY_DLL_DECL boss_pathaleon_the_calculatorAI : public ScriptedAI
ArcaneTorrent_Timer = 16000 + rand()%9000;
Domination_Timer = 25000 + rand()%15000;
ArcaneExplosion_Timer = 8000 + rand()%5000;
+
Enraged = false;
+
Counter = 0;
summons.DespawnAll();
}
@@ -71,25 +83,31 @@ struct TRINITY_DLL_DECL boss_pathaleon_the_calculatorAI : public ScriptedAI
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void KilledUnit(Unit* victim)
{
DoScriptText(RAND(SAY_SLAY_1,SAY_SLAY_2), m_creature);
}
+
void JustDied(Unit* Killer)
{
DoScriptText(SAY_DEATH, m_creature);
+
summons.DespawnAll();
}
+
void JustSummoned(Creature *summon) {summons.Summon(summon);}
void SummonedCreatureDespawn(Creature *summon) {summons.Despawn(summon);}
+
void UpdateAI(const uint32 diff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
if (Summon_Timer < diff)
{
- for (uint8 i = 0; i < 3; ++i)
+ for(uint8 i = 0; i < 3; ++i)
{
Unit* target = SelectUnit(SELECT_TARGET_RANDOM,0);
Creature* Wraith = m_creature->SummonCreature(21062,m_creature->GetPositionX(), m_creature->GetPositionY(), m_creature->GetPositionZ(),0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 25000);
@@ -99,25 +117,30 @@ struct TRINITY_DLL_DECL boss_pathaleon_the_calculatorAI : public ScriptedAI
DoScriptText(SAY_SUMMON, m_creature);
Summon_Timer = 30000 + rand()%15000;
}else Summon_Timer -= diff;
+
if (ManaTap_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_MANA_TAP);
ManaTap_Timer = 14000 + rand()%8000;
}else ManaTap_Timer -= diff;
+
if (ArcaneTorrent_Timer < diff)
{
DoCast(m_creature->getVictim(),SPELL_ARCANE_TORRENT);
ArcaneTorrent_Timer = 12000 + rand()%6000;
}else ArcaneTorrent_Timer -= diff;
+
if (Domination_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1))
{
DoScriptText(RAND(SAY_DOMINATION_1,SAY_DOMINATION_2), m_creature);
+
DoCast(target,SPELL_DOMINATION);
}
Domination_Timer = 25000 + rand()%5000;
}else Domination_Timer -= diff;
+
//Only casting if Heroic Mode is used
if (HeroicMode)
{
@@ -127,12 +150,15 @@ struct TRINITY_DLL_DECL boss_pathaleon_the_calculatorAI : public ScriptedAI
ArcaneExplosion_Timer = 10000 + rand()%4000;
}else ArcaneExplosion_Timer -= diff;
}
+
if (!Enraged && m_creature->GetHealth()*100 / m_creature->GetMaxHealth() < 21)
{
DoCast(m_creature, SPELL_FRENZY);
DoScriptText(SAY_ENRAGE, m_creature);
Enraged = true;
+
}
+
DoMeleeAttackIfReady();
}
};
@@ -140,35 +166,44 @@ CreatureAI* GetAI_boss_pathaleon_the_calculator(Creature* pCreature)
{
return new boss_pathaleon_the_calculatorAI (pCreature);
}
+
struct TRINITY_DLL_DECL mob_nether_wraithAI : public ScriptedAI
{
mob_nether_wraithAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ArcaneMissiles_Timer;
uint32 Detonation_Timer;
uint32 Die_Timer;
bool Detonation;
+
void Reset()
{
ArcaneMissiles_Timer = 1000 + rand()%3000;
Detonation_Timer = 20000;
Die_Timer = 2200;
Detonation = false;
+
}
+
void EnterCombat(Unit* who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (ArcaneMissiles_Timer < diff)
{
if (Unit* target = SelectUnit(SELECT_TARGET_RANDOM,1))
DoCast(target,SPELL_ARCANE_MISSILES);
else
DoCast(m_creature->getVictim(),SPELL_ARCANE_MISSILES);
+
ArcaneMissiles_Timer = 5000 + rand()%5000;
}else ArcaneMissiles_Timer -=diff;
+
if (!Detonation)
{
if (Detonation_Timer < diff)
@@ -177,6 +212,7 @@ struct TRINITY_DLL_DECL mob_nether_wraithAI : public ScriptedAI
Detonation = true;
}else Detonation_Timer -= diff;
}
+
if (Detonation)
{
if (Die_Timer < diff)
@@ -185,13 +221,16 @@ struct TRINITY_DLL_DECL mob_nether_wraithAI : public ScriptedAI
m_creature->RemoveCorpse();
}else Die_Timer -= diff;
}
+
DoMeleeAttackIfReady();
}
+
};
CreatureAI* GetAI_mob_nether_wraith(Creature* pCreature)
{
return new mob_nether_wraithAI (pCreature);
}
+
void AddSC_boss_pathaleon_the_calculator()
{
Script *newscript;
@@ -199,6 +238,7 @@ void AddSC_boss_pathaleon_the_calculator()
newscript->Name = "boss_pathaleon_the_calculator";
newscript->GetAI = &GetAI_boss_pathaleon_the_calculator;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_nether_wraith";
newscript->GetAI = &GetAI_mob_nether_wraith;
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/def_mechanar.h b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/def_mechanar.h
index 97c756d0fef..d1b53eebf53 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/def_mechanar.h
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/def_mechanar.h
@@ -1,5 +1,6 @@
#ifndef DEF_MECHANAR_H
#define DEF_MECHANAR_H
+
#define DATA_NETHERMANCER_EVENT 1
#endif
diff --git a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp
index c4d36178e0f..e43190620d9 100644
--- a/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp
+++ b/src/bindings/scripts/scripts/outland/tempest_keep/the_mechanar/instance_mechanar.cpp
@@ -13,43 +13,55 @@
* along 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_Mechanar
SD%Complete: 100
SDComment:
SDCategory: Mechanar
EndScriptData */
+
#include "precompiled.h"
#include "def_mechanar.h"
+
#define MAX_ENCOUNTER 1
+
struct TRINITY_DLL_DECL instance_mechanar : public ScriptedInstance
{
instance_mechanar(Map* pMap) : ScriptedInstance(pMap) {Initialize();};
+
uint32 m_auiEncounter[MAX_ENCOUNTER];
+
void Initialize()
{
memset(&m_auiEncounter, 0, sizeof(m_auiEncounter));
}
+
bool IsEncounterInProgress() const
{
- for (uint8 i = 0; i < MAX_ENCOUNTER; ++i)
+ for(uint8 i = 0; i < MAX_ENCOUNTER; ++i)
if (m_auiEncounter[i] == IN_PROGRESS)
return true;
+
return false;
}
+
uint32 GetData(uint32 type)
{
switch(type)
{
case DATA_NETHERMANCER_EVENT: return m_auiEncounter[0];
}
+
return false;
}
+
uint64 GetData64 (uint32 identifier)
{
return 0;
}
+
void SetData(uint32 type, uint32 data)
{
switch(type)
@@ -58,10 +70,12 @@ struct TRINITY_DLL_DECL instance_mechanar : public ScriptedInstance
}
}
};
+
InstanceData* GetInstanceData_instance_mechanar(Map* pMap)
{
return new instance_mechanar(pMap);
}
+
void AddSC_instance_mechanar()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/outland/terokkar_forest.cpp b/src/bindings/scripts/scripts/outland/terokkar_forest.cpp
index 0474b5f5de9..76776234d98 100644
--- a/src/bindings/scripts/scripts/outland/terokkar_forest.cpp
+++ b/src/bindings/scripts/scripts/outland/terokkar_forest.cpp
@@ -13,12 +13,14 @@
* along 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: 85
SDComment: Quest support: 9889, 10009, 10873, 10896, 10898, 11096, 10052, 10051. Skettis->Ogri'la Flight
SDCategory: Terokkar Forest
EndScriptData */
+
/* ContentData
mob_unkor_the_ruthless
mob_infested_root_walker
@@ -28,23 +30,31 @@ npc_floon
npc_isla_starmane
npc_slim
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
+
/*######
## mob_unkor_the_ruthless
######*/
+
#define SAY_SUBMIT -1000351
+
#define FACTION_HOSTILE 45
#define FACTION_FRIENDLY 35
#define QUEST_DONTKILLTHEFATONE 9889
+
#define SPELL_PULVERIZE 2676
//#define SPELL_QUID9889 32174
+
struct TRINITY_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI
{
mob_unkor_the_ruthlessAI(Creature* c) : ScriptedAI(c) {}
+
bool CanDoQuest;
uint32 UnkorUnfriendly_Timer;
uint32 Pulverize_Timer;
+
void Reset()
{
CanDoQuest = false;
@@ -53,7 +63,9 @@ struct TRINITY_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI
m_creature->SetStandState(UNIT_STAND_STATE_STAND);
m_creature->setFaction(FACTION_HOSTILE);
}
+
void EnterCombat(Unit *who) {}
+
void DoNice()
{
DoScriptText(SAY_SUBMIT, m_creature);
@@ -64,6 +76,7 @@ struct TRINITY_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI
m_creature->CombatStop(true);
UnkorUnfriendly_Timer = 60000;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetTypeId() == TYPEID_PLAYER)
@@ -71,7 +84,7 @@ struct TRINITY_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI
{
if (Group* pGroup = CAST_PLR(done_by)->GetGroup())
{
- for (GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
+ for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *pGroupie = itr->getSource();
if (pGroupie &&
@@ -92,6 +105,7 @@ struct TRINITY_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (CanDoQuest)
@@ -110,28 +124,36 @@ struct TRINITY_DLL_DECL mob_unkor_the_ruthlessAI : public ScriptedAI
}else UnkorUnfriendly_Timer -= diff;
}
}
+
if (!UpdateVictim())
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* pCreature)
{
return new mob_unkor_the_ruthlessAI (pCreature);
}
+
/*######
## mob_infested_root_walker
######*/
+
struct TRINITY_DLL_DECL mob_infested_root_walkerAI : public ScriptedAI
{
mob_infested_root_walkerAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
void EnterCombat(Unit *who) { }
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by && done_by->GetTypeId() == TYPEID_PLAYER)
@@ -146,6 +168,7 @@ CreatureAI* GetAI_mob_infested_root_walker(Creature* pCreature)
return new mob_infested_root_walkerAI (pCreature);
}
+
/*######
## mob_skywing
######*/
@@ -153,11 +176,13 @@ struct TRINITY_DLL_DECL npc_skywingAI : public npc_escortAI
{
public:
npc_skywingAI(Creature *c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
if (!pPlayer)
return;
+
switch(i)
{
case 8:
@@ -165,11 +190,14 @@ public:
break;
}
}
+
void EnterCombat(Unit* who) {}
+
void MoveInLineOfSight(Unit *who)
{
if (HasEscortState(STATE_ESCORT_ESCORTING))
return;
+
if (who->GetTypeId() == TYPEID_PLAYER)
{
if (CAST_PLR(who)->GetQuestStatus(10898) == QUEST_STATUS_INCOMPLETE)
@@ -182,24 +210,31 @@ public:
}
}
}
+
void Reset() {}
+
void UpdateAI(const uint32 diff)
{
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_skywingAI(Creature* pCreature)
{
return new npc_skywingAI(pCreature);
}
+
/*######
## mob_rotting_forest_rager
######*/
+
struct TRINITY_DLL_DECL mob_rotting_forest_ragerAI : public ScriptedAI
{
mob_rotting_forest_ragerAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
void EnterCombat(Unit *who) { }
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
if (done_by->GetTypeId() == TYPEID_PLAYER)
@@ -213,11 +248,14 @@ CreatureAI* GetAI_mob_rotting_forest_rager(Creature* pCreature)
{
return new mob_rotting_forest_ragerAI (pCreature);
}
+
/*######
## mob_netherweb_victim
######*/
+
#define QUEST_TARGET 22459
//#define SPELL_FREE_WEBBED 38950
+
const uint32 netherwebVictims[6] =
{
18470, 16805, 21242, 18452, 22482, 21285
@@ -225,9 +263,11 @@ const uint32 netherwebVictims[6] =
struct TRINITY_DLL_DECL mob_netherweb_victimAI : public ScriptedAI
{
mob_netherweb_victimAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() { }
void EnterCombat(Unit *who) { }
void MoveInLineOfSight(Unit *who) { }
+
void JustDied(Unit* Killer)
{
if (Killer->GetTypeId() == TYPEID_PLAYER)
@@ -241,6 +281,7 @@ struct TRINITY_DLL_DECL mob_netherweb_victimAI : public ScriptedAI
}
else
m_creature->SummonCreature(netherwebVictims[rand()%6], 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000);
+
if (rand()%100 < 75)
m_creature->SummonCreature(netherwebVictims[rand()%6], 0.0f, 0.0f, 0.0f,0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000);
m_creature->SummonCreature(netherwebVictims[rand()%6], 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000);
@@ -252,30 +293,38 @@ CreatureAI* GetAI_mob_netherweb_victim(Creature* pCreature)
{
return new mob_netherweb_victimAI (pCreature);
}
+
/*######
## 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!"
+
enum eFloon
{
SAY_FLOON_ATTACK = -1000352,
+
SPELL_SILENCE = 6726,
SPELL_FROSTBOLT = 9672,
SPELL_FROST_NOVA = 11831,
+
FACTION_HOSTILE_FL = 1738,
QUEST_CRACK_SKULLS = 10009
};
+
struct TRINITY_DLL_DECL npc_floonAI : public ScriptedAI
{
npc_floonAI(Creature* c) : ScriptedAI(c)
{
m_uiNormFaction = c->getFaction();
}
+
uint32 m_uiNormFaction;
uint32 Silence_Timer;
uint32 Frostbolt_Timer;
uint32 FrostNova_Timer;
+
void Reset()
{
Silence_Timer = 2000;
@@ -284,40 +333,50 @@ struct TRINITY_DLL_DECL npc_floonAI : public ScriptedAI
if (m_creature->getFaction() != m_uiNormFaction)
m_creature->setFaction(m_uiNormFaction);
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new npc_floonAI (pCreature);
}
+
bool GossipHello_npc_floon(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_CRACK_SKULLS) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_FLOON1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(9442, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_floon(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -334,25 +393,32 @@ bool GossipSelect_npc_floon(Player* pPlayer, Creature* pCreature, uint32 uiSende
}
return true;
}
+
/*######
## npc_isla_starmane
######*/
+
#define SAY_PROGRESS_1 -1000353
#define SAY_PROGRESS_2 -1000354
#define SAY_PROGRESS_3 -1000355
#define SAY_PROGRESS_4 -1000356
+
#define QUEST_EFTW_H 10052
#define QUEST_EFTW_A 10051
#define GO_CAGE 182794
#define SPELL_CAT 32447
+
struct TRINITY_DLL_DECL npc_isla_starmaneAI : public npc_escortAI
{
npc_isla_starmaneAI(Creature* c) : npc_escortAI(c) {}
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 0:
@@ -378,10 +444,12 @@ struct TRINITY_DLL_DECL npc_isla_starmaneAI : public npc_escortAI
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE); break;
}
}
+
void Reset()
{
me->RestoreFaction();
}
+
void JustDied(Unit* killer)
{
if (Player* pPlayer = GetPlayerForEscort())
@@ -393,6 +461,7 @@ struct TRINITY_DLL_DECL npc_isla_starmaneAI : public npc_escortAI
}
}
};
+
bool QuestAccept_npc_isla_starmane(Player* pPlayer, Creature* pCreature, Quest const* quest)
{
if (quest->GetQuestId() == QUEST_EFTW_H || quest->GetQuestId() == QUEST_EFTW_A)
@@ -402,10 +471,12 @@ bool QuestAccept_npc_isla_starmane(Player* pPlayer, Creature* pCreature, Quest c
}
return true;
}
+
CreatureAI* GetAI_npc_isla_starmaneAI(Creature* pCreature)
{
return new npc_isla_starmaneAI(pCreature);
}
+
/*######
## go_skull_pile
######*/
@@ -413,6 +484,7 @@ CreatureAI* GetAI_npc_isla_starmaneAI(Creature* pCreature)
#define GOSSIP_S_KARROG "Summon Karrog"
#define GOSSIP_S_GEZZARAK_THE_HUNTRESS "Summon Gezzarak the Huntress"
#define GOSSIP_S_VAKKIZ_THE_WINDRAGER "Summon Vakkiz the Windrager"
+
bool GossipHello_go_skull_pile(Player* pPlayer, GameObject* pGo)
{
if ((pPlayer->GetQuestStatus(11885) == QUEST_STATUS_INCOMPLETE) || pPlayer->GetQuestRewardStatus(11885))
@@ -422,9 +494,11 @@ bool GossipHello_go_skull_pile(Player* pPlayer, GameObject* pGo)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_S_GEZZARAK_THE_HUNTRESS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 3);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_S_VAKKIZ_THE_WINDRAGER, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
}
+
pPlayer->SEND_GOSSIP_MENU(pGo->GetGOInfo()->questgiver.gossipID, pGo->GetGUID());
return true;
}
+
void SendActionMenu_go_skull_pile(Player* pPlayer, GameObject* pGo, uint32 uiAction)
{
switch(uiAction)
@@ -443,6 +517,7 @@ void SendActionMenu_go_skull_pile(Player* pPlayer, GameObject* pGo, uint32 uiAct
break;
}
}
+
bool GossipSelect_go_skull_pile(Player* pPlayer, GameObject* pGo, uint32 uiSender, uint32 uiAction)
{
switch(uiSender)
@@ -451,13 +526,16 @@ bool GossipSelect_go_skull_pile(Player* pPlayer, GameObject* pGo, uint32 uiSende
}
return true;
}
+
/*######
## npc_slim
######*/
+
enum eSlim
{
FACTION_CONSORTIUM = 933
};
+
bool GossipHello_npc_slim(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isVendor() && pPlayer->GetReputationRank(FACTION_CONSORTIUM) >= REP_FRIENDLY)
@@ -467,31 +545,42 @@ bool GossipHello_npc_slim(Player* pPlayer, Creature* pCreature)
}
else
pPlayer->SEND_GOSSIP_MENU(9895, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_slim(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*########
####npc_Akuno
#####*/
+
#define QUEST_NPC_AKUNO 10887
#define Summon 21661
+
struct TRINITY_DLL_DECL npc_akunoAI : public npc_escortAI
{
npc_akunoAI(Creature *c) : npc_escortAI(c) {}
+
bool IsWalking;
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if(!pPlayer)
return;
+
if(IsWalking && !m_creature->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
m_creature->AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
switch(i)
{
case 0: m_creature->setFaction(5); break;
@@ -506,6 +595,7 @@ struct TRINITY_DLL_DECL npc_akunoAI : public npc_escortAI
break;
}
}
+
void Reset()
{
if (IsWalking && !m_creature->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
@@ -516,6 +606,7 @@ struct TRINITY_DLL_DECL npc_akunoAI : public npc_escortAI
IsWalking=false;
}
};
+
bool QuestAccept_npc_akuno(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
if(pQuest->GetQuestId() == QUEST_NPC_AKUNO)
@@ -525,54 +616,66 @@ bool QuestAccept_npc_akuno(Player* pPlayer, Creature* pCreature, Quest const* pQ
}
return true;
}
+
CreatureAI* GetAI_npc_akuno(Creature* pCreature)
{
return new npc_akunoAI(pCreature);
}
+
void AddSC_terokkar_forest()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "mob_unkor_the_ruthless";
newscript->GetAI = &GetAI_mob_unkor_the_ruthless;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_infested_root_walker";
newscript->GetAI = &GetAI_mob_infested_root_walker;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_rotting_forest_rager";
newscript->GetAI = &GetAI_mob_rotting_forest_rager;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_netherweb_victim";
newscript->GetAI = &GetAI_mob_netherweb_victim;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_floon";
newscript->GetAI = &GetAI_npc_floon;
newscript->pGossipHello = &GossipHello_npc_floon;
newscript->pGossipSelect = &GossipSelect_npc_floon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_isla_starmane";
newscript->GetAI = &GetAI_npc_isla_starmaneAI;
newscript->pQuestAccept = &QuestAccept_npc_isla_starmane;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_skull_pile";
newscript->pGOHello = &GossipHello_go_skull_pile;
newscript->pGOSelect = &GossipSelect_go_skull_pile;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_skywing";
newscript->GetAI = &GetAI_npc_skywingAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_slim";
newscript->pGossipHello = &GossipHello_npc_slim;
newscript->pGossipSelect = &GossipSelect_npc_slim;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_akuno";
newscript->GetAI = &GetAI_npc_akuno;
diff --git a/src/bindings/scripts/scripts/outland/zangarmarsh.cpp b/src/bindings/scripts/scripts/outland/zangarmarsh.cpp
index a8b66a8764c..d03e161a2c5 100644
--- a/src/bindings/scripts/scripts/outland/zangarmarsh.cpp
+++ b/src/bindings/scripts/scripts/outland/zangarmarsh.cpp
@@ -13,12 +13,14 @@
* along 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: 9752, 9785, 9803, 10009. Mark Of ... buffs.
SDCategory: Zangarmarsh
EndScriptData */
+
/* ContentData
npcs_ashyen_and_keleth
npc_cooshcoosh
@@ -27,16 +29,20 @@ npc_mortog_steamhead
npc_kayra_longmane
npc_timothy_daniels
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.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."
//signed for 17900 but used by 17900,17901
#define GOSSIP_REWARD_BLESS -1000359
//#define TEXT_BLESSINGS "<You need higher standing with Cenarion Expedition to recive a blessing.>"
+
bool GossipHello_npcs_ashyen_and_keleth(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetReputationRank(942) > REP_NEUTRAL)
@@ -47,8 +53,10 @@ bool GossipHello_npcs_ashyen_and_keleth(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_BLESS_KEL, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
}
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npcs_ashyen_and_keleth(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -56,6 +64,7 @@ bool GossipSelect_npcs_ashyen_and_keleth(Player* pPlayer, Creature* pCreature, u
pCreature->setPowerType(POWER_MANA);
pCreature->SetMaxPower(POWER_MANA,200); //set a "fake" mana value, we can't depend on database doing it in this case
pCreature->SetPower(POWER_MANA,200);
+
if (pCreature->GetEntry() == 17900) //check which Creature we are dealing with
{
switch (pPlayer->GetReputationRank(942))
@@ -78,6 +87,7 @@ bool GossipSelect_npcs_ashyen_and_keleth(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
if (pCreature->GetEntry() == 17901)
{
switch (pPlayer->GetReputationRank(942)) //mark of war
@@ -105,54 +115,68 @@ bool GossipSelect_npcs_ashyen_and_keleth(Player* pPlayer, Creature* pCreature, u
}
return true;
}
+
/*######
## npc_cooshcoosh
######*/
+
#define GOSSIP_COOSH "You owe Sim'salabim money. Hand them over or die!"
+
enum eCooshhooosh
{
SPELL_LIGHTNING_BOLT = 9532,
QUEST_CRACK_SKULLS = 10009,
FACTION_HOSTILE_CO = 45
};
+
struct TRINITY_DLL_DECL npc_cooshcooshAI : public ScriptedAI
{
npc_cooshcooshAI(Creature* c) : ScriptedAI(c)
{
m_uiNormFaction = c->getFaction();
}
+
uint32 m_uiNormFaction;
uint32 LightningBolt_Timer;
+
void Reset()
{
LightningBolt_Timer = 2000;
if (m_creature->getFaction() != m_uiNormFaction)
m_creature->setFaction(m_uiNormFaction);
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
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* pCreature)
{
return new npc_cooshcooshAI (pCreature);
}
+
bool GossipHello_npc_cooshcoosh(Player* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(QUEST_CRACK_SKULLS) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_COOSH, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(9441, pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_cooshcoosh(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF)
@@ -163,19 +187,25 @@ bool GossipSelect_npc_cooshcoosh(Player* pPlayer, Creature* pCreature, uint32 ui
}
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* pPlayer, Creature* pCreature)
{
if (pPlayer->GetQuestStatus(9803) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_KUR1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
+
pPlayer->SEND_GOSSIP_MENU(9226, pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_elder_kuruti(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiAction)
@@ -207,16 +237,21 @@ bool GossipSelect_npc_elder_kuruti(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*######
## npc_mortog_steamhead
######*/
+
bool GossipHello_npc_mortog_steamhead(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isVendor() && pPlayer->GetReputationRank(942) == REP_EXALTED)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_mortog_steamhead(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
@@ -225,9 +260,11 @@ bool GossipSelect_npc_mortog_steamhead(Player* pPlayer, Creature* pCreature, uin
}
return true;
}
+
/*######
## npc_kayra_longmane
######*/
+
enum eKayra
{
SAY_START = -1000360,
@@ -236,18 +273,24 @@ enum eKayra
SAY_AMBUSH2 = -1000363,
SAY_NEAR_END = -1000364,
SAY_END = -1000365, //this is signed for 10646
+
QUEST_ESCAPE_FROM = 9752,
NPC_SLAVEBINDER = 18042
};
+
struct TRINITY_DLL_DECL npc_kayra_longmaneAI : public npc_escortAI
{
npc_kayra_longmaneAI(Creature* c) : npc_escortAI(c) {}
+
void Reset() { }
+
void WaypointReached(uint32 i)
{
Player* pPlayer = GetPlayerForEscort();
+
if (!pPlayer)
return;
+
switch(i)
{
case 4:
@@ -275,39 +318,49 @@ struct TRINITY_DLL_DECL npc_kayra_longmaneAI : public npc_escortAI
}
}
};
+
bool QuestAccept_npc_kayra_longmane(Player* pPlayer, Creature* pCreature, Quest const* pQuest)
{
if (pQuest->GetQuestId() == QUEST_ESCAPE_FROM)
{
DoScriptText(SAY_START, pCreature, pPlayer);
+
if (npc_escortAI* pEscortAI = CAST_AI(npc_kayra_longmaneAI, pCreature->AI()))
pEscortAI->Start(false, false, pPlayer->GetGUID());
}
return true;
}
+
CreatureAI* GetAI_npc_kayra_longmaneAI(Creature* pCreature)
{
return new npc_kayra_longmaneAI(pCreature);
}
+
/*######
## npc_timothy_daniels
######*/
+
#define GOSSIP_TIMOTHY_DANIELS_ITEM1 "Specialist, eh? Just what kind of specialist are you, anyway?"
#define GOSSIP_TEXT_BROWSE_POISONS "Let me browse your reagents and poison supplies."
+
enum eTimothy
{
GOSSIP_TEXTID_TIMOTHY_DANIELS1 = 9239
};
+
bool GossipHello_npc_timothy_daniels(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isVendor())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_POISONS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TIMOTHY_DANIELS_ITEM1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_timothy_daniels(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -319,40 +372,49 @@ bool GossipSelect_npc_timothy_daniels(Player* pPlayer, Creature* pCreature, uint
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
break;
}
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_cooshcoosh";
newscript->GetAI = &GetAI_npc_cooshcoosh;
newscript->pGossipHello = &GossipHello_npc_cooshcoosh;
newscript->pGossipSelect = &GossipSelect_npc_cooshcoosh;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_elder_kuruti";
newscript->pGossipHello = &GossipHello_npc_elder_kuruti;
newscript->pGossipSelect = &GossipSelect_npc_elder_kuruti;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_mortog_steamhead";
newscript->pGossipHello = &GossipHello_npc_mortog_steamhead;
newscript->pGossipSelect = &GossipSelect_npc_mortog_steamhead;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_kayra_longmane";
newscript->GetAI = &GetAI_npc_kayra_longmaneAI;
newscript->pQuestAccept = &QuestAccept_npc_kayra_longmane;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_timothy_daniels";
newscript->pGossipHello = &GossipHello_npc_timothy_daniels;
diff --git a/src/bindings/scripts/scripts/world/areatrigger_scripts.cpp b/src/bindings/scripts/scripts/world/areatrigger_scripts.cpp
index ed84c0e2b0c..5aa7ecb7f04 100644
--- a/src/bindings/scripts/scripts/world/areatrigger_scripts.cpp
+++ b/src/bindings/scripts/scripts/world/areatrigger_scripts.cpp
@@ -13,26 +13,33 @@
* along 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_legion_teleporter 4560 Teleporter TO Invasion Point: Cataclysm
EndContentData */
+
#include "precompiled.h"
+
/*#####
## at_legion_teleporter
#####*/
+
enum eLegionTeleporter
{
SPELL_TELE_A_TO = 37387,
QUEST_GAINING_ACCESS_A = 10589,
+
SPELL_TELE_H_TO = 37389,
QUEST_GAINING_ACCESS_H = 10604
};
+
bool AreaTrigger_at_legion_teleporter(Player* pPlayer, AreaTriggerEntry* pAt)
{
if (pPlayer->isAlive() && !pPlayer->isInCombat())
@@ -42,33 +49,41 @@ bool AreaTrigger_at_legion_teleporter(Player* pPlayer, AreaTriggerEntry* pAt)
pPlayer->CastSpell(pPlayer,SPELL_TELE_A_TO,false);
return true;
}
+
if (pPlayer->GetTeam()== HORDE && pPlayer->GetQuestRewardStatus(QUEST_GAINING_ACCESS_H))
{
pPlayer->CastSpell(pPlayer,SPELL_TELE_H_TO,false);
return true;
}
+
return false;
}
return false;
}
+
enum eRavenholdt
{
QUEST_MANOR_RAVENHOLDT = 6681,
NPC_RAVENHOLDT = 13936
};
+
bool AreaTrigger_at_ravenholdt(Player* pPlayer, AreaTriggerEntry* pAt)
{
if (pPlayer->GetQuestStatus(QUEST_MANOR_RAVENHOLDT) == QUEST_STATUS_INCOMPLETE)
pPlayer->KilledMonsterCredit(NPC_RAVENHOLDT, 0);
+
return false;
}
+
void AddSC_areatrigger_scripts()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "at_legion_teleporter";
newscript->pAreaTrigger = &AreaTrigger_at_legion_teleporter;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "at_ravenholdt";
newscript->pAreaTrigger = &AreaTrigger_at_ravenholdt;
diff --git a/src/bindings/scripts/scripts/world/boss_emeriss.cpp b/src/bindings/scripts/scripts/world/boss_emeriss.cpp
index 233bbac5fa5..fa53ed89d21 100644
--- a/src/bindings/scripts/scripts/world/boss_emeriss.cpp
+++ b/src/bindings/scripts/scripts/world/boss_emeriss.cpp
@@ -13,17 +13,21 @@
* along 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"
+
enum eEnums
{
SAY_AGGRO = -1000401,
SAY_CASTCORRUPTION = -1000402, //signed for 6182
+
SPELL_SLEEP = 24777,
SPELL_NOXIOUSBREATH = 24818,
SPELL_TAILSWEEP = 15847,
@@ -31,9 +35,11 @@ enum eEnums
SPELL_VOLATILEINFECTION = 24928,
SPELL_CORRUPTIONOFEARTH = 24910
};
+
struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
{
boss_emerissAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiSleep_Timer;
uint32 m_uiNoxiousBreath_Timer;
uint32 m_uiTailSweep_Timer;
@@ -41,6 +47,7 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
uint32 m_uiVolatileInfection_Timer;
uint32 m_uiCorruptionsCasted;
+
void Reset()
{
m_uiSleep_Timer = 15000 + rand()%5000;
@@ -50,24 +57,29 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
m_uiVolatileInfection_Timer = 12000;
m_uiCorruptionsCasted = 0;
}
+
void Aggro(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void UpdateAI(const uint32 uiDiff)
{
//Return since we have no target
if (!UpdateVictim())
return;
+
//Sleep_Timer
if (m_uiSleep_Timer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_SLEEP);
+
m_uiSleep_Timer = 8000 + rand()%8000;
}
else
m_uiSleep_Timer -= uiDiff;
+
//NoxiousBreath_Timer
if (m_uiNoxiousBreath_Timer < uiDiff)
{
@@ -76,6 +88,7 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
}
else
m_uiNoxiousBreath_Timer -= uiDiff;
+
//Tailsweep every 2 seconds
if (m_uiTailSweep_Timer < uiDiff)
{
@@ -84,6 +97,7 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
}
else
m_uiTailSweep_Timer -= uiDiff;
+
//MarkOfNature_Timer
//if (m_uiMarkOfNature_Timer < uiDiff)
//{
@@ -93,6 +107,7 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
//else
// m_uiMarkOfNature_Timer -= uiDiff;
+
//VolatileInfection_Timer
if (m_uiVolatileInfection_Timer < uiDiff)
{
@@ -101,6 +116,7 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
}
else
m_uiVolatileInfection_Timer -= uiDiff;
+
//CorruptionofEarth_Timer
//CorruptionofEarth at 75%, 50% and 25%
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= (100-(25*m_uiCorruptionsCasted)))
@@ -109,13 +125,16 @@ struct TRINITY_DLL_DECL boss_emerissAI : public ScriptedAI
DoScriptText(SAY_CASTCORRUPTION, m_creature);
DoCast(m_creature->getVictim(), SPELL_CORRUPTIONOFEARTH);
}
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_emeriss(Creature* pCreature)
{
return new boss_emerissAI (pCreature);
}
+
void AddSC_boss_emeriss()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/world/boss_lethon.cpp b/src/bindings/scripts/scripts/world/boss_lethon.cpp
index 7b323b256f3..e387891efed 100644
--- a/src/bindings/scripts/scripts/world/boss_lethon.cpp
+++ b/src/bindings/scripts/scripts/world/boss_lethon.cpp
@@ -13,11 +13,13 @@
* along 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/world/boss_taerar.cpp b/src/bindings/scripts/scripts/world/boss_taerar.cpp
index d11da293921..6659924c41b 100644
--- a/src/bindings/scripts/scripts/world/boss_taerar.cpp
+++ b/src/bindings/scripts/scripts/world/boss_taerar.cpp
@@ -13,17 +13,21 @@
* along 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: 70
SDComment: Mark of Nature & Teleport NYI. Fix the way to be banished.
SDCategory: Bosses
EndScriptData */
+
#include "precompiled.h"
+
enum eEnums
{
SAY_AGGRO = -1000399, //signed for 20021
SAY_SUMMONSHADE = -1000400, //signed for 20021
+
//Spells of Taerar
SPELL_SLEEP = 24777,
SPELL_NOXIOUSBREATH = 24818,
@@ -31,20 +35,25 @@ enum eEnums
// SPELL_MARKOFNATURE = 25040, // Not working
SPELL_ARCANEBLAST = 24857,
SPELL_BELLOWINGROAR = 22686,
+
SPELL_SUMMONSHADE_1 = 24841,
SPELL_SUMMONSHADE_2 = 24842,
SPELL_SUMMONSHADE_3 = 24843,
+
//Spells of Shades of Taerar
SPELL_POSIONCLOUD = 24840,
SPELL_POSIONBREATH = 20667
};
+
uint32 m_auiSpellSummonShade[]=
{
SPELL_SUMMONSHADE_1, SPELL_SUMMONSHADE_2, SPELL_SUMMONSHADE_3
};
+
struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
{
boss_taerarAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiSleep_Timer;
uint32 m_uiNoxiousBreath_Timer;
uint32 m_uiTailSweep_Timer;
@@ -53,7 +62,9 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
uint32 m_uiBellowingRoar_Timer;
uint32 m_uiShades_Timer;
uint32 m_uiShadesSummoned;
+
bool m_bShades;
+
void Reset()
{
m_uiSleep_Timer = 15000 + rand()%5000;
@@ -64,17 +75,21 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
m_uiBellowingRoar_Timer = 30000;
m_uiShades_Timer = 60000; //The time that Taerar is banished
m_uiShadesSummoned = 0;
+
m_bShades = false;
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustSummoned(Creature* pSummoned)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
pSummoned->AI()->AttackStart(pTarget);
}
+
void UpdateAI(const uint32 uiDiff)
{
if (m_bShades && m_uiShades_Timer < uiDiff)
@@ -90,18 +105,22 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
//Do nothing while banished
return;
}
+
//Return since we have no target
if (!UpdateVictim())
return;
+
//Sleep_Timer
if (m_uiSleep_Timer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_SLEEP);
+
m_uiSleep_Timer = 8000 + rand()%7000;
}
else
m_uiSleep_Timer -= uiDiff;
+
//NoxiousBreath_Timer
if (m_uiNoxiousBreath_Timer < uiDiff)
{
@@ -110,6 +129,7 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
}
else
m_uiNoxiousBreath_Timer -= uiDiff;
+
//Tailsweep every 2 seconds
if (m_uiTailSweep_Timer < uiDiff)
{
@@ -118,6 +138,7 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
}
else
m_uiTailSweep_Timer -= uiDiff;
+
//MarkOfNature_Timer
//if (m_uiMarkOfNature_Timer < uiDiff)
//{
@@ -126,6 +147,7 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
//}
//else
// m_uiMarkOfNature_Timer -= uiDiff;
+
//ArcaneBlast_Timer
if (m_uiArcaneBlast_Timer < uiDiff)
{
@@ -134,6 +156,7 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
}
else
m_uiArcaneBlast_Timer -= uiDiff;
+
//BellowingRoar_Timer
if (m_uiBellowingRoar_Timer < uiDiff)
{
@@ -142,6 +165,7 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
}
else
m_uiBellowingRoar_Timer -= uiDiff;
+
//Summon 3 Shades at 75%, 50% and 25% (if bShades is true we already left in line 117, no need to check here again)
if (!m_bShades && (m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= (100-(25*m_uiShadesSummoned)))
{
@@ -149,36 +173,47 @@ struct TRINITY_DLL_DECL boss_taerarAI : public ScriptedAI
{
//Inturrupt any spell casting
m_creature->InterruptNonMeleeSpells(false);
+
//horrible workaround, need to fix
m_creature->setFaction(35);
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
DoScriptText(SAY_SUMMONSHADE, m_creature);
+
int iSize = sizeof(m_auiSpellSummonShade) / sizeof(uint32);
- for (int i = 0; i < iSize; ++i)
+
+ for(int i = 0; i < iSize; ++i)
m_creature->CastSpell(pTarget, m_auiSpellSummonShade[i], true);
+
++m_uiShadesSummoned; // prevent casting twice at same health
m_bShades = true;
}
m_uiShades_Timer = 60000;
}
+
DoMeleeAttackIfReady();
}
};
+
// Shades of Taerar Script
struct TRINITY_DLL_DECL boss_shadeoftaerarAI : public ScriptedAI
{
boss_shadeoftaerarAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiPoisonCloud_Timer;
uint32 m_uiPosionBreath_Timer;
+
void Reset()
{
m_uiPoisonCloud_Timer = 8000;
m_uiPosionBreath_Timer = 12000;
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//PoisonCloud_Timer
if (m_uiPoisonCloud_Timer < uiDiff)
{
@@ -187,6 +222,7 @@ struct TRINITY_DLL_DECL boss_shadeoftaerarAI : public ScriptedAI
}
else
m_uiPoisonCloud_Timer -= uiDiff;
+
//PosionBreath_Timer
if (m_uiPosionBreath_Timer < uiDiff)
{
@@ -195,24 +231,30 @@ struct TRINITY_DLL_DECL boss_shadeoftaerarAI : public ScriptedAI
}
else
m_uiPosionBreath_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_taerar(Creature* pCreature)
{
return new boss_taerarAI (pCreature);
}
+
CreatureAI* GetAI_boss_shadeoftaerar(Creature* pCreature)
{
return new boss_shadeoftaerarAI (pCreature);
}
+
void AddSC_boss_taerar()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_taerar";
newscript->GetAI = &GetAI_boss_taerar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "boss_shade_of_taerar";
newscript->GetAI = &GetAI_boss_shadeoftaerar;
diff --git a/src/bindings/scripts/scripts/world/boss_ysondre.cpp b/src/bindings/scripts/scripts/world/boss_ysondre.cpp
index fe4d0eb159e..2f3948d38d9 100644
--- a/src/bindings/scripts/scripts/world/boss_ysondre.cpp
+++ b/src/bindings/scripts/scripts/world/boss_ysondre.cpp
@@ -13,37 +13,46 @@
* along 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"
+
enum eEnums
{
SAY_AGGRO = -1000360, //signed for 17969
SAY_SUMMONDRUIDS = -1000361, //signed for 17969
+
SPELL_SLEEP = 24777,
SPELL_NOXIOUSBREATH = 24818,
SPELL_TAILSWEEP = 15847,
//SPELL_MARKOFNATURE = 25040, // Not working
SPELL_LIGHTNINGWAVE = 24819,
SPELL_SUMMONDRUIDS = 24795,
+
SPELL_SUMMON_PLAYER = 24776,
+
//druid spells
SPELL_MOONFIRE = 21669
};
+
// Ysondre script
struct TRINITY_DLL_DECL boss_ysondreAI : public ScriptedAI
{
boss_ysondreAI(Creature* pCreature) : ScriptedAI(pCreature) {}
+
uint32 m_uiSleep_Timer;
uint32 m_uiNoxiousBreath_Timer;
uint32 m_uiTailSweep_Timer;
//uint32 m_uiMarkOfNature_Timer;
uint32 m_uiLightningWave_Timer;
uint32 m_uiSummonDruidModifier;
+
void Reset()
{
m_uiSleep_Timer = 15000 + rand()%5000;
@@ -53,28 +62,34 @@ struct TRINITY_DLL_DECL boss_ysondreAI : public ScriptedAI
m_uiLightningWave_Timer = 12000;
m_uiSummonDruidModifier = 0;
}
+
void EnterCombat(Unit* pWho)
{
DoScriptText(SAY_AGGRO, m_creature);
}
+
void JustSummoned(Creature* pSummoned)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
pSummoned->AI()->AttackStart(pTarget);
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//Sleep_Timer
if (m_uiSleep_Timer < uiDiff)
{
if (Unit* pTarget = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(pTarget, SPELL_SLEEP);
+
m_uiSleep_Timer = 8000 + rand()%7000;
}
else
m_uiSleep_Timer -= uiDiff;
+
//NoxiousBreath_Timer
if (m_uiNoxiousBreath_Timer < uiDiff)
{
@@ -83,15 +98,18 @@ struct TRINITY_DLL_DECL boss_ysondreAI : public ScriptedAI
}
else
m_uiNoxiousBreath_Timer -= uiDiff;
+
//Tailsweep every 2 seconds
if (m_uiTailSweep_Timer < uiDiff)
{
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_TAILSWEEP);
+
m_uiTailSweep_Timer = 2000;
}
else
m_uiTailSweep_Timer -= uiDiff;
+
//MarkOfNature_Timer
//if (m_uiMarkOfNature_Timer < uiDiff)
//{
@@ -100,40 +118,51 @@ struct TRINITY_DLL_DECL boss_ysondreAI : public ScriptedAI
//}
//else
// m_uiMarkOfNature_Timer -= uiDiff;
+
//LightningWave_Timer
if (m_uiLightningWave_Timer < uiDiff)
{
//Cast LIGHTNINGWAVE on a Random target
if (Unit *target = SelectUnit(SELECT_TARGET_RANDOM, 0))
DoCast(target, SPELL_LIGHTNINGWAVE);
+
m_uiLightningWave_Timer = 7000 + rand()%5000;
}
else
m_uiLightningWave_Timer -= uiDiff;
+
//Summon Druids
if ((m_creature->GetHealth()*100 / m_creature->GetMaxHealth()) <= (100-(25*m_uiSummonDruidModifier)))
{
DoScriptText(SAY_SUMMONDRUIDS, m_creature);
- for (int i = 0; i < 10; ++i)
+
+ for(int i = 0; i < 10; ++i)
DoCast(m_creature, SPELL_SUMMONDRUIDS, true);
+
++m_uiSummonDruidModifier;
}
+
DoMeleeAttackIfReady();
}
};
+
// Summoned druid script
struct TRINITY_DLL_DECL mob_dementeddruidsAI : public ScriptedAI
{
mob_dementeddruidsAI(Creature *c) : ScriptedAI(c) {}
+
uint32 m_uiMoonFire_Timer;
+
void Reset()
{
m_uiMoonFire_Timer = 3000;
}
+
void UpdateAI(const uint32 uiDiff)
{
if (!UpdateVictim())
return;
+
//MoonFire_Timer
if (m_uiMoonFire_Timer < uiDiff)
{
@@ -142,24 +171,30 @@ struct TRINITY_DLL_DECL mob_dementeddruidsAI : public ScriptedAI
}
else
m_uiMoonFire_Timer -= uiDiff;
+
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_boss_ysondre(Creature* pCreature)
{
return new boss_ysondreAI (pCreature);
}
+
CreatureAI* GetAI_mob_dementeddruids(Creature* pCreature)
{
return new mob_dementeddruidsAI (pCreature);
}
+
void AddSC_boss_ysondre()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "boss_ysondre";
newscript->GetAI = &GetAI_boss_ysondre;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_dementeddruids";
newscript->GetAI = &GetAI_mob_dementeddruids;
diff --git a/src/bindings/scripts/scripts/world/go_scripts.cpp b/src/bindings/scripts/scripts/world/go_scripts.cpp
index 05f21918479..668dd513191 100644
--- a/src/bindings/scripts/scripts/world/go_scripts.cpp
+++ b/src/bindings/scripts/scripts/world/go_scripts.cpp
@@ -13,12 +13,14 @@
* along 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, 6481, 10990, 10991, 10992, Field_Repair_Bot->Teaches spell 22704. Barov_journal->Teaches spell 26089
SDCategory: Game Objects
EndScriptData */
+
/* ContentData
go_cat_figurine (the "trap" version of GO, two different exist)
go_northern_crystal_pylon
@@ -38,22 +40,28 @@ go_tablet_of_the_seven
go_tele_to_dalaran_crystal
go_tele_to_violet_stand
EndContentData */
+
#include "precompiled.h"
+
/*######
## go_cat_figurine
######*/
+
enum eCatFigurine
{
SPELL_SUMMON_GHOST_SABER = 5968,
};
+
bool GOHello_go_cat_figurine(Player* pPlayer, GameObject* pGo)
{
pPlayer->CastSpell(pPlayer,SPELL_SUMMON_GHOST_SABER,true);
return false;
}
+
/*######
## go_crystal_pylons (3x)
######*/
+
bool GOHello_go_northern_crystal_pylon(Player* pPlayer, GameObject* pGo)
{
if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
@@ -61,10 +69,13 @@ bool GOHello_go_northern_crystal_pylon(Player* pPlayer, GameObject* pGo)
pPlayer->PrepareQuestMenu(pGo->GetGUID());
pPlayer->SendPreparedQuest(pGo->GetGUID());
}
+
if (pPlayer->GetQuestStatus(4285) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(4285);
+
return true;
}
+
bool GOHello_go_eastern_crystal_pylon(Player* pPlayer, GameObject* pGo)
{
if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
@@ -72,10 +83,13 @@ bool GOHello_go_eastern_crystal_pylon(Player* pPlayer, GameObject* pGo)
pPlayer->PrepareQuestMenu(pGo->GetGUID());
pPlayer->SendPreparedQuest(pGo->GetGUID());
}
+
if (pPlayer->GetQuestStatus(4287) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(4287);
+
return true;
}
+
bool GOHello_go_western_crystal_pylon(Player* pPlayer, GameObject* pGo)
{
if (pGo->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER)
@@ -83,13 +97,17 @@ bool GOHello_go_western_crystal_pylon(Player* pPlayer, GameObject* pGo)
pPlayer->PrepareQuestMenu(pGo->GetGUID());
pPlayer->SendPreparedQuest(pGo->GetGUID());
}
+
if (pPlayer->GetQuestStatus(4288) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(4288);
+
return true;
}
+
/*######
## go_barov_journal
######*/
+
bool GOHello_go_barov_journal(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->HasSkill(SKILL_TAILORING) && pPlayer->GetBaseSkillValue(SKILL_TAILORING) >= 280 && !pPlayer->HasSpell(26086))
@@ -98,9 +116,11 @@ bool GOHello_go_barov_journal(Player* pPlayer, GameObject* pGo)
}
return true;
}
+
/*######
## go_field_repair_bot_74A
######*/
+
bool GOHello_go_field_repair_bot_74A(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->HasSkill(SKILL_ENGINERING) && pPlayer->GetBaseSkillValue(SKILL_ENGINERING) >= 300 && !pPlayer->HasSpell(22704))
@@ -109,32 +129,41 @@ bool GOHello_go_field_repair_bot_74A(Player* pPlayer, GameObject* pGo)
}
return true;
}
+
/*######
## go_gilded_brazier
######*/
+
enum eGildedBrazier
{
NPC_STILLBLADE = 17716,
};
+
bool GOHello_go_gilded_brazier(Player* pPlayer, GameObject* pGO)
{
if (pGO->GetGoType() == GAMEOBJECT_TYPE_GOOBER)
if (Creature* pCreature = pPlayer->SummonCreature(NPC_STILLBLADE, 8087.632, -7542.740, 151.568, 0.122, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 5000))
pCreature->AI()->AttackStart(pPlayer);
+
return true;
}
+
/*######
## go_orb_of_command
######*/
+
bool GOHello_go_orb_of_command(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestRewardStatus(7761))
pPlayer->CastSpell(pPlayer,23460,true);
+
return true;
}
+
/*######
## go_tablet_of_madness
######*/
+
bool GOHello_go_tablet_of_madness(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->HasSkill(SKILL_ALCHEMY) && pPlayer->GetSkillValue(SKILL_ALCHEMY) >= 300 && !pPlayer->HasSpell(24266))
@@ -143,30 +172,39 @@ bool GOHello_go_tablet_of_madness(Player* pPlayer, GameObject* pGo)
}
return true;
}
+
/*######
## go_tablet_of_the_seven
######*/
+
//TODO: use gossip option ("Transcript the Tablet") instead, if Trinity adds support.
bool GOHello_go_tablet_of_the_seven(Player* pPlayer, GameObject* pGo)
{
if (pGo->GetGoType() != GAMEOBJECT_TYPE_QUESTGIVER)
return true;
+
if (pPlayer->GetQuestStatus(4296) == QUEST_STATUS_INCOMPLETE)
pPlayer->CastSpell(pPlayer,15065,false);
+
return true;
}
+
/*#####
## go_jump_a_tron
######*/
+
bool GOHello_go_jump_a_tron(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestStatus(10111) == QUEST_STATUS_INCOMPLETE)
pPlayer->CastSpell(pPlayer,33382,true);
+
return true;
}
+
/*######
## go_ethereum_prison
######*/
+
enum eEthereumPrison
{
SPELL_REP_LC = 39456,
@@ -176,14 +214,17 @@ enum eEthereumPrison
SPELL_REP_KT = 39475,
SPELL_REP_SPOR = 39476
};
+
const uint32 NpcPrisonEntry[] =
{
22810, 22811, 22812, 22813, 22814, 22815, //good guys
20783, 20784, 20785, 20786, 20788, 20789, 20790 //bad guys
};
+
bool GOHello_go_ethereum_prison(Player* pPlayer, GameObject* pGo)
{
int Random = rand() % (sizeof(NpcPrisonEntry) / sizeof(uint32));
+
if (Creature* pCreature = pPlayer->SummonCreature(NpcPrisonEntry[Random],
pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ(), pGo->GetAngle(pPlayer),
TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000))
@@ -191,6 +232,7 @@ bool GOHello_go_ethereum_prison(Player* pPlayer, GameObject* pGo)
if (!pCreature->IsHostileTo(pPlayer))
{
uint32 Spell = 0;
+
if (FactionTemplateEntry const* pFaction = pCreature->getFactionTemplateEntry())
{
switch(pFaction->faction)
@@ -202,6 +244,7 @@ bool GOHello_go_ethereum_prison(Player* pPlayer, GameObject* pGo)
case 989: Spell = SPELL_REP_KT; break;
case 970: Spell = SPELL_REP_SPOR; break;
}
+
if (Spell)
pCreature->CastSpell(pPlayer, Spell, false);
else
@@ -209,49 +252,65 @@ bool GOHello_go_ethereum_prison(Player* pPlayer, GameObject* pGo)
}
}
}
+
return false;
}
+
/*######
## go_ethereum_stasis
######*/
+
const uint32 NpcStasisEntry[] =
{
22825, 20888, 22827, 22826, 22828
};
+
bool GOHello_go_ethereum_stasis(Player* pPlayer, GameObject* pGo)
{
int Random = rand() % (sizeof(NpcStasisEntry) / sizeof(uint32));
+
pPlayer->SummonCreature(NpcStasisEntry[Random],
pGo->GetPositionX(), pGo->GetPositionY(), pGo->GetPositionZ(), pGo->GetAngle(pPlayer),
TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
+
return false;
}
+
/*######
## go_resonite_cask
######*/
+
enum eResoniteCask
{
NPC_GOGGEROC = 11920
};
+
bool GOHello_go_resonite_cask(Player* pPlayer, GameObject* pGO)
{
if (pGO->GetGoType() == GAMEOBJECT_TYPE_GOOBER)
pGO->SummonCreature(NPC_GOGGEROC, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000);
+
return false;
}
+
/*######
## go_sacred_fire_of_life
######*/
+
#define NPC_ARIKARA 10882
+
bool GOHello_go_sacred_fire_of_life(Player* pPlayer, GameObject* pGO)
{
if (pGO->GetGoType() == GAMEOBJECT_TYPE_GOOBER)
pPlayer->SummonCreature(NPC_ARIKARA, -5008.338, -2118.894, 83.657, 0.874, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000);
+
return true;
}
+
/*######
## go_shrine_of_the_birds
######*/
+
enum eShrineOfTheBirds
{
NPC_HAWK_GUARD = 22992,
@@ -261,11 +320,14 @@ enum eShrineOfTheBirds
GO_SHRINE_EAGLE = 185547,
GO_SHRINE_FALCON = 185553
};
+
bool GOHello_go_shrine_of_the_birds(Player* pPlayer, GameObject* pGo)
{
uint32 BirdEntry = 0;
+
float fX, fY, fZ;
pGo->GetClosePoint(fX, fY, fZ, pGo->GetObjectSize(), INTERACTION_DISTANCE);
+
switch(pGo->GetEntry())
{
case GO_SHRINE_HAWK:
@@ -278,73 +340,96 @@ bool GOHello_go_shrine_of_the_birds(Player* pPlayer, GameObject* pGo)
BirdEntry = NPC_FALCON_GUARD;
break;
}
+
if (BirdEntry)
pPlayer->SummonCreature(BirdEntry, fX, fY, fZ, pGo->GetOrientation(), TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 60000);
+
return false;
}
+
/*######
## go_southfury_moonstone
######*/
+
enum eSouthfury
{
NPC_RIZZLE = 23002,
SPELL_BLACKJACK = 39865, //stuns player
SPELL_SUMMON_RIZZLE = 39866
+
};
+
bool GOHello_go_southfury_moonstone(Player* pPlayer, GameObject* pGo)
{
//implicitTarget=48 not implemented as of writing this code, and manual summon may be just ok for our purpose
//pPlayer->CastSpell(pPlayer,SPELL_SUMMON_RIZZLE,false);
+
if (Creature* pCreature = pPlayer->SummonCreature(NPC_RIZZLE, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_DEAD_DESPAWN, 0))
pCreature->CastSpell(pPlayer,SPELL_BLACKJACK,false);
+
return false;
}
+
/*######
## go_tele_to_dalaran_crystal
######*/
+
enum eDalaranCrystal
{
QUEST_LEARN_LEAVE_RETURN = 12790,
QUEST_TELE_CRYSTAL_FLAG = 12845
};
+
bool GOHello_go_tele_to_dalaran_crystal(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestRewardStatus(QUEST_TELE_CRYSTAL_FLAG))
return false;
+
//TODO: must send error message (what kind of message? On-screen?)
return true;
}
+
/*######
## go_tele_to_violet_stand
######*/
+
bool GOHello_go_tele_to_violet_stand(Player* pPlayer, GameObject* pGo)
{
if (pPlayer->GetQuestRewardStatus(QUEST_LEARN_LEAVE_RETURN) || pPlayer->GetQuestStatus(QUEST_LEARN_LEAVE_RETURN) == QUEST_STATUS_INCOMPLETE)
return false;
+
return true;
}
+
/*######
## go_fel_crystalforge
######*/
+
#define GOSSIP_FEL_CRYSTALFORGE_TEXT 31000
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_TEXT_RETURN 31001
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_1 "Purchase 1 Unstable Flask of the Beast for the cost of 10 Apexis Shards"
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_5 "Purchase 5 Unstable Flask of the Beast for the cost of 50 Apexis Shards"
#define GOSSIP_FEL_CRYSTALFORGE_ITEM_RETURN "Use the fel crystalforge to make another purchase."
+
enum eFelCrystalforge
{
SPELL_CREATE_1_FLASK_OF_BEAST = 40964,
SPELL_CREATE_5_FLASK_OF_BEAST = 40965,
};
+
bool GOHello_go_fel_crystalforge(Player* pPlayer, GameObject* pGO)
{
if ( pGO->GetGoType() == GAMEOBJECT_TYPE_QUESTGIVER ) /* != GAMEOBJECT_TYPE_QUESTGIVER) */
pPlayer->PrepareQuestMenu(pGO->GetGUID()); /* return true*/
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_FEL_CRYSTALFORGE_ITEM_1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_FEL_CRYSTALFORGE_ITEM_5, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
+
pPlayer->SEND_GOSSIP_MENU(GOSSIP_FEL_CRYSTALFORGE_TEXT, pGO->GetGUID());
+
return true;
}
+
bool GOSelect_go_fel_crystalforge(Player* pPlayer, GameObject* pGO, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -367,19 +452,23 @@ bool GOSelect_go_fel_crystalforge(Player* pPlayer, GameObject* pGO, uint32 uiSen
}
return true;
}
+
/*######
## go_bashir_crystalforge
######*/
+
enum eBashirCrystalforge
{
SPELL_CREATE_1_FLASK_OF_SORCERER = 40968,
SPELL_CREATE_5_FLASK_OF_SORCERER = 40970,
};
+
bool GOHello_go_bashir_crystalforge(Player* pPlayer, GameObject* pGO)
{
pPlayer->CastSpell(pPlayer,SPELL_CREATE_1_FLASK_OF_SORCERER,false);
return false;
}
+
bool GOHello_go_wg2voa_portal(Player* pPlayer, GameObject* pGO)
{
// teleport player inside VoA if faction controls WG, core has already set faction for this GO
@@ -387,9 +476,11 @@ bool GOHello_go_wg2voa_portal(Player* pPlayer, GameObject* pGO)
pPlayer->SetPvP(false);
return false;
}
+
/*######
## matrix_punchograph
######*/
+
enum eMatrixPunchograph
{
ITEM_WHITE_PUNCH_CARD = 9279,
@@ -406,6 +497,7 @@ enum eMatrixPunchograph
MATRIX_PUNCHOGRAPH_3005_C = 142476,
MATRIX_PUNCHOGRAPH_3005_D = 142696,
};
+
bool GOHello_go_matrix_punchograph(Player* pPlayer, GameObject* pGo)
{
switch(pGo->GetEntry())
@@ -443,98 +535,122 @@ bool GOHello_go_matrix_punchograph(Player* pPlayer, GameObject* pGo)
}
return false;
}
+
void AddSC_go_scripts()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "go_cat_figurine";
newscript->pGOHello = &GOHello_go_cat_figurine;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_northern_crystal_pylon";
newscript->pGOHello = &GOHello_go_northern_crystal_pylon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_eastern_crystal_pylon";
newscript->pGOHello = &GOHello_go_eastern_crystal_pylon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_western_crystal_pylon";
newscript->pGOHello = &GOHello_go_western_crystal_pylon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_barov_journal";
newscript->pGOHello = &GOHello_go_barov_journal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_field_repair_bot_74A";
newscript->pGOHello = &GOHello_go_field_repair_bot_74A;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_gilded_brazier";
newscript->pGOHello = &GOHello_go_gilded_brazier;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_orb_of_command";
newscript->pGOHello = &GOHello_go_orb_of_command;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_shrine_of_the_birds";
newscript->pGOHello = &GOHello_go_shrine_of_the_birds;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_southfury_moonstone";
newscript->pGOHello = &GOHello_go_southfury_moonstone;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_tablet_of_madness";
newscript->pGOHello = &GOHello_go_tablet_of_madness;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_tablet_of_the_seven";
newscript->pGOHello = &GOHello_go_tablet_of_the_seven;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_jump_a_tron";
newscript->pGOHello = &GOHello_go_jump_a_tron;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_ethereum_prison";
newscript->pGOHello = &GOHello_go_ethereum_prison;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_ethereum_stasis";
newscript->pGOHello = &GOHello_go_ethereum_stasis;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_resonite_cask";
newscript->pGOHello = &GOHello_go_resonite_cask;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_sacred_fire_of_life";
newscript->pGOHello = &GOHello_go_sacred_fire_of_life;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_tele_to_dalaran_crystal";
newscript->pGOHello = &GOHello_go_tele_to_dalaran_crystal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_tele_to_violet_stand";
newscript->pGOHello = &GOHello_go_tele_to_violet_stand;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_fel_crystalforge";
newscript->pGOHello = &GOHello_go_fel_crystalforge;
newscript->pGOSelect = &GOSelect_go_fel_crystalforge;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_bashir_crystalforge";
newscript->pGOHello = &GOHello_go_bashir_crystalforge;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_wg2voa_portal";
newscript->pGOHello = &GOHello_go_wg2voa_portal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "go_matrix_punchograph";
newscript->pGOHello = &GOHello_go_matrix_punchograph;
diff --git a/src/bindings/scripts/scripts/world/guards.cpp b/src/bindings/scripts/scripts/world/guards.cpp
index 218e6fc489a..ead30387b18 100644
--- a/src/bindings/scripts/scripts/world/guards.cpp
+++ b/src/bindings/scripts/scripts/world/guards.cpp
@@ -13,12 +13,14 @@
* along 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
@@ -42,15 +44,19 @@ 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"
@@ -67,13 +73,17 @@ EndContentData */
#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"
+
/*******************************************************
* guard_azuremyst start
*******************************************************/
+
bool GossipHello_guard_azuremyst(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -86,6 +96,7 @@ bool GossipHello_guard_azuremyst(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(10066, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_azuremyst(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -139,6 +150,7 @@ void SendDefaultMenu_guard_azuremyst(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendClassTrainerMenu_guard_azuremyst(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -173,6 +185,7 @@ void SendClassTrainerMenu_guard_azuremyst(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_azuremyst(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -234,6 +247,7 @@ void SendProfTrainerMenu_guard_azuremyst(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_azuremyst(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -244,16 +258,20 @@ bool GossipSelect_guard_azuremyst(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_azuremyst end
*******************************************************/
+
CreatureAI* GetAI_guard_azuremyst(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_bluffwatcher start
*******************************************************/
+
bool GossipHello_guard_bluffwatcher(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -270,6 +288,7 @@ bool GossipHello_guard_bluffwatcher(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(3543, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -338,6 +357,7 @@ void SendDefaultMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
void SendBattleMasterMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -356,6 +376,7 @@ void SendBattleMasterMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreatur
break;
}
}
+
void SendClassTrainerMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -386,6 +407,7 @@ void SendClassTrainerMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreatur
break;
}
}
+
void SendProfTrainerMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -440,6 +462,7 @@ void SendProfTrainerMenu_guard_bluffwatcher(Player* pPlayer, Creature* pCreature
break;
}
}
+
bool GossipSelect_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -451,16 +474,20 @@ bool GossipSelect_guard_bluffwatcher(Player* pPlayer, Creature* pCreature, uint3
}
return true;
}
+
/*******************************************************
* guard_bluffwatcher end
*******************************************************/
+
CreatureAI* GetAI_guard_bluffwatcher(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_contested start
*******************************************************/
+
CreatureAI* GetAI_guard_contested(Creature* pCreature)
{
return new guardAI (pCreature);
@@ -468,9 +495,11 @@ CreatureAI* GetAI_guard_contested(Creature* pCreature)
/*******************************************************
* guard_contested end
*******************************************************/
+
/*******************************************************
* guard_darnassus start
*******************************************************/
+
bool GossipHello_guard_darnassus(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -487,6 +516,7 @@ bool GossipHello_guard_darnassus(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(3016, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_darnassus(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -552,6 +582,7 @@ void SendDefaultMenu_guard_darnassus(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendBattleMasterMenu_guard_darnassus(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -570,6 +601,7 @@ void SendBattleMasterMenu_guard_darnassus(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendClassTrainerMenu_guard_darnassus(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -596,6 +628,7 @@ void SendClassTrainerMenu_guard_darnassus(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_darnassus(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -642,6 +675,7 @@ void SendProfTrainerMenu_guard_darnassus(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_darnassus(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -653,16 +687,20 @@ bool GossipSelect_guard_darnassus(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_darnassus end
*******************************************************/
+
CreatureAI* GetAI_guard_darnassus(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_dunmorogh start
*******************************************************/
+
bool GossipHello_guard_dunmorogh(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -673,8 +711,10 @@ bool GossipHello_guard_dunmorogh(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
pPlayer->SEND_GOSSIP_MENU(4287, pCreature->GetGUID());
+
return true;
}
+
void SendDefaultMenu_guard_dunmorogh(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -724,6 +764,7 @@ void SendDefaultMenu_guard_dunmorogh(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendClassTrainerMenu_guard_dunmorogh(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -758,6 +799,7 @@ void SendClassTrainerMenu_guard_dunmorogh(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_dunmorogh(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -809,6 +851,7 @@ void SendProfTrainerMenu_guard_dunmorogh(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_dunmorogh(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -819,16 +862,20 @@ bool GossipSelect_guard_dunmorogh(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_dunmorogh end
*******************************************************/
+
CreatureAI* GetAI_guard_dunmorogh(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_durotar start
*******************************************************/
+
bool GossipHello_guard_durotar(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -840,6 +887,7 @@ bool GossipHello_guard_durotar(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(4037, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_durotar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -886,6 +934,7 @@ void SendDefaultMenu_guard_durotar(Player* pPlayer, Creature* pCreature, uint32
break;
}
}
+
void SendClassTrainerMenu_guard_durotar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -920,6 +969,7 @@ void SendClassTrainerMenu_guard_durotar(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
void SendProfTrainerMenu_guard_durotar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -969,6 +1019,7 @@ void SendProfTrainerMenu_guard_durotar(Player* pPlayer, Creature* pCreature, uin
break;
}
}
+
bool GossipSelect_guard_durotar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -979,16 +1030,20 @@ bool GossipSelect_guard_durotar(Player* pPlayer, Creature* pCreature, uint32 uiS
}
return true;
}
+
/*******************************************************
* guard_durotar end
*******************************************************/
+
CreatureAI* GetAI_guard_durotar(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_elwynnforest start
*******************************************************/
+
bool GossipHello_guard_elwynnforest(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -1001,6 +1056,7 @@ bool GossipHello_guard_elwynnforest(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(933, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_elwynnforest(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1050,6 +1106,7 @@ void SendDefaultMenu_guard_elwynnforest(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
void SendClassTrainerMenu_guard_elwynnforest(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1086,6 +1143,7 @@ void SendClassTrainerMenu_guard_elwynnforest(Player* pPlayer, Creature* pCreatur
break;
}
}
+
void SendProfTrainerMenu_guard_elwynnforest(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1141,6 +1199,7 @@ void SendProfTrainerMenu_guard_elwynnforest(Player* pPlayer, Creature* pCreature
break;
}
}
+
bool GossipSelect_guard_elwynnforest(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -1151,16 +1210,20 @@ bool GossipSelect_guard_elwynnforest(Player* pPlayer, Creature* pCreature, uint3
}
return true;
}
+
/*******************************************************
* guard_elwynnforest end
*******************************************************/
+
CreatureAI* GetAI_guard_elwynnforest(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_eversong start
*******************************************************/
+
bool GossipHello_guard_eversong(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BATHANDLER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -1172,6 +1235,7 @@ bool GossipHello_guard_eversong(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(10180, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_eversong(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1219,6 +1283,7 @@ void SendDefaultMenu_guard_eversong(Player* pPlayer, Creature* pCreature, uint32
break;
}
}
+
void SendClassTrainerMenu_guard_eversong(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1252,6 +1317,7 @@ void SendClassTrainerMenu_guard_eversong(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
void SendProfTrainerMenu_guard_eversong(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1307,6 +1373,7 @@ void SendProfTrainerMenu_guard_eversong(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
bool GossipSelect_guard_eversong(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -1317,16 +1384,20 @@ bool GossipSelect_guard_eversong(Player* pPlayer, Creature* pCreature, uint32 ui
}
return true;
}
+
/*******************************************************
* guard_eversong end
*******************************************************/
+
CreatureAI* GetAI_guard_eversong(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_exodar start
*******************************************************/
+
bool GossipHello_guard_exodar(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -1343,6 +1414,7 @@ bool GossipHello_guard_exodar(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(9551, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1416,6 +1488,7 @@ void SendDefaultMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 u
break;
}
}
+
void SendBattleMasterMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1442,6 +1515,7 @@ void SendBattleMasterMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uin
break;
}
}
+
void SendClassTrainerMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1476,6 +1550,7 @@ void SendClassTrainerMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uin
break;
}
}
+
void SendProfTrainerMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1538,6 +1613,7 @@ void SendProfTrainerMenu_guard_exodar(Player* pPlayer, Creature* pCreature, uint
break;
}
}
+
bool GossipSelect_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -1549,16 +1625,20 @@ bool GossipSelect_guard_exodar(Player* pPlayer, Creature* pCreature, uint32 uiSe
}
return true;
}
+
/*******************************************************
* guard_exodar end
*******************************************************/
+
CreatureAI* GetAI_guard_exodar(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_ironforge start
*******************************************************/
+
bool GossipHello_guard_ironforge(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -1576,6 +1656,7 @@ bool GossipHello_guard_ironforge(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(2760, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_ironforge(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1651,6 +1732,7 @@ void SendDefaultMenu_guard_ironforge(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendBattleMasterMenu_guard_ironforge(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1669,6 +1751,7 @@ void SendBattleMasterMenu_guard_ironforge(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendClassTrainerMenu_guard_ironforge(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1708,6 +1791,7 @@ void SendClassTrainerMenu_guard_ironforge(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_ironforge(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1766,6 +1850,7 @@ void SendProfTrainerMenu_guard_ironforge(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_ironforge(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -1777,16 +1862,20 @@ bool GossipSelect_guard_ironforge(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_ironforge end
*******************************************************/
+
CreatureAI* GetAI_guard_ironforge(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_mulgore start
*******************************************************/
+
bool GossipHello_guard_mulgore(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -1798,6 +1887,7 @@ bool GossipHello_guard_mulgore(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(3543, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_mulgore(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1840,6 +1930,7 @@ void SendDefaultMenu_guard_mulgore(Player* pPlayer, Creature* pCreature, uint32
break;
}
}
+
void SendClassTrainerMenu_guard_mulgore(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1862,6 +1953,7 @@ void SendClassTrainerMenu_guard_mulgore(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
void SendProfTrainerMenu_guard_mulgore(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -1909,6 +2001,7 @@ void SendProfTrainerMenu_guard_mulgore(Player* pPlayer, Creature* pCreature, uin
break;
}
}
+
bool GossipSelect_guard_mulgore(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -1919,16 +2012,20 @@ bool GossipSelect_guard_mulgore(Player* pPlayer, Creature* pCreature, uint32 uiS
}
return true;
}
+
/*******************************************************
* guard_mulgore end
*******************************************************/
+
CreatureAI* GetAI_guard_mulgore(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_orgrimmar start
*******************************************************/
+
bool GossipHello_guard_orgrimmar(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -1945,8 +2042,10 @@ bool GossipHello_guard_orgrimmar(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_CLASSTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_PROFTRAINER , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13);
pPlayer->SEND_GOSSIP_MENU(2593, pCreature->GetGUID());
+
return true;
}
+
void SendDefaultMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2026,6 +2125,7 @@ void SendDefaultMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendBattleMasterMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2044,6 +2144,7 @@ void SendBattleMasterMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendClassTrainerMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2082,6 +2183,7 @@ void SendClassTrainerMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2140,6 +2242,7 @@ void SendProfTrainerMenu_guard_orgrimmar(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -2151,16 +2254,20 @@ bool GossipSelect_guard_orgrimmar(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_orgrimmar end
*******************************************************/
+
CreatureAI* GetAI_guard_orgrimmar(Creature* pCreature)
{
return new guardAI_orgrimmar (pCreature);
}
+
/*******************************************************
* guard_shattrath start
*******************************************************/
+
bool GossipHello_guard_shattrath(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_TAVERN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -2175,8 +2282,10 @@ bool GossipHello_guard_shattrath(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_ALCHEMYLAB , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10);
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_GEMMERCHANT , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 11);
pPlayer->SEND_GOSSIP_MENU(10321, pCreature->GetGUID());
+
return true;
}
+
void SendDefaultMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2243,6 +2352,7 @@ void SendDefaultMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendBankMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -2256,6 +2366,7 @@ void SendBankMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 u
pPlayer->SEND_GOSSIP_MENU(10381, pCreature->GetGUID());
}
}
+
void SendInnMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -2269,6 +2380,7 @@ void SendInnMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 ui
pPlayer->SEND_GOSSIP_MENU(10384, pCreature->GetGUID());
}
}
+
void SendMailboxMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2291,6 +2403,7 @@ void SendMailboxMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendStableMasterMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -2304,6 +2417,7 @@ void SendStableMasterMenu_guard_shattrath(Player* pPlayer, Creature* pCreature,
pPlayer->SEND_GOSSIP_MENU(10321, pCreature->GetGUID());
}
}
+
void SendBattleMasterMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2322,6 +2436,7 @@ void SendBattleMasterMenu_guard_shattrath(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2360,6 +2475,7 @@ void SendProfTrainerMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
void SendGemMerchantMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -2373,6 +2489,7 @@ void SendGemMerchantMenu_guard_shattrath(Player* pPlayer, Creature* pCreature, u
pPlayer->SEND_GOSSIP_MENU(10699, pCreature->GetGUID());
}
}
+
bool GossipSelect_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -2388,27 +2505,34 @@ bool GossipSelect_guard_shattrath(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_shattrath end
*******************************************************/
+
CreatureAI* GetAI_guard_shattrath(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* 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 TRINITY_DLL_DECL guard_shattrath_aldorAI : public guardAI
{
guard_shattrath_aldorAI(Creature *c) : guardAI(c) {}
+
uint32 Exile_Timer;
uint32 Banish_Timer;
uint64 PlayerGUID;
bool CanTeleport;
+
void Reset()
{
Banish_Timer = 5000;
@@ -2416,11 +2540,14 @@ struct TRINITY_DLL_DECL guard_shattrath_aldorAI : public guardAI
PlayerGUID = 0;
CanTeleport = false;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (CanTeleport)
{
if (Exile_Timer < diff)
@@ -2447,9 +2574,11 @@ struct TRINITY_DLL_DECL guard_shattrath_aldorAI : public guardAI
CanTeleport = true;
}
}else Banish_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
bool GossipHello_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_TAVERN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -2466,6 +2595,7 @@ bool GossipHello_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(10524, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2526,6 +2656,7 @@ void SendDefaultMenu_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2564,6 +2695,7 @@ void SendProfTrainerMenu_guard_shattrath_aldor(Player* pPlayer, Creature* pCreat
break;
}
}
+
bool GossipSelect_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -2574,23 +2706,29 @@ bool GossipSelect_guard_shattrath_aldor(Player* pPlayer, Creature* pCreature, ui
}
return true;
}
+
/*******************************************************
* guard_shattrath_aldor end
*******************************************************/
+
CreatureAI* GetAI_guard_shattrath_aldor(Creature* pCreature)
{
return new guard_shattrath_aldorAI (pCreature);
}
+
/*******************************************************
* guard_shattrath_scryer
*******************************************************/
+
struct TRINITY_DLL_DECL guard_shattrath_scryerAI : public guardAI
{
guard_shattrath_scryerAI(Creature *c) : guardAI(c) {}
+
uint32 Exile_Timer;
uint32 Banish_Timer;
uint64 PlayerGUID;
bool CanTeleport;
+
void Reset()
{
Banish_Timer = 5000;
@@ -2598,11 +2736,14 @@ struct TRINITY_DLL_DECL guard_shattrath_scryerAI : public guardAI
PlayerGUID = 0;
CanTeleport = false;
}
+
void EnterCombat(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (CanTeleport)
{
if (Exile_Timer < diff)
@@ -2629,9 +2770,11 @@ struct TRINITY_DLL_DECL guard_shattrath_scryerAI : public guardAI
CanTeleport = true;
}
}else Banish_Timer -= diff;
+
DoMeleeAttackIfReady();
}
};
+
bool GossipHello_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_TAVERN , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -2648,6 +2791,7 @@ bool GossipHello_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(10430, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2707,6 +2851,7 @@ void SendDefaultMenu_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature
break;
}
}
+
void SendProfTrainerMenu_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2745,6 +2890,7 @@ void SendProfTrainerMenu_guard_shattrath_scryer(Player* pPlayer, Creature* pCrea
break;
}
}
+
bool GossipSelect_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -2755,16 +2901,20 @@ bool GossipSelect_guard_shattrath_scryer(Player* pPlayer, Creature* pCreature, u
}
return true;
}
+
/*******************************************************
* guard_shattrath_scryer end
*******************************************************/
+
CreatureAI* GetAI_guard_shattrath_scryer(Creature* pCreature)
{
return new guard_shattrath_scryerAI (pCreature);
}
+
/*******************************************************
* guard_silvermoon start
*******************************************************/
+
bool GossipHello_guard_silvermoon(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -2781,6 +2931,7 @@ bool GossipHello_guard_silvermoon(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(9316, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2856,6 +3007,7 @@ void SendDefaultMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint
break;
}
}
+
void SendAuctionhouseMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -2869,6 +3021,7 @@ void SendAuctionhouseMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature,
pPlayer->SEND_GOSSIP_MENU(9319, pCreature->GetGUID());
}
}
+
void SendInnMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF + 1)
@@ -2882,6 +3035,7 @@ void SendInnMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 u
pPlayer->SEND_GOSSIP_MENU(9603, pCreature->GetGUID());
}
}
+
void SendBattleMasterMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2908,6 +3062,7 @@ void SendBattleMasterMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendClassTrainerMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -2942,6 +3097,7 @@ void SendClassTrainerMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3004,6 +3160,7 @@ void SendProfTrainerMenu_guard_silvermoon(Player* pPlayer, Creature* pCreature,
break;
}
}
+
bool GossipSelect_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -3017,16 +3174,20 @@ bool GossipSelect_guard_silvermoon(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*******************************************************
* guard_silvermoon end
*******************************************************/
+
CreatureAI* GetAI_guard_silvermoon(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_stormwind start
*******************************************************/
+
bool GossipHello_guard_stormwind(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_AUCTIONHOUSE , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -3045,6 +3206,7 @@ bool GossipHello_guard_stormwind(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(933, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_stormwind(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3125,6 +3287,7 @@ void SendDefaultMenu_guard_stormwind(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendBattleMasterMenu_guard_stormwind(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3143,6 +3306,7 @@ void SendBattleMasterMenu_guard_stormwind(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendClassTrainerMenu_guard_stormwind(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3186,6 +3350,7 @@ void SendClassTrainerMenu_guard_stormwind(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_stormwind(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3244,6 +3409,7 @@ void SendProfTrainerMenu_guard_stormwind(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_stormwind(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -3255,16 +3421,20 @@ bool GossipSelect_guard_stormwind(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_stormwind end
*******************************************************/
+
CreatureAI* GetAI_guard_stormwind(Creature* pCreature)
{
return new guardAI_stormwind (pCreature);
}
+
/*******************************************************
* guard_teldrassil start
*******************************************************/
+
bool GossipHello_guard_teldrassil(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -3277,6 +3447,7 @@ bool GossipHello_guard_teldrassil(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(4316, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_teldrassil(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3321,6 +3492,7 @@ void SendDefaultMenu_guard_teldrassil(Player* pPlayer, Creature* pCreature, uint
break;
}
}
+
void SendClassTrainerMenu_guard_teldrassil(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3347,6 +3519,7 @@ void SendClassTrainerMenu_guard_teldrassil(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_teldrassil(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3390,6 +3563,7 @@ void SendProfTrainerMenu_guard_teldrassil(Player* pPlayer, Creature* pCreature,
break;
}
}
+
bool GossipSelect_guard_teldrassil(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -3400,16 +3574,20 @@ bool GossipSelect_guard_teldrassil(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*******************************************************
* guard_teldrassil end
*******************************************************/
+
CreatureAI* GetAI_guard_teldrassil(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_tirisfal start
*******************************************************/
+
bool GossipHello_guard_tirisfal(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -3421,6 +3599,7 @@ bool GossipHello_guard_tirisfal(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(4097, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_tirisfal(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3465,6 +3644,7 @@ void SendDefaultMenu_guard_tirisfal(Player* pPlayer, Creature* pCreature, uint32
break;
}
}
+
void SendClassTrainerMenu_guard_tirisfal(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3491,6 +3671,7 @@ void SendClassTrainerMenu_guard_tirisfal(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
void SendProfTrainerMenu_guard_tirisfal(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3544,6 +3725,7 @@ void SendProfTrainerMenu_guard_tirisfal(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
bool GossipSelect_guard_tirisfal(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -3554,16 +3736,20 @@ bool GossipSelect_guard_tirisfal(Player* pPlayer, Creature* pCreature, uint32 ui
}
return true;
}
+
/*******************************************************
* guard_tirisfal end
*******************************************************/
+
CreatureAI* GetAI_guard_tirisfal(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* guard_undercity start
*******************************************************/
+
bool GossipHello_guard_undercity(Player* pPlayer, Creature* pCreature)
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_TEXT_BANK , GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
@@ -3581,6 +3767,7 @@ bool GossipHello_guard_undercity(Player* pPlayer, Creature* pCreature)
pPlayer->SEND_GOSSIP_MENU(3543, pCreature->GetGUID());
return true;
}
+
void SendDefaultMenu_guard_undercity(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3652,6 +3839,7 @@ void SendDefaultMenu_guard_undercity(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendBattleMasterMenu_guard_undercity(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3670,6 +3858,7 @@ void SendBattleMasterMenu_guard_undercity(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendClassTrainerMenu_guard_undercity(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3696,6 +3885,7 @@ void SendClassTrainerMenu_guard_undercity(Player* pPlayer, Creature* pCreature,
break;
}
}
+
void SendProfTrainerMenu_guard_undercity(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch (uiAction)
@@ -3750,6 +3940,7 @@ void SendProfTrainerMenu_guard_undercity(Player* pPlayer, Creature* pCreature, u
break;
}
}
+
bool GossipSelect_guard_undercity(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch (uiSender)
@@ -3761,131 +3952,155 @@ bool GossipSelect_guard_undercity(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*******************************************************
* guard_undercity end
*******************************************************/
+
CreatureAI* GetAI_guard_undercity(Creature* pCreature)
{
return new guardAI (pCreature);
}
+
/*******************************************************
* 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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_bluffwatcher";
newscript->pGossipHello = &GossipHello_guard_bluffwatcher;
newscript->pGossipSelect = &GossipSelect_guard_bluffwatcher;
newscript->GetAI = &GetAI_guard_bluffwatcher;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_contested";
newscript->GetAI = &GetAI_guard_contested;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_darnassus";
newscript->pGossipHello = &GossipHello_guard_darnassus;
newscript->pGossipSelect = &GossipSelect_guard_darnassus;
newscript->GetAI = &GetAI_guard_darnassus;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_dunmorogh";
newscript->pGossipHello = &GossipHello_guard_dunmorogh;
newscript->pGossipSelect = &GossipSelect_guard_dunmorogh;
newscript->GetAI = &GetAI_guard_dunmorogh;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_durotar";
newscript->pGossipHello = &GossipHello_guard_durotar;
newscript->pGossipSelect = &GossipSelect_guard_durotar;
newscript->GetAI = &GetAI_guard_durotar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_elwynnforest";
newscript->pGossipHello = &GossipHello_guard_elwynnforest;
newscript->pGossipSelect = &GossipSelect_guard_elwynnforest;
newscript->GetAI = &GetAI_guard_elwynnforest;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_eversong";
newscript->pGossipHello = &GossipHello_guard_eversong;
newscript->pGossipSelect = &GossipSelect_guard_eversong;
newscript->GetAI = &GetAI_guard_eversong;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_exodar";
newscript->pGossipHello = &GossipHello_guard_exodar;
newscript->pGossipSelect = &GossipSelect_guard_exodar;
newscript->GetAI = &GetAI_guard_exodar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_ironforge";
newscript->pGossipHello = &GossipHello_guard_ironforge;
newscript->pGossipSelect = &GossipSelect_guard_ironforge;
newscript->GetAI = &GetAI_guard_ironforge;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_mulgore";
newscript->pGossipHello = &GossipHello_guard_mulgore;
newscript->pGossipSelect = &GossipSelect_guard_mulgore;
newscript->GetAI = &GetAI_guard_mulgore;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_orgrimmar";
newscript->pGossipHello = &GossipHello_guard_orgrimmar;
newscript->pGossipSelect = &GossipSelect_guard_orgrimmar;
newscript->GetAI = &GetAI_guard_orgrimmar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_shattrath";
newscript->pGossipHello = &GossipHello_guard_shattrath;
newscript->pGossipSelect = &GossipSelect_guard_shattrath;
newscript->GetAI = &GetAI_guard_shattrath;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_silvermoon";
newscript->pGossipHello = &GossipHello_guard_silvermoon;
newscript->pGossipSelect = &GossipSelect_guard_silvermoon;
newscript->GetAI = &GetAI_guard_silvermoon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_stormwind";
newscript->pGossipHello = &GossipHello_guard_stormwind;
newscript->pGossipSelect = &GossipSelect_guard_stormwind;
newscript->GetAI = &GetAI_guard_stormwind;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_teldrassil";
newscript->pGossipHello = &GossipHello_guard_teldrassil;
newscript->pGossipSelect = &GossipSelect_guard_teldrassil;
newscript->GetAI = &GetAI_guard_teldrassil;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_tirisfal";
newscript->pGossipHello = &GossipHello_guard_tirisfal;
newscript->pGossipSelect = &GossipSelect_guard_tirisfal;
newscript->GetAI = &GetAI_guard_tirisfal;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "guard_undercity";
newscript->pGossipHello = &GossipHello_guard_undercity;
diff --git a/src/bindings/scripts/scripts/world/item_scripts.cpp b/src/bindings/scripts/scripts/world/item_scripts.cpp
index b1f04f67e56..0e0f5795be9 100644
--- a/src/bindings/scripts/scripts/world/item_scripts.cpp
+++ b/src/bindings/scripts/scripts/world/item_scripts.cpp
@@ -13,12 +13,14 @@
* along 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_draenei_fishing_net(i23654) Hacklike implements chance to spawn item or creature
item_nether_wraith_beacon(i31742) Summons creatures for quest Becoming a Spellfire Tailor (q10832)
@@ -26,19 +28,24 @@ item_flying_machine(i34060,i34061) Engineering crafted flying machines
item_gor_dreks_ointment(i30175) Protecting Our Own(q10488)
item_only_for_flight Items which should only useable while flying
EndContentData */
+
#include "precompiled.h"
#include "Spell.h"
+
/*#####
# item_only_for_flight
#####*/
+
enum eOnlyForFlight
{
SPELL_ARCANE_CHARGES = 45072
};
+
bool ItemUse_item_only_for_flight(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
uint32 itemId = _Item->GetEntry();
bool disabled = false;
+
//for special scripts
switch(itemId)
{
@@ -55,16 +62,20 @@ bool ItemUse_item_only_for_flight(Player* pPlayer, Item* _Item, SpellCastTargets
Spell::SendCastResult(pPlayer, pSpellInfo, 1, SPELL_FAILED_NOT_ON_GROUND);
break;
}
+
// allow use in flight only
if (pPlayer->isInFlight() && !disabled)
return false;
+
// error
pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,_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* pPlayer, Item* _Item, SpellCastTargets const& targets)
@@ -96,9 +107,11 @@ bool ItemUse_item_draenei_fishing_net(Player* pPlayer, Item* _Item, SpellCastTar
//}
return false;
}
+
/*#####
# item_nether_wraith_beacon
#####*/
+
bool ItemUse_item_nether_wraith_beacon(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
if (pPlayer->GetQuestStatus(10832) == QUEST_STATUS_INCOMPLETE)
@@ -111,36 +124,45 @@ bool ItemUse_item_nether_wraith_beacon(Player* pPlayer, Item* _Item, SpellCastTa
}
return false;
}
+
/*#####
# item_flying_machine
#####*/
+
bool ItemUse_item_flying_machine(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
uint32 itemId = _Item->GetEntry();
if (itemId == 34060)
if (pPlayer->GetBaseSkillValue(SKILL_RIDING) >= 225)
return false;
+
if (itemId == 34061)
if (pPlayer->GetBaseSkillValue(SKILL_RIDING) == 300)
return false;
+
debug_log("TSCR: Player attempt to use item %u, but did not meet riding requirement",itemId);
pPlayer->SendEquipError(EQUIP_ERR_ERR_CANT_EQUIP_SKILL,_Item,NULL);
return true;
}
+
/*#####
# item_gor_dreks_ointment
#####*/
+
bool ItemUse_item_gor_dreks_ointment(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
if (targets.getUnitTarget() && targets.getUnitTarget()->GetTypeId()==TYPEID_UNIT &&
targets.getUnitTarget()->GetEntry() == 20748 && !targets.getUnitTarget()->HasAura(32578))
return false;
+
pPlayer->SendEquipError(EQUIP_ERR_CANT_DO_RIGHT_NOW,_Item,NULL);
return true;
}
+
/*#####
# item_incendiary_explosives
#####*/
+
bool ItemUse_item_incendiary_explosives(Player* pPlayer, Item* _Item, SpellCastTargets const& targets)
{
if (pPlayer->FindNearestCreature(26248,15) || pPlayer->FindNearestCreature(26249,15))
@@ -153,9 +175,11 @@ bool ItemUse_item_incendiary_explosives(Player* pPlayer, Item* _Item, SpellCastT
return true;
}
}
+
/*#####
# item_mysterious_egg
#####*/
+
bool ItemExpire_item_mysterious_egg(Player* pPlayer, ItemPrototype const * _ItemProto)
{
ItemPosCountVec dest;
@@ -166,9 +190,11 @@ bool ItemExpire_item_mysterious_egg(Player* pPlayer, ItemPrototype const * _Item
}
return true;
}
+
/*#####
# item_disgusting_jar
#####*/
+
bool ItemExpire_item_disgusting_jar(Player* pPlayer, ItemPrototype const * _ItemProto)
{
ItemPosCountVec dest;
@@ -179,6 +205,7 @@ bool ItemExpire_item_disgusting_jar(Player* pPlayer, ItemPrototype const * _Item
}
return true;
}
+
/*#####
# item_harvesters_gift
#####*/
@@ -187,6 +214,7 @@ bool ItemUse_item_harvesters_gift(Player* pPlayer, Item* _Item, SpellCastTargets
{
std::list<Creature*> MinionList;
pPlayer->GetAllMinionsByEntry(MinionList,GHOULS);
+
if (pPlayer->GetQuestStatus(12698) == QUEST_STATUS_INCOMPLETE)
{
if (!MinionList.empty())
@@ -209,41 +237,51 @@ bool ItemUse_item_harvesters_gift(Player* pPlayer, Item* _Item, SpellCastTargets
return true;
}
}
+
void AddSC_item_scripts()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "item_only_for_flight";
newscript->pItemUse = &ItemUse_item_only_for_flight;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_draenei_fishing_net";
newscript->pItemUse = &ItemUse_item_draenei_fishing_net;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_nether_wraith_beacon";
newscript->pItemUse = &ItemUse_item_nether_wraith_beacon;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_flying_machine";
newscript->pItemUse = &ItemUse_item_flying_machine;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_gor_dreks_ointment";
newscript->pItemUse = &ItemUse_item_gor_dreks_ointment;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_incendiary_explosives";
newscript->pItemUse = &ItemUse_item_incendiary_explosives;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_mysterious_egg";
newscript->pItemExpire = &ItemExpire_item_mysterious_egg;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_disgusting_jar";
newscript->pItemExpire = &ItemExpire_item_disgusting_jar;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "item_harvesters_gift";
newscript->pItemUse = &ItemUse_item_harvesters_gift;
diff --git a/src/bindings/scripts/scripts/world/mob_generic_creature.cpp b/src/bindings/scripts/scripts/world/mob_generic_creature.cpp
index 6c398779410..45807337f5e 100644
--- a/src/bindings/scripts/scripts/world/mob_generic_creature.cpp
+++ b/src/bindings/scripts/scripts/world/mob_generic_creature.cpp
@@ -13,26 +13,33 @@
* along 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 TRINITY_DLL_DECL generic_creatureAI : public ScriptedAI
{
generic_creatureAI(Creature *c) : ScriptedAI(c) {}
+
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 EnterCombat(Unit *who)
{
if (!m_creature->IsWithinMeleeRange(who))
@@ -40,32 +47,39 @@ struct TRINITY_DLL_DECL generic_creatureAI : public ScriptedAI
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 (!m_creature->isInCombat() && 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 (!UpdateVictim())
return;
+
//If we are within range melee the target
if (m_creature->IsWithinMeleeRange(m_creature->getVictim()))
{
@@ -74,22 +88,27 @@ struct TRINITY_DLL_DECL generic_creatureAI : public ScriptedAI
{
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();
}
}
@@ -100,12 +119,15 @@ struct TRINITY_DLL_DECL generic_creatureAI : public ScriptedAI
{
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, NOMINAL_MELEE_RANGE, 0, SELECT_EFFECT_DONTCARE);
+
//Found a spell, check if we arn't on cooldown
if (info && !GlobalCooldown)
{
@@ -114,12 +136,15 @@ struct TRINITY_DLL_DECL generic_creatureAI : public ScriptedAI
{
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)
{
@@ -131,10 +156,12 @@ struct TRINITY_DLL_DECL generic_creatureAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_generic_creature(Creature* pCreature)
{
return new generic_creatureAI (pCreature);
}
+
struct TRINITY_DLL_DECL trigger_periodicAI : public NullCreatureAI
{
trigger_periodicAI(Creature* c) : NullCreatureAI(c)
@@ -143,8 +170,10 @@ struct TRINITY_DLL_DECL trigger_periodicAI : public NullCreatureAI
interval = me->GetAttackTime(BASE_ATTACK);
timer = interval;
}
+
uint32 timer, interval;
const SpellEntry * spell;
+
void UpdateAI(const uint32 diff)
{
if (timer < diff)
@@ -157,6 +186,7 @@ struct TRINITY_DLL_DECL trigger_periodicAI : public NullCreatureAI
timer -= diff;
}
};
+
struct TRINITY_DLL_DECL trigger_deathAI : public NullCreatureAI
{
trigger_deathAI(Creature* c) : NullCreatureAI(c) {}
@@ -166,10 +196,13 @@ struct TRINITY_DLL_DECL trigger_deathAI : public NullCreatureAI
me->CastSpell(killer, me->m_spells[0], true);
}
};
+
struct TRINITY_DLL_DECL mob_webwrapAI : public NullCreatureAI
{
mob_webwrapAI(Creature *c) : NullCreatureAI(c), victimGUID(0) {}
+
uint64 victimGUID;
+
void SetGUID(const uint64 &guid, int32 param)
{
victimGUID = guid;
@@ -177,6 +210,7 @@ struct TRINITY_DLL_DECL mob_webwrapAI : public NullCreatureAI
if (Unit *victim = Unit::GetUnit(*me, victimGUID))
victim->CastSpell(victim, me->m_spells[0], true, NULL, NULL, me->GetGUID());
}
+
void JustDied(Unit *killer)
{
if (me->m_spells[0] && victimGUID)
@@ -184,18 +218,22 @@ struct TRINITY_DLL_DECL mob_webwrapAI : public NullCreatureAI
victim->RemoveAurasDueToSpell(me->m_spells[0], me->GetGUID());
}
};
+
CreatureAI* GetAI_trigger_periodic(Creature* pCreature)
{
return new trigger_periodicAI (pCreature);
}
+
CreatureAI* GetAI_trigger_death(Creature* pCreature)
{
return new trigger_deathAI (pCreature);
}
+
CreatureAI* GetAI_mob_webwrap(Creature* pCreature)
{
return new mob_webwrapAI (pCreature);
}
+
void AddSC_generic_creature()
{
Script *newscript;
@@ -203,14 +241,17 @@ void AddSC_generic_creature()
newscript->Name = "generic_creature";
newscript->GetAI = &GetAI_generic_creature;
newscript->RegisterSelf();*/
+
newscript = new Script;
newscript->Name = "trigger_periodic";
newscript->GetAI = &GetAI_trigger_periodic;
newscript->RegisterSelf();
+
/*newscript = new Script;
newscript->Name = "trigger_death";
newscript->GetAI = &GetAI_trigger_death;
newscript->RegisterSelf();*/
+
newscript = new Script;
newscript->Name = "mob_webwrap";
newscript->GetAI = &GetAI_mob_webwrap;
diff --git a/src/bindings/scripts/scripts/world/npc_innkeeper.cpp b/src/bindings/scripts/scripts/world/npc_innkeeper.cpp
index 865de42c0e8..da124eeb720 100644
--- a/src/bindings/scripts/scripts/world/npc_innkeeper.cpp
+++ b/src/bindings/scripts/scripts/world/npc_innkeeper.cpp
@@ -13,20 +13,25 @@
* along 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()
{
/*
@@ -41,10 +46,12 @@ bool isEventActive()
}*/
return false;
}
+
bool GossipHello_npc_innkeeper(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (isEventActive()&& !pPlayer->GetAura(SPELL_TRICK_OR_TREATED, pPlayer->GetGUID()))
{
char* localizedEntry;
@@ -65,18 +72,22 @@ bool GossipHello_npc_innkeeper(Player* pPlayer, Creature* pCreature)
default:
localizedEntry=LOCALE_TRICK_OR_TREAT_0;
}
+
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, localizedEntry, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+HALLOWEEN_EVENTID);
}
+
pPlayer->TalkedToCreature(pCreature->GetEntry(), pCreature->GetGUID());
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_innkeeper(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+HALLOWEEN_EVENTID && isEventActive() && !pPlayer->GetAura(SPELL_TRICK_OR_TREATED, pPlayer->GetGUID()))
{
pPlayer->CLOSE_GOSSIP_MENU();
pPlayer->CastSpell(pPlayer, SPELL_TRICK_OR_TREATED, true);
+
// either trick or treat, 50% chance
if (rand()%2)
{
@@ -121,6 +132,7 @@ bool GossipSelect_npc_innkeeper(Player* pPlayer, Creature* pCreature, uint32 uiS
}
return false; // the player didn't select "trick or treat" or cheated, normal core handling
}
+
void AddSC_npc_innkeeper()
{
Script *newscript;
diff --git a/src/bindings/scripts/scripts/world/npc_professions.cpp b/src/bindings/scripts/scripts/world/npc_professions.cpp
index 59b4f4c5ff4..484339c59bb 100644
--- a/src/bindings/scripts/scripts/world/npc_professions.cpp
+++ b/src/bindings/scripts/scripts/world/npc_professions.cpp
@@ -13,144 +13,186 @@
* along 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* pPlayer) //tailor, alchemy
{
return 200000;
}
+
int32 DoHighUnlearnCost(Player* pPlayer) //tailor, alchemy
{
return 1500000;
}
+
int32 DoMedUnlearnCost(Player* pPlayer) //blacksmith, leatherwork
{
uint32 level = pPlayer->getLevel();
@@ -161,6 +203,7 @@ int32 DoMedUnlearnCost(Player* pPlayer) //blacksmith, leath
else
return 1000000;
}
+
int32 DoLowUnlearnCost(Player* pPlayer) //blacksmith
{
uint32 level = pPlayer->getLevel();
@@ -169,21 +212,26 @@ int32 DoLowUnlearnCost(Player* pPlayer) //blacksmith
else
return 100000;
}
+
/*###
# unlearning related profession spells
###*/
+
bool EquippedOk(Player* pPlayer, uint32 spellId)
{
SpellEntry const* spell = GetSpellStore()->LookupEntry(spellId);
+
if (!spell)
return false;
- for (uint8 i=0; i<3; ++i)
+
+ for(uint8 i=0; i<3; ++i)
{
uint32 reqSpell = spell->EffectTriggerSpell[i];
if (!reqSpell)
continue;
+
Item* pItem;
- for (uint8 j = EQUIPMENT_SLOT_START; j < EQUIPMENT_SLOT_END; j++)
+ for(uint8 j = EQUIPMENT_SLOT_START; j < EQUIPMENT_SLOT_END; j++)
{
pItem = pPlayer->GetItemByPos(INVENTORY_SLOT_BAG_0, j);
if (pItem)
@@ -197,6 +245,7 @@ bool EquippedOk(Player* pPlayer, uint32 spellId)
}
return true;
}
+
void ProfessionUnlearnSpells(Player* pPlayer, uint32 type)
{
switch (type)
@@ -289,15 +338,18 @@ void ProfessionUnlearnSpells(Player* pPlayer, uint32 type)
break;
}
}
+
/*###
# start menues alchemy
###*/
+
bool HasAlchemySpell(Player* pPlayer)
{
if (pPlayer->HasSpell(S_TRANSMUTE) || pPlayer->HasSpell(S_ELIXIR) || pPlayer->HasSpell(S_POTION))
return true;
return false;
}
+
bool GossipHello_npc_prof_alchemy(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
@@ -306,7 +358,9 @@ bool GossipHello_npc_prof_alchemy(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
if (pCreature->isTrainer())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
+
uint32 eCreature = pCreature->GetEntry();
+
if (pPlayer->HasSkill(SKILL_ALCHEMY) && pPlayer->GetBaseSkillValue(SKILL_ALCHEMY)>=350 && pPlayer->getLevel() > 67)
{
if (pPlayer->GetQuestRewardStatus(10899) || pPlayer->GetQuestRewardStatus(10902) || pPlayer->GetQuestRewardStatus(10897))
@@ -334,9 +388,11 @@ bool GossipHello_npc_prof_alchemy(Player* pPlayer, Creature* pCreature)
}
}
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
void SendActionMenu_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch(uiAction)
@@ -405,6 +461,7 @@ void SendActionMenu_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendConfirmLearn_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -430,6 +487,7 @@ void SendConfirmLearn_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uin
}
}
}
+
void SendConfirmUnlearn_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -455,6 +513,7 @@ void SendConfirmUnlearn_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, u
}
}
}
+
bool GossipSelect_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiSender)
@@ -466,15 +525,18 @@ bool GossipSelect_npc_prof_alchemy(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*###
# start menues blacksmith
###*/
+
bool HasWeaponSub(Player* pPlayer)
{
if (pPlayer->HasSpell(S_HAMMER) || pPlayer->HasSpell(S_AXE) || pPlayer->HasSpell(S_SWORD))
return true;
return false;
}
+
bool GossipHello_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
@@ -483,6 +545,7 @@ bool GossipHello_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
if (pCreature->isTrainer())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
+
uint32 eCreature = pCreature->GetEntry();
//WEAPONSMITH & ARMORSMITH
if (pPlayer->GetBaseSkillValue(SKILL_BLACKSMITHING)>=225)
@@ -533,9 +596,11 @@ bool GossipHello_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature)
break;
}
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
void SendActionMenu_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch(uiAction)
@@ -661,6 +726,7 @@ void SendActionMenu_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, ui
break;
}
}
+
void SendConfirmLearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -686,6 +752,7 @@ void SendConfirmLearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature,
}
}
}
+
void SendConfirmUnlearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -701,6 +768,7 @@ void SendConfirmUnlearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature
//unknown textID (TALK_UNLEARN_AXEORWEAPON)
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
break;
+
case 11191:
pPlayer->ADD_GOSSIP_ITEM_EXTENDED(0, GOSSIP_UNLEARN_HAMMER, GOSSIP_SENDER_CHECK, uiAction, BOX_UNLEARN_WEAPON_SPEC, DoMedUnlearnCost(pPlayer),false);
//unknown textID (TALK_HAMMER_UNLEARN)
@@ -719,6 +787,7 @@ void SendConfirmUnlearn_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature
}
}
}
+
bool GossipSelect_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiSender)
@@ -730,43 +799,54 @@ bool GossipSelect_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, uint
}
return true;
}
+
/*bool QuestComplete_npc_prof_blacksmith(Player* pPlayer, Creature* pCreature, Quest const *_Quest)
{
if ((_Quest->GetQuestId() == 5283) || (_Quest->GetQuestId() == 5301)) //armorsmith
pCreature->CastSpell(pPlayer, 17451, true);
+
if ((_Quest->GetQuestId() == 5284) || (_Quest->GetQuestId() == 5302)) //weaponsmith
pCreature->CastSpell(pPlayer, 17452, true);
+
return true;
}*/
+
/*###
# engineering trinkets
###*/
+
enum eEngineeringTrinkets
{
NPC_ZAP = 14742,
NPC_JHORDY = 14743,
NPC_KABLAM = 21493,
NPC_SMILES = 21494,
+
SPELL_LEARN_TO_EVERLOOK = 23490,
SPELL_LEARN_TO_GADGET = 23491,
SPELL_LEARN_TO_AREA52 = 36956,
SPELL_LEARN_TO_TOSHLEY = 36957,
+
SPELL_TO_EVERLOOK = 23486,
SPELL_TO_GADGET = 23489,
SPELL_TO_AREA52 = 36954,
SPELL_TO_TOSHLEY = 36955,
+
ITEM_GNOMISH_CARD = 10790,
ITEM_GOBLIN_CARD = 10791
};
+
#define GOSSIP_ITEM_ZAP "[PH] Unknown"
#define GOSSIP_ITEM_JHORDY "I must build a beacon for this marvelous device!"
#define GOSSIP_ITEM_KABLAM "[PH] Unknown"
#define GOSSIP_ITEM_SMILES "[PH] Unknown"
+
bool GossipHello_npc_engineering_tele_trinket(Player* pPlayer, Creature* pCreature)
{
uint32 NpcTextId = 0;
std::string GossipItem;
bool CanLearn = false;
+
if (pPlayer->HasSkill(SKILL_ENGINERING))
{
switch(pCreature->GetEntry())
@@ -825,20 +905,25 @@ bool GossipHello_npc_engineering_tele_trinket(Player* pPlayer, Creature* pCreatu
break;
}
}
+
if (CanLearn)
{
if (pPlayer->HasItemCount(ITEM_GOBLIN_CARD,1) || pPlayer->HasItemCount(ITEM_GNOMISH_CARD,1))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GossipItem, pCreature->GetEntry(), GOSSIP_ACTION_INFO_DEF+1);
}
+
pPlayer->SEND_GOSSIP_MENU(NpcTextId ? NpcTextId : pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_engineering_tele_trinket(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
pPlayer->CLOSE_GOSSIP_MENU();
+
if (uiSender != pCreature->GetEntry())
return true;
+
switch(uiSender)
{
case NPC_ZAP:
@@ -854,11 +939,14 @@ bool GossipSelect_npc_engineering_tele_trinket(Player* pPlayer, Creature* pCreat
pPlayer->CastSpell(pPlayer, SPELL_LEARN_TO_TOSHLEY, false);
break;
}
+
return true;
}
+
/*###
# start menues leatherworking
###*/
+
bool GossipHello_npc_prof_leather(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
@@ -867,7 +955,9 @@ bool GossipHello_npc_prof_leather(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
if (pCreature->isTrainer())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
+
uint32 eCreature = pCreature->GetEntry();
+
if (pPlayer->HasSkill(SKILL_LEATHERWORKING) && pPlayer->GetBaseSkillValue(SKILL_LEATHERWORKING)>=250 && pPlayer->getLevel() > 49)
{
switch (eCreature)
@@ -889,9 +979,11 @@ bool GossipHello_npc_prof_leather(Player* pPlayer, Creature* pCreature)
break;
}
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
void SendActionMenu_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch(uiAction)
@@ -947,6 +1039,7 @@ void SendActionMenu_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint3
break;
}
}
+
void SendConfirmUnlearn_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -975,6 +1068,7 @@ void SendConfirmUnlearn_npc_prof_leather(Player* pPlayer, Creature* pCreature, u
}
}
}
+
bool GossipSelect_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiSender)
@@ -985,15 +1079,18 @@ bool GossipSelect_npc_prof_leather(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*###
# start menues tailoring
###*/
+
bool HasTailorSpell(Player* pPlayer)
{
if (pPlayer->HasSpell(S_MOONCLOTH) || pPlayer->HasSpell(S_SHADOWEAVE) || pPlayer->HasSpell(S_SPELLFIRE))
return true;
return false;
}
+
bool GossipHello_npc_prof_tailor(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
@@ -1002,6 +1099,7 @@ bool GossipHello_npc_prof_tailor(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_VENDOR, GOSSIP_TEXT_BROWSE_GOODS, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRADE);
if (pCreature->isTrainer())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
+
uint32 eCreature = pCreature->GetEntry();
//TAILORING SPEC
if (pPlayer->HasSkill(SKILL_TAILORING) && pPlayer->GetBaseSkillValue(SKILL_TAILORING)>=350 && pPlayer->getLevel() > 59)
@@ -1031,9 +1129,11 @@ bool GossipHello_npc_prof_tailor(Player* pPlayer, Creature* pCreature)
}
}
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
void SendActionMenu_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch(uiAction)
@@ -1117,6 +1217,7 @@ void SendActionMenu_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32
break;
}
}
+
void SendConfirmLearn_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -1142,6 +1243,7 @@ void SendConfirmLearn_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint
}
}
}
+
void SendConfirmUnlearn_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
if (uiAction)
@@ -1167,6 +1269,7 @@ void SendConfirmUnlearn_npc_prof_tailor(Player* pPlayer, Creature* pCreature, ui
}
}
}
+
bool GossipSelect_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiSender)
@@ -1178,46 +1281,58 @@ bool GossipSelect_npc_prof_tailor(Player* pPlayer, Creature* pCreature, uint32 u
}
return true;
}
+
/*###
# start menues for GO (engineering and leatherworking)
###*/
+
/*bool GOHello_go_soothsaying_for_dummies(Player* pPlayer, GameObject* pGo)
{
pPlayer->PlayerTalkClass->GetGossipMenu()->AddMenuItem(0,GOSSIP_LEARN_DRAGON, GOSSIP_SENDER_INFO, GOSSIP_ACTION_INFO_DEF, "", 0);
+
pPlayer->SEND_GOSSIP_MENU(5584, pGo->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;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_prof_blacksmith";
newscript->pGossipHello = &GossipHello_npc_prof_blacksmith;
newscript->pGossipSelect = &GossipSelect_npc_prof_blacksmith;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_engineering_tele_trinket";
newscript->pGossipHello = &GossipHello_npc_engineering_tele_trinket;
newscript->pGossipSelect = &GossipSelect_npc_engineering_tele_trinket;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_prof_leather";
newscript->pGossipHello = &GossipHello_npc_prof_leather;
newscript->pGossipSelect = &GossipSelect_npc_prof_leather;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_prof_tailor";
newscript->pGossipHello = &GossipHello_npc_prof_tailor;
newscript->pGossipSelect = &GossipSelect_npc_prof_tailor;
newscript->RegisterSelf();
+
/*newscript = new Script;
newscript->Name = "go_soothsaying_for_dummies";
newscript->pGOHello = &GOHello_go_soothsaying_for_dummies;
diff --git a/src/bindings/scripts/scripts/world/npc_taxi.cpp b/src/bindings/scripts/scripts/world/npc_taxi.cpp
index 0dfc0f7e51d..44f05abbfc0 100644
--- a/src/bindings/scripts/scripts/world/npc_taxi.cpp
+++ b/src/bindings/scripts/scripts/world/npc_taxi.cpp
@@ -13,6 +13,7 @@
* along 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_Taxi
SD%Complete: 0%
@@ -20,7 +21,9 @@ SDComment: To be used for taxi NPCs that are located globally.
SDCategory: NPCs
EndScriptData
*/
+
#include "precompiled.h"
+
#define GOSSIP_SUSURRUS "I am ready."
#define GOSSIP_NETHER_DRAKE "I'm ready to fly! Take me up, dragon!"
#define GOSSIP_BRAZEN "I am ready to go to Durnholde Keep."
@@ -49,10 +52,12 @@ EndScriptData
#define GOSSIP_CRIMSONWING "<Ride the gryphons to Survey Alcaz Island>"
#define GOSSIP_THRICESTAR1 "Do you think I could take a ride on one of those flying machines?"
#define GOSSIP_THRICESTAR2 "Kara, I need to be flown out the Dens of Dying to find Bixie."
+
bool GossipHello_npc_taxi(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
switch(pCreature->GetEntry()) {
case 17435: // Azuremyst Isle - Susurrus
if (pPlayer->HasItemCount(23843,1,true))
@@ -72,6 +77,7 @@ bool GossipHello_npc_taxi(Player* pPlayer, Creature* pCreature)
//Mission: The Murketh and Shaadraz Gateways
if (pPlayer->GetQuestStatus(10146) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DABIREE1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 4);
+
//Shatter Point
if (!pPlayer->GetQuestRewardStatus(10340))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_DABIREE2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 5);
@@ -80,6 +86,7 @@ bool GossipHello_npc_taxi(Player* pPlayer, Creature* pCreature)
//Mission: The Abyssal Shelf || Return to the Abyssal Shelf
if (pPlayer->GetQuestStatus(10163) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(10346) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_WINDBELLOW1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 6);
+
//Go to the Front
if (pPlayer->GetQuestStatus(10382) != QUEST_STATUS_NONE && !pPlayer->GetQuestRewardStatus(10382))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_WINDBELLOW2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 7);
@@ -88,9 +95,11 @@ bool GossipHello_npc_taxi(Player* pPlayer, Creature* pCreature)
//Mission: The Murketh and Shaadraz Gateways
if (pPlayer->GetQuestStatus(10129) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BRACK1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 8);
+
//Mission: The Abyssal Shelf || Return to the Abyssal Shelf
if (pPlayer->GetQuestStatus(10162) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(10347) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BRACK2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 9);
+
//Spinebreaker Post
if (pPlayer->GetQuestStatus(10242) == QUEST_STATUS_COMPLETE && !pPlayer->GetQuestRewardStatus(10242))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_BRACK3, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 10);
@@ -102,6 +111,7 @@ bool GossipHello_npc_taxi(Player* pPlayer, Creature* pCreature)
case 25059: // Isle of Quel'Danas - Ayren Cloudbreaker
if (pPlayer->GetQuestStatus(11532) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(11533) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_CLOUDBREAKER1, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 12);
+
if (pPlayer->GetQuestStatus(11542) == QUEST_STATUS_INCOMPLETE || pPlayer->GetQuestStatus(11543) == QUEST_STATUS_INCOMPLETE)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_CLOUDBREAKER2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 13);
break;
@@ -153,9 +163,11 @@ bool GossipHello_npc_taxi(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_THRICESTAR2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 27);
break;
}
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_taxi(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction) {
@@ -277,11 +289,14 @@ bool GossipSelect_npc_taxi(Player* pPlayer, Creature* pCreature, uint32 uiSender
pPlayer->CastSpell(pPlayer, 51446, false);
break;
}
+
return true;
}
+
void AddSC_npc_taxi()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_taxi";
newscript->pGossipHello = &GossipHello_npc_taxi;
diff --git a/src/bindings/scripts/scripts/world/npcs_special.cpp b/src/bindings/scripts/scripts/world/npcs_special.cpp
index 422713cfff3..ce1d73caf5e 100644
--- a/src/bindings/scripts/scripts/world/npcs_special.cpp
+++ b/src/bindings/scripts/scripts/world/npcs_special.cpp
@@ -13,6 +13,7 @@
* along 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
@@ -20,6 +21,7 @@ SDComment: To be used for special NPCs that are located globally.
SDCategory: NPCs
EndScriptData
*/
+
/* ContentData
npc_air_force_bots 80% support for misc (invisible) guard bots in areas where player allowed to fly. Summon guards after a preset time if tagged by spell
npc_chicken_cluck 100% support for quest 3861 (Cluck!)
@@ -34,30 +36,37 @@ npc_rogue_trainer 80% Scripted trainers, so they are able to offer ite
npc_sayge 100% Darkmoon event fortune teller, buff player based on answers given
npc_snake_trap_serpents 80% AI for snakes that summoned by Snake Trap
EndContentData */
+
#include "precompiled.h"
#include "escort_ai.h"
#include "ObjectMgr.h"
+
/*########
# npc_air_force_bots
#########*/
+
enum SpawnType
{
SPAWNTYPE_TRIPWIRE_ROOFTOP, // no warning, summon Creature at smaller range
SPAWNTYPE_ALARMBOT, // cast guards mark and summon npc - if player shows up with that buff duration < 5 seconds attack
};
+
struct SpawnAssociation
{
uint32 m_uiThisCreatureEntry;
uint32 m_uiSpawnedCreatureEntry;
SpawnType m_SpawnType;
};
+
enum eEnums
{
SPELL_GUARDS_MARK = 38067,
AURA_DURATION_TIME_LEFT = 5000
};
+
const float RANGE_TRIPWIRE = 15.0f;
const float RANGE_GUARDS_MARK = 50.0f;
+
SpawnAssociation m_aSpawnAssociations[] =
{
{2614, 15241, SPAWNTYPE_ALARMBOT}, //Air Force Alarm Bot (Alliance)
@@ -88,14 +97,17 @@ SpawnAssociation m_aSpawnAssociations[] =
{22125, 22122, SPAWNTYPE_ALARMBOT}, //Air Force Guard Post (Cenarion - Stormcrow)
{22126, 22122, SPAWNTYPE_ALARMBOT} //Air Force Trip Wire - Rooftop (Cenarion Expedition)
};
+
struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
{
npc_air_force_botsAI(Creature* pCreature) : ScriptedAI(pCreature)
{
m_pSpawnAssoc = NULL;
m_uiSpawnedGUID = 0;
+
// find the correct spawnhandling
static uint32 uiEntryCount = sizeof(m_aSpawnAssociations)/sizeof(SpawnAssociation);
+
for (uint8 i=0; i<uiEntryCount; ++i)
{
if (m_aSpawnAssociations[i].m_uiThisCreatureEntry == pCreature->GetEntry())
@@ -104,11 +116,13 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
break;
}
}
+
if (!m_pSpawnAssoc)
error_db_log("TCSR: Creature template entry %u has ScriptName npc_air_force_bots, but it's not handled by that script", pCreature->GetEntry());
else
{
CreatureInfo const* spawnedTemplate = GetCreatureTemplateStore(m_pSpawnAssoc->m_uiSpawnedCreatureEntry);
+
if (!spawnedTemplate)
{
m_pSpawnAssoc = NULL;
@@ -117,12 +131,16 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
}
}
}
+
SpawnAssociation* m_pSpawnAssoc;
uint64 m_uiSpawnedGUID;
+
void Reset() { }
+
Creature* SummonGuard()
{
Creature* pSummoned = m_creature->SummonCreature(m_pSpawnAssoc->m_uiSpawnedCreatureEntry, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 300000);
+
if (pSummoned)
m_uiSpawnedGUID = pSummoned->GetGUID();
else
@@ -130,35 +148,46 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
error_db_log("TCSR: npc_air_force_bots: wasn't able to spawn Creature %u", m_pSpawnAssoc->m_uiSpawnedCreatureEntry);
m_pSpawnAssoc = NULL;
}
+
return pSummoned;
}
+
Creature* GetSummonedGuard()
{
Creature* pCreature = Unit::GetCreature(*m_creature, m_uiSpawnedGUID);
+
if (pCreature && pCreature->isAlive())
return pCreature;
+
return NULL;
}
+
void MoveInLineOfSight(Unit* pWho)
{
if (!m_pSpawnAssoc)
return;
+
if (pWho->isTargetableForAttack() && m_creature->IsHostileTo(pWho))
{
Player* pPlayerTarget = pWho->GetTypeId() == TYPEID_PLAYER ? CAST_PLR(pWho) : NULL;
+
// airforce guards only spawn for players
if (!pPlayerTarget)
return;
+
Creature* pLastSpawnedGuard = m_uiSpawnedGUID == 0 ? NULL : GetSummonedGuard();
+
// prevent calling Unit::GetUnit at next MoveInLineOfSight call - speedup
if (!pLastSpawnedGuard)
m_uiSpawnedGUID = 0;
+
switch(m_pSpawnAssoc->m_SpawnType)
{
case SPAWNTYPE_ALARMBOT:
{
if (!pWho->IsWithinDistInMap(m_creature, RANGE_GUARDS_MARK))
return;
+
Aura* pMarkAura = pWho->GetAura(SPELL_GUARDS_MARK, 0);
if (pMarkAura)
{
@@ -166,9 +195,11 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
if (!pLastSpawnedGuard)
{
pLastSpawnedGuard = SummonGuard();
+
if (!pLastSpawnedGuard)
return;
}
+
if (pMarkAura->GetAuraDuration() < AURA_DURATION_TIME_LEFT)
{
if (!pLastSpawnedGuard->getVictim())
@@ -179,8 +210,10 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
{
if (!pLastSpawnedGuard)
pLastSpawnedGuard = SummonGuard();
+
if (!pLastSpawnedGuard)
return;
+
pLastSpawnedGuard->CastSpell(pWho, SPELL_GUARDS_MARK, true);
}
break;
@@ -189,10 +222,13 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
{
if (!pWho->IsWithinDistInMap(m_creature, RANGE_TRIPWIRE))
return;
+
if (!pLastSpawnedGuard)
pLastSpawnedGuard = SummonGuard();
+
if (!pLastSpawnedGuard)
return;
+
// ROOFTOP only triggers if the player is on the ground
if (!pPlayerTarget->IsFlying())
{
@@ -205,29 +241,38 @@ struct TRINITY_DLL_DECL npc_air_force_botsAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_air_force_bots(Creature* pCreature)
{
return new npc_air_force_botsAI(pCreature);
}
+
/*########
# npc_chicken_cluck
#########*/
+
#define EMOTE_HELLO -1070004
#define EMOTE_CLUCK_TEXT -1070006
+
#define QUEST_CLUCK 3861
#define FACTION_FRIENDLY 35
#define FACTION_CHICKEN 31
+
struct TRINITY_DLL_DECL npc_chicken_cluckAI : public ScriptedAI
{
npc_chicken_cluckAI(Creature *c) : ScriptedAI(c) {}
+
uint32 ResetFlagTimer;
+
void Reset()
{
ResetFlagTimer = 120000;
m_creature->setFaction(FACTION_CHICKEN);
m_creature->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_QUESTGIVER);
}
+
void EnterCombat(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
@@ -239,9 +284,11 @@ struct TRINITY_DLL_DECL npc_chicken_cluckAI : public ScriptedAI
return;
}else ResetFlagTimer -= diff;
}
+
if (UpdateVictim())
DoMeleeAttackIfReady();
}
+
void ReceiveEmote(Player* pPlayer, uint32 emote)
{
switch(emote)
@@ -265,33 +312,43 @@ struct TRINITY_DLL_DECL npc_chicken_cluckAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_chicken_cluck(Creature* pCreature)
{
return new npc_chicken_cluckAI(pCreature);
}
+
bool QuestAccept_npc_chicken_cluck(Player* pPlayer, Creature* pCreature, const Quest *_Quest)
{
if (_Quest->GetQuestId() == QUEST_CLUCK)
CAST_AI(npc_chicken_cluckAI, pCreature->AI())->Reset();
+
return true;
}
+
bool QuestComplete_npc_chicken_cluck(Player* pPlayer, Creature* pCreature, const Quest *_Quest)
{
if (_Quest->GetQuestId() == QUEST_CLUCK)
CAST_AI(npc_chicken_cluckAI, pCreature->AI())->Reset();
+
return true;
}
+
/*######
## npc_dancing_flames
######*/
+
#define SPELL_BRAZIER 45423
#define SPELL_SEDUCTION 47057
#define SPELL_FIERY_AURA 45427
+
struct TRINITY_DLL_DECL npc_dancing_flamesAI : public ScriptedAI
{
npc_dancing_flamesAI(Creature *c) : ScriptedAI(c) {}
+
bool active;
uint32 can_iteract;
+
void Reset()
{
active = true;
@@ -307,6 +364,7 @@ struct TRINITY_DLL_DECL npc_dancing_flamesAI : public ScriptedAI
m_creature->BuildHeartBeatMsg(&data);
m_creature->SendMessageToSet(&data,true);
}
+
void UpdateAI(const uint32 diff)
{
if (!active)
@@ -318,13 +376,16 @@ struct TRINITY_DLL_DECL npc_dancing_flamesAI : public ScriptedAI
}else can_iteract -= diff;
}
}
+
void EnterCombat(Unit* who){}
+
void ReceiveEmote(Player* pPlayer, uint32 emote)
{
if (m_creature->IsWithinLOS(pPlayer->GetPositionX(),pPlayer->GetPositionY(),pPlayer->GetPositionZ()) && m_creature->IsWithinDistInMap(pPlayer,30.0f))
{
m_creature->SetInFront(pPlayer);
active = false;
+
WorldPacket data;
m_creature->BuildHeartBeatMsg(&data);
m_creature->SendMessageToSet(&data,true);
@@ -344,25 +405,31 @@ struct TRINITY_DLL_DECL npc_dancing_flamesAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_dancing_flames(Creature* pCreature)
{
return new npc_dancing_flamesAI(pCreature);
}
+
/*######
## Triage quest
######*/
+
//signed for 9623
#define SAY_DOC1 -1000201
#define SAY_DOC2 -1000202
#define SAY_DOC3 -1000203
+
#define DOCTOR_ALLIANCE 12939
#define DOCTOR_HORDE 12920
#define ALLIANCE_COORDS 7
#define HORDE_COORDS 6
+
struct Location
{
float x, y, z, o;
};
+
static Location AllianceCoords[]=
{
{-3757.38, -4533.05, 14.16, 3.62}, // Top-far-right bunk as seen from entrance
@@ -373,10 +440,12 @@ static Location AllianceCoords[]=
{-3749.51, -4527.08, 14.07, 5.26}, // Mid-left bunk
{-3746.37, -4525.35, 14.16, 5.22}, // Left bunk near entrance
};
+
//alliance run to where
#define A_RUNTOX -3742.96
#define A_RUNTOY -4531.52
#define A_RUNTOZ 11.91
+
static Location HordeCoords[]=
{
{-1013.75, -3492.59, 62.62, 4.34}, // Left, Behind
@@ -386,73 +455,98 @@ static Location HordeCoords[]=
{-1017.25, -3500.85, 62.98, 4.34}, // Left, front
{-1020.95, -3499.21, 62.98, 4.34} // Right, Front
};
+
//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 TRINITY_DLL_DECL npc_doctorAI : public ScriptedAI
{
npc_doctorAI(Creature *c) : ScriptedAI(c) {}
+
uint64 PlayerGUID;
+
uint32 SummonPatient_Timer;
uint32 SummonPatientCount;
uint32 PatientDiedCount;
uint32 PatientSavedCount;
+
bool Event;
+
std::list<uint64> Patients;
std::vector<Location*> Coordinates;
+
void Reset()
{
PlayerGUID = 0;
+
SummonPatient_Timer = 10000;
SummonPatientCount = 0;
PatientDiedCount = 0;
PatientSavedCount = 0;
+
Patients.clear();
Coordinates.clear();
+
Event = false;
+
m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
}
+
void BeginEvent(Player* pPlayer);
void PatientDied(Location* Point);
void PatientSaved(Creature* soldier, Player* pPlayer, Location* Point);
void UpdateAI(const uint32 diff);
+
void EnterCombat(Unit* who){}
};
+
/*#####
## npc_injured_patient (handles all the patients, no matter Horde or Alliance)
#####*/
+
struct TRINITY_DLL_DECL npc_injured_patientAI : public ScriptedAI
{
npc_injured_patientAI(Creature *c) : ScriptedAI(c) {}
+
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, UNIT_STAND_STATE_DEAD);
+
uint32 mobId = m_creature->GetEntry();
+
switch (mobId)
{ //lower max health
case 12923:
@@ -469,7 +563,9 @@ struct TRINITY_DLL_DECL npc_injured_patientAI : public ScriptedAI
break;
}
}
+
void EnterCombat(Unit* who){}
+
void SpellHit(Unit *caster, const SpellEntry *spell)
{
if (caster->GetTypeId() == TYPEID_PLAYER && m_creature->isAlive() && spell->Id == 20804)
@@ -478,15 +574,21 @@ struct TRINITY_DLL_DECL npc_injured_patientAI : public ScriptedAI
if (Doctorguid)
if (Creature* Doctor = Unit::GetCreature(*m_creature, Doctorguid))
CAST_AI(npc_doctorAI, Doctor->AI())->PatientSaved(m_creature, CAST_PLR(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, UNIT_STAND_STATE_STAND);
+
DoScriptText(RAND(SAY_DOC1,SAY_DOC2,SAY_DOC3), m_creature);
+
uint32 mobId = m_creature->GetEntry();
m_creature->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
switch (mobId)
{
case 12923:
@@ -502,6 +604,7 @@ struct TRINITY_DLL_DECL npc_injured_patientAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
//lower HP on every world tick makes it a useful counter, not officlone though
@@ -509,12 +612,14 @@ struct TRINITY_DLL_DECL npc_injured_patientAI : public ScriptedAI
{
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)
{
if (Creature* Doctor = Unit::GetCreature((*m_creature), Doctorguid))
@@ -523,55 +628,66 @@ struct TRINITY_DLL_DECL npc_injured_patientAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_injured_patient(Creature* pCreature)
{
return new npc_injured_patientAI (pCreature);
}
+
/*
npc_doctor (continue)
*/
+
void npc_doctorAI::BeginEvent(Player* pPlayer)
{
PlayerGUID = pPlayer->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)
+ 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)
+ 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* pPlayer = Unit::GetPlayer(PlayerGUID);
if (pPlayer && ((pPlayer->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (pPlayer->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)))
{
++PatientDiedCount;
+
if (PatientDiedCount > 5 && Event)
{
if (pPlayer->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE)
pPlayer->FailQuest(6624);
else if (pPlayer->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)
pPlayer->FailQuest(6622);
+
Reset();
return;
}
+
Coordinates.push_back(Point);
}
else
// If no player or player abandon quest in progress
Reset();
}
+
void npc_doctorAI::PatientSaved(Creature* soldier, Player* pPlayer, Location* Point)
{
if (pPlayer && PlayerGUID == pPlayer->GetGUID())
@@ -579,28 +695,33 @@ void npc_doctorAI::PatientSaved(Creature* soldier, Player* pPlayer, Location* Po
if ((pPlayer->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE) || (pPlayer->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE))
{
++PatientSavedCount;
+
if (PatientSavedCount == 15)
{
if (!Patients.empty())
{
std::list<uint64>::iterator itr;
- for (itr = Patients.begin(); itr != Patients.end(); ++itr)
+ for(itr = Patients.begin(); itr != Patients.end(); ++itr)
{
if (Creature* Patient = Unit::GetCreature((*m_creature), *itr))
Patient->setDeathState(JUST_DIED);
}
}
+
if (pPlayer->GetQuestStatus(6624) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(6624);
else if (pPlayer->GetQuestStatus(6622) == QUEST_STATUS_INCOMPLETE)
pPlayer->AreaExploredOrEventHappens(6622);
+
Reset();
return;
}
+
Coordinates.push_back(Point);
}
}
}
+
void npc_doctorAI::UpdateAI(const uint32 diff)
{
if (Event && SummonPatientCount >= 20)
@@ -608,16 +729,20 @@ void npc_doctorAI::UpdateAI(const uint32 diff)
Reset();
return;
}
+
if (Event)
{
if (SummonPatient_Timer < diff)
{
Creature* Patient = NULL;
Location* Point = NULL;
+
if (Coordinates.empty())
return;
+
std::vector<Location*>::iterator itr = Coordinates.begin()+rand()%Coordinates.size();
uint32 patientEntry = 0;
+
switch(m_creature->GetEntry())
{
case DOCTOR_ALLIANCE: patientEntry = AllianceSoldierId[rand()%3]; break;
@@ -626,16 +751,22 @@ void npc_doctorAI::UpdateAI(const uint32 diff)
error_log("TSCR: 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)
{
//303, this flag appear to be required for client side item->spell to work (TARGET_SINGLE_FRIEND)
Patient->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+
Patients.push_back(Patient->GetGUID());
CAST_AI(npc_injured_patientAI, Patient->AI())->Doctorguid = m_creature->GetGUID();
+
if (Point)
CAST_AI(npc_injured_patientAI, Patient->AI())->Coord = Point;
+
Coordinates.erase(itr);
}
SummonPatient_Timer = 10000;
@@ -643,34 +774,43 @@ void npc_doctorAI::UpdateAI(const uint32 diff)
}else SummonPatient_Timer -= diff;
}
}
+
bool QuestAccept_npc_doctor(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if ((quest->GetQuestId() == 6624) || (quest->GetQuestId() == 6622))
CAST_AI(npc_doctorAI, pCreature->AI())->BeginEvent(pPlayer);
+
return true;
}
+
CreatureAI* GetAI_npc_doctor(Creature* pCreature)
{
return new npc_doctorAI (pCreature);
}
+
/*######
## npc_garments_of_quests
######*/
+
//TODO: get text for each NPC
+
enum eGarments
{
SPELL_LESSER_HEAL_R2 = 2052,
SPELL_FORTITUDE_R1 = 1243,
+
QUEST_MOON = 5621,
QUEST_LIGHT_1 = 5624,
QUEST_LIGHT_2 = 5625,
QUEST_SPIRIT = 5648,
QUEST_DARKNESS = 5650,
+
ENTRY_SHAYA = 12429,
ENTRY_ROBERTS = 12423,
ENTRY_DOLF = 12427,
ENTRY_KORJA = 12430,
ENTRY_DG_KEL = 12428,
+
//used by 12429,12423,12427,12430,12428, but signed for 12429
SAY_COMMON_HEALED = -1000164,
SAY_DG_KEL_THANKS = -1000165,
@@ -684,24 +824,34 @@ enum eGarments
SAY_SHAYA_THANKS = -1000173,
SAY_SHAYA_GOODBYE = -1000174, //signed for 21469
};
+
struct TRINITY_DLL_DECL npc_garments_of_questsAI : public npc_escortAI
{
npc_garments_of_questsAI(Creature *c) : npc_escortAI(c) {Reset();}
+
uint64 caster;
+
bool bIsHealed;
bool bCanRun;
+
uint32 RunAwayTimer;
+
void Reset()
{
caster = 0;
+
bIsHealed = false;
bCanRun = false;
+
RunAwayTimer = 5000;
+
m_creature->SetStandState(UNIT_STAND_STATE_KNEEL);
//expect database to have RegenHealth=0
m_creature->SetHealth(int(m_creature->GetMaxHealth()*0.7));
}
+
void EnterCombat(Unit *who) {}
+
void SpellHit(Unit* pCaster, const SpellEntry *Spell)
{
if (Spell->Id == SPELL_LESSER_HEAL_R2 || Spell->Id == SPELL_FORTITUDE_R1)
@@ -709,9 +859,11 @@ struct TRINITY_DLL_DECL npc_garments_of_questsAI : public npc_escortAI
//not while in combat
if (m_creature->isInCombat())
return;
+
//nothing to be done now
if (bIsHealed && bCanRun)
return;
+
if (pCaster->GetTypeId() == TYPEID_PLAYER)
{
switch(m_creature->GetEntry())
@@ -802,15 +954,18 @@ struct TRINITY_DLL_DECL npc_garments_of_questsAI : public npc_escortAI
}
break;
}
+
//give quest credit, not expect any special quest objectives
if (bCanRun)
CAST_PLR(pCaster)->TalkedToCreature(m_creature->GetEntry(),m_creature->GetGUID());
}
}
}
+
void WaypointReached(uint32 uiPoint)
{
}
+
void UpdateAI(const uint32 diff)
{
if (bCanRun && !m_creature->isInCombat())
@@ -827,38 +982,49 @@ struct TRINITY_DLL_DECL npc_garments_of_questsAI : public npc_escortAI
case ENTRY_KORJA: DoScriptText(SAY_KORJA_GOODBYE,m_creature,pUnit); break;
case ENTRY_DG_KEL: DoScriptText(SAY_DG_KEL_GOODBYE,m_creature,pUnit); break;
}
+
Start(false,true,true);
}
else
EnterEvadeMode(); //something went wrong
+
RunAwayTimer = 30000;
}else RunAwayTimer -= diff;
}
+
npc_escortAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_garments_of_quests(Creature* pCreature)
{
return new npc_garments_of_questsAI(pCreature);
}
+
/*######
## npc_guardian
######*/
+
#define SPELL_DEATHTOUCH 5
+
struct TRINITY_DLL_DECL npc_guardianAI : public ScriptedAI
{
npc_guardianAI(Creature *c) : ScriptedAI(c) {}
+
void Reset()
{
m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
}
+
void EnterCombat(Unit *who)
{
}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
return;
+
if (m_creature->isAttackReady())
{
m_creature->CastSpell(m_creature->getVictim(),SPELL_DEATHTOUCH, true);
@@ -866,13 +1032,16 @@ struct TRINITY_DLL_DECL npc_guardianAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_npc_guardian(Creature* pCreature)
{
return new npc_guardianAI (pCreature);
}
+
/*######
## npc_kingdom_of_dalaran_quests
######*/
+
enum eKingdomDalaran
{
SPELL_TELEPORT_DALARAN = 53360,
@@ -881,17 +1050,22 @@ enum eKingdomDalaran
QUEST_MAGICAL_KINGDOM_H = 12791,
QUEST_MAGICAL_KINGDOM_N = 12796
};
+
#define GOSSIP_ITEM_TELEPORT_TO "I am ready to be teleported to Dalaran."
+
bool GossipHello_npc_kingdom_of_dalaran_quests(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->HasItemCount(ITEM_KT_SIGNET,1) && (!pPlayer->GetQuestRewardStatus(QUEST_MAGICAL_KINGDOM_A) ||
!pPlayer->GetQuestRewardStatus(QUEST_MAGICAL_KINGDOM_H) || !pPlayer->GetQuestRewardStatus(QUEST_MAGICAL_KINGDOM_N)))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_ITEM_TELEPORT_TO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
+
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
return true;
}
+
bool GossipSelect_npc_kingdom_of_dalaran_quests(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_INFO_DEF+1)
@@ -901,17 +1075,21 @@ bool GossipSelect_npc_kingdom_of_dalaran_quests(Player* pPlayer, Creature* pCrea
}
return true;
}
+
/*######
## npc_mount_vendor
######*/
+
bool GossipHello_npc_mount_vendor(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
bool canBuy;
canBuy = false;
uint32 vendor = pCreature->GetEntry();
uint8 race = pPlayer->getRace();
+
switch (vendor)
{
case 384: //Katie Hunter
@@ -968,6 +1146,7 @@ bool GossipHello_npc_mount_vendor(Player* pPlayer, Creature* pCreature)
else canBuy = true;
break;
}
+
if (canBuy)
{
if (pCreature->isVendor())
@@ -976,33 +1155,43 @@ bool GossipHello_npc_mount_vendor(Player* pPlayer, Creature* pCreature)
}
return true;
}
+
bool GossipSelect_npc_mount_vendor(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
if (uiAction == GOSSIP_ACTION_TRADE)
pPlayer->SEND_VENDORLIST(pCreature->GetGUID());
+
return true;
}
+
/*######
## npc_rogue_trainer
######*/
+
#define GOSSIP_HELLO_ROGUE1 "I wish to unlearn my talents"
#define GOSSIP_HELLO_ROGUE2 "<Take the letter>"
+
bool GossipHello_npc_rogue_trainer(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pCreature->isTrainer())
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_TEXT_TRAIN, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_TRAIN);
+
if (pCreature->isCanTrainingAndResetTalentsOf(pPlayer))
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_TRAINER, GOSSIP_HELLO_ROGUE1, GOSSIP_SENDER_MAIN, GOSSIP_OPTION_UNLEARNTALENTS);
+
if (pPlayer->getClass() == CLASS_ROGUE && pPlayer->getLevel() >= 24 && !pPlayer->HasItemCount(17126,1) && !pPlayer->GetQuestRewardStatus(6681))
{
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_ROGUE2, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(5996, pCreature->GetGUID());
} else
pPlayer->SEND_GOSSIP_MENU(pCreature->GetNpcTextId(), pCreature->GetGUID());
+
return true;
}
+
bool GossipSelect_npc_rogue_trainer(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiAction)
@@ -1021,9 +1210,11 @@ bool GossipSelect_npc_rogue_trainer(Player* pPlayer, Creature* pCreature, uint32
}
return true;
}
+
/*######
## npc_sayge
######*/
+
#define SPELL_DMG 23768 //dmg
#define SPELL_RES 23769 //res
#define SPELL_ARM 23767 //arm
@@ -1033,6 +1224,7 @@ bool GossipSelect_npc_rogue_trainer(Player* pPlayer, Creature* pCreature, uint32
#define SPELL_STR 23735 //str
#define SPELL_AGI 23736 //agi
#define SPELL_FORTUNE 23765 //faire fortune
+
#define GOSSIP_HELLO_SAYGE "Yes"
#define GOSSIP_SENDACTION_SAYGE1 "Slay the Man"
#define GOSSIP_SENDACTION_SAYGE2 "Turn him over to liege"
@@ -1051,10 +1243,12 @@ bool GossipSelect_npc_rogue_trainer(Player* pPlayer, Creature* pCreature, uint32
#define GOSSIP_SENDACTION_SAYGE15 "Take credit, share the gold"
#define GOSSIP_SENDACTION_SAYGE16 "Let the knight take credit"
#define GOSSIP_SENDACTION_SAYGE17 "Thanks"
+
bool GossipHello_npc_sayge(Player* pPlayer, Creature* pCreature)
{
if (pCreature->isQuestGiver())
pPlayer->PrepareQuestMenu(pCreature->GetGUID());
+
if (pPlayer->HasSpellCooldown(SPELL_INT) ||
pPlayer->HasSpellCooldown(SPELL_ARM) ||
pPlayer->HasSpellCooldown(SPELL_DMG) ||
@@ -1069,8 +1263,10 @@ bool GossipHello_npc_sayge(Player* pPlayer, Creature* pCreature)
pPlayer->ADD_GOSSIP_ITEM(GOSSIP_ICON_CHAT, GOSSIP_HELLO_SAYGE, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF+1);
pPlayer->SEND_GOSSIP_MENU(7339, pCreature->GetGUID());
}
+
return true;
}
+
void SendAction_npc_sayge(Player* pPlayer, Creature* pCreature, uint32 uiAction)
{
switch(uiAction)
@@ -1116,6 +1312,7 @@ void SendAction_npc_sayge(Player* pPlayer, Creature* pCreature, uint32 uiAction)
break;
}
}
+
bool GossipSelect_npc_sayge(Player* pPlayer, Creature* pCreature, uint32 uiSender, uint32 uiAction)
{
switch(uiSender)
@@ -1166,11 +1363,14 @@ bool GossipSelect_npc_sayge(Player* pPlayer, Creature* pCreature, uint32 uiSende
}
return true;
}
+
struct TRINITY_DLL_DECL npc_steam_tonkAI : public ScriptedAI
{
npc_steam_tonkAI(Creature *c) : ScriptedAI(c) {}
+
void Reset() {}
void EnterCombat(Unit *who) {}
+
void OnPossess(bool apply)
{
if (apply)
@@ -1178,31 +1378,40 @@ struct TRINITY_DLL_DECL npc_steam_tonkAI : public ScriptedAI
// Initialize the action bar without the melee attack command
m_creature->InitCharmInfo();
m_creature->GetCharmInfo()->InitEmptyActionBar(false);
+
m_creature->SetReactState(REACT_PASSIVE);
}
else
m_creature->SetReactState(REACT_AGGRESSIVE);
}
+
};
+
CreatureAI* GetAI_npc_steam_tonk(Creature* pCreature)
{
return new npc_steam_tonkAI(pCreature);
}
+
#define SPELL_TONK_MINE_DETONATE 25099
+
struct TRINITY_DLL_DECL npc_tonk_mineAI : public ScriptedAI
{
npc_tonk_mineAI(Creature *c) : ScriptedAI(c)
{
m_creature->SetReactState(REACT_PASSIVE);
}
+
uint32 ExplosionTimer;
+
void Reset()
{
ExplosionTimer = 3000;
}
+
void EnterCombat(Unit *who) {}
void AttackStart(Unit *who) {}
void MoveInLineOfSight(Unit *who) {}
+
void UpdateAI(const uint32 diff)
{
if (ExplosionTimer < diff)
@@ -1213,18 +1422,22 @@ struct TRINITY_DLL_DECL npc_tonk_mineAI : public ScriptedAI
ExplosionTimer -= diff;
}
};
+
CreatureAI* GetAI_npc_tonk_mine(Creature* pCreature)
{
return new npc_tonk_mineAI(pCreature);
}
+
/*####
## npc_winter_reveler
####*/
+
bool ReceiveEmote_npc_winter_reveler(Player* pPlayer, Creature* pCreature, uint32 emote)
{
//TODO: check auralist.
if (pPlayer->HasAura(26218))
return false;
+
if (emote == TEXTEMOTE_KISS)
{
pCreature->CastSpell(pCreature, 26218, false);
@@ -1238,51 +1451,68 @@ bool ReceiveEmote_npc_winter_reveler(Player* pPlayer, Creature* pCreature, uint3
}
return true;
}
+
/*####
## npc_brewfest_reveler
####*/
+
bool ReceiveEmote_npc_brewfest_reveler(Player* pPlayer, Creature* pCreature, uint32 emote)
{
if (emote == TEXTEMOTE_DANCE)
pCreature->CastSpell(pPlayer, 41586, false);
+
return true;
}
+
/*####
## npc_snake_trap_serpents
####*/
+
#define SPELL_MIND_NUMBING_POISON 8692 //Viper
#define SPELL_DEADLY_POISON 34655 //Venomous Snake
#define SPELL_CRIPPLING_POISON 3409 //Viper
+
#define VENOMOUS_SNAKE_TIMER 1200
#define VIPER_TIMER 3000
+
#define C_VIPER 19921
+
#define RAND 5
+
struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
{
npc_snake_trap_serpentsAI(Creature *c) : ScriptedAI(c) {}
+
uint32 SpellTimer;
bool IsViper;
bool Spawn;
+
void EnterCombat(Unit *who) {}
+
void Reset()
{
Spawn = true;
SpellTimer = 0;
+
CreatureInfo const *Info = m_creature->GetCreatureInfo();
+
if (Info->Entry == C_VIPER)
IsViper = true;
else
IsViper = false;
+
//We have to reload the states from db for summoned guardians
m_creature->SetMaxHealth(Info->maxhealth);
m_creature->SetHealth(Info->maxhealth);
m_creature->SetStatFloatValue(UNIT_FIELD_MINDAMAGE, Info->mindmg);
m_creature->SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, Info->maxdmg);
+
//Add delta to make them not all hit the same time
uint32 delta = (rand() % 7) *100;
m_creature->SetStatFloatValue(UNIT_FIELD_BASEATTACKTIME, Info->baseattacktime + delta);
m_creature->SetStatFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER , Info->attackpower);
}
+
//Redefined for random target selection:
void MoveInLineOfSight(Unit *who)
{
@@ -1290,6 +1520,7 @@ struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
{
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))
{
@@ -1302,6 +1533,7 @@ struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
}
}
}
+
void UpdateAI(const uint32 diff)
{
if (Spawn)
@@ -1313,12 +1545,14 @@ struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
if (Owner->getAttackerForHelper())
AttackStart(Owner->getAttackerForHelper());
}
+
if (!m_creature->getVictim())
{
if (m_creature->isInCombat())
DoStopAttack();
return;
}
+
if (SpellTimer < diff)
{
if (IsViper) //Viper
@@ -1330,8 +1564,10 @@ struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
spell = SPELL_MIND_NUMBING_POISON;
else
spell = SPELL_CRIPPLING_POISON;
+
DoCast(m_creature->getVictim(),spell);
}
+
SpellTimer = VIPER_TIMER;
}
else //Venomous Snake
@@ -1344,10 +1580,12 @@ struct TRINITY_DLL_DECL npc_snake_trap_serpentsAI : public ScriptedAI
DoMeleeAttackIfReady();
}
};
+
CreatureAI* GetAI_npc_snake_trap_serpents(Creature* pCreature)
{
return new npc_snake_trap_serpentsAI(pCreature);
}
+
#define SAY_RANDOM_MOJO0 "Now that's what I call froggy-style!"
#define SAY_RANDOM_MOJO1 "Your lily pad or mine?"
#define SAY_RANDOM_MOJO2 "This won't take long, did it?"
@@ -1357,6 +1595,7 @@ CreatureAI* GetAI_npc_snake_trap_serpents(Creature* pCreature)
#define SAY_RANDOM_MOJO6a "Listen, "
#define SAY_RANDOM_MOJO6b ", I know of a little swamp not too far from here...."
#define SAY_RANDOM_MOJO7 "There's just never enough Mojo to go around..."
+
struct TRINITY_DLL_DECL mob_mojoAI : public ScriptedAI
{
mob_mojoAI(Creature *c) : ScriptedAI(c) {Reset();}
@@ -1421,13 +1660,16 @@ struct TRINITY_DLL_DECL mob_mojoAI : public ScriptedAI
}
}
};
+
CreatureAI* GetAI_mob_mojo(Creature* pCreature)
{
return new mob_mojoAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_mirror_image : CasterAI
{
npc_mirror_image(Creature *c) : CasterAI(c) {}
+
void InitializeAI()
{
CasterAI::InitializeAI();
@@ -1441,12 +1683,15 @@ struct TRINITY_DLL_DECL npc_mirror_image : CasterAI
// Clone Me!
owner->CastSpell(me, 45204, false);
}
+
// Do not reload Creature templates on evade mode enter - prevent visual lost
void EnterEvadeMode()
{
if (me->IsInEvadeMode() || !me->isAlive())
return;
+
Unit *owner = me->GetCharmerOrOwner();
+
me->CombatStop(true);
if (owner && !me->hasUnitState(UNIT_STAT_FOLLOW))
{
@@ -1455,14 +1700,18 @@ struct TRINITY_DLL_DECL npc_mirror_image : CasterAI
}
}
};
+
CreatureAI* GetAI_npc_mirror_image(Creature* pCreature)
{
return new npc_mirror_image (pCreature);
}
+
struct TRINITY_DLL_DECL npc_ebon_gargoyleAI : CasterAI
{
npc_ebon_gargoyleAI(Creature *c) : CasterAI(c) {}
+
int despawnTimer;
+
void InitializeAI()
{
CasterAI::InitializeAI();
@@ -1476,32 +1725,38 @@ struct TRINITY_DLL_DECL npc_ebon_gargoyleAI : CasterAI
Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_creature, m_creature, 30);
Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_creature, targets, u_check);
m_creature->VisitNearbyObject(30, searcher);
- for (std::list<Unit*>::iterator iter = targets.begin(); iter != targets.end(); ++iter)
+ for(std::list<Unit*>::iterator iter = targets.begin(); iter != targets.end(); ++iter)
if((*iter)->GetAura(49206,owner->GetGUID()))
{
me->Attack((*iter),false);
break;
}
}
+
void JustDied(Unit *killer)
{
// Stop Feeding Gargoyle when it dies
if (Unit *owner = me->GetOwner())
owner->RemoveAurasDueToSpell(50514);
}
+
// Fly away when dismissed
void SpellHit(Unit *source, const SpellEntry *spell)
{
if(spell->Id != 50515 || !me->isAlive() )
return;
+
Unit *owner = me->GetOwner();
+
if (!owner || owner != source)
return;
+
// Stop Fighting
me->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE, true);
// Sanctuary
me->CastSpell(m_creature, 54661, true);
me->SetReactState(REACT_PASSIVE);
+
// Fly Away
me->AddUnitMovementFlag(MOVEMENTFLAG_FLY_MODE);
me->SetSpeed(MOVE_FLIGHT, 0.25f, true);
@@ -1510,9 +1765,11 @@ struct TRINITY_DLL_DECL npc_ebon_gargoyleAI : CasterAI
float y = me->GetPositionY() + 10 * sin(me->GetOrientation());
float z = me->GetPositionZ() + 25;
me->GetMotionMaster()->MovePoint(0, x, y, z);
+
// Despawn as soon as possible
despawnTimer = 4 * IN_MILISECONDS;
}
+
void UpdateAI(const uint32 diff)
{
if (despawnTimer > 0)
@@ -1528,25 +1785,31 @@ struct TRINITY_DLL_DECL npc_ebon_gargoyleAI : CasterAI
CasterAI::UpdateAI(diff);
}
};
+
CreatureAI* GetAI_npc_ebon_gargoyle(Creature* pCreature)
{
return new npc_ebon_gargoyleAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_lightwellAI : public PassiveAI
{
npc_lightwellAI(Creature *c) : PassiveAI(c) {}
+
void Reset()
{
m_creature->CastSpell(m_creature, 59907, false); // Spell for Lightwell Charges
}
};
+
CreatureAI* GetAI_npc_lightwellAI(Creature* pCreature)
{
return new npc_lightwellAI (pCreature);
}
+
struct TRINITY_DLL_DECL npc_training_dummy : Scripted_NoMovementAI
{
npc_training_dummy(Creature *c) : Scripted_NoMovementAI(c) {}
+
uint32 ResetTimer;
void Reset()
{
@@ -1555,12 +1818,15 @@ struct TRINITY_DLL_DECL npc_training_dummy : Scripted_NoMovementAI
m_creature->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_STUN, true);
ResetTimer = 10000;
}
+
void DamageTaken(Unit *done_by, uint32 &damage)
{
ResetTimer = 10000;
damage = 0;
}
+
void EnterCombat(Unit *who){return;}
+
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
@@ -1576,100 +1842,123 @@ struct TRINITY_DLL_DECL npc_training_dummy : Scripted_NoMovementAI
}
void MoveInLineOfSight(Unit *who){return;}
};
+
CreatureAI* GetAI_npc_training_dummy(Creature* pCreature)
{
return new npc_training_dummy (pCreature);
}
+
void AddSC_npcs_special()
{
Script *newscript;
+
newscript = new Script;
newscript->Name = "npc_air_force_bots";
newscript->GetAI = &GetAI_npc_air_force_bots;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_chicken_cluck";
newscript->GetAI = &GetAI_npc_chicken_cluck;
newscript->pQuestAccept = &QuestAccept_npc_chicken_cluck;
newscript->pQuestComplete = &QuestComplete_npc_chicken_cluck;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_dancing_flames";
newscript->GetAI = &GetAI_npc_dancing_flames;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_injured_patient";
newscript->GetAI = &GetAI_npc_injured_patient;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_doctor";
newscript->GetAI = &GetAI_npc_doctor;
newscript->pQuestAccept = &QuestAccept_npc_doctor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_garments_of_quests";
newscript->GetAI = &GetAI_npc_garments_of_quests;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_guardian";
newscript->GetAI = &GetAI_npc_guardian;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_kingdom_of_dalaran_quests";
newscript->pGossipHello = &GossipHello_npc_kingdom_of_dalaran_quests;
newscript->pGossipSelect = &GossipSelect_npc_kingdom_of_dalaran_quests;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_mount_vendor";
newscript->pGossipHello = &GossipHello_npc_mount_vendor;
newscript->pGossipSelect = &GossipSelect_npc_mount_vendor;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_rogue_trainer";
newscript->pGossipHello = &GossipHello_npc_rogue_trainer;
newscript->pGossipSelect = &GossipSelect_npc_rogue_trainer;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_sayge";
newscript->pGossipHello = &GossipHello_npc_sayge;
newscript->pGossipSelect = &GossipSelect_npc_sayge;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_steam_tonk";
newscript->GetAI = &GetAI_npc_steam_tonk;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_tonk_mine";
newscript->GetAI = &GetAI_npc_tonk_mine;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_winter_reveler";
//newscript->pReceiveEmote = &ReceiveEmote_npc_winter_reveler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_brewfest_reveler";
//newscript->pReceiveEmote = &ReceiveEmote_npc_brewfest_reveler;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_snake_trap_serpents";
newscript->GetAI = &GetAI_npc_snake_trap_serpents;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_mirror_image";
newscript->GetAI = &GetAI_npc_mirror_image;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_ebon_gargoyle";
newscript->GetAI = &GetAI_npc_ebon_gargoyle;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_lightwell";
newscript->GetAI = &GetAI_npc_lightwellAI;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "mob_mojo";
newscript->GetAI = &GetAI_mob_mojo;
newscript->RegisterSelf();
+
newscript = new Script;
newscript->Name = "npc_training_dummy";
newscript->GetAI = &GetAI_npc_training_dummy;
diff --git a/src/bindings/scripts/system/ScriptLoader.cpp b/src/bindings/scripts/system/ScriptLoader.cpp
index c19015181c5..aa36753cf84 100644
--- a/src/bindings/scripts/system/ScriptLoader.cpp
+++ b/src/bindings/scripts/system/ScriptLoader.cpp
@@ -1,13 +1,17 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#include "precompiled.h"
+
//custom
+
//examples
extern void AddSC_example_creature();
extern void AddSC_example_escort();
extern void AddSC_example_gossip_codebox();
extern void AddSC_example_misc();
+
//world
extern void AddSC_areatrigger_scripts();
extern void AddSC_boss_emeriss();
@@ -20,6 +24,7 @@ extern void AddSC_item_scripts();
extern void AddSC_npc_professions();
extern void AddSC_npcs_special();
extern void AddSC_npc_taxi();
+
//eastern kingdoms
extern void AddSC_blackrock_depths(); //Blackrock Depths
extern void AddSC_boss_ambassador_flamelash();
@@ -164,6 +169,7 @@ extern void AddSC_boss_hazzarah();
extern void AddSC_boss_renataki();
extern void AddSC_boss_wushoolay();
extern void AddSC_instance_zulgurub();
+
//extern void AddSC_alterac_mountains();
extern void AddSC_arathi_highlands();
extern void AddSC_blasted_lands();
@@ -189,6 +195,7 @@ extern void AddSC_undercity();
extern void AddSC_western_plaguelands();
extern void AddSC_westfall();
extern void AddSC_wetlands();
+
//kalimdor
extern void AddSC_instance_blackfathom_deeps(); //Blackfathom Depths
extern void AddSC_hyjal(); //CoT Battle for Mt. Hyjal
@@ -229,6 +236,7 @@ extern void AddSC_instance_temple_of_ahnqiraj();
extern void AddSC_wailing_caverns(); //Wailing caverns
extern void AddSC_instance_wailing_caverns();
extern void AddSC_zulfarrak(); //Zul'Farrak
+
extern void AddSC_ashenvale();
extern void AddSC_azshara();
extern void AddSC_azuremyst_isle();
@@ -251,6 +259,7 @@ extern void AddSC_thousand_needles();
extern void AddSC_thunder_bluff();
extern void AddSC_ungoro_crater();
extern void AddSC_winterspring();
+
//northrend
extern void AddSC_boss_slad_ran();
extern void AddSC_boss_moorabi();
@@ -330,6 +339,7 @@ extern void AddSC_boss_xevozz();
extern void AddSC_boss_zuramat();
extern void AddSC_instance_violet_hold();
extern void AddSC_violet_hold();
+
extern void AddSC_borean_tundra();
extern void AddSC_dragonblight();
extern void AddSC_grizzly_hills();
@@ -339,6 +349,7 @@ extern void AddSC_sholazar_basin();
extern void AddSC_storm_peaks();
extern void AddSC_wintergrasp();
extern void AddSC_zuldrak();
+
//outland
extern void AddSC_boss_exarch_maladaar(); //Auchindoun Auchenai Crypts
extern void AddSC_boss_shirrak_the_dead_watcher();
@@ -409,6 +420,7 @@ extern void AddSC_boss_gatewatcher_iron_hand(); //TK The Mechanar
extern void AddSC_boss_nethermancer_sepethrea();
extern void AddSC_boss_pathaleon_the_calculator();
extern void AddSC_instance_mechanar();
+
extern void AddSC_blades_edge_mountains();
extern void AddSC_boss_doomlordkazzak();
extern void AddSC_boss_doomwalker();
@@ -420,14 +432,17 @@ extern void AddSC_shattrath_city();
extern void AddSC_terokkar_forest();
extern void AddSC_zangarmarsh();
extern void AddSC_onevents();
+
void AddScripts()
{
//custom
+
//examples
AddSC_example_creature();
AddSC_example_escort();
AddSC_example_gossip_codebox();
AddSC_example_misc();
+
//world
AddSC_areatrigger_scripts();
AddSC_boss_emeriss();
@@ -440,6 +455,7 @@ void AddScripts()
AddSC_npc_professions();
AddSC_npcs_special();
AddSC_npc_taxi();
+
//eastern kingdoms
AddSC_blackrock_depths(); //Blackrock Depths
AddSC_boss_ambassador_flamelash();
@@ -584,6 +600,7 @@ void AddScripts()
AddSC_boss_renataki();
AddSC_boss_wushoolay();
AddSC_instance_zulgurub();
+
//AddSC_alterac_mountains();
AddSC_arathi_highlands();
AddSC_blasted_lands();
@@ -609,6 +626,7 @@ void AddScripts()
AddSC_western_plaguelands();
AddSC_westfall();
AddSC_wetlands();
+
//kalimdor
AddSC_instance_blackfathom_deeps(); //Blackfathom Depths
AddSC_hyjal(); //CoT Battle for Mt. Hyjal
@@ -649,6 +667,7 @@ void AddScripts()
AddSC_wailing_caverns(); //Wailing caverns
AddSC_instance_wailing_caverns();
AddSC_zulfarrak(); //Zul'Farrak
+
AddSC_ashenvale();
AddSC_azshara();
AddSC_azuremyst_isle();
@@ -671,6 +690,7 @@ void AddScripts()
AddSC_thunder_bluff();
AddSC_ungoro_crater();
AddSC_winterspring();
+
//northrend
AddSC_boss_slad_ran(); //Gundrak
AddSC_boss_moorabi();
@@ -750,6 +770,7 @@ void AddScripts()
AddSC_boss_zuramat();
AddSC_instance_violet_hold();
AddSC_violet_hold();
+
AddSC_borean_tundra();
AddSC_dragonblight();
AddSC_grizzly_hills();
@@ -759,6 +780,7 @@ void AddScripts()
AddSC_storm_peaks();
AddSC_wintergrasp();
AddSC_zuldrak();
+
//outland
AddSC_boss_exarch_maladaar(); //Auchindoun Auchenai Crypts
AddSC_boss_shirrak_the_dead_watcher();
@@ -829,6 +851,7 @@ void AddScripts()
AddSC_boss_nethermancer_sepethrea();
AddSC_boss_pathaleon_the_calculator();
AddSC_instance_mechanar();
+
AddSC_blades_edge_mountains();
AddSC_boss_doomlordkazzak();
AddSC_boss_doomwalker();
diff --git a/src/bindings/scripts/system/ScriptLoader.h b/src/bindings/scripts/system/ScriptLoader.h
index ad992e5cf12..57fb7d821f1 100644
--- a/src/bindings/scripts/system/ScriptLoader.h
+++ b/src/bindings/scripts/system/ScriptLoader.h
@@ -1,7 +1,10 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_SCRIPTLOADER_H
#define SC_SCRIPTLOADER_H
+
void AddScripts();
+
#endif
diff --git a/src/bindings/scripts/system/system.cpp b/src/bindings/scripts/system/system.cpp
index 1518f5bb29e..4ed51825262 100644
--- a/src/bindings/scripts/system/system.cpp
+++ b/src/bindings/scripts/system/system.cpp
@@ -17,27 +17,34 @@
* 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"
#include "system.h"
#include "ProgressBar.h"
#include "ObjectMgr.h"
#include "Database/DatabaseEnv.h"
+
DatabaseType TScriptDB;
+
SystemMgr::SystemMgr()
{
}
+
SystemMgr& SystemMgr::Instance()
{
static SystemMgr pSysMgr;
return pSysMgr;
}
+
void SystemMgr::LoadVersion()
{
//Get Version information
QueryResult* pResult = TScriptDB.PQuery("SELECT script_version FROM version LIMIT 1");
+
if (pResult)
{
Field* pFields = pResult->Fetch();
+
outstring_log("TSCR: Database version is: %s", pFields[0].GetString());
outstring_log("");
}
@@ -47,48 +54,61 @@ void SystemMgr::LoadVersion()
outstring_log("");
}
}
+
void SystemMgr::LoadScriptTexts()
{
outstring_log("TSCR: Loading Script Texts...");
LoadTrinityStrings(TScriptDB,"script_texts",TEXT_SOURCE_RANGE,1+(TEXT_SOURCE_RANGE*2));
+
QueryResult* pResult = TScriptDB.PQuery("SELECT entry, sound, type, language, emote FROM script_texts");
+
outstring_log("TSCR: Loading Script Texts additional data...");
+
if (pResult)
{
barGoLink bar(pResult->GetRowCount());
uint32 uiCount = 0;
+
do
{
bar.step();
Field* pFields = pResult->Fetch();
StringTextData pTemp;
+
int32 iId = pFields[0].GetInt32();
pTemp.uiSoundId = pFields[1].GetUInt32();
pTemp.uiType = pFields[2].GetUInt32();
pTemp.uiLanguage = pFields[3].GetUInt32();
pTemp.uiEmote = pFields[4].GetUInt32();
+
if (iId >= 0)
{
error_db_log("TSCR: Entry %i in table `script_texts` is not a negative value.", iId);
continue;
}
+
if (iId > TEXT_SOURCE_RANGE || iId <= TEXT_SOURCE_RANGE*2)
{
error_db_log("TSCR: Entry %i in table `script_texts` is out of accepted entry range for table.", iId);
continue;
}
+
if (pTemp.uiSoundId)
{
if (!GetSoundEntriesStore()->LookupEntry(pTemp.uiSoundId))
error_db_log("TSCR: Entry %i in table `script_texts` has soundId %u but sound does not exist.", iId, pTemp.uiSoundId);
}
+
if (!GetLanguageDescByID(pTemp.uiLanguage))
error_db_log("TSCR: Entry %i in table `script_texts` using Language %u but Language does not exist.", iId, pTemp.uiLanguage);
+
if (pTemp.uiType > CHAT_TYPE_ZONE_YELL)
error_db_log("TSCR: Entry %i in table `script_texts` has Type %u but this Chat Type does not exist.", iId, pTemp.uiType);
+
m_mTextDataMap[iId] = pTemp;
++uiCount;
} while (pResult->NextRow());
+
outstring_log("");
outstring_log(">> Loaded %u additional Script Texts data.", uiCount);
}
@@ -100,48 +120,61 @@ void SystemMgr::LoadScriptTexts()
outstring_log(">> Loaded 0 additional Script Texts data. DB table `script_texts` is empty.");
}
}
+
void SystemMgr::LoadScriptTextsCustom()
{
outstring_log("TSCR: Loading Custom Texts...");
LoadTrinityStrings(TScriptDB,"custom_texts",TEXT_SOURCE_RANGE*2,1+(TEXT_SOURCE_RANGE*3));
+
QueryResult* pResult = TScriptDB.PQuery("SELECT entry, sound, type, language, emote FROM custom_texts");
+
outstring_log("TSCR: Loading Custom Texts additional data...");
+
if (pResult)
{
barGoLink bar(pResult->GetRowCount());
uint32 uiCount = 0;
+
do
{
bar.step();
Field* pFields = pResult->Fetch();
StringTextData pTemp;
+
int32 iId = pFields[0].GetInt32();
pTemp.uiSoundId = pFields[1].GetUInt32();
pTemp.uiType = pFields[2].GetUInt32();
pTemp.uiLanguage = pFields[3].GetUInt32();
pTemp.uiEmote = pFields[4].GetUInt32();
+
if (iId >= 0)
{
error_db_log("TSCR: Entry %i in table `custom_texts` is not a negative value.", iId);
continue;
}
+
if (iId > TEXT_SOURCE_RANGE*2 || iId <= TEXT_SOURCE_RANGE*3)
{
error_db_log("TSCR: Entry %i in table `custom_texts` is out of accepted entry range for table.", iId);
continue;
}
+
if (pTemp.uiSoundId)
{
if (!GetSoundEntriesStore()->LookupEntry(pTemp.uiSoundId))
error_db_log("TSCR: Entry %i in table `custom_texts` has soundId %u but sound does not exist.", iId, pTemp.uiSoundId);
}
+
if (!GetLanguageDescByID(pTemp.uiLanguage))
error_db_log("TSCR: Entry %i in table `custom_texts` using Language %u but Language does not exist.", iId, pTemp.uiLanguage);
+
if (pTemp.uiType > CHAT_TYPE_ZONE_YELL)
error_db_log("TSCR: Entry %i in table `custom_texts` has Type %u but this Chat Type does not exist.", iId, pTemp.uiType);
+
m_mTextDataMap[iId] = pTemp;
++uiCount;
} while (pResult->NextRow());
+
outstring_log("");
outstring_log(">> Loaded %u additional Custom Texts data.", uiCount);
}
@@ -153,11 +186,14 @@ void SystemMgr::LoadScriptTextsCustom()
outstring_log(">> Loaded 0 additional Custom Texts data. DB table `custom_texts` is empty.");
}
}
+
void SystemMgr::LoadScriptWaypoints()
{
// Drop Existing Waypoint list
m_mPointMoveMap.clear();
+
uint64 uiCreatureCount = 0;
+
// Load Waypoints
QueryResult* pResult = TScriptDB.PQuery("SELECT COUNT(entry) FROM script_waypoint GROUP BY entry");
if (pResult)
@@ -165,17 +201,22 @@ void SystemMgr::LoadScriptWaypoints()
uiCreatureCount = pResult->GetRowCount();
delete pResult;
}
+
outstring_log("TSCR: Loading Script Waypoints for %u creature(s)...", uiCreatureCount);
+
pResult = TScriptDB.PQuery("SELECT entry, pointid, location_x, location_y, location_z, waittime FROM script_waypoint ORDER BY pointid");
+
if (pResult)
{
barGoLink bar(pResult->GetRowCount());
uint32 uiNodeCount = 0;
+
do
{
bar.step();
Field* pFields = pResult->Fetch();
ScriptPointMove pTemp;
+
pTemp.uiCreatureEntry = pFields[0].GetUInt32();
uint32 uiEntry = pTemp.uiCreatureEntry;
pTemp.uiPointId = pFields[1].GetUInt32();
@@ -183,18 +224,24 @@ void SystemMgr::LoadScriptWaypoints()
pTemp.fY = pFields[3].GetFloat();
pTemp.fZ = pFields[4].GetFloat();
pTemp.uiWaitTime = pFields[5].GetUInt32();
+
CreatureInfo const* pCInfo = GetCreatureTemplateStore(pTemp.uiCreatureEntry);
+
if (!pCInfo)
{
error_db_log("TSCR: DB table script_waypoint has waypoint for non-existant creature entry %u", pTemp.uiCreatureEntry);
continue;
}
+
if (!pCInfo->ScriptID)
error_db_log("TSCR: DB table script_waypoint has waypoint for creature entry %u, but creature does not have ScriptName defined and then useless.", pTemp.uiCreatureEntry);
+
m_mPointMoveMap[uiEntry].push_back(pTemp);
++uiNodeCount;
} while (pResult->NextRow());
+
delete pResult;
+
outstring_log("");
outstring_log(">> Loaded %u Script Waypoint nodes.", uiNodeCount);
}
diff --git a/src/bindings/scripts/system/system.h b/src/bindings/scripts/system/system.h
index 83742a3c329..479f856514a 100644
--- a/src/bindings/scripts/system/system.h
+++ b/src/bindings/scripts/system/system.h
@@ -1,10 +1,14 @@
/* Copyright (C) 2006 - 2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software licensed under GPL version 2
* Please see the included DOCS/LICENSE.TXT for more information */
+
#ifndef SC_SYSTEM_H
#define SC_SYSTEM_H
+
extern DatabaseType TScriptDB;
+
#define TEXT_SOURCE_RANGE -1000000 //the amount of entries each text source has available
+
//TODO: find better namings and definitions.
//N=Neutral, A=Alliance, H=Horde.
//NEUTRAL or FRIEND = Hostility to player surroundings (not a good definition)
@@ -14,16 +18,21 @@ enum eEscortFaction
FACTION_ESCORT_A_NEUTRAL_PASSIVE = 10,
FACTION_ESCORT_H_NEUTRAL_PASSIVE = 33,
FACTION_ESCORT_N_NEUTRAL_PASSIVE = 113,
+
FACTION_ESCORT_A_NEUTRAL_ACTIVE = 231,
FACTION_ESCORT_H_NEUTRAL_ACTIVE = 232,
FACTION_ESCORT_N_NEUTRAL_ACTIVE = 250,
+
FACTION_ESCORT_N_FRIEND_PASSIVE = 290,
FACTION_ESCORT_N_FRIEND_ACTIVE = 495,
+
FACTION_ESCORT_A_PASSIVE = 774,
FACTION_ESCORT_H_PASSIVE = 775,
+
FACTION_ESCORT_N_ACTIVE = 1986,
FACTION_ESCORT_H_ACTIVE = 2046
};
+
struct ScriptPointMove
{
uint32 uiCreatureEntry;
@@ -33,6 +42,7 @@ struct ScriptPointMove
float fZ;
uint32 uiWaitTime;
};
+
struct StringTextData
{
uint32 uiSoundId;
@@ -40,39 +50,53 @@ struct StringTextData
uint32 uiLanguage;
uint32 uiEmote;
};
+
#define pSystemMgr SystemMgr::Instance()
+
class SystemMgr
{
public:
SystemMgr();
~SystemMgr() {}
+
static SystemMgr& Instance();
+
//Maps and lists
typedef UNORDERED_MAP<int32, StringTextData> TextDataMap;
typedef UNORDERED_MAP<uint32, std::vector<ScriptPointMove> > PointMoveMap;
+
//Database
void LoadVersion();
void LoadScriptTexts();
void LoadScriptTextsCustom();
void LoadScriptWaypoints();
+
//Retrive from storage
StringTextData const* GetTextData(int32 uiTextId) const
{
TextDataMap::const_iterator itr = m_mTextDataMap.find(uiTextId);
+
if (itr == m_mTextDataMap.end())
return NULL;
+
return &itr->second;
}
+
std::vector<ScriptPointMove> const &GetPointMoveList(uint32 uiCreatureEntry) const
{
static std::vector<ScriptPointMove> vEmpty;
+
PointMoveMap::const_iterator itr = m_mPointMoveMap.find(uiCreatureEntry);
+
if (itr == m_mPointMoveMap.end())
return vEmpty;
+
return itr->second;
}
+
protected:
TextDataMap m_mTextDataMap; //additional data for text strings
PointMoveMap m_mPointMoveMap; //coordinates for waypoints
};
+
#endif
diff --git a/src/bindings/scripts/trinityscripts.rc b/src/bindings/scripts/trinityscripts.rc
index 0dfb6f08ec6..ae93c329eb6 100644
--- a/src/bindings/scripts/trinityscripts.rc
+++ b/src/bindings/scripts/trinityscripts.rc
@@ -15,34 +15,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "../../trinitycore/resource.h"
#include "../../shared/revision.h"
+
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h" //"afxres.h"
+
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
+
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
+
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
//IDI_APPICON ICON "TrinityRealm.ico"
+
/////////////////////////////////////////////////////////////////////////////
// Neutre (Par défaut système) resources
+
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
#pragma code_page(1252)
#endif //_WIN32
+
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
+
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILEVER
PRODUCTVERSION PRODUCTVER
diff --git a/src/framework/Dynamic/FactoryHolder.h b/src/framework/Dynamic/FactoryHolder.h
index ab3671fc2c3..858b8c1e1d3 100644
--- a/src/framework/Dynamic/FactoryHolder.h
+++ b/src/framework/Dynamic/FactoryHolder.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_FACTORY_HOLDER
#define TRINITY_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 T, class Key = std::string>
@@ -31,16 +34,20 @@ class TRINITY_DLL_DECL FactoryHolder
public:
typedef ObjectRegistry<FactoryHolder<T, Key >, Key > FactoryHolderRegistry;
typedef Trinity::Singleton<FactoryHolderRegistry > 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.
diff --git a/src/framework/Dynamic/ObjectRegistry.h b/src/framework/Dynamic/ObjectRegistry.h
index d266c280eb5..8c2659e8f53 100644
--- a/src/framework/Dynamic/ObjectRegistry.h
+++ b/src/framework/Dynamic/ObjectRegistry.h
@@ -17,14 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_OBJECTREGISTRY_H
#define TRINITY_OBJECTREGISTRY_H
+
#include "Platform/Define.h"
#include "Utilities/UnorderedMap.h"
#include "Policies/Singleton.h"
+
#include <string>
#include <vector>
#include <map>
+
/** ObjectRegistry holds all registry item of the same type
*/
template<class T, class Key = std::string>
@@ -32,12 +36,14 @@ class TRINITY_DLL_DECL ObjectRegistry
{
public:
typedef std::map<Key, T *> 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)
{
@@ -49,9 +55,11 @@ class TRINITY_DLL_DECL ObjectRegistry
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)
{
@@ -63,11 +71,13 @@ class TRINITY_DLL_DECL ObjectRegistry
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<Key> &l) const
{
@@ -77,14 +87,17 @@ class TRINITY_DLL_DECL ObjectRegistry
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 Trinity::OperatorNew<ObjectRegistry<T, Key> >;
+
// protected for friend use since it should be a singleton
ObjectRegistry() {}
~ObjectRegistry()
diff --git a/src/framework/GameSystem/Grid.h b/src/framework/GameSystem/Grid.h
index 04f0735ce87..cebaebc68c6 100644
--- a/src/framework/GameSystem/Grid.h
+++ b/src/framework/GameSystem/Grid.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GRID_H
#define TRINITY_GRID_H
+
/*
@class Grid
Grid is a logical segment of the game world represented inside TrinIty.
@@ -29,12 +31,15 @@
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 A, class T, class O> class GridLoader;
+
template
<
class ACTIVE_OBJECT,
@@ -47,10 +52,12 @@ class TRINITY_DLL_DECL Grid
// allows the GridLoader to access its internals
template<class A, class T, class O> 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<class SPECIFIC_OBJECT> void AddWorldObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
@@ -58,6 +65,7 @@ class TRINITY_DLL_DECL Grid
if(!i_objects.template insert<SPECIFIC_OBJECT>(hdl, obj))
assert(false);
}
+
/** an object of interested exits the grid
*/
template<class SPECIFIC_OBJECT> void RemoveWorldObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
@@ -65,38 +73,47 @@ class TRINITY_DLL_DECL Grid
if(!i_objects.template remove<SPECIFIC_OBJECT>(obj, hdl))
assert(false);
}
+
/** Accessors: Returns a specific type of object in the WORDL_OBJECT_TYPES
*/
template<class SPECIFIC_OBJECT> const SPECIFIC_OBJECT* GetWorldObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT* fake) const { return i_objects.template find<SPECIFIC_OBJECT>(hdl); }
template<class SPECIFIC_OBJECT> SPECIFIC_OBJECT* GetWorldObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT *fake) { return i_objects.template find<SPECIFIC_OBJECT>(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<class T> void Visit(TypeContainerVisitor<T, TypeMapContainer<GRID_OBJECT_TYPES> > &visitor)
{
visitor.Visit(i_container);
}
+
/** Grid visitor for world objects
*/
template<class T> void Visit(TypeContainerVisitor<T, TypeMapContainer<WORLD_OBJECT_TYPES> > &visitor)
{
visitor.Visit(i_objects);
}
+
/** Returns the number of object within the grid.
*/
unsigned int ActiveObjectsInGrid(void) const { return /*m_activeGridObjects.size()+*/i_objects.template Count<ACTIVE_OBJECT>(); }
+
/** Accessors: Returns a specific type of object in the GRID_OBJECT_TYPES
*/
template<class SPECIFIC_OBJECT> const SPECIFIC_OBJECT* GetGridObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT *fake) const { return i_container.template find<SPECIFIC_OBJECT>(hdl, fake); }
template<class SPECIFIC_OBJECT> SPECIFIC_OBJECT* GetGridObject(OBJECT_HANDLE hdl, SPECIFIC_OBJECT *fake) { return i_container.template find<SPECIFIC_OBJECT>(hdl, fake); }
+
/** Inserts a container type object into the grid.
*/
template<class SPECIFIC_OBJECT> void AddGridObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
@@ -104,6 +121,7 @@ class TRINITY_DLL_DECL Grid
if(!i_container.template insert<SPECIFIC_OBJECT>(hdl, obj))
assert(false);
}
+
/** Removes a containter type object from the grid
*/
template<class SPECIFIC_OBJECT> void RemoveGridObject(SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
@@ -111,17 +129,21 @@ class TRINITY_DLL_DECL Grid
if(!i_container.template remove<SPECIFIC_OBJECT>(obj, hdl))
assert(false);
}
+
/*bool NoWorldObjectInGrid() const
{
return i_objects.GetElements().isEmpty();
}
+
bool NoGridObjectInGrid() const
{
return i_container.GetElements().isEmpty();
}*/
private:
+
typedef typename ThreadModel::Lock Guard;
typedef typename ThreadModel::VolatileType VolatileType;
+
TypeMapContainer<GRID_OBJECT_TYPES> i_container;
TypeMapContainer<WORLD_OBJECT_TYPES> i_objects;
//typedef std::set<void*> ActiveGridObjects;
diff --git a/src/framework/GameSystem/GridLoader.h b/src/framework/GameSystem/GridLoader.h
index c5eb1155e48..8a483044f8f 100644
--- a/src/framework/GameSystem/GridLoader.h
+++ b/src/framework/GameSystem/GridLoader.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GRIDLOADER_H
#define TRINITY_GRIDLOADER_H
+
/**
@class GridLoader
The GridLoader is working in conjuction with the Grid and responsible
@@ -29,9 +31,11 @@
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,
@@ -41,6 +45,7 @@ class GRID_OBJECT_TYPES
class TRINITY_DLL_DECL GridLoader
{
public:
+
/** Loads the grid
*/
template<class LOADER>
@@ -50,6 +55,7 @@ class TRINITY_DLL_DECL GridLoader
loader.Load(grid);
grid.UnlockGrid();
}
+
/** Stop the grid
*/
template<class STOPER>
diff --git a/src/framework/GameSystem/GridRefManager.h b/src/framework/GameSystem/GridRefManager.h
index 3d97135f867..bd32f836e18 100644
--- a/src/framework/GameSystem/GridRefManager.h
+++ b/src/framework/GameSystem/GridRefManager.h
@@ -17,18 +17,24 @@
* 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 OBJECT>
class GridReference;
+
template<class OBJECT>
class GridRefManager : public RefManager<GridRefManager<OBJECT>, OBJECT>
{
public:
typedef LinkedListHead::Iterator< GridReference<OBJECT> > iterator;
+
GridReference<OBJECT>* getFirst() { return (GridReference<OBJECT>*)RefManager<GridRefManager<OBJECT>, OBJECT>::getFirst(); }
GridReference<OBJECT>* getLast() { return (GridReference<OBJECT>*)RefManager<GridRefManager<OBJECT>, OBJECT>::getLast(); }
+
iterator begin() { return iterator(getFirst()); }
iterator end() { return iterator(NULL); }
iterator rbegin() { return iterator(getLast()); }
diff --git a/src/framework/GameSystem/GridReference.h b/src/framework/GameSystem/GridReference.h
index e6bd5da1237..d4bd6752515 100644
--- a/src/framework/GameSystem/GridReference.h
+++ b/src/framework/GameSystem/GridReference.h
@@ -17,11 +17,15 @@
* 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 OBJECT>
class GridRefManager;
+
template<class OBJECT>
class TRINITY_DLL_SPEC GridReference : public Reference<GridRefManager<OBJECT>, OBJECT>
{
diff --git a/src/framework/GameSystem/NGrid.h b/src/framework/GameSystem/NGrid.h
index f6dac7db955..6ba5d10ffc1 100644
--- a/src/framework/GameSystem/NGrid.h
+++ b/src/framework/GameSystem/NGrid.h
@@ -17,13 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_NGRID_H
#define TRINITY_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:
@@ -37,15 +41,18 @@ public:
void setUnloadReferenceLock( bool on ) { i_unloadReferenceLock = on; }
void incUnloadActiveLock() { ++i_unloadActiveLockCount; }
void decUnloadActiveLock() { if(i_unloadActiveLockCount) --i_unloadActiveLockCount; }
+
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;
uint16 i_unloadActiveLockCount : 16; // lock from active object spawn points (prevent clone loading)
bool i_unloadExplicitLock : 1; // explicit manual lock or config setting
bool i_unloadReferenceLock : 1; // lock from instance map copy
};
+
typedef enum
{
GRID_STATE_INVALID = 0,
@@ -54,6 +61,7 @@ typedef enum
GRID_STATE_REMOVAL= 3,
MAX_GRID_STATE = 4
} grid_state_t;
+
template
<
unsigned int N,
@@ -65,36 +73,42 @@ class ThreadModel = Trinity::SingleThreaded<ACTIVE_OBJECT>
class TRINITY_DLL_DECL NGrid
{
public:
+
typedef Grid<ACTIVE_OBJECT, WORLD_OBJECT_TYPES, GRID_OBJECT_TYPES, ThreadModel> GridType;
NGrid(uint32 id, int32 x, int32 y, time_t expiry, bool unload = true)
: i_gridId(id), i_x(x), i_y(y), i_cellstate(GRID_STATE_INVALID), i_GridObjectDataLoaded(false)
{
i_GridInfo = GridInfo(expiry, unload);
}
+
const GridType& operator()(unsigned short x, unsigned short y) const
{
ASSERT(x < N);
ASSERT(y < N);
return i_cells[x][y];
}
+
GridType& operator()(unsigned short x, unsigned short y)
{
ASSERT(x < N);
ASSERT(y < N);
return i_cells[x][y];
}
+
const uint32& GetGridId(void) const { return i_gridId; }
void SetGridId(const uint32 id) const { i_gridId = id; }
grid_state_t GetGridState(void) const { return i_cellstate; }
void SetGridState(grid_state_t s) { i_cellstate = s; }
int32 getX() const { return i_x; }
int32 getY() const { return i_y; }
+
void link(GridRefManager<NGrid<N, ACTIVE_OBJECT, WORLD_OBJECT_TYPES, GRID_OBJECT_TYPES, ThreadModel> >* 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 getUnloadLock() const { return i_GridInfo.getUnloadLock(); }
@@ -104,24 +118,29 @@ class TRINITY_DLL_DECL NGrid
void decUnloadActiveLock() { i_GridInfo.decUnloadActiveLock(); }
void ResetTimeTracker(time_t interval) { i_GridInfo.ResetTimeTracker(interval); }
void UpdateTimeTracker(time_t diff) { i_GridInfo.UpdateTimeTracker(diff); }
+
template<class SPECIFIC_OBJECT> void AddWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
{
getGridType(x, y).AddWorldObject(obj, hdl);
}
+
template<class SPECIFIC_OBJECT> void RemoveWorldObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
{
getGridType(x, y).RemoveWorldObject(obj, hdl);
}
+
template<class T, class TT> void Visit(TypeContainerVisitor<T, TypeMapContainer<TT> > &visitor)
{
for(unsigned int x=0; x < N; ++x)
for(unsigned int y=0; y < N; ++y)
getGridType(x, y).Visit(visitor);
}
+
template<class T, class TT> void Visit(const uint32 &x, const uint32 &y, TypeContainerVisitor<T, TypeMapContainer<TT> > &visitor)
{
getGridType(x, y).Visit(visitor);
}
+
unsigned int ActiveObjectsInGrid(void) const
{
unsigned int count=0;
@@ -130,29 +149,36 @@ class TRINITY_DLL_DECL NGrid
count += i_cells[x][y].ActiveObjectsInGrid();
return count;
}
+
template<class SPECIFIC_OBJECT> const SPECIFIC_OBJECT* GetGridObject(const uint32 x, const uint32 y, OBJECT_HANDLE hdl) const
{
return getGridType(x, y).template GetGridObject<SPECIFIC_OBJECT>(hdl);
}
+
template<class SPECIFIC_OBJECT> SPECIFIC_OBJECT* GetGridObject(const uint32 x, const uint32 y, OBJECT_HANDLE hdl)
{
return getGridType(x, y).template GetGridObject<SPECIFIC_OBJECT>(hdl);
}
+
template<class SPECIFIC_OBJECT> bool AddGridObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
{
return getGridType(x, y).AddGridObject(hdl, obj);
}
+
template<class SPECIFIC_OBJECT> bool RemoveGridObject(const uint32 x, const uint32 y, SPECIFIC_OBJECT *obj, OBJECT_HANDLE hdl)
{
return getGridType(x, y).RemoveGridObject(obj, hdl);
}
+
private:
+
GridType& getGridType(const uint32& x, const uint32& y)
{
ASSERT(x < N);
ASSERT(y < N);
return i_cells[x][y];
}
+
uint32 i_gridId;
GridInfo i_GridInfo;
GridReference<NGrid<N, ACTIVE_OBJECT, WORLD_OBJECT_TYPES, GRID_OBJECT_TYPES, ThreadModel> > i_Reference;
diff --git a/src/framework/GameSystem/TypeContainer.h b/src/framework/GameSystem/TypeContainer.h
index 9d32d398527..9e4c6da65e2 100644
--- a/src/framework/GameSystem/TypeContainer.h
+++ b/src/framework/GameSystem/TypeContainer.h
@@ -17,17 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TYPECONTAINER_H
#define TRINITY_TYPECONTAINER_H
+
/*
* Here, you'll find a series of containers that allow you to hold multiple
* types of object at the same time.
*/
+
#include <map>
#include <vector>
#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,
@@ -38,6 +42,7 @@ template<class OBJECT> struct ContainerMapList
//std::map<OBJECT_HANDLE, OBJECT *> _element;
GridRefManager<OBJECT> _element;
};
+
template<> struct ContainerMapList<TypeNull> /* nothing is in type null */
{
};
@@ -46,6 +51,7 @@ template<class H, class T> struct ContainerMapList<TypeList<H, T> >
ContainerMapList<H> _elements;
ContainerMapList<T> _TailElements;
};
+
/*
* @class ContaierArrayList is a multi-type container for
* array of elements.
@@ -54,6 +60,7 @@ template<class OBJECT> struct ContainerArrayList
{
std::vector<OBJECT> _element;
};
+
// termination condition
template<> struct ContainerArrayList<TypeNull> {};
// recursion
@@ -62,6 +69,7 @@ template<class H, class T> struct ContainerArrayList<TypeList<H, T> >
ContainerArrayList<H> _elements;
ContainerArrayList<T> _TailElements;
};
+
/*
* @class ContainerList is a simple list of different types of elements
*
@@ -70,6 +78,7 @@ template<class OBJECT> struct ContainerList
{
OBJECT _element;
};
+
/* TypeNull is underfined */
template<> struct ContainerList<TypeNull> {};
template<class H, class T> struct ContainerList<TypeList<H, T> >
@@ -77,35 +86,44 @@ template<class H, class T> struct ContainerList<TypeList<H, T> >
ContainerList<H> _elements;
ContainerMapList<T> _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 OBJECT_TYPES>
class TRINITY_DLL_DECL TypeMapContainer
{
public:
template<class SPECIFIC_TYPE> size_t Count() const { return Trinity::Count(i_elements, (SPECIFIC_TYPE*)NULL); }
+
template<class SPECIFIC_TYPE> SPECIFIC_TYPE* find(OBJECT_HANDLE hdl, SPECIFIC_TYPE *fake) { return Trinity::Find(i_elements, hdl,fake); }
+
/// find a specific type of object in the container
template<class SPECIFIC_TYPE> const SPECIFIC_TYPE* find(OBJECT_HANDLE hdl, SPECIFIC_TYPE *fake) const { return Trinity::Find(i_elements, hdl,fake); }
+
/// inserts a specific object into the container
template<class SPECIFIC_TYPE> bool insert(OBJECT_HANDLE hdl, SPECIFIC_TYPE *obj)
{
SPECIFIC_TYPE* t = Trinity::Insert(i_elements, obj, hdl);
return (t != NULL);
}
+
/// Removes the object from the container, and returns the removed object
template<class SPECIFIC_TYPE> bool remove(SPECIFIC_TYPE* obj, OBJECT_HANDLE hdl)
{
SPECIFIC_TYPE* t = Trinity::Remove(i_elements, obj, hdl);
return (t != NULL);
}
+
ContainerMapList<OBJECT_TYPES> & GetElements(void) { return i_elements; }
const ContainerMapList<OBJECT_TYPES> & GetElements(void) const { return i_elements;}
+
private:
ContainerMapList<OBJECT_TYPES> i_elements;
};
diff --git a/src/framework/GameSystem/TypeContainerFunctions.h b/src/framework/GameSystem/TypeContainerFunctions.h
index 618c2584961..f9945f84ac7 100644
--- a/src/framework/GameSystem/TypeContainerFunctions.h
+++ b/src/framework/GameSystem/TypeContainerFunctions.h
@@ -17,16 +17,20 @@
* 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 <map>
+
namespace Trinity
{
/* ContainerMapList Helpers */
@@ -35,22 +39,27 @@ namespace Trinity
{
return elements._element.getSize();
};
+
template<class SPECIFIC_TYPE> size_t Count(const ContainerMapList<TypeNull> &/*elements*/, SPECIFIC_TYPE* /*fake*/)
{
return 0;
}
+
template<class SPECIFIC_TYPE, class T> size_t Count(const ContainerMapList<T> &/*elements*/, SPECIFIC_TYPE* /*fake*/)
{
return 0;
}
+
template<class SPECIFIC_TYPE, class T> size_t Count(const ContainerMapList<TypeList<SPECIFIC_TYPE, T> >&elements, SPECIFIC_TYPE* fake)
{
return Count(elements._elements,fake);
}
+
template<class SPECIFIC_TYPE, class H, class T> size_t Count(const ContainerMapList<TypeList<H, T> >&elements, SPECIFIC_TYPE* fake)
{
return Count(elements._TailElements, fake);
}
+
// non-const find functions
template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Find(ContainerMapList<SPECIFIC_TYPE> &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/)
{
@@ -58,20 +67,24 @@ namespace Trinity
//return (iter == elements._element.end() ? NULL : iter->second);
return NULL;
};
+
template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Find(ContainerMapList<TypeNull> &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/)
{
return NULL; // terminate recursion
}
+
template<class SPECIFIC_TYPE, class T> SPECIFIC_TYPE* Find(ContainerMapList<T> &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/)
{
return NULL; // this is a missed
}
+
template<class SPECIFIC_TYPE, class H, class T> SPECIFIC_TYPE* Find(ContainerMapList<TypeList<H, T> >&/*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<class SPECIFIC_TYPE> const SPECIFIC_TYPE* Find(const ContainerMapList<SPECIFIC_TYPE> &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/)
{
@@ -79,21 +92,26 @@ namespace Trinity
//return (iter == elements._element.end() ? NULL : iter->second);
return NULL;
};
+
template<class SPECIFIC_TYPE> const SPECIFIC_TYPE* Find(const ContainerMapList<TypeNull> &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/)
{
return NULL;
}
+
template<class SPECIFIC_TYPE, class T> const SPECIFIC_TYPE* Find(const ContainerMapList<T> &/*elements*/, OBJECT_HANDLE /*hdl*/, SPECIFIC_TYPE* /*fake*/)
{
return NULL;
}
+
template<class SPECIFIC_TYPE, class H, class T> SPECIFIC_TYPE* Find(const ContainerMapList<TypeList<H, T> >&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<class SPECIFIC_TYPE> SPECIFIC_TYPE* Insert(ContainerMapList<SPECIFIC_TYPE> &elements, SPECIFIC_TYPE *obj, OBJECT_HANDLE /*hdl*/)
{
@@ -101,21 +119,25 @@ namespace Trinity
obj->GetGridRef().link(&elements._element, obj);
return obj;
};
+
template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Insert(ContainerMapList<TypeNull> &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/)
{
return NULL;
}
+
// this is a missed
template<class SPECIFIC_TYPE, class T> SPECIFIC_TYPE* Insert(ContainerMapList<T> &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/)
{
return NULL; // a missed
}
+
// Recursion
template<class SPECIFIC_TYPE, class H, class T> SPECIFIC_TYPE* Insert(ContainerMapList<TypeList<H, T> >&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<class SPECIFIC_TYPE> SPECIFIC_TYPE* Remove(ContainerMapList<SPECIFIC_TYPE> & /*elements*/, SPECIFIC_TYPE *obj, OBJECT_HANDLE /*hdl*/)
{
@@ -129,21 +151,25 @@ namespace Trinity
obj->GetGridRef().unlink();
return obj;
}
+
template<class SPECIFIC_TYPE> SPECIFIC_TYPE* Remove(ContainerMapList<TypeNull> &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/)
{
return NULL;
}
+
// this is a missed
template<class SPECIFIC_TYPE, class T> SPECIFIC_TYPE* Remove(ContainerMapList<T> &/*elements*/, SPECIFIC_TYPE * /*obj*/, OBJECT_HANDLE /*hdl*/)
{
return NULL; // a missed
}
+
template<class SPECIFIC_TYPE, class T, class H> SPECIFIC_TYPE* Remove(ContainerMapList<TypeList<H, T> > &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/TypeContainerVisitor.h b/src/framework/GameSystem/TypeContainerVisitor.h
index fc861cfe3da..97570b370c2 100644
--- a/src/framework/GameSystem/TypeContainerVisitor.h
+++ b/src/framework/GameSystem/TypeContainerVisitor.h
@@ -17,82 +17,101 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TYPECONTAINERVISITOR_H
#define TRINITY_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 T, class Y> class TypeContainerVisitor;
+
// visitor helper
template<class VISITOR, class TYPE_CONTAINER> void VisitorHelper(VISITOR &v, TYPE_CONTAINER &c)
{
v.Visit(c);
};
+
// terminate condition for container list
template<class VISITOR> void VisitorHelper(VISITOR &v, ContainerList<TypeNull> &c)
{
}
+
template<class VISITOR, class T> void VisitorHelper(VISITOR &v, ContainerList<T> &c)
{
v.Visit(c._element);
}
+
// recursion for container list
template<class VISITOR, class H, class T> void VisitorHelper(VISITOR &v, ContainerList<TypeList<H, T> > &c)
{
VisitorHelper(v, c._elements);
VisitorHelper(v, c._TailElements);
}
+
// terminate condition container map list
template<class VISITOR> void VisitorHelper(VISITOR &/*v*/, ContainerMapList<TypeNull> &/*c*/)
{
}
+
template<class VISITOR, class T> void VisitorHelper(VISITOR &v, ContainerMapList<T> &c)
{
v.Visit(c._element);
}
+
// recursion container map list
template<class VISITOR, class H, class T> void VisitorHelper(VISITOR &v, ContainerMapList<TypeList<H, T> > &c)
{
VisitorHelper(v, c._elements);
VisitorHelper(v, c._TailElements);
}
+
// array list
template<class VISITOR, class T> void VisitorHelper(VISITOR &v, ContainerArrayList<T> &c)
{
v.Visit(c._element);
}
+
template<class VISITOR> void VisitorHelper(VISITOR &/*v*/, ContainerArrayList<TypeNull> &/*c*/)
{
}
+
// recursion
template<class VISITOR, class H, class T> void VisitorHelper(VISITOR &v, ContainerArrayList<TypeList<H, T> > &c)
{
VisitorHelper(v, c._elements);
VisitorHelper(v, c._TailElements);
}
+
// for TypeMapContainer
template<class VISITOR, class OBJECT_TYPES> void VisitorHelper(VISITOR &v, TypeMapContainer<OBJECT_TYPES> &c)
{
VisitorHelper(v, c.GetElements());
}
+
template<class VISITOR, class TYPE_CONTAINER>
class TRINITY_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;
};
diff --git a/src/framework/Network/SocketDefines.h b/src/framework/Network/SocketDefines.h
index 713562f0376..e69f2fb41e0 100644
--- a/src/framework/Network/SocketDefines.h
+++ b/src/framework/Network/SocketDefines.h
@@ -17,17 +17,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_SOCKETDEFINES_H
#define TRINITY_SOCKETDEFINES_H
+
#ifdef WIN32
+
/* Windows socket definitions
*/
#define FD_SETSIZE 1024
#include <winsock2.h>
#include <Ws2tcpip.h>
+
typedef SOCKET SocketHandle;
typedef fd_set SelectSet;
+
#else
+
/* The unix socket definitions
*/
#include <sys/socket.h>
@@ -35,6 +41,7 @@ typedef fd_set SelectSet;
#ifdef __APPLE_CC__
#include <sys/select.h>
#endif
+
typedef int SocketHandle;
typedef fd_set SelectSet;
#endif
diff --git a/src/framework/Platform/CompilerDefs.h b/src/framework/Platform/CompilerDefs.h
index 863d8bdc2ce..691acb2e6d7 100644
--- a/src/framework/Platform/CompilerDefs.h
+++ b/src/framework/Platform/CompilerDefs.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_COMPILERDEFS_H
#define TRINITY_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
@@ -35,10 +38,12 @@
#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__ )
@@ -50,6 +55,7 @@
#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
diff --git a/src/framework/Platform/Define.h b/src/framework/Platform/Define.h
index df4d7deaf7d..d29a53dbf31 100644
--- a/src/framework/Platform/Define.h
+++ b/src/framework/Platform/Define.h
@@ -17,14 +17,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_DEFINE_H
#define TRINITY_DEFINE_H
+
#include <sys/types.h>
+
#include <ace/Basic_Types.h>
#include <ace/ACE_export.h>
+
#include "Platform/CompilerDefs.h"
+
#define TRINITY_LITTLEENDIAN 0
#define TRINITY_BIGENDIAN 1
+
#if !defined(TRINITY_ENDIAN)
# if defined (ACE_BIG_ENDIAN)
# define TRINITY_ENDIAN TRINITY_BIGENDIAN
@@ -32,6 +38,7 @@
# define TRINITY_ENDIAN TRINITY_LITTLEENDIAN
# endif //ACE_BYTE_ORDER
#endif //TRINITY_ENDIAN
+
#if PLATFORM == PLATFORM_WINDOWS
# define TRINITY_EXPORT __declspec(dllexport)
# define TRINITY_LIBRARY_HANDLE HMODULE
@@ -72,6 +79,7 @@
# endif //__APPLE_CC__
# define TRINITY_PATH_MAX PATH_MAX
#endif //PLATFORM
+
#if PLATFORM == PLATFORM_WINDOWS
# ifdef TRINITY_WIN32_DLL_IMPORT
# define TRINITY_DLL_DECL __declspec(dllimport)
@@ -85,6 +93,7 @@
#else //PLATFORM != PLATFORM_WINDOWS
# define TRINITY_DLL_DECL
#endif //PLATFORM
+
#if PLATFORM == PLATFORM_WINDOWS
# define TRINITY_DLL_SPEC __declspec(dllexport)
# ifndef DECLSPEC_NORETURN
@@ -94,6 +103,7 @@
# define TRINITY_DLL_SPEC
# define DECLSPEC_NORETURN
#endif //PLATFORM
+
#if !defined(DEBUG)
# define TRINITY_INLINE inline
#else //DEBUG
@@ -102,6 +112,7 @@
# endif //TRINITY_DEBUG
# define TRINITY_INLINE
#endif //!DEBUG
+
#if COMPILER == COMPILER_GNU
# define ATTR_NORETURN __attribute__((noreturn))
# define ATTR_PRINTF(F,V) __attribute__ ((format (printf, F, V)))
@@ -109,6 +120,7 @@
# define ATTR_NORETURN
# define ATTR_PRINTF(F,V)
#endif //COMPILER == COMPILER_GNU
+
typedef ACE_INT64 int64;
typedef ACE_INT32 int32;
typedef ACE_INT16 int16;
@@ -117,15 +129,19 @@ typedef ACE_UINT64 uint64;
typedef ACE_UINT32 uint32;
typedef ACE_UINT16 uint16;
typedef ACE_UINT8 uint8;
+
#if COMPILER != COMPILER_MICROSOFT
typedef uint16 WORD;
typedef uint32 DWORD;
#endif //COMPILER
+
typedef uint64 OBJECT_HANDLE;
+
#define MaNGOS Trinity
#define MANGOS_DLL_DECL TRINITY_DLL_DECL
#define MANGOS_DLL_SPEC TRINITY_DLL_SPEC
#define GetMangosString GetTrinityString
+
#if defined(MANGOS_DEBUG) || defined(TRINITY_DEBUG)
# ifndef TRINITY_DEBUG
# define TRINITY_DEBUG
@@ -134,15 +150,19 @@ typedef uint64 OBJECT_HANDLE;
# define MANGOS_DEBUG
# endif
#endif
+
#if !defined(DEBUG) && !defined(MANGOS_DEBUG) && !defined(TRINITY_DEBUG)
#define MULTI_THREAD_MAP
#endif
+
#ifdef MULTI_THREAD_MAP
#define MAP_BASED_RAND_GEN
#endif
+
#ifndef CLIENT_VER
#define CLIENT_VER 313
#endif
+
#endif //TRINITY_DEFINE_H
diff --git a/src/framework/Policies/CreationPolicy.h b/src/framework/Policies/CreationPolicy.h
index 9bca121b632..91bec9eab37 100644
--- a/src/framework/Policies/CreationPolicy.h
+++ b/src/framework/Policies/CreationPolicy.h
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CREATIONPOLICY_H
#define TRINITY_CREATIONPOLICY_H
+
#include <stdlib.h>
#include "Platform/Define.h"
+
namespace Trinity
{
/**
@@ -33,6 +36,7 @@ namespace Trinity
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.
@@ -59,8 +63,10 @@ namespace Trinity
static MaxAlign si_localStatic;
return new(&si_localStatic) T;
}
+
static void Destroy(T *obj) { obj->~T(); }
};
+
/**
* CreateUsingMalloc by pass the memory manger.
*/
@@ -74,12 +80,14 @@ namespace Trinity
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.
*/
@@ -91,6 +99,7 @@ namespace Trinity
{
return CALL_BACK::createCallBack();
}
+
static void Destroy(T *p)
{
CALL_BACK::destroyCallBack(p);
diff --git a/src/framework/Policies/ObjectLifeTime.cpp b/src/framework/Policies/ObjectLifeTime.cpp
index d86e96a8e87..852e382a0e9 100644
--- a/src/framework/Policies/ObjectLifeTime.cpp
+++ b/src/framework/Policies/ObjectLifeTime.cpp
@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <cstdlib>
#include "ObjectLifeTime.h"
+
namespace Trinity
{
extern "C" void external_wrapper(void *p)
{
std::atexit( (void (*)())p );
}
+
void TRINITY_DLL_SPEC at_exit( void (*func)() )
{
external_wrapper((void*)func);
diff --git a/src/framework/Policies/ObjectLifeTime.h b/src/framework/Policies/ObjectLifeTime.h
index 51765d4d4b4..86303f889fb 100644
--- a/src/framework/Policies/ObjectLifeTime.h
+++ b/src/framework/Policies/ObjectLifeTime.h
@@ -17,14 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_OBJECTLIFETIME_H
#define TRINITY_OBJECTLIFETIME_H
+
#include <stdexcept>
#include "Platform/Define.h"
+
typedef void (* Destroyer)(void);
+
namespace Trinity
{
void TRINITY_DLL_SPEC at_exit( void (*func)() );
+
template <class T>
class TRINITY_DLL_DECL ObjectLifeTime
{
@@ -33,8 +38,11 @@ namespace Trinity
{
at_exit( destroyer );
}
+
DECLSPEC_NORETURN static void OnDeadReference(void) ATTR_NORETURN;
+
};
+
template <class T>
void ObjectLifeTime<T>::OnDeadReference(void) // We don't handle Dead Reference for now
{
diff --git a/src/framework/Policies/Singleton.h b/src/framework/Policies/Singleton.h
index d8d4fd4df20..66daaba5a9a 100644
--- a/src/framework/Policies/Singleton.h
+++ b/src/framework/Policies/Singleton.h
@@ -17,14 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_SINGLETON_H
#define TRINITY_SINGLETON_H
+
/**
* @brief class Singleton
*/
+
#include "CreationPolicy.h"
#include "ThreadingModel.h"
#include "ObjectLifeTime.h"
+
namespace Trinity
{
template
@@ -38,14 +42,19 @@ namespace Trinity
{
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;
diff --git a/src/framework/Policies/SingletonImp.h b/src/framework/Policies/SingletonImp.h
index 44f2d184410..89a4738ae22 100644
--- a/src/framework/Policies/SingletonImp.h
+++ b/src/framework/Policies/SingletonImp.h
@@ -17,11 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_SINGLETONIMPL_H
#define TRINITY_SINGLETONIMPL_H
+
#include "Singleton.h"
+
// avoid the using namespace here cuz
// its a .h file afterall
+
template
<
typename T,
@@ -47,8 +51,10 @@ Trinity::Singleton<T, ThreadingModel, CreatePolicy, LifeTimePolicy >::Instance()
LifeTimePolicy::ScheduleCall(&DestroySingleton);
}
}
+
return *si_instance;
}
+
template
<
typename T,
@@ -63,18 +69,22 @@ Trinity::Singleton<T, ThreadingModel, CreatePolicy, LifeTimePolicy>::DestroySing
si_instance = NULL;
si_destroyed = true;
}
+
#define INSTANTIATE_SINGLETON_1(TYPE) \
template class TRINITY_DLL_DECL Trinity::Singleton<TYPE, Trinity::SingleThreaded<TYPE>, Trinity::OperatorNew<TYPE>, Trinity::ObjectLifeTime<TYPE> >; \
template<> TYPE* Trinity::Singleton<TYPE, Trinity::SingleThreaded<TYPE>, Trinity::OperatorNew<TYPE>, Trinity::ObjectLifeTime<TYPE> >::si_instance = 0; \
template<> bool Trinity::Singleton<TYPE, Trinity::SingleThreaded<TYPE>, Trinity::OperatorNew<TYPE>, Trinity::ObjectLifeTime<TYPE> >::si_destroyed = false
+
#define INSTANTIATE_SINGLETON_2(TYPE, THREADINGMODEL) \
template class TRINITY_DLL_DECL Trinity::Singleton<TYPE, THREADINGMODEL, Trinity::OperatorNew<TYPE>, Trinity::ObjectLifeTime<TYPE> >; \
template<> TYPE* Trinity::Singleton<TYPE, THREADINGMODEL, Trinity::OperatorNew<TYPE>, Trinity::ObjectLifeTime<TYPE> >::si_instance = 0; \
template<> bool Trinity::Singleton<TYPE, THREADINGMODEL, Trinity::OperatorNew<TYPE>, Trinity::ObjectLifeTime<TYPE> >::si_destroyed = false
+
#define INSTANTIATE_SINGLETON_3(TYPE, THREADINGMODEL, CREATIONPOLICY ) \
template class TRINITY_DLL_DECL Trinity::Singleton<TYPE, THREADINGMODEL, CREATIONPOLICY, Trinity::ObjectLifeTime<TYPE> >; \
template<> TYPE* Trinity::Singleton<TYPE, THREADINGMODEL, CREATIONPOLICY, Trinity::ObjectLifeTime<TYPE> >::si_instance = 0; \
template<> bool Trinity::Singleton<TYPE, THREADINGMODEL, CREATIONPOLICY, Trinity::ObjectLifeType<TYPE> >::si_destroyed = false
+
#define INSTANTIATE_SINGLETON_4(TYPE, THREADINGMODEL, CREATIONPOLICY, OBJECTLIFETIME) \
template class TRINITY_DLL_DECL Trinity::Singleton<TYPE, THREADINGMODEL, CREATIONPOLICY, OBJECTLIFETIME >; \
template<> TYPE* Trinity::Singleton<TYPE, THREADINGMODEL, CREATIONPOLICY, OBJECTLIFETIME >::si_instance = 0; \
diff --git a/src/framework/Policies/ThreadingModel.h b/src/framework/Policies/ThreadingModel.h
index 3e0593789b5..a380fe78323 100644
--- a/src/framework/Policies/ThreadingModel.h
+++ b/src/framework/Policies/ThreadingModel.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_THREADINGMODEL_H
#define TRINITY_THREADINGMODEL_H
+
/**
* @class ThreadingModel<T>
*
*/
+
#include "Platform/Define.h"
+
namespace Trinity
{
inline void Guard(void *) {}
+
template<typename MUTEX> class TRINITY_DLL_DECL GeneralLock
{
public:
@@ -34,6 +39,7 @@ namespace Trinity
{
i_mutex.acquire();
}
+
~GeneralLock()
{
i_mutex.release();
@@ -43,10 +49,12 @@ namespace Trinity
GeneralLock& operator=(const GeneralLock &);
MUTEX &i_mutex;
};
+
template <class T>
class TRINITY_DLL_DECL SingleThreaded
{
public:
+
struct Lock // empty object
{
Lock() {}
@@ -55,31 +63,40 @@ namespace Trinity
{
}
};
+
typedef T VolatileType;
};
+
// object level lockable
template<class T, class MUTEX>
class TRINITY_DLL_DECL ObjectLevelLockable
{
public:
ObjectLevelLockable() : i_mtx() {}
+
friend class Lock;
+
class Lock
{
public:
Lock(ObjectLevelLockable<T, MUTEX> &host) : i_lock(host.i_mtx)
{
}
+
private:
GeneralLock<MUTEX> i_lock;
};
+
typedef volatile T VolatileType;
+
private:
// prevent the compiler creating a copy construct
ObjectLevelLockable(const ObjectLevelLockable<T, MUTEX> &);
ObjectLevelLockable<T, MUTEX>& operator=(const ObjectLevelLockable<T, MUTEX> &);
+
MUTEX i_mtx;
};
+
template<class T, class MUTEX>
class TRINITY_DLL_DECL ClassLevelLockable
{
@@ -87,7 +104,9 @@ namespace Trinity
class Lock;
friend class Lock;
typedef volatile T VolatileType;
+
ClassLevelLockable() {}
+
class Lock
{
public:
@@ -96,11 +115,15 @@ namespace Trinity
Lock() { ClassLevelLockable<T, MUTEX>::si_mtx.acquire(); }
~Lock() { ClassLevelLockable<T, MUTEX>::si_mtx.release(); }
};
+
private:
static MUTEX si_mtx;
};
+
}
+
template<class T, class MUTEX> MUTEX Trinity::ClassLevelLockable<T, MUTEX>::si_mtx;
+
#define INSTANTIATE_CLASS_MUTEX(CTYPE,MUTEX) \
template class TRINITY_DLL_DECL Trinity::ClassLevelLockable<CTYPE, MUTEX >
#endif
diff --git a/src/framework/Utilities/ByteConverter.h b/src/framework/Utilities/ByteConverter.h
index 38910abfe4d..d9282c1787d 100644
--- a/src/framework/Utilities/ByteConverter.h
+++ b/src/framework/Utilities/ByteConverter.h
@@ -17,13 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_BYTECONVERTER_H
#define TRINITY_BYTECONVERTER_H
+
/** ByteConverter reverse your byte order. This is use
for cross platform where they have different endians.
*/
+
#include<Platform/Define.h>
#include<algorithm>
+
namespace ByteConverter
{
template<size_t T>
@@ -32,13 +36,16 @@ namespace ByteConverter
std::swap(*val, *(val + T - 1));
convert<T - 2>(val + 1);
}
+
template<> inline void convert<0>(char *) {}
template<> inline void convert<1>(char *) {} // ignore central byte
+
template<typename T> inline void apply(T *val)
{
convert<sizeof(T)>((char *)(val));
}
}
+
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
template<typename T> inline void EndianConvert(T& val) { ByteConverter::apply<T>(&val); }
template<typename T> inline void EndianConvertReverse(T&) { }
@@ -46,11 +53,14 @@ template<typename T> inline void EndianConvertReverse(T&) { }
template<typename T> inline void EndianConvert(T&) { }
template<typename T> inline void EndianConvertReverse(T& val) { ByteConverter::apply<T>(&val); }
#endif
+
template<typename T> void EndianConvert(T*); // will generate link error
template<typename T> void EndianConvertReverse(T*); // will generate link error
+
inline void EndianConvert(uint8&) { }
inline void EndianConvert( int8&) { }
inline void EndianConvertReverse(uint8&) { }
inline void EndianConvertReverse( int8&) { }
+
#endif
diff --git a/src/framework/Utilities/Callback.h b/src/framework/Utilities/Callback.h
index 959a493944f..28c0931e6e7 100644
--- a/src/framework/Utilities/Callback.h
+++ b/src/framework/Utilities/Callback.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CALLBACK_H
#define TRINITY_CALLBACK_H
+
/// ------------ BASE CLASSES ------------
+
namespace Trinity
{
template < class Class, typename ParamType1 = void, typename ParamType2 = void, typename ParamType3 = void, typename ParamType4 = void >
@@ -40,6 +43,7 @@ namespace Trinity
_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 >
{
@@ -57,6 +61,7 @@ namespace Trinity
_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 >
{
@@ -73,6 +78,7 @@ namespace Trinity
_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 >
{
@@ -88,6 +94,7 @@ namespace Trinity
_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 >
{
@@ -102,7 +109,9 @@ namespace Trinity
_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
{
@@ -120,6 +129,7 @@ namespace Trinity
_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 >
{
@@ -136,6 +146,7 @@ namespace Trinity
_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 >
{
@@ -151,6 +162,7 @@ namespace Trinity
_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 >
{
@@ -165,6 +177,7 @@ namespace Trinity
_SCallback(_SCallback < ParamType1 > const& cb)
: m_method(cb.m_method), m_param1(cb.m_param1) {}
};
+
template < >
class _SCallback < >
{
@@ -179,7 +192,9 @@ namespace Trinity
: m_method(cb.m_method) {}
};
}
+
/// --------- GENERIC CALLBACKS ----------
+
namespace Trinity
{
class ICallback
@@ -188,6 +203,7 @@ namespace Trinity
virtual void Execute() = 0;
virtual ~ICallback() {}
};
+
template < class CB >
class _ICallback : public CB, public ICallback
{
@@ -195,6 +211,7 @@ namespace Trinity
_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 > >
@@ -205,6 +222,7 @@ namespace Trinity
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 > >
@@ -215,6 +233,7 @@ namespace Trinity
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 > >
@@ -225,6 +244,7 @@ namespace Trinity
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 > >
@@ -235,6 +255,7 @@ namespace Trinity
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 > >
{
@@ -245,8 +266,11 @@ namespace Trinity
: _ICallback< C0 >(C0(object, method)) {}
};
}
+
/// ---------- QUERY CALLBACKS -----------
+
class QueryResult;
+
namespace Trinity
{
class IQueryCallback
@@ -257,6 +281,7 @@ namespace Trinity
virtual void SetResult(QueryResult* result) = 0;
virtual QueryResult* GetResult() = 0;
};
+
template < class CB >
class _IQueryCallback : public CB, public IQueryCallback
{
@@ -266,6 +291,7 @@ namespace Trinity
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 > >
@@ -276,6 +302,7 @@ namespace Trinity
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 > >
@@ -286,6 +313,7 @@ namespace Trinity
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 > >
@@ -296,6 +324,7 @@ namespace Trinity
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* > >
{
@@ -305,7 +334,9 @@ namespace Trinity
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 > >
@@ -316,6 +347,7 @@ namespace Trinity
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 > >
@@ -326,6 +358,7 @@ namespace Trinity
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 > >
@@ -336,6 +369,7 @@ namespace Trinity
SQueryCallback(typename QC1::Method method, QueryResult* result, ParamType1 param1)
: _IQueryCallback< QC1 >(QC1(method, result, param1)) {}
};
+
template < >
class SQueryCallback < > : public _IQueryCallback< _SCallback < QueryResult* > >
{
@@ -346,5 +380,6 @@ namespace Trinity
: _IQueryCallback< QC0 >(QC0(method, result)) {}
};
}
+
#endif
diff --git a/src/framework/Utilities/CountedReference/Reference.h b/src/framework/Utilities/CountedReference/Reference.h
index 49156178258..1e1083e1a66 100644
--- a/src/framework/Utilities/CountedReference/Reference.h
+++ b/src/framework/Utilities/CountedReference/Reference.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_REFERENCE_H
#define TRINITY_REFERENCE_H
+
/**
* Referencer<T>
* Referencer is an object that holds a reference holder that hold a reference
@@ -29,10 +31,12 @@
* reference around. Objects can be reference counted in both single threaded
* model and multi-threaded model
*/
+
#include <stdexcept>
#include "Platform/Define.h"
#include "Policies/ThreadingModel.h"
#include "ReferenceHolder.h"
+
template
<
typename T,
@@ -43,39 +47,52 @@ class TRINITY_DLL_DECL Referencer
typedef typename THREADING_MODEL::Lock Lock;
typedef ReferenceHolder<T, THREADING_MODEL> 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<Referencer *>(this)->_referencee(); }
operator T&(void) { return _referencee(); }
operator const T&(void) const { return *const_cast<Referencer *>(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;
};
diff --git a/src/framework/Utilities/CountedReference/ReferenceHolder.h b/src/framework/Utilities/CountedReference/ReferenceHolder.h
index 80576a8700d..4cfb7d16ded 100644
--- a/src/framework/Utilities/CountedReference/ReferenceHolder.h
+++ b/src/framework/Utilities/CountedReference/ReferenceHolder.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_REFERENCEHOLDER_H
#define TRINITY_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,
diff --git a/src/framework/Utilities/CountedReference/ReferenceImpl.h b/src/framework/Utilities/CountedReference/ReferenceImpl.h
index 22c31021bd8..c3116a21cf4 100644
--- a/src/framework/Utilities/CountedReference/ReferenceImpl.h
+++ b/src/framework/Utilities/CountedReference/ReferenceImpl.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_REFERENCEIMPL_H
#define TRINITY_REFERENCEIMPL_H
+
#include "Reference.h"
+
template
<
typename T,
@@ -34,6 +37,7 @@ Referencer<T, THREADING_MODEL>::Referencer(T *ref)
++i_holder->i_referenceCount;
}
}
+
template
<
typename T,
@@ -45,6 +49,7 @@ Referencer<T, THREADING_MODEL>::~Referencer()
deReference(i_holder);
i_holder = NULL;
}
+
template
<
typename T,
@@ -60,6 +65,7 @@ Referencer<T, THREADING_MODEL>::operator=(const Referencer<T, THREADING_MODEL> &
i_holder = obj.i_holder;
return *this;
}
+
template
<
typename T,
@@ -76,8 +82,10 @@ Referencer<T, THREADING_MODEL>::operator=(T *ref)
i_holder = new ReferenceeHolder(ref);
++i_holder->i_referenceCount;
}
+
return *this;
}
+
template
<
typename T,
@@ -88,21 +96,25 @@ Referencer<T, THREADING_MODEL>::deReference(ReferenceHolder<T, THREADING_MODEL>
{
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,
@@ -114,6 +126,7 @@ Referencer<T, THREADING_MODEL>::addReference(ReferenceHolder<T, THREADING_MODEL>
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
index 75cc5776602..852f84bc4e2 100644
--- a/src/framework/Utilities/EventProcessor.cpp
+++ b/src/framework/Utilities/EventProcessor.cpp
@@ -17,20 +17,25 @@
* 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(true);
}
+
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)
@@ -38,6 +43,7 @@ void EventProcessor::Update(uint32 p_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))
@@ -53,34 +59,41 @@ void EventProcessor::Update(uint32 p_time)
}
}
}
+
void EventProcessor::KillAllEvents(bool force)
{
// prevent event insertions
m_aborting = true;
+
// first, abort all existing events
for (EventList::iterator i = m_events.begin(); i != m_events.end();)
{
EventList::iterator i_old = i;
++i;
+
i_old->second->to_Abort = true;
i_old->second->Abort(m_time);
if(force || i_old->second->IsDeletable())
{
delete i_old->second;
+
if(!force) // need per-element cleanup
m_events.erase (i_old);
}
}
+
// fast clear event list (in force case)
if(force)
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<uint64, BasicEvent*>(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
index ef33fb1ce9a..d0a98c80e30 100644
--- a/src/framework/Utilities/EventProcessor.h
+++ b/src/framework/Utilities/EventProcessor.h
@@ -17,11 +17,16 @@
* 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<map>
+
// Note. All times are in milliseconds here.
+
class BasicEvent
{
public:
@@ -29,24 +34,32 @@ class BasicEvent
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 bool IsDeletable() const { return true; } // this event can be safely deleted
+
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<uint64, BasicEvent*> EventList;
+
class EventProcessor
{
public:
EventProcessor();
~EventProcessor();
+
void Update(uint32 p_time);
void KillAllEvents(bool force);
void AddEvent(BasicEvent* Event, uint64 e_time, bool set_addtime = true);
diff --git a/src/framework/Utilities/LinkedList.h b/src/framework/Utilities/LinkedList.h
index 0d10d6a0121..171ff3601e3 100644
--- a/src/framework/Utilities/LinkedList.h
+++ b/src/framework/Utilities/LinkedList.h
@@ -17,31 +17,40 @@
* 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; }
+
LinkedListElement * nocheck_next() { return iNext; }
LinkedListElement const* nocheck_next() const { return iNext; }
LinkedListElement * nocheck_prev() { return iPrev; }
LinkedListElement const* nocheck_prev() const { return iPrev; }
+
void delink()
{
if(isInList())
@@ -49,6 +58,7 @@ class LinkedListElement
iNext->iPrev = iPrev; iPrev->iNext = iNext; iNext = NULL; iPrev = NULL;
}
}
+
void insertBefore(LinkedListElement* pElem)
{
pElem->iNext = this;
@@ -56,6 +66,7 @@ class LinkedListElement
iPrev->iNext = pElem;
iPrev = pElem;
}
+
void insertAfter(LinkedListElement* pElem)
{
pElem->iPrev = this;
@@ -64,7 +75,9 @@ class LinkedListElement
iNext = pElem;
}
};
+
//============================================
+
class LinkedListHead
{
private:
@@ -75,23 +88,30 @@ class LinkedListHead
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)
@@ -108,8 +128,10 @@ class LinkedListHead
else
return iSize;
}
+
void incSize() { ++iSize; }
void decSize() { --iSize; }
+
template<class _Ty>
class Iterator
{
@@ -123,85 +145,105 @@ class LinkedListHead
typedef _Ty& reference;
typedef _Ty const & const_reference;
+
Iterator() : _Ptr(0)
{ // construct with null node pointer
}
+
Iterator(pointer _Pnode) : _Ptr(_Pnode)
{ // construct with node pointer _Pnode
}
+
Iterator& operator=(Iterator const &_Right)
{
return (*this) = _Right._Ptr;
}
+
Iterator& operator=(const_pointer const &_Right)
{
_Ptr = (pointer)_Right;
return (*this);
}
+
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));
}
+
bool operator==(const_reference _Right) const
{ // test for reference equality
return (_Ptr == &_Right);
}
+
bool operator!=(const_reference _Right) const
{ // test for reference equality
return (_Ptr != &_Right);
}
+
pointer _Mynode()
{ // return node pointer
return (_Ptr);
}
+
protected:
pointer _Ptr; // pointer to node
};
+
typedef Iterator<LinkedListElement> iterator;
};
+
//============================================
#endif
diff --git a/src/framework/Utilities/LinkedReference/RefManager.h b/src/framework/Utilities/LinkedReference/RefManager.h
index 819e7775bf6..ed580f49ec2 100644
--- a/src/framework/Utilities/LinkedReference/RefManager.h
+++ b/src/framework/Utilities/LinkedReference/RefManager.h
@@ -17,25 +17,31 @@
* 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 TO, class FROM> class RefManager : public LinkedListHead
{
public:
typedef LinkedListHead::Iterator< Reference<TO, FROM> > iterator;
RefManager() { }
virtual ~RefManager() { clearReferences(); }
+
Reference<TO, FROM>* getFirst() { return ((Reference<TO, FROM>*) LinkedListHead::getFirst()); }
Reference<TO, FROM> const* getFirst() const { return ((Reference<TO, FROM> const*) LinkedListHead::getFirst()); }
Reference<TO, FROM>* getLast() { return ((Reference<TO, FROM>*) LinkedListHead::getLast()); }
Reference<TO, FROM> const* getLast() const { return ((Reference<TO, FROM> const*) 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;
@@ -46,6 +52,7 @@ template <class TO, class FROM> class RefManager : public LinkedListHead
}
}
};
+
//=====================================================
#endif
diff --git a/src/framework/Utilities/LinkedReference/Reference.h b/src/framework/Utilities/LinkedReference/Reference.h
index 7ac5bd4ef3d..c77d3a2cbdb 100644
--- a/src/framework/Utilities/LinkedReference/Reference.h
+++ b/src/framework/Utilities/LinkedReference/Reference.h
@@ -17,10 +17,14 @@
* 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 TO, class FROM> class Reference : public LinkedListElement
{
private:
@@ -29,13 +33,16 @@ template <class TO, class FROM> class Reference : public LinkedListElement
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
void link(TO* toObj, FROM* fromObj)
{
@@ -49,31 +56,39 @@ template <class TO, class FROM> class Reference : public LinkedListElement
targetObjectBuildLink();
}
}
+
// We don't need the reference anymore. Call comes from the refFrom object
// Tell our refTo object, that the link is cut
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
void invalidate() // the iRefFrom MUST remain!!
{
sourceObjectDestroyLink(); delink(); iRefTo = NULL;
}
+
bool isValid() const // Only check the iRefTo
{
return iRefTo != NULL;
}
+
Reference<TO,FROM> * next() { return((Reference<TO,FROM> *) LinkedListElement::next()); }
Reference<TO,FROM> const * next() const { return((Reference<TO,FROM> const *) LinkedListElement::next()); }
Reference<TO,FROM> * prev() { return((Reference<TO,FROM> *) LinkedListElement::prev()); }
Reference<TO,FROM> const * prev() const { return((Reference<TO,FROM> const *) LinkedListElement::prev()); }
+
Reference<TO,FROM> * nocheck_next() { return((Reference<TO,FROM> *) LinkedListElement::nocheck_next()); }
Reference<TO,FROM> const * nocheck_next() const { return((Reference<TO,FROM> const *) LinkedListElement::nocheck_next()); }
Reference<TO,FROM> * nocheck_prev() { return((Reference<TO,FROM> *) LinkedListElement::nocheck_prev()); }
Reference<TO,FROM> const * nocheck_prev() const { return((Reference<TO,FROM> const *) LinkedListElement::nocheck_prev()); }
+
TO* operator ->() const { return iRefTo; }
TO* getTarget() const { return iRefTo; }
+
FROM* getSource() const { return iRefFrom; }
};
+
//=====================================================
#endif
diff --git a/src/framework/Utilities/TypeList.h b/src/framework/Utilities/TypeList.h
index e1f888e1afe..07b1ebad3e6 100644
--- a/src/framework/Utilities/TypeList.h
+++ b/src/framework/Utilities/TypeList.h
@@ -17,20 +17,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TYPELIST_H
#define TRINITY_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<typename HEAD, typename TAIL>
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<T1,TypeNull>
#define TYPELIST_2(T1, T2) TypeList<T1, TYPELIST_1(T2) >
diff --git a/src/framework/Utilities/UnorderedMap.h b/src/framework/Utilities/UnorderedMap.h
index a93300b197e..dd539cc0bda 100644
--- a/src/framework/Utilities/UnorderedMap.h
+++ b/src/framework/Utilities/UnorderedMap.h
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_UNORDERED_MAP_H
#define TRINITY_UNORDERED_MAP_H
+
#include "Platform/CompilerDefs.h"
#include "Platform/Define.h"
+
#if COMPILER == COMPILER_INTEL
#include <ext/hash_map>
#elif COMPILER == COMPILER_GNU && (__GNUC__ > 4 || __GNUC__ == 4 && __GNUC_MINOR__ >= 3)
@@ -32,6 +35,7 @@
#else
#include <hash_map>
#endif
+
#ifdef _STLPORT_VERSION
#define UNORDERED_MAP std::hash_map
using std::hash_map;
@@ -47,6 +51,7 @@ using std::hash_map;
#define UNORDERED_MAP std::tr1::unordered_map
#elif COMPILER == COMPILER_GNU && __GNUC__ >= 3
#define UNORDERED_MAP __gnu_cxx::hash_map
+
namespace __gnu_cxx
{
template<> struct hash<unsigned long long>
@@ -57,7 +62,9 @@ namespace __gnu_cxx
{
size_t operator()(T * const &__x) const { return (size_t)__x; }
};
+
};
+
#else
#define UNORDERED_MAP std::hash_map
using std::hash_map;
diff --git a/src/game/AccountMgr.cpp b/src/game/AccountMgr.cpp
index d4c4c4aecce..b3c9a76e720 100644
--- a/src/game/AccountMgr.cpp
+++ b/src/game/AccountMgr.cpp
@@ -17,40 +17,53 @@
* 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 "Policies/SingletonImp.h"
+
#include "AccountMgr.h"
#include "ObjectAccessor.h"
#include "Player.h"
#include "Util.h"
#include "Auth/Sha1.h"
+
extern DatabaseType loginDatabase;
+
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
+
normalizeString(username);
normalizeString(password);
+
if(GetId(username))
{
return AOR_NAME_ALREDY_EXIST; // username does already exist
}
+
if(!loginDatabase.PExecute("INSERT INTO account(username,sha_pass_hash,joindate) VALUES('%s','%s',NOW())", username.c_str(), CalculateShaPassHash(username, 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 realmlist,account LEFT JOIN realmcharacters ON acctid=account.id WHERE acctid IS NULL");
+
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)
{
@@ -59,6 +72,7 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid)
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 = ObjectAccessor::GetObjectInWorld(guid, (Player*)NULL))
{
@@ -66,54 +80,76 @@ AccountOpResult AccountMgr::DeleteAccount(uint32 accid)
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;
+
normalizeString(new_uname);
normalizeString(new_passwd);
+
std::string safe_new_uname = new_uname;
loginDatabase.escape_string(safe_new_uname);
+
if(!loginDatabase.PExecute("UPDATE account SET v='0',s='0',username='%s',sha_pass_hash='%s' WHERE id='%d'", safe_new_uname.c_str(),
CalculateShaPassHash(new_uname, new_passwd).c_str(), accid))
return AOR_DB_INTERNAL_ERROR; // unexpected error
+
return AOR_OK;
}
+
AccountOpResult AccountMgr::ChangePassword(uint32 accid, std::string new_passwd)
{
std::string username;
+
if(!GetName(accid, username))
return AOR_NAME_NOT_EXIST; // account doesn't exist
+
if (utf8length(new_passwd) > MAX_ACCOUNT_STR)
return AOR_PASS_TOO_LONG;
+
normalizeString(new_passwd);
+
// also reset s and v to force update at next realmd login
if(!loginDatabase.PExecute("UPDATE account SET v='0', s='0', sha_pass_hash='%s' WHERE id='%d'",
CalculateShaPassHash(username, 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);
@@ -127,6 +163,7 @@ uint32 AccountMgr::GetId(std::string username)
return id;
}
}
+
uint32 AccountMgr::GetSecurity(uint32 acc_id)
{
QueryResult *result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE id = '%u'", acc_id);
@@ -136,8 +173,10 @@ uint32 AccountMgr::GetSecurity(uint32 acc_id)
delete result;
return sec;
}
+
return 0;
}
+
bool AccountMgr::GetName(uint32 acc_id, std::string &name)
{
QueryResult *result = loginDatabase.PQuery("SELECT username FROM account WHERE id = '%u'", acc_id);
@@ -147,31 +186,41 @@ bool AccountMgr::GetName(uint32 acc_id, std::string &name)
delete result;
return true;
}
+
return false;
}
+
bool AccountMgr::CheckPassword(uint32 accid, std::string passwd)
{
std::string username;
if(!GetName(accid, username))
return false;
+
normalizeString(passwd);
+
QueryResult *result = loginDatabase.PQuery("SELECT 1 FROM account WHERE id='%d' AND sha_pass_hash='%s'", accid, CalculateShaPassHash(username, passwd).c_str());
if (result)
{
delete result;
return true;
}
+
return false;
}
+
bool AccountMgr::normalizeString(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);
}
+
std::string AccountMgr::CalculateShaPassHash(std::string& name, std::string& password)
{
Sha1Hash sha;
@@ -180,8 +229,10 @@ std::string AccountMgr::CalculateShaPassHash(std::string& name, std::string& pas
sha.UpdateData(":");
sha.UpdateData(password);
sha.Finalize();
+
std::string encoded;
hexEncodeByteArray(sha.GetDigest(), sha.GetLength(), encoded);
+
return encoded;
}
diff --git a/src/game/AccountMgr.h b/src/game/AccountMgr.h
index c35b9a37575..c14110036a0 100644
--- a/src/game/AccountMgr.h
+++ b/src/game/AccountMgr.h
@@ -17,11 +17,15 @@
* 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 <string>
+
#include "Common.h"
#include "Policies/Singleton.h"
+
enum AccountOpResult
{
AOR_OK,
@@ -31,23 +35,29 @@ enum AccountOpResult
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);
uint32 GetSecurity(uint32 acc_id);
bool GetName(uint32 acc_id, std::string &name);
std::string CalculateShaPassHash(std::string& name, std::string& password);
+
static bool normalizeString(std::string& utf8str);
};
+
#define accmgr Trinity::Singleton<AccountMgr>::Instance()
#endif
diff --git a/src/game/AchievementMgr.cpp b/src/game/AchievementMgr.cpp
index 50daa5c565f..c9b98c3ddef 100644
--- a/src/game/AchievementMgr.cpp
+++ b/src/game/AchievementMgr.cpp
@@ -15,6 +15,7 @@
* 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 "DBCEnums.h"
#include "ObjectMgr.h"
@@ -22,6 +23,7 @@
#include "WorldPacket.h"
#include "Database/DatabaseEnv.h"
#include "Policies/SingletonImp.h"
+
#include "AchievementMgr.h"
#include "ArenaTeam.h"
#include "CellImpl.h"
@@ -32,11 +34,14 @@
#include "Player.h"
#include "ProgressBar.h"
#include "SpellMgr.h"
+
#include "MapManager.h"
#include "BattleGround.h"
#include "BattleGroundAB.h"
+
INSTANTIATE_SINGLETON_1(AchievementGlobalMgr);
+
namespace MaNGOS
{
class AchievementChatBuilder
@@ -47,6 +52,7 @@ namespace MaNGOS
void operator()(WorldPacket& data, int32 loc_idx)
{
char const* text = objmgr.GetMangosString(i_textId,loc_idx);
+
data << uint8(i_msgtype);
data << uint32(LANG_UNIVERSAL);
data << uint64(i_player.GetGUID());
@@ -57,6 +63,7 @@ namespace MaNGOS
data << uint8(0);
data << uint32(i_achievementId);
}
+
private:
Player const& i_player;
ChatMsg i_msgtype;
@@ -65,6 +72,7 @@ namespace MaNGOS
};
} // namespace MaNGOS
+
bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
{
if(dataType >= MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE)
@@ -72,6 +80,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
sLog.outErrorDb( "Table `achievement_criteria_data` for criteria (Entry: %u) have wrong data type (%u), ignore.", criteria->ID,dataType);
return false;
}
+
switch(criteria->requiredType)
{
case ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE:
@@ -89,6 +98,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
sLog.outErrorDb( "Table `achievement_criteria_data` have data for not supported criteria type (Entry: %u Type: %u), ignore.", criteria->ID, criteria->requiredType);
return false;
}
+
switch(dataType)
{
case ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE:
@@ -235,6 +245,7 @@ bool AchievementCriteriaData::IsValid(AchievementCriteriaEntry const* criteria)
}
return false;
}
+
bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, uint32 miscvalue1 /*= 0*/) const
{
switch(dataType)
@@ -306,20 +317,25 @@ bool AchievementCriteriaData::Meets(Player const* source, Unit const* target, ui
}
return false;
}
+
bool AchievementCriteriaDataSet::Meets(Player const* source, Unit const* target, uint32 miscvalue /*= 0*/) const
{
for(Storage::const_iterator itr = storage.begin(); itr != storage.end(); ++itr)
if(!itr->Meets(source,target,miscvalue))
return false;
+
return true;
}
+
AchievementMgr::AchievementMgr(Player *player)
{
m_player = player;
}
+
AchievementMgr::~AchievementMgr()
{
}
+
void AchievementMgr::Reset()
{
for(CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin(); iter!=m_completedAchievements.end(); ++iter)
@@ -328,34 +344,43 @@ void AchievementMgr::Reset()
data << uint32(iter->first);
m_player->SendDirectMessage(&data);
}
+
for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
{
WorldPacket data(SMSG_CRITERIA_DELETED,4);
data << uint32(iter->first);
m_player->SendDirectMessage(&data);
}
+
m_completedAchievements.clear();
m_criteriaProgress.clear();
DeleteFromDB(m_player->GetGUIDLow());
+
// re-fill data
CheckAllAchievementCriteria();
}
+
void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1, uint32 miscvalue2)
{
if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
sLog.outDetail("AchievementMgr::ResetAchievementCriteria(%u, %u, %u)", type, miscvalue1, miscvalue2);
+
if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
return;
+
AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
{
AchievementCriteriaEntry const *achievementCriteria = (*i);
+
AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
if (!achievement)
continue;
+
// don't update already completed criteria
if (IsCompletedCriteria(achievementCriteria,achievement))
continue;
+
switch (type)
{
case ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE: // have total statistic also not expected to be reset
@@ -374,6 +399,7 @@ void AchievementMgr::ResetAchievementCriteria(AchievementCriteriaTypes type, uin
}
}
}
+
void AchievementMgr::DeleteFromDB(uint32 lowguid)
{
CharacterDatabase.BeginTransaction ();
@@ -381,6 +407,7 @@ void AchievementMgr::DeleteFromDB(uint32 lowguid)
CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE guid = %u",lowguid);
CharacterDatabase.CommitTransaction ();
}
+
void AchievementMgr::SaveToDB()
{
if(!m_completedAchievements.empty())
@@ -392,6 +419,7 @@ void AchievementMgr::SaveToDB()
{
if(!iter->second.changed)
continue;
+
/// first new/changed record prefix
if(!need_execute)
{
@@ -405,20 +433,25 @@ void AchievementMgr::SaveToDB()
ssdel << ", ";
ssins << ", ";
}
+
// new/changed record data
ssdel << iter->first;
ssins << "("<<GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << uint64(iter->second.date) << ")";
+
/// mark as saved in db
iter->second.changed = false;
}
+
if(need_execute)
ssdel << ")";
+
if(need_execute)
{
CharacterDatabase.Execute( ssdel.str().c_str() );
CharacterDatabase.Execute( ssins.str().c_str() );
}
}
+
if(!m_criteriaProgress.empty())
{
/// prepare deleting and insert
@@ -430,6 +463,7 @@ void AchievementMgr::SaveToDB()
{
if(!iter->second.changed)
continue;
+
// deleted data (including 0 progress state)
{
/// first new/changed record prefix (for any counter value)
@@ -441,9 +475,11 @@ void AchievementMgr::SaveToDB()
/// next new/changed record prefix
else
ssdel << ", ";
+
// new/changed record data
ssdel << iter->first;
}
+
// store data only for real progress
if(iter->second.counter != 0)
{
@@ -456,14 +492,18 @@ void AchievementMgr::SaveToDB()
/// next new/changed record prefix
else
ssins << ", ";
+
// new/changed record data
ssins << "(" << GetPlayer()->GetGUIDLow() << ", " << iter->first << ", " << iter->second.counter << ", " << iter->second.date << ")";
}
+
/// mark as updated in db
iter->second.changed = false;
}
+
if(need_execute_del) // DELETE ... IN (.... _)_
ssdel << ")";
+
if(need_execute_del || need_execute_ins)
{
if(need_execute_del)
@@ -473,6 +513,7 @@ void AchievementMgr::SaveToDB()
}
}
}
+
void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult)
{
if(achievementResult)
@@ -480,24 +521,30 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri
do
{
Field *fields = achievementResult->Fetch();
+
uint32 achievement_id = fields[0].GetUInt32();
+
// don't must happen: cleanup at server startup in achievementmgr.LoadCompletedAchievements()
if(!sAchievementStore.LookupEntry(achievement_id))
continue;
+
CompletedAchievementData& ca = m_completedAchievements[achievement_id];
ca.date = time_t(fields[1].GetUInt64());
ca.changed = false;
} while(achievementResult->NextRow());
delete achievementResult;
}
+
if(criteriaResult)
{
do
{
Field *fields = criteriaResult->Fetch();
+
uint32 id = fields[0].GetUInt32();
uint32 counter = fields[1].GetUInt32();
time_t date = time_t(fields[2].GetUInt64());
+
AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(id);
if (!criteria)
{
@@ -506,8 +553,10 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri
CharacterDatabase.PExecute("DELETE FROM character_achievement_progress WHERE criteria = %u",id);
continue;
}
+
if (criteria->timeLimit && time_t(date + criteria->timeLimit) < time(NULL))
continue;
+
CriteriaProgress& progress = m_criteriaProgress[id];
progress.counter = counter;
progress.date = date;
@@ -515,21 +564,26 @@ void AchievementMgr::LoadFromDB(QueryResult *achievementResult, QueryResult *cri
} while(criteriaResult->NextRow());
delete criteriaResult;
}
+
}
+
void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
{
if(GetPlayer()->GetSession()->PlayerLoading())
return;
+
#ifdef TRINITY_DEBUG
if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
sLog.outDebug("AchievementMgr::SendAchievementEarned(%u)", achievement->ID);
#endif
+
if(Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId()))
{
MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_GUILD_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
guild->BroadcastWorker(say_do,GetPlayer());
}
+
if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_KILL|ACHIEVEMENT_FLAG_REALM_FIRST_REACH))
{
// broadcast realm first reached
@@ -543,9 +597,11 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
else
{
CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
+
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::AchievementChatBuilder say_builder(*GetPlayer(), CHAT_MSG_ACHIEVEMENT, LANG_ACHIEVEMENT_EARNED,achievement->ID);
MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> say_do(say_builder);
MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::AchievementChatBuilder> > say_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
@@ -553,6 +609,7 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY));
}
+
WorldPacket data(SMSG_ACHIEVEMENT_EARNED, 8+4+8);
data.append(GetPlayer()->GetPackGUID());
data << uint32(achievement->ID);
@@ -560,12 +617,15 @@ void AchievementMgr::SendAchievementEarned(AchievementEntry const* achievement)
data << uint32(0);
GetPlayer()->SendMessageToSetInRange(&data, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY), true);
}
+
void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progress)
{
WorldPacket data(SMSG_CRITERIA_UPDATE, 8+4+8);
data << uint32(id);
+
// the counter is packed like a packed Guid
data.appendPackGUID(progress->counter);
+
data.append(GetPlayer()->GetPackGUID());
data << uint32(0);
data << uint32(secsToTimeBitFields(progress->date));
@@ -573,6 +633,7 @@ void AchievementMgr::SendCriteriaUpdate(uint32 id, CriteriaProgress const* progr
data << uint32(0); // timer 2
GetPlayer()->SendDirectMessage(&data);
}
+
/**
* called at player login. The player might have fulfilled some achievements when the achievement system wasn't working yet
*/
@@ -582,6 +643,7 @@ void AchievementMgr::CheckAllAchievementCriteria()
for(uint32 i=0; i<ACHIEVEMENT_CRITERIA_TYPE_TOTAL; ++i)
UpdateAchievementCriteria(AchievementCriteriaTypes(i));
}
+
static const uint32 achievIdByArenaSlot[MAX_ARENA_SLOT] = { 1057, 1107, 1108 };
static const uint32 achievIdForDangeon[][4] =
{
@@ -593,6 +655,7 @@ static const uint32 achievIdForDangeon[][4] =
{ 2219, false, false, true },
{ 0, false, false, false }
};
+
/**
* this function will be called whenever the user might have done a criteria relevant action
*/
@@ -600,23 +663,30 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
{
if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
sLog.outDetail("AchievementMgr::UpdateAchievementCriteria(%u, %u, %u, %u)", type, miscvalue1, miscvalue2, time);
+
if (!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
return;
+
AchievementCriteriaEntryList const& achievementCriteriaList = achievementmgr.GetAchievementCriteriaByType(type);
for(AchievementCriteriaEntryList::const_iterator i = achievementCriteriaList.begin(); i!=achievementCriteriaList.end(); ++i)
{
AchievementCriteriaEntry const *achievementCriteria = (*i);
+
if (achievementCriteria->groupFlag & ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP && GetPlayer()->GetGroup())
continue;
+
AchievementEntry const *achievement = sAchievementStore.LookupEntry(achievementCriteria->referredAchievement);
if (!achievement)
continue;
+
if ((achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_HORDE && GetPlayer()->GetTeam() != HORDE) ||
(achievement->factionFlag == ACHIEVEMENT_FACTION_FLAG_ALLIANCE && GetPlayer()->GetTeam() != ALLIANCE))
continue;
+
// don't update already completed criteria
if (IsCompletedCriteria(achievementCriteria,achievement))
continue;
+
switch (type)
{
// std. case: increment at 1
@@ -659,7 +729,9 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_HIGHEST);
break;
+
// specialized cases
+
case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
{
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
@@ -667,6 +739,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if (achievementCriteria->win_bg.bgMapID != GetPlayer()->GetMapId())
continue;
+
if (achievementCriteria->win_bg.additionalRequirement1_type)
{
// those requirements couldn't be found in the dbc
@@ -680,6 +753,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
BattleGround* bg = GetPlayer()->GetBattleGround();
if (!bg)
continue;
+
switch(achievementCriteria->referredAchievement)
{
case 161: // AB, Overcome a 500 resource disadvantage
@@ -702,6 +776,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue; // not implemented
}
}
+
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
break;
}
@@ -712,10 +787,12 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if(achievementCriteria->kill_creature.creatureID != miscvalue1)
continue;
+
// those requirements couldn't be found in the dbc
AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
if(!data || !data->Meets(GetPlayer(),unit))
continue;
+
SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
break;
}
@@ -754,6 +831,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// speedup for non-login case
if(miscvalue1 && miscvalue1 != achievementCriteria->complete_quests_in_zone.zoneID)
continue;
+
uint32 counter =0;
for(QuestStatusMap::const_iterator itr = GetPlayer()->getQuestStatusMap().begin(); itr!=GetPlayer()->getQuestStatusMap().end(); itr++)
{
@@ -794,11 +872,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
BattleGround* bg = GetPlayer()->GetBattleGround();
if(!bg || !bg->isArena() || ArenaTeam::GetSlotByType(bg->GetArenaType()) != j)
notfit = true;
+
break;
}
}
if(notfit)
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
}
@@ -807,9 +887,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if(!miscvalue1)
continue;
+
Map const* map = GetPlayer()->IsInWorld() ? GetPlayer()->GetMap() : MapManager::Instance().FindMap(GetPlayer()->GetMapId(), GetPlayer()->GetInstanceId());
if(!map || !map->IsDungeon())
continue;
+
// search case
bool found = false;
for(int j = 0; achievIdForDangeon[j][0]; ++j)
@@ -834,17 +916,20 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!achievIdForDangeon[j][3])
break; // for
}
+
found = true;
break; // for
}
}
if(!found)
continue;
+
//FIXME: work only for instances where max==min for players
if(((InstanceMap*)map)->GetMaxPlayers() != achievementCriteria->death_in_dungeon.manLimit)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
+
}
case ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE:
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
@@ -858,9 +943,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if(!miscvalue1)
continue;
+
// if team check required: must kill by opposition faction
if(achievement->ID==318 && miscvalue2==GetPlayer()->GetTeam())
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING:
@@ -868,10 +955,12 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if(!miscvalue1)
continue;
+
// those requirements couldn't be found in the dbc
AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
if(!data || !data->Meets(GetPlayer(),unit))
continue;
+
// miscvalue1 is the ingame fallheight*100 as stored in dbc
SetCriteriaProgress(achievementCriteria, miscvalue1);
break;
@@ -898,6 +987,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!GetPlayer()->GetQuestRewardStatus(achievementCriteria->complete_quest.questID))
continue;
}
+
// exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
switch(achievement->ID)
{
@@ -918,6 +1008,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
break;
}
+
SetCriteriaProgress(achievementCriteria, 1);
break;
}
@@ -932,18 +1023,22 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
{
if (!miscvalue1 || miscvalue1 != achievementCriteria->cast_spell.spellID)
continue;
+
// those requirements couldn't be found in the dbc
AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
if(!data)
continue;
+
if(!data->Meets(GetPlayer(),unit))
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
}
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL:
if(miscvalue1 && miscvalue1!=achievementCriteria->learn_spell.spellID)
continue;
+
if(GetPlayer()->HasSpell(achievementCriteria->learn_spell.spellID))
SetCriteriaProgress(achievementCriteria, 1);
break;
@@ -955,6 +1050,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if (miscvalue1 != achievementCriteria->loot_type.lootType)
continue;
+
// zone specific
if(achievementCriteria->loot_type.lootTypeCount==1)
{
@@ -963,6 +1059,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!data || !data->Meets(GetPlayer(),unit))
continue;
}
+
SetCriteriaProgress(achievementCriteria, miscvalue2, PROGRESS_ACCUMULATE);
break;
}
@@ -976,6 +1073,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// miscvalue1 contains the personal rating
if (!miscvalue1) // no update at login
continue;
+
// additional requirements
if(achievementCriteria->win_rated_arena.flag==ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE)
{
@@ -988,6 +1086,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
}
}
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM:
@@ -1011,23 +1110,28 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
WorldMapOverlayEntry const* worldOverlayEntry = sWorldMapOverlayStore.LookupEntry(achievementCriteria->explore_area.areaReference);
if(!worldOverlayEntry)
break;
+
bool matchFound = false;
for (int j = 0; j < MAX_WORLD_MAP_OVERLAY_AREA_IDX; ++j)
{
uint32 area_id = worldOverlayEntry->areatableID[j];
if(!area_id) // array have 0 only in empty tail
break;
+
int32 exploreFlag = GetAreaFlagByAreaID(area_id);
if(exploreFlag < 0)
continue;
+
uint32 playerIndexOffset = uint32(exploreFlag) / 32;
uint32 mask = 1<< (uint32(exploreFlag) % 32);
+
if(GetPlayer()->GetUInt32Value(PLAYER_EXPLORED_ZONES_1 + playerIndexOffset) & mask)
{
matchFound = true;
break;
}
}
+
if(matchFound)
SetCriteriaProgress(achievementCriteria, 1);
break;
@@ -1040,6 +1144,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// skip faction check only at loading
if (miscvalue1 && miscvalue1 != achievementCriteria->gain_reputation.factionID)
continue;
+
int32 reputation = GetPlayer()->GetReputationMgr().GetReputation(achievementCriteria->gain_reputation.factionID);
if (reputation > 0)
SetCriteriaProgress(achievementCriteria, reputation);
@@ -1068,9 +1173,11 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(miscvalue2 != achievementCriteria->roll_greed_on_loot.rollValue)
continue;
ItemPrototype const *pProto = objmgr.GetItemPrototype( miscvalue1 );
+
uint32 requiredItemLevel = 0;
if (achievementCriteria->ID == 2412 || achievementCriteria->ID == 2358)
requiredItemLevel = 185;
+
if(!pProto || pProto->ItemLevel <requiredItemLevel)
continue;
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
@@ -1090,6 +1197,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if(!data || !data->Meets(GetPlayer(),unit))
continue;
}
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
}
@@ -1098,14 +1206,17 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
{
if (!miscvalue1)
continue;
+
if (achievementCriteria->healing_done.flag == ACHIEVEMENT_CRITERIA_CONDITION_MAP)
{
if(GetPlayer()->GetMapId() != achievementCriteria->healing_done.mapid)
continue;
+
// map specific case (BG in fact) expected player targeted damage/heal
if(!unit || unit->GetTypeId()!=TYPEID_PLAYER)
continue;
}
+
SetCriteriaProgress(achievementCriteria, miscvalue1, PROGRESS_ACCUMULATE);
break;
}
@@ -1115,6 +1226,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if (miscvalue1 != achievementCriteria->equip_item.itemID)
continue;
+
SetCriteriaProgress(achievementCriteria, 1);
break;
case ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT:
@@ -1123,6 +1235,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if (miscvalue1 != achievementCriteria->use_gameobject.goEntry)
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT:
@@ -1130,12 +1243,14 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
continue;
if (miscvalue1 != achievementCriteria->fish_in_gameobject.goEntry)
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS:
{
if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skillline_spell.skillLine)
continue;
+
uint32 spellCount = 0;
for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
spellIter != GetPlayer()->GetSpellMap().end();
@@ -1155,15 +1270,18 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
// AchievementMgr::UpdateAchievementCriteria might also be called on login - skip in this case
if (!miscvalue1)
continue;
+
if (achievementCriteria->win_duel.duelCount)
{
// those requirements couldn't be found in the dbc
AchievementCriteriaDataSet const* data = achievementmgr.GetCriteriaDataSet(achievementCriteria);
if (!data)
continue;
+
if (!data->Meets(GetPlayer(),unit))
continue;
}
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION:
@@ -1179,6 +1297,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
{
if (miscvalue1 && miscvalue1 != achievementCriteria->learn_skill_line.skillLine)
continue;
+
uint32 spellCount = 0;
for (PlayerSpellMap::const_iterator spellIter = GetPlayer()->GetSpellMap().begin();
spellIter != GetPlayer()->GetSpellMap().end();
@@ -1198,11 +1317,13 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
case ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS:
if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_class.classID)
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_HK_RACE:
if (!miscvalue1 || miscvalue1 != achievementCriteria->hk_race.raceID)
continue;
+
SetCriteriaProgress(achievementCriteria, 1, PROGRESS_ACCUMULATE);
break;
case ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED:
@@ -1243,6 +1364,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
}
if(IsCompletedCriteria(achievementCriteria,achievement))
CompletedCriteriaFor(achievement);
+
// check again the completeness for SUMM and REQ COUNT achievements,
// as they don't depend on the completed criteria but on the sum of the progress of each individual criteria
if (achievement->flags & ACHIEVEMENT_FLAG_SUMM)
@@ -1250,6 +1372,7 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
if (IsCompletedAchievement(achievement))
CompletedAchievement(achievement);
}
+
if(AchievementEntryList const* achRefList = achievementmgr.GetAchievementByReferencedId(achievement->ID))
{
for(AchievementEntryList::const_iterator itr = achRefList->begin(); itr != achRefList->end(); ++itr)
@@ -1258,23 +1381,29 @@ void AchievementMgr::UpdateAchievementCriteria(AchievementCriteriaTypes type, ui
}
}
}
+
static const uint32 achievIdByClass[MAX_CLASSES] = { 0, 459, 465 , 462, 458, 464, 461, 467, 460, 463, 0, 466 };
static const uint32 achievIdByRace[MAX_RACES] = { 0, 1408, 1410, 1407, 1409, 1413, 1411, 1404, 1412, 0, 1405, 1406 };
+
bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achievementCriteria, AchievementEntry const* achievement)
{
// counter can never complete
if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
return false;
+
if(achievement->flags & (ACHIEVEMENT_FLAG_REALM_FIRST_REACH | ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
{
// someone on this realm has already completed that achievement
if(achievementmgr.IsRealmCompleted(achievement))
return false;
}
+
CriteriaProgressMap::const_iterator itr = m_criteriaProgress.find(achievementCriteria->ID);
if(itr == m_criteriaProgress.end())
return false;
+
CriteriaProgress const* progress = &itr->second;
+
switch(achievementCriteria->requiredType)
{
case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
@@ -1287,10 +1416,12 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
for(int i = 1; i < MAX_CLASSES; ++i)
if(achievIdByClass[i] == achievement->ID && i != GetPlayer()->getClass())
return false;
+
// skip wrong race achievements
for(int i = 1; i < MAX_RACES; ++i)
if(achievIdByRace[i] == achievement->ID && i != GetPlayer()->getRace())
return false;
+
// appropriate class/race or not class/race specific
return progress->counter >= achievementCriteria->reach_level.level;
}
@@ -1399,29 +1530,36 @@ bool AchievementMgr::IsCompletedCriteria(AchievementCriteriaEntry const* achieve
}
return false;
}
+
void AchievementMgr::CompletedCriteriaFor(AchievementEntry const* achievement)
{
// counter can never complete
if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER)
return;
+
// already completed and stored
if (m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
return;
+
if (IsCompletedAchievement(achievement))
CompletedAchievement(achievement);
}
+
bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
{
// counter can never complete
if(entry->flags & ACHIEVEMENT_FLAG_COUNTER)
return false;
+
// for achievement with referenced achievement criterias get from referenced and counter from self
uint32 achievmentForTestId = entry->refAchievement ? entry->refAchievement : entry->ID;
uint32 achievmentForTestCount = entry->count;
+
AchievementCriteriaEntryList const* cList = achievementmgr.GetAchievementCriteriaByAchievement(achievmentForTestId);
if(!cList)
return false;
uint32 count = 0;
+
// For SUMM achievements, we have to count the progress of each criteria of the achievement.
// Oddly, the target count is NOT countained in the achievement, but in each individual criteria
if (entry->flags & ACHIEVEMENT_FLAG_SUMM)
@@ -1429,48 +1567,62 @@ bool AchievementMgr::IsCompletedAchievement(AchievementEntry const* entry)
for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
{
AchievementCriteriaEntry const* criteria = *itr;
+
CriteriaProgressMap::const_iterator itrProgress = m_criteriaProgress.find(criteria->ID);
if(itrProgress == m_criteriaProgress.end())
continue;
+
CriteriaProgress const* progress = &itrProgress->second;
count += progress->counter;
+
// for counters, field4 contains the main count requirement
if (count >= criteria->raw.count)
return true;
}
return false;
}
+
// Default case - need complete all or
bool completed_all = true;
for(AchievementCriteriaEntryList::const_iterator itr = cList->begin(); itr != cList->end(); ++itr)
{
AchievementCriteriaEntry const* criteria = *itr;
+
bool completed = IsCompletedCriteria(criteria,entry);
+
// found an uncompleted criteria, but DONT return false yet - there might be a completed criteria with ACHIEVEMENT_CRITERIA_COMPLETE_FLAG_ALL
if(completed)
++count;
else
completed_all = false;
+
// completed as have req. count of completed criterias
if(achievmentForTestCount > 0 && achievmentForTestCount <= count)
return true;
}
+
// all criterias completed requirement
if(completed_all && achievmentForTestCount==0)
return true;
+
return false;
}
+
void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry, uint32 changeValue, ProgressType ptype)
{
if((sLog.getLogFilter() & LOG_FILTER_ACHIEVEMENT_UPDATES)==0)
sLog.outDetail("AchievementMgr::SetCriteriaProgress(%u, %u) for (GUID:%u)", entry->ID, changeValue, m_player->GetGUIDLow());
+
CriteriaProgress *progress = NULL;
+
CriteriaProgressMap::iterator iter = m_criteriaProgress.find(entry->ID);
+
if(iter == m_criteriaProgress.end())
{
// not create record for 0 counter
if(changeValue == 0)
return;
+
progress = &m_criteriaProgress[entry->ID];
progress->counter = changeValue;
progress->date = time(NULL);
@@ -1478,6 +1630,7 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
else
{
progress = &iter->second;
+
uint32 newValue = 0;
switch(ptype)
{
@@ -1495,62 +1648,81 @@ void AchievementMgr::SetCriteriaProgress(AchievementCriteriaEntry const* entry,
newValue = progress->counter < changeValue ? changeValue : progress->counter;
break;
}
+
// not update (not mark as changed) if counter will have same value
if(progress->counter == newValue)
return;
+
progress->counter = newValue;
}
+
progress->changed = true;
+
if(entry->timeLimit)
{
time_t now = time(NULL);
if(time_t(progress->date + entry->timeLimit) < now)
progress->counter = 1;
+
// also it seems illogical, the timeframe will be extended at every criteria update
progress->date = now;
}
SendCriteriaUpdate(entry->ID,progress);
}
+
void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
{
sLog.outDetail("AchievementMgr::CompletedAchievement(%u)", achievement->ID);
+
if(!sWorld.getConfig(CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS) && m_player->GetSession()->GetSecurity() > SEC_PLAYER)
return;
+
if(achievement->flags & ACHIEVEMENT_FLAG_COUNTER || m_completedAchievements.find(achievement->ID)!=m_completedAchievements.end())
return;
+
SendAchievementEarned(achievement);
CompletedAchievementData& ca = m_completedAchievements[achievement->ID];
ca.date = time(NULL);
ca.changed = true;
+
// don't insert for ACHIEVEMENT_FLAG_REALM_FIRST_KILL since otherwise only the first group member would reach that achievement
// TODO: where do set this instead?
if(!(achievement->flags & ACHIEVEMENT_FLAG_REALM_FIRST_KILL))
achievementmgr.SetRealmCompleted(achievement);
+
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT);
+
// reward items and titles if any
AchievementReward const* reward = achievementmgr.GetAchievementReward(achievement);
+
// no rewards
if(!reward)
return;
+
// titles
if(uint32 titleId = reward->titleId[GetPlayer()->GetTeam() == HORDE ? 0 : 1])
{
if(CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(titleId))
GetPlayer()->SetTitle(titleEntry);
}
+
// mail
if(reward->sender)
{
Item* item = reward->itemId ? Item::CreateItem(reward->itemId,1,GetPlayer ()) : NULL;
+
MailItemsInfo mi;
if(item)
{
// save new item before send
item->SaveToDB(); // save for prevent lost at next mail load, if send fail then item will deleted
+
// item
mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
}
+
int loc_idx = GetPlayer()->GetSession()->GetSessionDbLocaleIndex();
+
// subject and text
std::string subject = reward->subject;
std::string text = reward->text;
@@ -1564,28 +1736,36 @@ void AchievementMgr::CompletedAchievement(AchievementEntry const* achievement)
text = loc->text[loc_idx];
}
}
+
uint32 itemTextId = objmgr.CreateItemText( text );
+
WorldSession::SendMailTo(GetPlayer(), MAIL_CREATURE, MAIL_STATIONERY_NORMAL, reward->sender, GetPlayer()->GetGUIDLow(), subject, itemTextId , &mi, 0, 0, MAIL_CHECK_MASK_NONE);
}
}
+
void AchievementMgr::SendAllAchievementData()
{
uint32 size = 18 + m_completedAchievements.size()*8 + m_criteriaProgress.size() * 36;
+
bool send = false;
+
WorldPacket data(SMSG_ALL_ACHIEVEMENT_DATA);
if( size < 0x8000 )
data.resize( size );
else
data.resize( 0x7fff );
// More than this causes client trouble
+
CompletedAchievementMap::const_iterator iter = m_completedAchievements.begin();
CriteriaProgressMap::const_iterator iter2 = m_criteriaProgress.begin();
+
bool cAchievements = false;
bool cProgress = false;
while( !cAchievements || !cProgress )
{
data.clear();
send = false;
+
if( !cAchievements )
{
for(; iter != m_completedAchievements.end() && !send; ++iter)
@@ -1594,9 +1774,11 @@ void AchievementMgr::SendAllAchievementData()
data << uint32(secsToTimeBitFields(iter->second.date));
send = data.size() > 0x7f00;
}
+
if( iter == m_completedAchievements.end() )
cAchievements = true;
}
+
data << int32(-1);
for(; iter2 != m_criteriaProgress.end() && !send; ++iter2)
{
@@ -1609,12 +1791,15 @@ void AchievementMgr::SendAllAchievementData()
data << uint32(0);
send = data.size() > 0x7f00;
}
+
if( iter2 == m_criteriaProgress.end() )
cProgress = true;
+
data << int32(-1);
GetPlayer()->GetSession()->SendPacket(&data);
}
}
+
void AchievementMgr::SendRespondInspectAchievements(Player* player)
{
// since we don't know the exact size of the packed GUIDs this is just an approximation
@@ -1623,6 +1808,7 @@ void AchievementMgr::SendRespondInspectAchievements(Player* player)
BuildAllDataPacket(&data);
player->GetSession()->SendPacket(&data);
}
+
/**
* used by SMSG_RESPOND_INSPECT_ACHIEVEMENT
*/
@@ -1634,6 +1820,7 @@ void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
*data << uint32(secsToTimeBitFields(iter->second.date));
}
*data << int32(-1);
+
for(CriteriaProgressMap::const_iterator iter = m_criteriaProgress.begin(); iter!=m_criteriaProgress.end(); ++iter)
{
*data << uint32(iter->first);
@@ -1644,72 +1831,91 @@ void AchievementMgr::BuildAllDataPacket(WorldPacket *data)
*data << uint32(0);
*data << uint32(0);
}
+
*data << int32(-1);
}
+
//==========================================================
AchievementCriteriaEntryList const& AchievementGlobalMgr::GetAchievementCriteriaByType(AchievementCriteriaTypes type)
{
return m_AchievementCriteriasByType[type];
}
+
void AchievementGlobalMgr::LoadAchievementCriteriaList()
{
if(sAchievementCriteriaStore.GetNumRows()==0)
{
barGoLink bar(1);
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 achievement criteria.");
return;
}
+
barGoLink bar( sAchievementCriteriaStore.GetNumRows() );
for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
{
bar.step();
+
AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
if(!criteria)
continue;
+
m_AchievementCriteriasByType[criteria->requiredType].push_back(criteria);
m_AchievementCriteriaListByAchievement[criteria->referredAchievement].push_back(criteria);
}
+
sLog.outString();
sLog.outString(">> Loaded %lu achievement criteria.",(unsigned long)m_AchievementCriteriasByType->size());
}
+
void AchievementGlobalMgr::LoadAchievementReferenceList()
{
if(sAchievementStore.GetNumRows()==0)
{
barGoLink bar(1);
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 achievement references.");
return;
}
+
uint32 count = 0;
barGoLink bar( sAchievementStore.GetNumRows() );
for (uint32 entryId = 0; entryId < sAchievementStore.GetNumRows(); ++entryId)
{
bar.step();
+
AchievementEntry const* achievement = sAchievementStore.LookupEntry(entryId);
if(!achievement || !achievement->refAchievement)
continue;
+
m_AchievementListByReferencedId[achievement->refAchievement].push_back(achievement);
++count;
}
+
sLog.outString();
sLog.outString(">> Loaded %u achievement references.",count);
}
+
void AchievementGlobalMgr::LoadAchievementCriteriaData()
{
m_criteriaDataMap.clear(); // need for reload case
+
QueryResult *result = WorldDatabase.Query("SELECT criteria_id, type, value1, value2 FROM achievement_criteria_data");
+
if(!result)
{
barGoLink bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded 0 additional achievement criteria data. DB table `achievement_criteria_data` is empty.");
return;
}
+
uint32 count = 0;
uint32 disabled_count = 0;
barGoLink bar(result->GetRowCount());
@@ -1718,34 +1924,45 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
bar.step();
Field *fields = result->Fetch();
uint32 criteria_id = fields[0].GetUInt32();
+
AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(criteria_id);
+
if (!criteria)
{
sLog.outErrorDb( "Table `achievement_criteria_data` have data for not existed criteria (Entry: %u), ignore.", criteria_id);
continue;
}
+
AchievementCriteriaData data(fields[1].GetUInt32(),fields[2].GetUInt32(),fields[3].GetUInt32());
+
if (!data.IsValid(criteria))
{
continue;
}
+
// this will allocate empty data set storage
AchievementCriteriaDataSet& dataSet = m_criteriaDataMap[criteria_id];
+
if (data.dataType == ACHIEVEMENT_CRITERIA_DATA_TYPE_DISABLED)
++disabled_count;
+
// add real data only for not NONE data types
if (data.dataType != ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
dataSet.Add(data);
+
// counting data by and data types
++count;
} while(result->NextRow());
+
delete result;
+
// post loading checks
for (uint32 entryId = 0; entryId < sAchievementCriteriaStore.GetNumRows(); ++entryId)
{
AchievementCriteriaEntry const* criteria = sAchievementCriteriaStore.LookupEntry(entryId);
if(!criteria)
continue;
+
switch(criteria->requiredType)
{
case ACHIEVEMENT_CRITERIA_TYPE_WIN_BG:
@@ -1759,6 +1976,7 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
AchievementEntry const* achievement = sAchievementStore.LookupEntry(criteria->referredAchievement);
if(!achievement)
continue;
+
// exist many achievements with this criteria, use at this moment hardcoded check to skil simple case
switch(achievement->ID)
{
@@ -1798,28 +2016,35 @@ void AchievementGlobalMgr::LoadAchievementCriteriaData()
default: // type not use DB data, ignore
continue;
}
+
if(!GetCriteriaDataSet(criteria))
sLog.outErrorDb( "Table `achievement_criteria_data` not have expected data for criteria (Entry: %u Type: %u) for achievement %u.", criteria->ID, criteria->requiredType, criteria->referredAchievement);
}
+
sLog.outString();
sLog.outString(">> Loaded %u additional achievement criteria data (%u disabled).",count,disabled_count);
}
+
void AchievementGlobalMgr::LoadCompletedAchievements()
{
QueryResult *result = CharacterDatabase.Query("SELECT achievement FROM character_achievement GROUP BY achievement");
+
if(!result)
{
barGoLink bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded 0 realm completed achievements . DB table `character_achievement` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
do
{
bar.step();
Field *fields = result->Fetch();
+
uint32 achievement_id = fields[0].GetUInt32();
if(!sAchievementStore.LookupEntry(achievement_id))
{
@@ -1828,30 +2053,41 @@ void AchievementGlobalMgr::LoadCompletedAchievements()
CharacterDatabase.PExecute("DELETE FROM character_achievement WHERE achievement = %u",achievement_id);
continue;
}
+
m_allCompletedAchievements.insert(achievement_id);
} while(result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %lu realm completed achievements.",(unsigned long)m_allCompletedAchievements.size());
}
+
void AchievementGlobalMgr::LoadRewards()
{
m_achievementRewards.clear(); // need for reload case
+
// 0 1 2 3 4 5 6
QueryResult *result = WorldDatabase.Query("SELECT entry, title_A, title_H, item, sender, subject, text FROM achievement_reward");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 achievement rewards. DB table `achievement_reward` is empty.");
return;
}
+
uint32 count = 0;
barGoLink bar(result->GetRowCount());
+
do
{
bar.step();
+
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
if (!sAchievementStore.LookupEntry(entry))
@@ -1859,6 +2095,7 @@ void AchievementGlobalMgr::LoadRewards()
sLog.outErrorDb( "Table `achievement_reward` has wrong achievement (Entry: %u), ignore", entry);
continue;
}
+
AchievementReward reward;
reward.titleId[0] = fields[1].GetUInt32();
reward.titleId[1] = fields[2].GetUInt32();
@@ -1866,14 +2103,17 @@ void AchievementGlobalMgr::LoadRewards()
reward.sender = fields[4].GetUInt32();
reward.subject = fields[5].GetCppString();
reward.text = fields[6].GetCppString();
+
if ((reward.titleId[0]==0)!=(reward.titleId[1]==0))
sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) has title (A: %u H: %u) only for one from teams.", entry, reward.titleId[0], reward.titleId[1]);
+
// must be title or mail at least
if (!reward.titleId[0] && !reward.titleId[1] && !reward.sender)
{
sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have title or item reward data, ignore.", entry);
continue;
}
+
if (reward.titleId[0])
{
CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[0]);
@@ -1883,6 +2123,7 @@ void AchievementGlobalMgr::LoadRewards()
reward.titleId[0] = 0;
}
}
+
if (reward.titleId[1])
{
CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(reward.titleId[1]);
@@ -1892,6 +2133,7 @@ void AchievementGlobalMgr::LoadRewards()
reward.titleId[1] = 0;
}
}
+
//check mail data before item for report including wrong item case
if (reward.sender)
{
@@ -1905,11 +2147,14 @@ void AchievementGlobalMgr::LoadRewards()
{
if (reward.itemId)
sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have item reward, item will not rewarded", entry);
+
if (!reward.subject.empty())
sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail subject.", entry);
+
if (!reward.text.empty())
sLog.outErrorDb( "Table `achievement_reward` (Entry: %u) not have sender data but have mail text.", entry);
}
+
if (reward.itemId)
{
if (!objmgr.GetItemPrototype(reward.itemId))
@@ -1918,37 +2163,52 @@ void AchievementGlobalMgr::LoadRewards()
reward.itemId = 0;
}
}
+
m_achievementRewards[entry] = reward;
++count;
+
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u achievement rewards", count );
}
+
void AchievementGlobalMgr::LoadRewardLocales()
{
m_achievementRewardLocales.clear(); // need for reload case
+
QueryResult *result = WorldDatabase.Query("SELECT entry,subject_loc1,text_loc1,subject_loc2,text_loc2,subject_loc3,text_loc3,subject_loc4,text_loc4,subject_loc5,text_loc5,subject_loc6,text_loc6,subject_loc7,text_loc7,subject_loc8,text_loc8 FROM locales_achievement_reward");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded 0 achievement reward locale strings. DB table `locales_achievement_reward` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 entry = fields[0].GetUInt32();
+
if(m_achievementRewards.find(entry)==m_achievementRewards.end())
{
sLog.outErrorDb( "Table `locales_achievement_reward` (Entry: %u) has locale strings for not existed achievement reward .", entry);
continue;
}
+
AchievementRewardLocale& data = m_achievementRewardLocales[entry];
+
for(int i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
@@ -1959,6 +2219,7 @@ void AchievementGlobalMgr::LoadRewardLocales()
{
if(data.subject.size() <= size_t(idx))
data.subject.resize(idx+1);
+
data.subject[idx] = str;
}
}
@@ -1970,13 +2231,16 @@ void AchievementGlobalMgr::LoadRewardLocales()
{
if(data.text.size() <= size_t(idx))
data.text.resize(idx+1);
+
data.text[idx] = str;
}
}
}
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu achievement reward locale strings", (unsigned long)m_achievementRewardLocales.size() );
}
diff --git a/src/game/AchievementMgr.h b/src/game/AchievementMgr.h
index 2a2f02e243f..5020539bb5c 100644
--- a/src/game/AchievementMgr.h
+++ b/src/game/AchievementMgr.h
@@ -17,23 +17,29 @@
*/
#ifndef __MANGOS_ACHIEVEMENTMGR_H
#define __MANGOS_ACHIEVEMENTMGR_H
+
#include <map>
#include <string>
+
#include "Common.h"
#include "Policies/Singleton.h"
#include "Database/DatabaseEnv.h"
#include "DBCEnums.h"
#include "DBCStores.h"
+
typedef std::list<AchievementCriteriaEntry const*> AchievementCriteriaEntryList;
typedef std::list<AchievementEntry const*> AchievementEntryList;
+
typedef std::map<uint32,AchievementCriteriaEntryList> AchievementCriteriaListByAchievement;
typedef std::map<uint32,AchievementEntryList> AchievementListByReferencedId;
+
struct CriteriaProgress
{
uint32 counter;
time_t date;
bool changed;
};
+
enum AchievementCriteriaDataType
{ // value1 value2 comment
ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE = 0, // 0 0
@@ -55,9 +61,12 @@ enum AchievementCriteriaDataType
ACHIEVEMENT_CRITERIA_DATA_TYPE_HOLIDAY = 16,// holiday_id 0 event in holiday time
ACHIEVEMENT_CRITERIA_DATA_TYPE_BG_LOSS_TEAM_SCORE = 17,// min_score max_score player's team win bg and opposition team have team score in range
};
+
#define MAX_ACHIEVEMENT_CRITERIA_DATA_TYPE 18 // maximum value in AchievementCriteriaDataType enum
+
class Player;
class Unit;
+
struct AchievementCriteriaData
{
AchievementCriteriaDataType dataType;
@@ -151,19 +160,23 @@ struct AchievementCriteriaData
uint32 value2;
} raw;
};
+
AchievementCriteriaData() : dataType(ACHIEVEMENT_CRITERIA_DATA_TYPE_NONE)
{
raw.value1 = 0;
raw.value2 = 0;
}
+
AchievementCriteriaData(uint32 _dataType, uint32 _value1, uint32 _value2) : dataType(AchievementCriteriaDataType(_dataType))
{
raw.value1 = _value1;
raw.value2 = _value2;
}
+
bool IsValid(AchievementCriteriaEntry const* criteria);
bool Meets(Player const* source, Unit const* target, uint32 miscvalue1 = 0) const;
};
+
struct AchievementCriteriaDataSet
{
typedef std::vector<AchievementCriteriaData> Storage;
@@ -173,7 +186,9 @@ struct AchievementCriteriaDataSet
Storage storage;
};
+
typedef std::map<uint32,AchievementCriteriaDataSet> AchievementCriteriaDataMap;
+
struct AchievementReward
{
uint32 titleId[2];
@@ -182,29 +197,37 @@ struct AchievementReward
std::string subject;
std::string text;
};
+
typedef std::map<uint32,AchievementReward> AchievementRewards;
+
struct AchievementRewardLocale
{
std::vector<std::string> subject;
std::vector<std::string> text;
};
+
typedef std::map<uint32,AchievementRewardLocale> AchievementRewardLocales;
+
struct CompletedAchievementData
{
time_t date;
bool changed;
};
+
typedef UNORDERED_MAP<uint32, CriteriaProgress> CriteriaProgressMap;
typedef UNORDERED_MAP<uint32, CompletedAchievementData> CompletedAchievementMap;
+
class Unit;
class Player;
class WorldPacket;
+
class AchievementMgr
{
public:
AchievementMgr(Player* pl);
~AchievementMgr();
+
void Reset();
static void DeleteFromDB(uint32 lowguid);
void LoadFromDB(QueryResult *achievementResult, QueryResult *criteriaResult);
@@ -216,6 +239,7 @@ class AchievementMgr
void SendAllAchievementData();
void SendRespondInspectAchievements(Player* player);
Player* GetPlayer() { return m_player;}
+
private:
enum ProgressType { PROGRESS_SET, PROGRESS_ACCUMULATE, PROGRESS_HIGHEST };
void SendAchievementEarned(AchievementEntry const* achievement);
@@ -226,10 +250,12 @@ class AchievementMgr
bool IsCompletedAchievement(AchievementEntry const* entry);
void CompleteAchievementsWithRefs(AchievementEntry const* entry);
void BuildAllDataPacket(WorldPacket *data);
+
Player* m_player;
CriteriaProgressMap m_criteriaProgress;
CompletedAchievementMap m_completedAchievements;
};
+
class AchievementGlobalMgr
{
public:
@@ -239,34 +265,41 @@ class AchievementGlobalMgr
AchievementCriteriaListByAchievement::const_iterator itr = m_AchievementCriteriaListByAchievement.find(id);
return itr != m_AchievementCriteriaListByAchievement.end() ? &itr->second : NULL;
}
+
AchievementEntryList const* GetAchievementByReferencedId(uint32 id) const
{
AchievementListByReferencedId::const_iterator itr = m_AchievementListByReferencedId.find(id);
return itr != m_AchievementListByReferencedId.end() ? &itr->second : NULL;
}
+
AchievementReward const* GetAchievementReward(AchievementEntry const* achievement) const
{
AchievementRewards::const_iterator iter = m_achievementRewards.find(achievement->ID);
return iter!=m_achievementRewards.end() ? &iter->second : NULL;
}
+
AchievementRewardLocale const* GetAchievementRewardLocale(AchievementEntry const* achievement) const
{
AchievementRewardLocales::const_iterator iter = m_achievementRewardLocales.find(achievement->ID);
return iter!=m_achievementRewardLocales.end() ? &iter->second : NULL;
}
+
AchievementCriteriaDataSet const* GetCriteriaDataSet(AchievementCriteriaEntry const *achievementCriteria)
{
AchievementCriteriaDataMap::const_iterator iter = m_criteriaDataMap.find(achievementCriteria->ID);
return iter!=m_criteriaDataMap.end() ? &iter->second : NULL;
}
+
bool IsRealmCompleted(AchievementEntry const* achievement) const
{
return m_allCompletedAchievements.find(achievement->ID) != m_allCompletedAchievements.end();
}
+
void SetRealmCompleted(AchievementEntry const* achievement)
{
m_allCompletedAchievements.insert(achievement->ID);
}
+
void LoadAchievementCriteriaList();
void LoadAchievementCriteriaData();
void LoadAchievementReferenceList();
@@ -275,16 +308,21 @@ class AchievementGlobalMgr
void LoadRewardLocales();
private:
AchievementCriteriaDataMap m_criteriaDataMap;
+
// store achievement criterias by type to speed up lookup
AchievementCriteriaEntryList m_AchievementCriteriasByType[ACHIEVEMENT_CRITERIA_TYPE_TOTAL];
// store achievement criterias by achievement to speed up lookup
AchievementCriteriaListByAchievement m_AchievementCriteriaListByAchievement;
// store achievements by referenced achievement id to speed up lookup
AchievementListByReferencedId m_AchievementListByReferencedId;
+
typedef std::set<uint32> AllCompletedAchievements;
AllCompletedAchievements m_allCompletedAchievements;
+
AchievementRewards m_achievementRewards;
AchievementRewardLocales m_achievementRewardLocales;
};
+
#define achievementmgr Trinity::Singleton<AchievementGlobalMgr>::Instance()
+
#endif
diff --git a/src/game/AnimalRandomMovementGenerator.h b/src/game/AnimalRandomMovementGenerator.h
index 4767eff91ad..9c64edcaa44 100644
--- a/src/game/AnimalRandomMovementGenerator.h
+++ b/src/game/AnimalRandomMovementGenerator.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_ANIMAL_RANDOMMOVEMENTGENERATOR_H
#define TRINITY_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,
diff --git a/src/game/ArenaTeam.cpp b/src/game/ArenaTeam.cpp
index c00c22b7b48..22d121c38c1 100644
--- a/src/game/ArenaTeam.cpp
+++ b/src/game/ArenaTeam.cpp
@@ -15,10 +15,13 @@
* 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"
#include "WorldPacket.h"
+
#include "ArenaTeam.h"
#include "World.h"
+
void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot)
{
int32 memberRating = int32(personal_rating) + mod;
@@ -27,6 +30,7 @@ void ArenaTeamMember::ModifyPersonalRating(Player* plr, int32 mod, uint32 slot)
plr->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (slot*6) + 5, personal_rating);
//sLog.outArena("Modify personal rating for player %s: personal rating %u, mod %d, rating %d", plr->GetName(), personal_rating, mod, rating);
}
+
ArenaTeam::ArenaTeam()
{
m_TeamId = 0;
@@ -48,22 +52,29 @@ ArenaTeam::ArenaTeam()
m_stats.wins_week = 0;
m_stats.wins_season = 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));
+
m_CaptainGuid = captainGuid;
m_Name = ArenaTeamName;
m_Type = type;
+
m_TeamId = objmgr.GenerateArenaTeamId();
+
// 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'", m_TeamId); - MAX(arenateam)+1 not exist
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid='%u'", m_TeamId);
@@ -72,18 +83,23 @@ bool ArenaTeam::Create(uint64 captainGuid, uint32 type, std::string ArenaTeamNam
m_TeamId, ArenaTeamName.c_str(), GUID_LOPART(m_CaptainGuid), m_Type, m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor);
CharacterDatabase.PExecute("INSERT INTO arena_team_stats (arenateamid, rating, games, wins, played, wins2, rank) VALUES "
"('%u', '%u', '%u', '%u', '%u', '%u', '%u')", m_TeamId, m_stats.rating, m_stats.games_week, m_stats.wins_week, m_stats.games_season, m_stats.wins_season, m_stats.rank);
+
CharacterDatabase.CommitTransaction();
+
AddMember(m_CaptainGuid);
sLog.outArena("New ArenaTeam created [Id: %u] [Type: %u] [Captain GUID: %u]", GetId(), GetType(), GetCaptain());
return true;
}
+
bool ArenaTeam::AddMember(const uint64& PlayerGuid)
{
std::string plName;
uint8 plClass;
+
// arena team is full (can't have more than type * 2 players!)
if(GetMembersSize() >= GetType() * 2)
return false;
+
Player *pl = objmgr.GetPlayer(PlayerGuid);
if(pl)
{
@@ -92,6 +108,7 @@ bool ArenaTeam::AddMember(const uint64& PlayerGuid)
sLog.outError("Arena::AddMember() : player already in this sized team");
return false;
}
+
plClass = (uint8)pl->getClass();
plName = pl->GetName();
}
@@ -101,9 +118,11 @@ bool ArenaTeam::AddMember(const uint64& PlayerGuid)
QueryResult *result = CharacterDatabase.PQuery("SELECT name, class FROM characters WHERE guid='%u'", GUID_LOPART(PlayerGuid));
if(!result)
return false;
+
plName = (*result)[0].GetCppString();
plClass = (*result)[1].GetUInt8();
delete result;
+
// check if player already in arenateam of that size
if(Player::GetArenaTeamIdFromDB(PlayerGuid, GetType()) != 0)
{
@@ -111,9 +130,11 @@ bool ArenaTeam::AddMember(const uint64& PlayerGuid)
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());
+
ArenaTeamMember newmember;
newmember.name = plName;
newmember.guid = PlayerGuid;
@@ -134,12 +155,15 @@ bool ArenaTeam::AddMember(const uint64& PlayerGuid)
newmember.personal_rating = 1500;
}
m_members.push_back(newmember);
+
CharacterDatabase.PExecute("INSERT INTO arena_team_member (arenateamid, guid, personal_rating) VALUES ('%u', '%u', '%u')", m_TeamId, GUID_LOPART(newmember.guid), newmember.personal_rating );
+
if(pl)
{
pl->SetInArenaTeam(m_TeamId, GetSlot());
pl->SetArenaTeamIdInvited(0);
pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot()*6) + 5, newmember.personal_rating );
+
// hide promote/remove buttons
if(m_CaptainGuid != PlayerGuid)
pl->SetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (GetSlot() * 6) + 1, 1);
@@ -147,12 +171,16 @@ bool ArenaTeam::AddMember(const uint64& PlayerGuid)
}
return true;
}
+
bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId)
{
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();
+
m_TeamId = fields[0].GetUInt32();
m_Name = fields[1].GetCppString();
m_CaptainGuid = MAKE_NEW_GUID(fields[2].GetUInt32(), 0, HIGHGUID_PLAYER);
@@ -162,10 +190,13 @@ bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId)
m_EmblemColor = fields[6].GetUInt32();
m_BorderStyle = fields[7].GetUInt32();
m_BorderColor = fields[8].GetUInt32();
+
delete result;
+
// only load here, so additional checks can be made
LoadStatsFromDB(ArenaTeamId);
LoadMembersFromDB(ArenaTeamId);
+
if(Empty())
{
// arena team is empty, delete from db
@@ -176,23 +207,30 @@ bool ArenaTeam::LoadArenaTeamFromDB(uint32 ArenaTeamId)
CharacterDatabase.CommitTransaction();
return false;
}
+
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();
+
m_stats.rating = fields[0].GetUInt32();
m_stats.games_week = fields[1].GetUInt32();
m_stats.wins_week = fields[2].GetUInt32();
m_stats.games_season = fields[3].GetUInt32();
m_stats.wins_season = fields[4].GetUInt32();
m_stats.rank = fields[5].GetUInt32();
+
delete result;
}
+
void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId)
{
// 0 1 2 3 4 5 6 7
@@ -202,6 +240,7 @@ void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId)
"WHERE member.arenateamid = '%u'", ArenaTeamId);
if(!result)
return;
+
do
{
Field *fields = result->Fetch();
@@ -218,16 +257,20 @@ void ArenaTeam::LoadMembersFromDB(uint32 ArenaTeamId)
}while( result->NextRow() );
delete result;
}
+
void ArenaTeam::SetCaptain(const 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);
+
// set new captain
m_CaptainGuid = guid;
+
// update database
CharacterDatabase.PExecute("UPDATE arena_team SET captainguid = '%u' WHERE arenateamid = '%u'", GUID_LOPART(guid), m_TeamId);
+
// enable remove/promote buttons
Player *newcaptain = objmgr.GetPlayer(guid);
if(newcaptain)
@@ -236,6 +279,7 @@ void ArenaTeam::SetCaptain(const uint64& guid)
sLog.outArena("Player: %s [GUID: %u] promoted player: %s [GUID: %u] to leader of arena team [Id: %u] [Type: %u].", oldcaptain->GetName(), oldcaptain->GetGUIDLow(), newcaptain->GetName(), newcaptain->GetGUID(), GetId(), GetType());
}
}
+
void ArenaTeam::DelMember(uint64 guid)
{
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
@@ -246,7 +290,9 @@ void ArenaTeam::DelMember(uint64 guid)
break;
}
}
+
Player *player = objmgr.GetPlayer(guid);
+
if(player)
{
player->SetInArenaTeam(0, GetSlot());
@@ -260,19 +306,23 @@ void ArenaTeam::DelMember(uint64 guid)
}
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u' AND guid = '%u'", GetId(), 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);
+
while (!m_members.empty())
{
// Removing from members is done in DelMember.
DelMember(m_members.front().guid);
}
+
if(Player *player = session->GetPlayer())
sLog.outArena("Player: %s [GUID: %u] disbanded arena team type: %u [Id: %u].", player->GetName(), player->GetGUIDLow(), GetType(), GetId());
+
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM arena_team WHERE arenateamid = '%u'", m_TeamId);
CharacterDatabase.PExecute("DELETE FROM arena_team_member WHERE arenateamid = '%u'", m_TeamId); //< this should be alredy done by calling DelMember(memberGuids[j]); for each member
@@ -280,18 +330,23 @@ void ArenaTeam::Disband(WorldSession *session)
CharacterDatabase.CommitTransaction();
objmgr.RemoveArenaTeam(m_TeamId);
}
+
void ArenaTeam::Roster(WorldSession *session)
{
Player *pl = NULL;
+
uint8 unk308 = 0;
+
WorldPacket data(SMSG_ARENA_TEAM_ROSTER, 100);
data << uint32(GetId()); // team id
data << uint8(unk308); // 308 unknown value but affect packet structure
data << uint32(GetMembersSize()); // members count
data << uint32(GetType()); // arena team type?
+
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
{
pl = objmgr.GetPlayer(itr->guid);
+
data << uint64(itr->guid); // guid
data << uint8((pl ? 1 : 0)); // online flag
data << itr->name; // member name
@@ -309,9 +364,11 @@ void ArenaTeam::Roster(WorldSession *session)
data << float(0.0); // 308 unk
}
}
+
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);
@@ -326,6 +383,7 @@ void ArenaTeam::Query(WorldSession *session)
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);
@@ -338,6 +396,7 @@ void ArenaTeam::Stats(WorldSession *session)
data << uint32(m_stats.rank); // rank
session->SendPacket(&data);
}
+
void ArenaTeam::NotifyStatsChanged()
{
// this is called after a rated match ended
@@ -349,11 +408,13 @@ void ArenaTeam::NotifyStatsChanged()
Stats(plr->GetSession());
}
}
+
void ArenaTeam::InspectStats(WorldSession *session, uint64 guid)
{
ArenaTeamMember* member = GetMember(guid);
if(!member)
return;
+
WorldPacket data(MSG_INSPECT_ARENA_TEAMS, 8+1+4*6);
data << uint64(guid); // player guid
data << uint8(GetSlot()); // slot (0...2)
@@ -365,6 +426,7 @@ void ArenaTeam::InspectStats(WorldSession *session, uint64 guid)
data << uint32(member->personal_rating); // personal rating
session->SendPacket(&data);
}
+
void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor)
{
m_BackgroundColor = backgroundColor;
@@ -372,8 +434,10 @@ void ArenaTeam::SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emb
m_EmblemColor = emblemColor;
m_BorderStyle = borderStyle;
m_BorderColor = borderColor;
+
CharacterDatabase.PExecute("UPDATE arena_team SET BackgroundColor='%u', EmblemStyle='%u', EmblemColor='%u', BorderStyle='%u', BorderColor='%u' WHERE arenateamid='%u'", m_BackgroundColor, m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_TeamId);
}
+
void ArenaTeam::SetStats(uint32 stat_type, uint32 value)
{
switch(stat_type)
@@ -407,6 +471,7 @@ void ArenaTeam::SetStats(uint32 stat_type, uint32 value)
break;
}
}
+
void ArenaTeam::BroadcastPacket(WorldPacket *packet)
{
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
@@ -416,6 +481,7 @@ void ArenaTeam::BroadcastPacket(WorldPacket *packet)
player->GetSession()->SendPacket(packet);
}
}
+
uint8 ArenaTeam::GetSlotByType( uint32 type )
{
switch(type)
@@ -429,45 +495,56 @@ uint8 ArenaTeam::GetSlotByType( uint32 type )
sLog.outError("FATAL: Unknown arena team type %u for some arena team", type);
return 0xFF;
}
+
bool ArenaTeam::HaveMember( const uint64& guid ) const
{
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
if(itr->guid == guid)
return true;
+
return false;
}
+
uint32 ArenaTeam::GetPoints(uint32 MemberRating)
{
// returns how many points would be awarded with this team type with this rating
float points;
+
uint32 rating = MemberRating + 150 < m_stats.rating ? MemberRating : m_stats.rating;
+
if(rating<=1500)
// points = (float)1500 * 0.22f + 14.0f;
points = 344.0f; // 3.1 change - teams with rating below 1500 get arena points for 1500 rating
else
points = 1511.26f / (1.0f + 1639.28f * exp(-0.00412f * (float)rating));
+
// type penalties for <5v5 teams
if(m_Type == ARENA_TEAM_2v2)
points *= 0.76f;
else if(m_Type == ARENA_TEAM_3v3)
points *= 0.88f;
+
return (uint32) points;
}
+
float ArenaTeam::GetChanceAgainst(uint32 own_rating, uint32 enemy_rating)
{
// returns the chance to win against a team with the given rating, used in the rating adjustment calculation
// ELO system
+
if (sWorld.getConfig(CONFIG_ARENA_SEASON_ID) >= 6)
if (enemy_rating < 1300)
enemy_rating = 1300;
return 1.0f/(1.0f+exp(log(10.0f)*(float)((float)enemy_rating - (float)own_rating)/400.0f));
}
+
void ArenaTeam::FinishGame(int32 mod)
{
if (int32(m_stats.rating) + mod < 0)
m_stats.rating = 0;
else
m_stats.rating += mod;
+
m_stats.games_week += 1;
m_stats.games_season += 1;
// update team's rank
@@ -479,6 +556,7 @@ void ArenaTeam::FinishGame(int32 mod)
++m_stats.rank;
}
}
+
int32 ArenaTeam::WonAgainst(uint32 againstRating)
{
// called when the team has won
@@ -490,9 +568,11 @@ int32 ArenaTeam::WonAgainst(uint32 againstRating)
FinishGame(mod);
m_stats.wins_week += 1;
m_stats.wins_season += 1;
+
// return the rating change, used to display it on the results screen
return mod;
}
+
int32 ArenaTeam::LostAgainst(uint32 againstRating)
{
// called when the team has lost
@@ -502,9 +582,11 @@ int32 ArenaTeam::LostAgainst(uint32 againstRating)
int32 mod = (int32)ceil(32.0f * (0.0f - chance));
// modify the team stats accordingly
FinishGame(mod);
+
// return the rating change, used to display it on the results screen
return mod;
}
+
void ArenaTeam::MemberLost(Player * plr, uint32 againstRating)
{
// called for each participant of a match after losing
@@ -526,6 +608,7 @@ void ArenaTeam::MemberLost(Player * plr, uint32 againstRating)
}
}
}
+
void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating)
{
// called for offline player after ending rated arena match!
@@ -547,6 +630,7 @@ void ArenaTeam::OfflineMemberLost(uint64 guid, uint32 againstRating)
}
}
}
+
void ArenaTeam::MemberWon(Player * plr, uint32 againstRating)
{
// called for each participant after winning a match
@@ -570,6 +654,7 @@ void ArenaTeam::MemberWon(Player * plr, uint32 againstRating)
}
}
}
+
void ArenaTeam::UpdateArenaPointsHelper(std::map<uint32, uint32>& PlayerPoints)
{
// called after a match has ended and the stats are already modified
@@ -586,6 +671,7 @@ void ArenaTeam::UpdateArenaPointsHelper(std::map<uint32, uint32>& PlayerPoints)
if (itr->games_week >= min_plays)
points_to_add = GetPoints(itr->personal_rating);
// OBSOLETE : CharacterDatabase.PExecute("UPDATE arena_team_member SET points_to_add = '%u' WHERE arenateamid = '%u' AND guid = '%u'", points_to_add, m_TeamId, itr->guid);
+
std::map<uint32, uint32>::iterator plr_itr = PlayerPoints.find(GUID_LOPART(itr->guid));
if (plr_itr != PlayerPoints.end())
{
@@ -597,6 +683,7 @@ void ArenaTeam::UpdateArenaPointsHelper(std::map<uint32, uint32>& PlayerPoints)
PlayerPoints[GUID_LOPART(itr->guid)] = points_to_add;
}
}
+
void ArenaTeam::SaveToDB()
{
// save team and member stats to db
@@ -609,6 +696,7 @@ void ArenaTeam::SaveToDB()
}
CharacterDatabase.CommitTransaction();
}
+
void ArenaTeam::FinishWeek()
{
m_stats.games_week = 0; // played this week
@@ -619,6 +707,7 @@ void ArenaTeam::FinishWeek()
itr->wins_week = 0;
}
}
+
bool ArenaTeam::IsFighting() const
{
for (MemberList::const_iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
@@ -631,6 +720,7 @@ bool ArenaTeam::IsFighting() const
}
return false;
}
+
/*
arenateam fields (id from 2.3.3 client):
1414 - arena team id 2v2
diff --git a/src/game/ArenaTeam.h b/src/game/ArenaTeam.h
index fe7a4d04ea5..dbeac2f09f0 100644
--- a/src/game/ArenaTeam.h
+++ b/src/game/ArenaTeam.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_ARENATEAM_H
#define TRINITYCORE_ARENATEAM_H
+
enum ArenaTeamCommandTypes
{
ERR_ARENA_TEAM_CREATE_S = 0x00,
@@ -27,6 +29,7 @@ enum ArenaTeamCommandTypes
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,
@@ -46,6 +49,7 @@ enum ArenaTeamCommandErrors
ERR_ARENA_TEAM_PLAYER_TO_LOW = 0x15,
ERR_ARENA_TEAM_FULL = 0x16
};
+
enum ArenaTeamEvents
{
ERR_ARENA_TEAM_JOIN_SS = 3, // player name + arena team name
@@ -55,6 +59,7 @@ enum ArenaTeamEvents
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
@@ -62,6 +67,7 @@ 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,
@@ -71,12 +77,14 @@ enum ArenaTeamStatTypes
STAT_TYPE_WINS_SEASON = 4,
STAT_TYPE_RANK = 5
};
+
enum ArenaTeamTypes
{
ARENA_TEAM_2v2 = 2,
ARENA_TEAM_3v3 = 3,
ARENA_TEAM_5v5 = 5
};
+
struct ArenaTeamMember
{
uint64 guid;
@@ -87,8 +95,10 @@ struct ArenaTeamMember
uint32 games_season;
uint32 wins_season;
uint32 personal_rating;
+
void ModifyPersonalRating(Player* plr, int32 mod, uint32 slot);
};
+
struct ArenaTeamStats
{
uint32 rating;
@@ -98,15 +108,20 @@ struct ArenaTeamStats
uint32 wins_season;
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<ArenaTeamMember> MemberList;
+
uint32 GetId() const { return m_TeamId; }
uint32 GetType() const { return m_Type; }
uint8 GetSlot() const { return GetSlotByType(GetType()); }
@@ -116,46 +131,61 @@ class ArenaTeam
const ArenaTeamStats& GetStats() const { return m_stats; }
void SetStats(uint32 stat_type, uint32 value);
uint32 GetRating() const { return m_stats.rating; }
+
uint32 GetEmblemStyle() const { return m_EmblemStyle; }
uint32 GetEmblemColor() const { return m_EmblemColor; }
uint32 GetBorderStyle() const { return m_BorderStyle; }
uint32 GetBorderColor() const { return m_BorderColor; }
uint32 GetBackgroundColor() const { return m_BackgroundColor; }
+
void SetCaptain(const uint64& guid);
bool AddMember(const uint64& PlayerGuid);
+
// Shouldn't be const uint64& ed, because than can reference guid from members on Disband
// and this method removes given record from list. So invalid reference can happen.
void DelMember(uint64 guid);
+
void SetEmblem(uint32 backgroundColor, uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor);
+
size_t GetMembersSize() const { return m_members.size(); }
bool Empty() const { return m_members.empty(); }
MemberList::iterator m_membersBegin() { return m_members.begin(); }
MemberList::iterator m_membersEnd() { return m_members.end(); }
bool HaveMember(const uint64& guid) const;
+
ArenaTeamMember* GetMember(const uint64& guid)
{
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
if(itr->guid == guid)
return &(*itr);
+
return NULL;
}
+
ArenaTeamMember* GetMember(const std::string& name)
{
for (MemberList::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
if(itr->name == name)
return &(*itr);
+
return NULL;
}
+
bool IsFighting() const;
+
bool LoadArenaTeamFromDB(uint32 ArenaTeamId);
void LoadMembersFromDB(uint32 ArenaTeamId);
void LoadStatsFromDB(uint32 ArenaTeamId);
+
void SaveToDB();
+
void BroadcastPacket(WorldPacket *packet);
+
void Roster(WorldSession *session);
void Query(WorldSession *session);
void Stats(WorldSession *session);
void InspectStats(WorldSession *session, uint64 guid);
+
uint32 GetPoints(uint32 MemberRating);
float GetChanceAgainst(uint32 own_rating, uint32 enemy_rating);
int32 WonAgainst(uint32 againstRating);
@@ -163,20 +193,27 @@ class ArenaTeam
int32 LostAgainst(uint32 againstRating);
void MemberLost(Player * plr, uint32 againstRating);
void OfflineMemberLost(uint64 guid, uint32 againstRating);
+
void UpdateArenaPointsHelper(std::map<uint32, uint32> & PlayerPoints);
+
void NotifyStatsChanged();
+
void FinishWeek();
void FinishGame(int32 mod);
+
protected:
+
uint32 m_TeamId;
uint32 m_Type;
std::string m_Name;
uint64 m_CaptainGuid;
+
uint32 m_BackgroundColor; // ARGB format
uint32 m_EmblemStyle; // icon id
uint32 m_EmblemColor; // ARGB format
uint32 m_BorderStyle; // border image id
uint32 m_BorderColor; // ARGB format
+
MemberList m_members;
ArenaTeamStats m_stats;
};
diff --git a/src/game/ArenaTeamHandler.cpp b/src/game/ArenaTeamHandler.cpp
index b5ecab3d424..c7ecf5b1d80 100644
--- a/src/game/ArenaTeamHandler.cpp
+++ b/src/game/ArenaTeamHandler.cpp
@@ -17,21 +17,26 @@
* 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 "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Database/DatabaseEnv.h"
+
#include "ArenaTeam.h"
#include "Log.h"
#include "ObjectMgr.h"
#include "SocialMgr.h"
+
void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recv_data)
{
sLog.outDebug("MSG_INSPECT_ARENA_TEAMS");
+
uint64 guid;
recv_data >> guid;
sLog.outDebug("Inspect Arena stats (GUID: %u TypeId: %u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)));
+
if(Player *plr = objmgr.GetPlayer(guid))
{
for (uint8 i = 0; i < MAX_ARENA_SLOT; ++i)
@@ -44,123 +49,160 @@ void WorldSession::HandleInspectArenaTeamsOpcode(WorldPacket & recv_data)
}
}
}
+
void WorldSession::HandleArenaTeamQueryOpcode(WorldPacket & recv_data)
{
sLog.outDebug( "WORLD: Received CMSG_ARENA_TEAM_QUERY" );
+
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" );
+
uint32 ArenaTeamId; // arena team id
recv_data >> ArenaTeamId;
+
ArenaTeam *arenateam = objmgr.GetArenaTeamById(ArenaTeamId);
if(!arenateam)
return;
+
arenateam->Roster(this);
}
+
void WorldSession::HandleArenaTeamInviteOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_INVITE");
+
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_CREATE_S, "", Invitedname, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
+
if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", player->GetName(), ERR_ARENA_TEAM_PLAYER_TO_LOW);
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)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,arenateam->GetName(),"",ERR_ARENA_TEAM_FULL);
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::HandleArenaTeamAcceptOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("CMSG_ARENA_TEAM_ACCEPT"); // empty opcode
+
ArenaTeam *at = objmgr.GetArenaTeamById(_player->GetArenaTeamIdInvited());
if(!at)
return;
+
if(_player->GetArenaTeamId(at->GetSlot()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ALREADY_IN_ARENA_TEAM); // already in arena team that size
return;
}
+
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && _player->GetTeam() != objmgr.GetPlayerTeamByGUID(at->GetCaptain()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_NOT_ALLIED);// not let enemies sign petition
return;
}
+
if(!at->AddMember(_player->GetGUID()))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S,"","",ERR_ARENA_TEAM_INTERNAL);// arena team not found
return;
}
+
// event
WorldPacket data;
BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_JOIN_SS, 2, _player->GetName(), at->GetName(), "");
at->BroadcastPacket(&data);
}
+
void WorldSession::HandleArenaTeamDeclineOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDebug("CMSG_ARENA_TEAM_DECLINE"); // empty opcode
+
_player->SetArenaTeamIdInvited(0); // no more invited
}
+
void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_LEAVE");
+
uint32 ArenaTeamId; // arena team id
recv_data >> ArenaTeamId;
+
ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
if(!at)
return;
@@ -177,94 +219,124 @@ void WorldSession::HandleArenaTeamLeaveOpcode(WorldPacket & recv_data)
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);
+
//send you are no longer member of team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, at->GetName(), "", 0);
}
+
void WorldSession::HandleArenaTeamDisbandOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_DISBAND");
+
uint32 ArenaTeamId; // arena team id
recv_data >> ArenaTeamId;
+
ArenaTeam *at = objmgr.GetArenaTeamById(ArenaTeamId);
if(!at)
return;
+
if(at->GetCaptain() != _player->GetGUID())
return;
+
if (at->IsFighting())
return;
+
at->Disband(this);
delete at;
}
+
void WorldSession::HandleArenaTeamRemoveOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_REMOVE");
+
uint32 ArenaTeamId;
std::string name;
+
recv_data >> ArenaTeamId;
recv_data >> name;
+
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;
}
+
if(!normalizePlayerName(name))
return;
+
ArenaTeamMember* member = at->GetMember(name);
if(!member) // member not found
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
+
if(at->GetCaptain() == member->guid)
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_QUIT_S, "", "", ERR_ARENA_TEAM_LEADER_LEAVE_S);
return;
}
+
at->DelMember(member->guid);
+
// event
WorldPacket data;
BuildArenaTeamEventPacket(&data, ERR_ARENA_TEAM_REMOVE_SSS, 3, name, at->GetName(), _player->GetName());
at->BroadcastPacket(&data);
}
+
void WorldSession::HandleArenaTeamLeaderOpcode(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_ARENA_TEAM_LEADER");
+
uint32 ArenaTeamId;
std::string name;
+
recv_data >> ArenaTeamId;
recv_data >> name;
+
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;
}
+
if(!normalizePlayerName(name))
return;
+
ArenaTeamMember* member = at->GetMember(name);
if(!member) // member not found
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", name, ERR_ARENA_TEAM_PLAYER_NOT_FOUND_S);
return;
}
+
if(at->GetCaptain() == member->guid) // target player already captain
return;
+
at->SetCaptain(member->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 team_action, const std::string& team, const std::string& player, uint32 error_id)
{
WorldPacket data(SMSG_ARENA_TEAM_COMMAND_RESULT, 4+team.length()+1+player.length()+1+4);
@@ -274,6 +346,7 @@ void WorldSession::SendArenaTeamCommandResult(uint32 team_action, const std::str
data << error_id;
SendPacket(&data);
}
+
void WorldSession::BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, uint8 str_count, const std::string& str1, const std::string& str2, const std::string& str3)
{
data->Initialize(SMSG_ARENA_TEAM_EVENT, 1+1+1);
@@ -298,6 +371,7 @@ void WorldSession::BuildArenaTeamEventPacket(WorldPacket *data, uint8 eventid, u
return;
}
}
+
void WorldSession::SendNotInArenaTeamPacket(uint8 type)
{
WorldPacket data(SMSG_ARENA_ERROR, 4+1); // 886 - You are not in a %uv%u arena team
@@ -307,12 +381,15 @@ void WorldSession::SendNotInArenaTeamPacket(uint8 type)
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"
@@ -326,15 +403,23 @@ ERR_ARENA_TEAM_FOUNDER_S "Congratulations, you are a founding member of %s! To
+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/AuctionHouseBot.cpp b/src/game/AuctionHouseBot.cpp
index 4722aace318..cef09af8252 100644
--- a/src/game/AuctionHouseBot.cpp
+++ b/src/game/AuctionHouseBot.cpp
@@ -2,8 +2,10 @@
#include "AuctionHouseMgr.h"
#include "AuctionHouseBot.h"
#include <vector>
+
#include "Policies/SingletonImp.h"
INSTANTIATE_SINGLETON_1(AuctionHouseBot);
+
using namespace std;
vector<uint32> npcItems;
vector<uint32> lootItems;
@@ -27,18 +29,22 @@ AuctionHouseBot::AuctionHouseBot()
debug_Out_Filters = false;
AHBSeller = false;
AHBBuyer = false;
+
//Begin Filters
+
Vendor_Items = false;
Loot_Items = false;
Other_Items = false;
Vendor_TGs = false;
Loot_TGs = false;
Other_TGs = false;
+
No_Bind = false;
Bind_When_Picked_Up = false;
Bind_When_Equipped = false;
Bind_When_Use = false;
Bind_Quest_Item = false;
+
DisableBeta_PTR_Unused = false;
DisablePermEnchant = false;
#if CLIENT_VER > 300
@@ -51,6 +57,7 @@ AuctionHouseBot::AuctionHouseBot()
DisableKeys = false;
DisableDuration = false;
DisableBOP_Or_Quest_NoReqLevel = false;
+
DisableWarriorItems = false;
DisablePaladinItems = false;
DisableHunterItems = false;
@@ -62,6 +69,7 @@ AuctionHouseBot::AuctionHouseBot()
DisableWarlockItems = false;
DisableUnusedClassItems = false;
DisableDruidItems = false;
+
DisableItemsBelowLevel = 0;
DisableItemsAboveLevel = 0;
DisableTGsBelowLevel = 0;
@@ -78,17 +86,22 @@ AuctionHouseBot::AuctionHouseBot()
DisableItemsAboveReqSkillRank = 0;
DisableTGsBelowReqSkillRank = 0;
DisableTGsAboveReqSkillRank = 0;
+
//End Filters
+
_lastrun_a = time(NULL);
_lastrun_h = time(NULL);
_lastrun_n = time(NULL);
+
AllianceConfig = AHBConfig(2);
HordeConfig = AHBConfig(6);
NeutralConfig = AHBConfig(7);
}
+
AuctionHouseBot::~AuctionHouseBot()
{
}
+
void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
{
if (!AHBSeller)
@@ -96,13 +109,16 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
if (debug_Out) sLog.outError("AHSeller: Disabled");
return;
}
+
uint32 minItems = config->GetMinItems();
uint32 maxItems = config->GetMaxItems();
+
if (maxItems == 0)
{
//if (debug_Out) sLog.outString("AHSeller: Auctions disabled");
return;
}
+
AuctionHouseEntry const* ahEntry = auctionmgr.GetAuctionHouseEntry(config->GetAHFID());
if (!ahEntry)
{
@@ -113,24 +129,31 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
{
return;
}
+
uint32 auctions = auctionHouse->Getcount();
+
if (auctions >= minItems)
{
//if (debug_Out) sLog.outString("AHSeller: Auctions above minimum");
return;
}
+
if (auctions >= maxItems)
{
//if (debug_Out) sLog.outString("AHSeller: Auctions at or above maximum");
return;
}
+
uint32 items = 0;
if ((maxItems - auctions) >= ItemsPerCycle)
items = ItemsPerCycle;
else
items = (maxItems - auctions);
+
if (debug_Out) sLog.outString("AHSeller: Adding %u Auctions", items);
+
uint32 AuctioneerGUID = 0;
+
switch (config->GetAHID())
{
case 2:
@@ -147,7 +170,9 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
AuctioneerGUID = 23442; //default to neutral 7
break;
}
+
if (debug_Out) sLog.outString("AHSeller: Current Auctineer GUID is %u", AuctioneerGUID);
+
uint32 greyTGcount = config->GetPercents(AHB_GREY_TG);
uint32 whiteTGcount = config->GetPercents(AHB_WHITE_TG);
uint32 greenTGcount = config->GetPercents(AHB_GREEN_TG);
@@ -166,6 +191,7 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
+ purpleTGcount + orangeTGcount + yellowTGcount
+ whiteIcount + greenIcount + blueIcount + purpleIcount
+ orangeIcount + yellowIcount;
+
uint32 greyTGoods = config->GetItemCounts(AHB_GREY_TG);
uint32 whiteTGoods = config->GetItemCounts(AHB_WHITE_TG);
uint32 greenTGoods = config->GetItemCounts(AHB_GREEN_TG);
@@ -173,6 +199,7 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
uint32 purpleTGoods = config->GetItemCounts(AHB_PURPLE_TG);
uint32 orangeTGoods = config->GetItemCounts(AHB_ORANGE_TG);
uint32 yellowTGoods = config->GetItemCounts(AHB_YELLOW_TG);
+
uint32 greyItems = config->GetItemCounts(AHB_GREY_I);
uint32 whiteItems = config->GetItemCounts(AHB_WHITE_I);
uint32 greenItems = config->GetItemCounts(AHB_GREEN_I);
@@ -181,6 +208,7 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
uint32 orangeItems = config->GetItemCounts(AHB_ORANGE_I);
uint32 yellowItems = config->GetItemCounts(AHB_YELLOW_I);
if (debug_Out) sLog.outString("AHSeller: %u items", items);
+
// only insert a few at a time, so as not to peg the processor
for (uint32 cnt = 1;cnt <= items;cnt++)
{
@@ -299,17 +327,20 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
break;
}
}
+
if (itemID == 0)
{
if (debug_Out) sLog.outError("AHSeller: Item::CreateItem() - ItemID is 0");
continue;
}
+
ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID);
if (prototype == NULL)
{
if (debug_Out) sLog.outError("AHSeller: Huh?!?! prototype == NULL");
continue;
}
+
Item* item = Item::CreateItem(itemID, 1, AHBplayer);
if (item == NULL)
{
@@ -317,12 +348,15 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
break;
}
item->AddToUpdateQueueOf(AHBplayer);
+
uint32 randomPropertyId = Item::GenerateItemRandomPropertyId(itemID);
if (randomPropertyId != 0)
item->SetItemRandomProperties(randomPropertyId);
+
uint64 buyoutPrice = 0;
uint64 bidPrice = 0;
uint32 stackCount = 1;
+
switch (SellMethod)
{
case 0:
@@ -332,6 +366,7 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
buyoutPrice = prototype->BuyPrice;
break;
}
+
if ((prototype->Quality >= 0) && (prototype->Quality <= AHB_MAX_QUALITY))
{
if (config->GetMaxStack(prototype->Quality) > 1 && item->GetMaxStackCount() > 1)
@@ -352,6 +387,7 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
item->RemoveFromUpdateQueueOf(AHBplayer);
continue;
}
+
uint32 etime = urand(1,3);
switch(etime)
{
@@ -369,7 +405,9 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
break;
}
item->SetCount(stackCount);
+
uint32 dep = auctionmgr.GetAuctionDeposit(ahEntry, etime, item);
+
AuctionEntry* auctionEntry = new AuctionEntry;
auctionEntry->Id = objmgr.GenerateAuctionID();
auctionEntry->auctioneer = AuctioneerGUID;
@@ -388,6 +426,7 @@ void AuctionHouseBot::addNewAuctions(Player *AHBplayer, AHBConfig *config)
auctionmgr.AddAItem(item);
auctionHouse->AddAuction(auctionEntry);
auctionEntry->SaveToDB();
+
switch(itemColor)
{
case 0:
@@ -445,26 +484,32 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
if (debug_Out) sLog.outError("AHBuyer: Disabled");
return;
}
+
QueryResult* result = CharacterDatabase.PQuery("SELECT id FROM auctionhouse WHERE itemowner<>%u AND buyguid<>%u", AHBplayerGUID, AHBplayerGUID);
+
if (!result)
{
delete result;
return;
}
+
if (result->GetRowCount() == 0)
{
delete result;
return;
}
+
// Fetches content of selected AH
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID());
vector<uint32> possibleBids;
+
do
{
uint32 tmpdata = result->Fetch()->GetUInt32();
possibleBids.push_back(tmpdata);
}while (result->NextRow());
delete result;
+
for (uint32 count = 1;count <= config->GetBidsPerInterval();++count)
{
// Do we have anything to bid? If not, stop here.
@@ -474,18 +519,23 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
count = config->GetBidsPerInterval();
continue;
}
+
// Choose random auction from possible auctions
uint32 vectorPos = urand(0, possibleBids.size() - 1);
vector<uint32>::iterator iter = possibleBids.begin();
advance(iter, vectorPos);
+
// from auctionhousehandler.cpp, creates auction pointer & player pointer
AuctionEntry* auction = auctionHouse->GetAuction(*iter);
+
// Erase the auction from the vector to prevent bidding on item in next iteration.
possibleBids.erase(iter);
+
if (!auction)
{
continue;
}
+
// get exact item information
Item *pItem = auctionmgr.GetAItem(auction->item_guidlow);
if (!pItem)
@@ -493,17 +543,21 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
if (debug_Out) sLog.outError("AHBuyer: Item %u doesn't exist, perhaps bought already?", auction->item_guidlow);
continue;
}
+
// get item prototype
ItemPrototype const* prototype = objmgr.GetItemPrototype(auction->item_template);
+
// check which price we have to use, startbid or if it is bidded already
uint32 currentprice;
if (auction->bid)
currentprice = auction->bid;
else
currentprice = auction->startbid;
+
// Prepare portion from maximum bid
double bidrate = static_cast<double>(urand(1, 100)) / 100;
long double bidMax = 0;
+
// check that bid has acceptable value and take bid based on vendorprice, stacksize and quality
switch (BuyMethod)
{
@@ -538,6 +592,7 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
break;
}
}
+
// check some special items, and do recalculating to their prices
switch (prototype->Class)
{
@@ -548,18 +603,22 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
default:
break;
}
+
if (bidMax == 0)
{
// quality check failed to get bidmax, let's get out of here
continue;
}
+
// Calculate our bid
long double bidvalue = currentprice + ((bidMax - currentprice) * bidrate);
// Convert to uint32
uint32 bidprice = static_cast<uint32>(bidvalue);
+
// Check our bid is high enough to be valid. If not, correct it to minimum.
if ((currentprice + auction->GetAuctionOutBid()) > bidprice)
bidprice = currentprice + auction->GetAuctionOutBid();
+
if (debug_Out)
{
sLog.outString("-------------------------------------------------");
@@ -589,9 +648,11 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
sLog.outString("AHBuyer: Ammo Type: %u", prototype->AmmoType);
sLog.outString("-------------------------------------------------");
}
+
// Check whether we do normal bid, or buyout
if ((bidprice < auction->buyout) || (auction->buyout == 0))
{
+
if (auction->bidder > 0)
{
if (auction->bidder == AHBplayer->GetGUIDLow())
@@ -605,8 +666,10 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
//pl->ModifyMoney(-int32(price));
}
}
+
auction->bidder = AHBplayer->GetGUIDLow();
auction->bid = bidprice;
+
// Saving auction into database
CharacterDatabase.PExecute("UPDATE auctionhouse SET buyguid = '%u',lastbid = '%u' WHERE id = '%u'", auction->bidder, auction->bid, auction->Id);
}
@@ -619,6 +682,7 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
}
auction->bidder = AHBplayer->GetGUIDLow();
auction->bid = auction->buyout;
+
// Send mails to buyer & seller
auctionmgr.SendAuctionSalePendingMail(auction);
auctionmgr.SendAuctionSuccessfulMail(auction);
@@ -630,15 +694,18 @@ void AuctionHouseBot::addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *con
}
}
}
+
void AuctionHouseBot::Update()
{
time_t _newrun = time(NULL);
if ((!AHBSeller) && (!AHBBuyer))
return;
+
WorldSession _session(AHBplayerAccount, NULL, SEC_PLAYER, true, 0, LOCALE_enUS);
Player _AHBplayer(&_session);
_AHBplayer.MinimalLoadFromDB(NULL, AHBplayerGUID);
ObjectAccessor::Instance().AddObject(&_AHBplayer);
+
// Only for testing, this can likely be removed, once I know it's working as expected.
/*
AuctionHouseObject* auctionHouse1 = auctionmgr.GetAuctionsMap(55);
@@ -652,6 +719,7 @@ void AuctionHouseBot::Update()
return;
}
*/
+
// Add New Bids
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
{
@@ -663,6 +731,7 @@ void AuctionHouseBot::Update()
addNewAuctionBuyerBotBid(&_AHBplayer, &AllianceConfig, &_session);
_lastrun_a = _newrun;
}
+
addNewAuctions(&_AHBplayer, &HordeConfig);
if (((_newrun - _lastrun_h) >= (HordeConfig.GetBiddingInterval() * MINUTE)) && (HordeConfig.GetBidsPerInterval() > 0))
{
@@ -672,6 +741,7 @@ void AuctionHouseBot::Update()
_lastrun_h = _newrun;
}
}
+
addNewAuctions(&_AHBplayer, &NeutralConfig);
if (((_newrun - _lastrun_n) >= (NeutralConfig.GetBiddingInterval() * MINUTE)) && (NeutralConfig.GetBidsPerInterval() > 0))
{
@@ -682,30 +752,37 @@ void AuctionHouseBot::Update()
}
ObjectAccessor::Instance().RemoveObject(&_AHBplayer);
}
+
void AuctionHouseBot::Initialize()
{
debug_Out = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG", false);
debug_Out_Filters = sConfig.GetBoolDefault("AuctionHouseBot.DEBUG_FILTERS", false);
+
AHBSeller = sConfig.GetBoolDefault("AuctionHouseBot.EnableSeller", false);
AHBBuyer = sConfig.GetBoolDefault("AuctionHouseBot.EnableBuyer", false);
SellMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForSeller", false);
BuyMethod = sConfig.GetBoolDefault("AuctionHouseBot.UseBuyPriceForBuyer", false);
+
AHBplayerAccount = sConfig.GetIntDefault("AuctionHouseBot.Account", 0);
AHBplayerGUID = sConfig.GetIntDefault("AuctionHouseBot.GUID", 0);
ItemsPerCycle = sConfig.GetIntDefault("AuctionHouseBot.ItemsPerCycle", 200);
+
//Begin Filters
+
Vendor_Items = sConfig.GetBoolDefault("AuctionHouseBot.VendorItems", false);
Loot_Items = sConfig.GetBoolDefault("AuctionHouseBot.LootItems", true);
Other_Items = sConfig.GetBoolDefault("AuctionHouseBot.OtherItems", false);
Vendor_TGs = sConfig.GetBoolDefault("AuctionHouseBot.VendorTradeGoods", false);
Loot_TGs = sConfig.GetBoolDefault("AuctionHouseBot.LootTradeGoods", true);
Other_TGs = sConfig.GetBoolDefault("AuctionHouseBot.OtherTradeGoods", false);
+
No_Bind = sConfig.GetBoolDefault("AuctionHouseBot.No_Bind", true);
Bind_When_Picked_Up = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Picked_Up", false);
Bind_When_Equipped = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Equipped", true);
Bind_When_Use = sConfig.GetBoolDefault("AuctionHouseBot.Bind_When_Use", true);
Bind_Quest_Item = sConfig.GetBoolDefault("AuctionHouseBot.Bind_Quest_Item", false);
+
DisableBeta_PTR_Unused = sConfig.GetBoolDefault("AuctionHouseBot.DisableBeta_PTR_Unused", false);
DisablePermEnchant = sConfig.GetBoolDefault("AuctionHouseBot.DisablePermEnchant", false);
#if CLIENT_VER > 300
@@ -718,6 +795,7 @@ void AuctionHouseBot::Initialize()
DisableKeys = sConfig.GetBoolDefault("AuctionHouseBot.DisableKeys", false);
DisableDuration = sConfig.GetBoolDefault("AuctionHouseBot.DisableDuration", false);
DisableBOP_Or_Quest_NoReqLevel = sConfig.GetBoolDefault("AuctionHouseBot.DisableBOP_Or_Quest_NoReqLevel", false);
+
DisableWarriorItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarriorItems", false);
DisablePaladinItems = sConfig.GetBoolDefault("AuctionHouseBot.DisablePaladinItems", false);
DisableHunterItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableHunterItems", false);
@@ -729,6 +807,7 @@ void AuctionHouseBot::Initialize()
DisableWarlockItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableWarlockItems", false);
DisableUnusedClassItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableUnusedClassItems", false);
DisableDruidItems = sConfig.GetBoolDefault("AuctionHouseBot.DisableDruidItems", false);
+
DisableItemsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsBelowLevel", 0);
DisableItemsAboveLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveLevel", 0);
DisableTGsBelowLevel = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowLevel", 0);
@@ -745,6 +824,7 @@ void AuctionHouseBot::Initialize()
DisableItemsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableItemsAboveReqSkillRank", 0);
DisableTGsBelowReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsBelowReqSkillRank", 0);
DisableTGsAboveReqSkillRank = sConfig.GetIntDefault("AuctionHouseBot.DisableTGsAboveReqSkillRank", 0);
+
//End Filters
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
{
@@ -752,6 +832,7 @@ void AuctionHouseBot::Initialize()
LoadValues(&HordeConfig);
}
LoadValues(&NeutralConfig);
+
if (AHBSeller)
{
QueryResult* results = (QueryResult*) NULL;
@@ -763,13 +844,16 @@ void AuctionHouseBot::Initialize()
{
Field* fields = results->Fetch();
npcItems.push_back(fields[0].GetUInt32());
+
} while (results->NextRow());
+
delete results;
}
else
{
if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", npcQuery);
}
+
char lootQuery[] = "SELECT item FROM creature_loot_template UNION "
"SELECT item FROM disenchant_loot_template UNION "
"SELECT item FROM fishing_loot_template UNION "
@@ -781,6 +865,7 @@ void AuctionHouseBot::Initialize()
"SELECT item FROM pickpocketing_loot_template UNION "
"SELECT item FROM prospecting_loot_template UNION "
"SELECT item FROM skinning_loot_template";
+
results = WorldDatabase.PQuery(lootQuery);
if (results != NULL)
{
@@ -788,18 +873,23 @@ void AuctionHouseBot::Initialize()
{
Field* fields = results->Fetch();
lootItems.push_back(fields[0].GetUInt32());
+
} while (results->NextRow());
+
delete results;
}
else
{
if (debug_Out) sLog.outString("AuctionHouseBot: \"%s\" failed", lootQuery);
}
+
for (uint32 itemID = 0; itemID < sItemStorage.MaxEntry; itemID++)
{
ItemPrototype const* prototype = objmgr.GetItemPrototype(itemID);
+
if (prototype == NULL)
continue;
+
switch (prototype->Bonding)
{
case NO_BIND:
@@ -826,6 +916,7 @@ void AuctionHouseBot::Initialize()
continue;
break;
}
+
switch (SellMethod)
{
case 0:
@@ -837,56 +928,71 @@ void AuctionHouseBot::Initialize()
continue;
break;
}
+
if ((prototype->Quality < 0) || (prototype->Quality > 6))
continue;
+
if ((Vendor_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS))
{
bool isVendorItem = false;
+
for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++)
{
if (itemID == npcItems[i])
isVendorItem = true;
}
+
if (isVendorItem)
continue;
}
+
if ((Vendor_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS))
{
bool isVendorTG = false;
+
for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorTG); i++)
{
if (itemID == npcItems[i])
isVendorTG = true;
}
+
if (isVendorTG)
continue;
}
+
if ((Loot_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS))
{
bool isLootItem = false;
+
for (unsigned int i = 0; (i < lootItems.size()) && (!isLootItem); i++)
{
if (itemID == lootItems[i])
isLootItem = true;
}
+
if (isLootItem)
continue;
}
+
if ((Loot_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS))
{
bool isLootTG = false;
+
for (unsigned int i = 0; (i < lootItems.size()) && (!isLootTG); i++)
{
if (itemID == lootItems[i])
isLootTG = true;
}
+
if (isLootTG)
continue;
}
+
if ((Other_Items == 0) && !(prototype->Class == ITEM_CLASS_TRADE_GOODS))
{
bool isVendorItem = false;
bool isLootItem = false;
+
for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorItem); i++)
{
if (itemID == npcItems[i])
@@ -900,10 +1006,12 @@ void AuctionHouseBot::Initialize()
if ((!isLootItem) && (!isVendorItem))
continue;
}
+
if ((Other_TGs == 0) && (prototype->Class == ITEM_CLASS_TRADE_GOODS))
{
bool isVendorTG = false;
bool isLootTG = false;
+
for (unsigned int i = 0; (i < npcItems.size()) && (!isVendorTG); i++)
{
if (itemID == npcItems[i])
@@ -917,6 +1025,7 @@ void AuctionHouseBot::Initialize()
if ((!isLootTG) && (!isVendorTG))
continue;
}
+
//TODO:Make list of items and create a vector
// Disable PTR/Beta/Unused items
if ((DisableBeta_PTR_Unused) && ((prototype->ItemId == 21878) || (prototype->ItemId == 27774) || (prototype->ItemId == 27811) || (prototype->ItemId == 28117) || (prototype->ItemId == 28112)))
@@ -924,12 +1033,14 @@ void AuctionHouseBot::Initialize()
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (PTR/Beta/Unused Item)", prototype->ItemId);
continue;
}
+
// Disable permanent enchants items
if ((DisablePermEnchant) && (prototype->Class == ITEM_CLASS_PERMANENT))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Permanent Enchant Item)", prototype->ItemId);
continue;
}
+
#if CLIENT_VER > 300
// Disable conjured items
if ((DisableConjured) && (prototype->IsConjuredConsumable()))
@@ -938,210 +1049,245 @@ void AuctionHouseBot::Initialize()
continue;
}
#endif
+
// Disable gems
if ((DisableGems) && (prototype->Class == ITEM_CLASS_GEM))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Gem)", prototype->ItemId);
continue;
}
+
// Disable money
if ((DisableMoney) && (prototype->Class == ITEM_CLASS_MONEY))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Money)", prototype->ItemId);
continue;
}
+
// Disable moneyloot
if ((DisableMoneyLoot) && (prototype->MinMoneyLoot > 0))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (MoneyLoot)", prototype->ItemId);
continue;
}
+
// Disable lootable items
if ((DisableLootable) && (prototype->Flags & 4))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Lootable Item)", prototype->ItemId);
continue;
}
+
// Disable Keys
if ((DisableKeys) && (prototype->Class == ITEM_CLASS_KEY))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Quest Item)", prototype->ItemId);
continue;
}
+
// Disable items with duration
if ((DisableDuration) && (prototype->Duration > 0))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Has a Duration)", prototype->ItemId);
continue;
}
+
// Disable items which are BOP or Quest Items and have a required level lower than the item level
if ((DisableBOP_Or_Quest_NoReqLevel) && ((prototype->Bonding == BIND_WHEN_PICKED_UP || prototype->Bonding == BIND_QUEST_ITEM) && (prototype->RequiredLevel < prototype->ItemLevel)))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (BOP or BQI and Required Level is less than Item Level)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Warrior
if ((DisableWarriorItems) && (prototype->AllowableClass == 1))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Warrior Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Paladin
if ((DisablePaladinItems) && (prototype->AllowableClass == 2))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Paladin Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Hunter
if ((DisableHunterItems) && (prototype->AllowableClass == 4))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Hunter Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Rogue
if ((DisableRogueItems) && (prototype->AllowableClass == 8))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Rogue Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Priest
if ((DisablePriestItems) && (prototype->AllowableClass == 16))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Priest Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for DK
if ((DisableDKItems) && (prototype->AllowableClass == 32))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (DK Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Shaman
if ((DisableShamanItems) && (prototype->AllowableClass == 64))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Shaman Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Mage
if ((DisableMageItems) && (prototype->AllowableClass == 128))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Mage Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Warlock
if ((DisableWarlockItems) && (prototype->AllowableClass == 256))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Warlock Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Unused Class
if ((DisableUnusedClassItems) && (prototype->AllowableClass == 512))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Unused Item)", prototype->ItemId);
continue;
}
+
// Disable items specifically for Druid
if ((DisableDruidItems) && (prototype->AllowableClass == 1024))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Druid Item)", prototype->ItemId);
continue;
}
+
// Disable Items below level X
if ((DisableItemsBelowLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableItemsBelowLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Items above level X
if ((DisableItemsAboveLevel) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableItemsAboveLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Trade Goods below level X
if ((DisableTGsBelowLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel < DisableTGsBelowLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Trade Goods above level X
if ((DisableTGsAboveLevel) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemLevel > DisableTGsAboveLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Items below GUID X
if ((DisableItemsBelowGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableItemsBelowGUID))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Items above GUID X
if ((DisableItemsAboveGUID) && (prototype->Class != ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableItemsAboveGUID))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Item Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Trade Goods below GUID X
if ((DisableTGsBelowGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId < DisableTGsBelowGUID))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Trade Goods above GUID X
if ((DisableTGsAboveGUID) && (prototype->Class == ITEM_CLASS_TRADE_GOODS) && (prototype->ItemId > DisableTGsAboveGUID))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (Trade Good Level = %u)", prototype->ItemId, prototype->ItemLevel);
continue;
}
+
// Disable Items for level lower than X
if ((DisableItemsBelowReqLevel) && (prototype->RequiredLevel < DisableItemsBelowReqLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel);
continue;
}
+
// Disable Items for level higher than X
if ((DisableItemsAboveReqLevel) && (prototype->RequiredLevel > DisableItemsAboveReqLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel);
continue;
}
+
// Disable Trade Goods for level lower than X
if ((DisableTGsBelowReqLevel) && (prototype->RequiredLevel < DisableTGsBelowReqLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel);
continue;
}
+
// Disable Trade Goods for level higher than X
if ((DisableTGsAboveReqLevel) && (prototype->RequiredLevel > DisableTGsAboveReqLevel))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Trade Good %u disabled (RequiredLevel = %u)", prototype->ItemId, prototype->RequiredLevel);
continue;
}
+
// Disable Items that require skill lower than X
if ((DisableItemsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableItemsBelowReqSkillRank))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank);
continue;
}
+
// Disable Items that require skill higher than X
if ((DisableItemsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableItemsAboveReqSkillRank))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank);
continue;
}
+
// Disable Trade Goods that require skill lower than X
if ((DisableTGsBelowReqSkillRank) && (prototype->RequiredSkillRank < DisableTGsBelowReqSkillRank))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank);
continue;
}
+
// Disable Trade Goods that require skill higher than X
if ((DisableTGsAboveReqSkillRank) && (prototype->RequiredSkillRank > DisableTGsAboveReqSkillRank))
{
if (debug_Out_Filters) sLog.outString("AuctionHouseBot: Item %u disabled (RequiredSkillRank = %u)", prototype->ItemId, prototype->RequiredSkillRank);
continue;
}
+
switch (prototype->Quality)
{
case AHB_GREY:
@@ -1150,36 +1296,42 @@ void AuctionHouseBot::Initialize()
else
greyItemsBin.push_back(itemID);
break;
+
case AHB_WHITE:
if (prototype->Class == ITEM_CLASS_TRADE_GOODS)
whiteTradeGoodsBin.push_back(itemID);
else
whiteItemsBin.push_back(itemID);
break;
+
case AHB_GREEN:
if (prototype->Class == ITEM_CLASS_TRADE_GOODS)
greenTradeGoodsBin.push_back(itemID);
else
greenItemsBin.push_back(itemID);
break;
+
case AHB_BLUE:
if (prototype->Class == ITEM_CLASS_TRADE_GOODS)
blueTradeGoodsBin.push_back(itemID);
else
blueItemsBin.push_back(itemID);
break;
+
case AHB_PURPLE:
if (prototype->Class == ITEM_CLASS_TRADE_GOODS)
purpleTradeGoodsBin.push_back(itemID);
else
purpleItemsBin.push_back(itemID);
break;
+
case AHB_ORANGE:
if (prototype->Class == ITEM_CLASS_TRADE_GOODS)
orangeTradeGoodsBin.push_back(itemID);
else
orangeItemsBin.push_back(itemID);
break;
+
case AHB_YELLOW:
if (prototype->Class == ITEM_CLASS_TRADE_GOODS)
yellowTradeGoodsBin.push_back(itemID);
@@ -1188,6 +1340,7 @@ void AuctionHouseBot::Initialize()
break;
}
}
+
if ((greyTradeGoodsBin.size() == 0) &&
(whiteTradeGoodsBin.size() == 0) &&
(greenTradeGoodsBin.size() == 0) &&
@@ -1206,6 +1359,7 @@ void AuctionHouseBot::Initialize()
sLog.outError("AuctionHouseBot: No items");
AHBSeller = 0;
}
+
sLog.outString("AuctionHouseBot:");
sLog.outString("loaded %u grey trade goods", greyTradeGoodsBin.size());
sLog.outString("loaded %u white trade goods", whiteTradeGoodsBin.size());
@@ -1225,9 +1379,11 @@ void AuctionHouseBot::Initialize()
sLog.outString("AuctionHouseBot by Paradox (original by ChrisK) has been loaded.");
sLog.outString("AuctionHouseBot now includes AHBuyer by Kerbe and Paradox");
}
+
void AuctionHouseBot::IncrementItemCounts(AuctionEntry* ah)
{
// from auctionhousehandler.cpp, creates auction pointer & player pointer
+
// get exact item information
Item *pItem = auctionmgr.GetAItem(ah->item_guidlow);
if (!pItem)
@@ -1235,9 +1391,12 @@ void AuctionHouseBot::IncrementItemCounts(AuctionEntry* ah)
if (debug_Out) sLog.outError("AHBot: Item %u doesn't exist, perhaps bought already?", ah->item_guidlow);
return;
}
+
// get item prototype
ItemPrototype const* prototype = objmgr.GetItemPrototype(ah->item_template);
+
AHBConfig *config;
+
FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(ah->GetHouseFaction());
if (!u_entry)
{
@@ -1259,13 +1418,17 @@ void AuctionHouseBot::IncrementItemCounts(AuctionEntry* ah)
if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction());
config = &NeutralConfig;
}
+
config->IncItemCounts(prototype->Class, prototype->Quality);
}
+
void AuctionHouseBot::DecrementItemCounts(AuctionEntry* ah, uint32 item_template)
{
// get item prototype
ItemPrototype const* prototype = objmgr.GetItemPrototype(item_template);
+
AHBConfig *config;
+
FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(ah->GetHouseFaction());
if (!u_entry)
{
@@ -1287,8 +1450,10 @@ void AuctionHouseBot::DecrementItemCounts(AuctionEntry* ah, uint32 item_template
if (debug_Out) sLog.outError("AHBot: %u returned as House Faction. Neutral", ah->GetHouseFaction());
config = &NeutralConfig;
}
+
config->DecItemCounts(prototype->Class, prototype->Quality);
}
+
void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char* args)
{
AHBConfig *config;
@@ -1336,12 +1501,15 @@ void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char*
case 0: //ahexpire
{
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID());
+
AuctionHouseObject::AuctionEntryMap::iterator itr;
itr = auctionHouse->GetAuctionsBegin();
+
while (itr != auctionHouse->GetAuctionsEnd())
{
if (itr->second->owner == AHBplayerGUID)
itr->second->expire_time = sWorld.GetGameTime();
+
++itr;
}
}
@@ -1396,6 +1564,7 @@ void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char*
uint32 purplei = (uint32) strtoul(param12, NULL, 0);
uint32 orangei = (uint32) strtoul(param13, NULL, 0);
uint32 yellowi = (uint32) strtoul(param14, NULL, 0);
+
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentgreytradegoods = '%u' WHERE auctionhouse = '%u'", greytg, ahMapID);
CharacterDatabase.PExecute("UPDATE auctionhousebot SET percentwhitetradegoods = '%u' WHERE auctionhouse = '%u'", whitetg, ahMapID);
@@ -1483,6 +1652,7 @@ void AuctionHouseBot::Commands(uint32 command, uint32 ahMapID, uint32 col, char*
break;
}
}
+
void AuctionHouseBot::LoadValues(AHBConfig *config)
{
if (debug_Out) sLog.outString("Start Settings for %s Auctionhouses:", CharacterDatabase.PQuery("SELECT name FROM auctionhousebot WHERE auctionhouse = %u",config->GetAHID())->Fetch()->GetString());
@@ -1601,8 +1771,10 @@ void AuctionHouseBot::LoadValues(AHBConfig *config)
}
AuctionHouseEntry const* ahEntry = auctionmgr.GetAuctionHouseEntry(config->GetAHFID());
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(config->GetAHFID());
+
config->ResetItemCounts();
uint32 auctions = auctionHouse->Getcount();
+
if (auctions)
{
for (AuctionHouseObject::AuctionEntryMap::const_iterator itr = auctionHouse->GetAuctionsBegin();itr != auctionHouse->GetAuctionsEnd();++itr)
diff --git a/src/game/AuctionHouseBot.h b/src/game/AuctionHouseBot.h
index 9e04dde87cf..d135af5c479 100644
--- a/src/game/AuctionHouseBot.h
+++ b/src/game/AuctionHouseBot.h
@@ -1,8 +1,10 @@
#ifndef AUCTION_HOUSE_BOT_H
#define AUCTION_HOUSE_BOT_H
+
#include "World.h"
#include "Config/ConfigEnv.h"
#include "ItemPrototype.h"
+
#define AHB_GREY 0
#define AHB_WHITE 1
#define AHB_GREEN 2
@@ -25,6 +27,7 @@
#define AHB_PURPLE_I 11
#define AHB_ORANGE_I 12
#define AHB_YELLOW_I 13
+
class AHBConfig
{
private:
@@ -81,6 +84,7 @@ private:
uint32 minBidPriceYellow;
uint32 maxBidPriceYellow;
uint32 maxStackYellow;
+
uint32 buyerPriceGrey;
uint32 buyerPriceWhite;
uint32 buyerPriceGreen;
@@ -90,6 +94,7 @@ private:
uint32 buyerPriceYellow;
uint32 buyerBiddingInterval;
uint32 buyerBidsPerInterval;
+
uint32 greytgp;
uint32 whitetgp;
uint32 greentgp;
@@ -104,6 +109,7 @@ private:
uint32 purpleip;
uint32 orangeip;
uint32 yellowip;
+
uint32 greyTGoods;
uint32 whiteTGoods;
uint32 greenTGoods;
@@ -111,6 +117,7 @@ private:
uint32 purpleTGoods;
uint32 orangeTGoods;
uint32 yellowTGoods;
+
uint32 greyItems;
uint32 whiteItems;
uint32 greenItems;
@@ -118,6 +125,7 @@ private:
uint32 purpleItems;
uint32 orangeItems;
uint32 yellowItems;
+
public:
AHBConfig(uint32 ahid)
{
@@ -174,6 +182,7 @@ public:
void SetPercentages(uint32 greytg, uint32 whitetg, uint32 greentg, uint32 bluetg, uint32 purpletg, uint32 orangetg, uint32 yellowtg, uint32 greyi, uint32 whitei, uint32 greeni, uint32 bluei, uint32 purplei, uint32 orangei, uint32 yellowi)
{
uint32 totalPercent = greytg + whitetg + greentg + bluetg + purpletg + orangetg + yellowtg + greyi + whitei + greeni + bluei + purplei + orangei + yellowi;
+
if (totalPercent == 0)
{
maxItems = 0;
@@ -883,6 +892,7 @@ public:
break;
}
}
+
void DecItemCounts(uint32 Class, uint32 Quality)
{
switch(Class)
@@ -895,6 +905,7 @@ public:
break;
}
}
+
void DecItemCounts(uint32 color)
{
switch(color)
@@ -945,6 +956,7 @@ public:
break;
}
}
+
void IncItemCounts(uint32 Class, uint32 Quality)
{
switch(Class)
@@ -957,6 +969,7 @@ public:
break;
}
}
+
void IncItemCounts(uint32 color)
{
switch(color)
@@ -1007,6 +1020,7 @@ public:
break;
}
}
+
void ResetItemCounts()
{
greyTGoods = 0;
@@ -1016,6 +1030,7 @@ public:
purpleTGoods = 0;
orangeTGoods = 0;
yellowTGoods = 0;
+
greyItems = 0;
whiteItems = 0;
greenItems = 0;
@@ -1024,6 +1039,7 @@ public:
orangeItems = 0;
yellowItems = 0;
}
+
uint32 TotalItemCounts()
{
return(
@@ -1034,6 +1050,7 @@ public:
purpleTGoods +
orangeTGoods +
yellowTGoods +
+
greyItems +
whiteItems +
greenItems +
@@ -1042,6 +1059,7 @@ public:
orangeItems +
yellowItems);
}
+
uint32 GetItemCounts(uint32 color)
{
switch(color)
@@ -1108,27 +1126,34 @@ public:
class AuctionHouseBot
{
private:
+
bool debug_Out;
bool debug_Out_Filters;
+
bool AHBSeller;
bool AHBBuyer;
bool BuyMethod;
bool SellMethod;
+
uint32 AHBplayerAccount;
uint32 AHBplayerGUID;
uint32 ItemsPerCycle;
+
//Begin Filters
+
bool Vendor_Items;
bool Loot_Items;
bool Other_Items;
bool Vendor_TGs;
bool Loot_TGs;
bool Other_TGs;
+
bool No_Bind;
bool Bind_When_Picked_Up;
bool Bind_When_Equipped;
bool Bind_When_Use;
bool Bind_Quest_Item;
+
bool DisableBeta_PTR_Unused;
bool DisablePermEnchant;
#if CLIENT_VER > 300
@@ -1141,6 +1166,7 @@ private:
bool DisableKeys;
bool DisableDuration;
bool DisableBOP_Or_Quest_NoReqLevel;
+
bool DisableWarriorItems;
bool DisablePaladinItems;
bool DisableHunterItems;
@@ -1152,6 +1178,7 @@ private:
bool DisableWarlockItems;
bool DisableUnusedClassItems;
bool DisableDruidItems;
+
uint32 DisableItemsBelowLevel;
uint32 DisableItemsAboveLevel;
uint32 DisableTGsBelowLevel;
@@ -1168,16 +1195,21 @@ private:
uint32 DisableItemsAboveReqSkillRank;
uint32 DisableTGsBelowReqSkillRank;
uint32 DisableTGsAboveReqSkillRank;
+
//End Filters
+
AHBConfig AllianceConfig;
AHBConfig HordeConfig;
AHBConfig NeutralConfig;
+
time_t _lastrun_a;
time_t _lastrun_h;
time_t _lastrun_n;
+
inline uint32 minValue(uint32 a, uint32 b) { return a <= b ? a : b; };
void addNewAuctions(Player *AHBplayer, AHBConfig *config);
void addNewAuctionBuyerBotBid(Player *AHBplayer, AHBConfig *config, WorldSession *session);
+
public:
AuctionHouseBot();
~AuctionHouseBot();
@@ -1189,5 +1221,7 @@ public:
void Commands(uint32, uint32, uint32, char*);
uint32 GetAHBplayerGUID() { return AHBplayerGUID; };
};
+
#define auctionbot Trinity::Singleton<AuctionHouseBot>::Instance()
+
#endif
diff --git a/src/game/AuctionHouseHandler.cpp b/src/game/AuctionHouseHandler.cpp
index 13df6c3a6a4..52790526dd7 100644
--- a/src/game/AuctionHouseHandler.cpp
+++ b/src/game/AuctionHouseHandler.cpp
@@ -17,11 +17,13 @@
* 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"
#include "Player.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
+
#include "AuctionHouseBot.h"
#include "AuctionHouseMgr.h"
#include "Log.h"
@@ -29,35 +31,43 @@
#include "UpdateMask.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)
{
uint64 guid; //NPC guid
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
SendAuctionHello(guid, unit);
}
+
//this void causes that auction window is opened
void WorldSession::SendAuctionHello(uint64 guid, Creature* unit)
{
AuctionHouseEntry const* ahEntry = AuctionHouseMgr::GetAuctionHouseEntry(unit->getFaction());
if (!ahEntry)
return;
+
WorldPacket data(MSG_AUCTION_HELLO, 12);
data << (uint64) guid;
data << (uint32) ahEntry->houseId;
SendPacket(&data);
}
+
//call this method when player bids, creates, or deletes auction
void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uint32 ErrorCode, uint32 bidError)
{
@@ -69,6 +79,7 @@ void WorldSession::SendAuctionCommandResult(uint32 auctionId, uint32 Action, uin
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)
{
@@ -82,6 +93,7 @@ void WorldSession::SendAuctionBidderNotification(uint32 location, uint32 auction
data << uint32(0);
SendPacket(&data);
}
+
//this void causes on client to display: "Your auction sold"
void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction)
{
@@ -95,42 +107,53 @@ void WorldSession::SendAuctionOwnerNotification(AuctionEntry* auction)
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 && !_player)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, auctionbot.GetAHBplayerGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
+
if (oldBidder && _player)
oldBidder->GetSession()->SendAuctionBidderNotification(auction->GetHouseId(), auction->Id, _player->GetGUID(), newPrice, auction->GetAuctionOutBid(), auction->item_template);
+
WorldSession::SendMailTo(oldBidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), 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->GetHouseId(), 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)
{
@@ -139,24 +162,31 @@ void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
recv_data >> auctioneer >> item;
recv_data >> bid >> buyout >> etime;
Player *pl = GetPlayer();
+
if (!item || !bid || !etime)
return; //check for cheaters
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(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;
}
+
AuctionHouseEntry const* auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(pCreature->getFaction());
if (!auctionHouseEntry)
{
sLog.outDebug("WORLD: HandleAuctionSellItem - Unit (GUID: %u) has wrong faction.", uint32(GUID_LOPART(auctioneer)));
return;
}
+
sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime);
+
// client send time in minutes, convert to common used sec time
etime *= MINUTE;
+
sLog.outDebug("WORLD: HandleAuctionSellItem - ETIME: %u", etime);
+
// client understand only 3 auction time
switch(etime)
{
@@ -167,9 +197,11 @@ void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
default:
return;
}
+
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
Item *it = pl->GetItemByGuid(item);
//do not allow to sell already auctioned items
if (auctionmgr.GetAItem(GUID_LOPART(item)))
@@ -184,17 +216,21 @@ void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
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;
}
+
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
+
//we have to take deposit :
uint32 deposit = auctionmgr.GetAuctionDeposit(auctionHouseEntry, etime, it);
if (pl->GetMoney() < deposit)
@@ -202,13 +238,17 @@ void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
SendAuctionCommandResult(0, AUCTION_SELL_ITEM, AUCTION_NOT_ENOUGHT_MONEY);
return;
}
+
if (GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
sLog.outCommand(GetAccountId(),"GM %s (Account: %u) create auction: %s (Entry: %u Count: %u)",
GetPlayerName(),GetAccountId(),it->GetProto()->Name1,it->GetEntry(),it->GetCount());
}
+
pl->ModifyMoney(-int32(deposit));
+
uint32 auction_time = uint32(etime * sWorld.getRate(RATE_AUCTION_TIME));
+
AuctionEntry *AH = new AuctionEntry;
AH->Id = objmgr.GenerateAuctionID();
if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
@@ -225,18 +265,23 @@ void WorldSession::HandleAuctionSellItem(WorldPacket & recv_data)
AH->expire_time = time(NULL) + auction_time;
AH->deposit = deposit;
AH->auctionHouseEntry = auctionHouseEntry;
+
sLog.outDetail("selling item %u to auctioneer %u with initial bid %u with buyout %u and with time %u (in sec) in auctionhouse %u", GUID_LOPART(item), AH->auctioneer, bid, buyout, auction_time, AH->GetHouseId());
auctionmgr.AddAItem(it);
auctionHouse->AddAuction(AH);
+
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
AH->SaveToDB();
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)
{
@@ -245,26 +290,33 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
uint32 price;
recv_data >> auctioneer;
recv_data >> auctionId >> price;
+
if (!auctionId || !price)
return; //check for cheaters
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
+
AuctionEntry *auction = auctionHouse->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())
@@ -273,9 +325,11 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
SendAuctionCommandResult(0, AUCTION_PLACE_BID, CANNOT_BID_YOUR_AUCTION_ERROR);
return;
}
+
// cheating
if (price <= auction->bid)
return;
+
// price too low for next bid if not buyout
if ((price < auction->buyout || auction->buyout == 0) &&
price < auction->bid + auction->GetAuctionOutBid())
@@ -283,12 +337,14 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
//auction has already higher bid, client tests it!
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)
@@ -311,8 +367,10 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
auction->bidder = pl->GetGUIDLow();
auction->bid = price;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_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
@@ -333,9 +391,11 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
auction->bidder = pl->GetGUIDLow();
auction->bid = auction->buyout;
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_AUCTION_BID, auction->buyout);
+
auctionmgr.SendAuctionSalePendingMail(auction);
auctionmgr.SendAuctionSuccessfulMail(auction);
auctionmgr.SendAuctionWonMail(auction);
+
SendAuctionCommandResult(auction->Id, AUCTION_PLACE_BID, AUCTION_OK);
auction->DeleteFromDB();
uint32 item_template = auction->item_template;
@@ -346,6 +406,7 @@ void WorldSession::HandleAuctionPlaceBid(WorldPacket & recv_data)
pl->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
}
+
//this void is called when auction_owner cancels his auction
void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
{
@@ -354,18 +415,23 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
recv_data >> auctioneer;
recv_data >> auctionId;
//sLog.outDebug("Cancel AUCTION AuctionID: %u", auctionId);
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
+
AuctionEntry *auction = auctionHouse->GetAuction(auctionId);
Player *pl = GetPlayer();
+
if (auction && auction->owner == pl->GetGUIDLow())
{
Item *pItem = auctionmgr.GetAItem(auction->item_guidlow);
@@ -383,8 +449,10 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
// 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->GetHouseId(), pl->GetGUIDLow(), msgAuctionCanceledOwner.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
}
@@ -402,6 +470,7 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
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
@@ -413,12 +482,14 @@ void WorldSession::HandleAuctionRemoveItem(WorldPacket & recv_data)
auctionmgr.RemoveAItem(auction->item_guidlow);
auctionHouse->RemoveAuction(auction, item_template);
}
+
//called when player lists his bids
void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data)
{
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;
@@ -427,16 +498,20 @@ void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data)
sLog.outError("Client sent bad opcode!!! with count: %u and size : %lu (must be: %u)", outbiddedCount, (unsigned long)recv_data.size(),(16 + outbiddedCount * 4));
outbiddedCount = 0;
}
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
+
WorldPacket data(SMSG_AUCTION_BIDDER_LIST_RESULT, (4+4+4));
Player *pl = GetPlayer();
data << (uint32) 0; //add 0 as count
@@ -454,39 +529,49 @@ void WorldSession::HandleAuctionListBidderItems(WorldPacket & recv_data)
++count;
}
}
+
auctionHouse->BuildListBidderItems(data,pl,count,totalcount);
data.put<uint32>(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)
{
uint32 listfrom;
uint64 guid;
+
recv_data >> guid;
recv_data >> listfrom; // not used in fact (this list not have page control in client)
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
+
WorldPacket data(SMSG_AUCTION_OWNER_LIST_RESULT, (4+4+4));
data << (uint32) 0; // amount place holder
+
uint32 count = 0;
uint32 totalcount = 0;
+
auctionHouse->BuildListOwnerItems(data,_player,count,totalcount);
data.put<uint32>(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)
{
@@ -494,47 +579,61 @@ void WorldSession::HandleAuctionListItems(WorldPacket & recv_data)
uint8 levelmin, levelmax, usable;
uint32 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;
+
recv_data >> levelmin >> levelmax;
recv_data >> auctionSlotID >> auctionMainCategory >> auctionSubCategory;
recv_data >> quality >> usable;
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
AuctionHouseObject* auctionHouse = auctionmgr.GetAuctionsMap(pCreature->getFaction());
+
//sLog.outDebug("Auctionhouse search (GUID: %u TypeId: %u)", , list from: %u, searchedname: %s, levelmin: %u, levelmax: %u, auctionSlotID: %u, auctionMainCategory: %u, auctionSubCategory: %u, quality: %u, usable: %u",
// GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)), listfrom, searchedname.c_str(), levelmin, levelmax, auctionSlotID, auctionMainCategory, auctionSubCategory, quality, usable);
+
WorldPacket data(SMSG_AUCTION_LIST_RESULT, (4+4+4));
uint32 count = 0;
uint32 totalcount = 0;
data << (uint32) 0;
+
// converting string that we try to find to lower case
std::wstring wsearchedname;
if (!Utf8toWStr(searchedname,wsearchedname))
return;
+
wstrToLower(wsearchedname);
+
auctionHouse->BuildListAuctionItems(data,_player,
wsearchedname, listfrom, levelmin, levelmax, usable,
auctionSlotID, auctionMainCategory, auctionSubCategory, quality,
count,totalcount);
+
data.put<uint32>(0, count);
data << (uint32) totalcount;
data << (uint32) 300; // unk 2.3.0 const?
SendPacket(&data);
}
+
void WorldSession::HandleAuctionListPendingSales(WorldPacket & recv_data)
{
sLog.outDebug("CMSG_AUCTION_LIST_PENDING_SALES");
recv_data.hexlike();
+
uint32 count = 0;
+
WorldPacket data(SMSG_AUCTION_LIST_PENDING_SALES, 4);
data << uint32(count); // count
/*for (uint32 i = 0; i < count; ++i)
diff --git a/src/game/AuctionHouseMgr.cpp b/src/game/AuctionHouseMgr.cpp
index 2f7ba808fb3..ffc0c55d9d3 100644
--- a/src/game/AuctionHouseMgr.cpp
+++ b/src/game/AuctionHouseMgr.cpp
@@ -15,6 +15,7 @@
* 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 "ObjectMgr.h"
#include "Player.h"
@@ -25,6 +26,7 @@
#include "Database/SQLStorage.h"
#include "Policies/SingletonImp.h"
#include "DBCStores.h"
+
#include "AccountMgr.h"
#include "AuctionHouseMgr.h"
#include "Item.h"
@@ -33,20 +35,26 @@
#include "ProgressBar.h"
#include <vector>
+
INSTANTIATE_SINGLETON_1(AuctionHouseMgr);
+
using namespace std;
+
AuctionHouseMgr::AuctionHouseMgr()
{
}
+
AuctionHouseMgr::~AuctionHouseMgr()
{
for (ItemMap::const_iterator itr = mAitems.begin(); itr != mAitems.end(); ++itr)
delete itr->second;
}
+
AuctionHouseObject * AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId)
{
if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
return &mNeutralAuctions;
+
// team have linked auction houses
FactionTemplateEntry const* u_entry = sFactionTemplateStore.LookupEntry(factionTemplateId);
if (!u_entry)
@@ -58,15 +66,18 @@ AuctionHouseObject * AuctionHouseMgr::GetAuctionsMap(uint32 factionTemplateId)
else
return &mNeutralAuctions;
}
+
uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem)
{
uint32 MSV = pItem->GetProto()->SellPrice;
int32 deposit;
uint32 timeHr = (((time / 60) / 60) / 12);
+
if (MSV > 0)
deposit = (int32)floor((double)MSV * (((double)(entry->depositPercent * 3) / 100.0f * (double)sWorld.getRate(RATE_AUCTION_DEPOSIT) * (double)pItem->GetCount()))) * timeHr;
else
deposit = 0;
+
sLog.outDebug("SellPrice:\t\t%u", MSV);
sLog.outDebug("Deposit Percent:\t%f", ((double)entry->depositPercent / 100.0f));
sLog.outDebug("Auction Time1:\t\t%u", time);
@@ -84,12 +95,14 @@ uint32 AuctionHouseMgr::GetAuctionDeposit(AuctionHouseEntry const* entry, uint32
return 0;
}
}
+
//does not clear ram
void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
{
Item *pItem = GetAItem(auction->item_guidlow);
if (!pItem)
return;
+
uint32 bidder_accId = 0;
uint32 bidder_security = 0;
uint64 bidder_guid = MAKE_NEW_GUID(auction->bidder, 0, HIGHGUID_PLAYER);
@@ -108,6 +121,7 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
{
bidder_accId = objmgr.GetPlayerAccountIdByGUID(bidder_guid);
bidder_security = accmgr.GetSecurity(bidder_accId);
+
if (bidder_security > SEC_PLAYER) // not do redundant DB requests
{
if (!objmgr.GetPlayerNameByGUID(bidder_guid,bidder_name))
@@ -119,34 +133,44 @@ void AuctionHouseMgr::SendAuctionWonMail(AuctionEntry *auction)
std::string owner_name;
if (!objmgr.GetPlayerNameByGUID(auction->owner,owner_name))
owner_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN);
+
uint32 owner_accid = objmgr.GetPlayerAccountIdByGUID(auction->owner);
+
sLog.outCommand(bidder_accId,"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);
}
}
+
// 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 = objmgr.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->GetHouseId(), auction->Id, bidder_guid, 0, 0, auction->item_template);
+
WorldSession::SendMailTo(bidder, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->bidder, msgAuctionWonSubject.str(), itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_AUCTION);
}
}
+
void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction)
{
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
@@ -157,19 +181,26 @@ void AuctionHouseMgr::SendAuctionSalePendingMail(AuctionEntry * auction)
{
std::ostringstream msgAuctionSalePendingSubject;
msgAuctionSalePendingSubject << auction->item_template << ":0:" << AUCTION_SALE_PENDING;
+
std::ostringstream msgAuctionSalePendingBody;
uint32 auctionCut = auction->GetAuctionCut();
+
time_t distrTime = time(NULL) + sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY);
+
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 = objmgr.CreateItemText(msgAuctionSalePendingBody.str());
+
WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), 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 AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction)
{
@@ -181,15 +212,21 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction)
{
std::ostringstream msgAuctionSuccessfulSubject;
msgAuctionSuccessfulSubject << auction->item_template << ":0:" << AUCTION_SUCCESSFUL;
+
std::ostringstream auctionSuccessfulBody;
uint32 auctionCut = auction->GetAuctionCut();
+
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 = objmgr.CreateItemText(auctionSuccessfulBody.str());
+
uint32 profit = auction->bid + auction->deposit - auctionCut;
+
//FIXME: what do if owner offline
if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID())
{
@@ -200,12 +237,14 @@ void AuctionHouseMgr::SendAuctionSuccessfulMail(AuctionEntry * auction)
WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), auction->owner, msgAuctionSuccessfulSubject.str(), itemTextId, NULL, profit, 0, MAIL_CHECK_MASK_AUCTION, sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY));
}
}
+
//does not clear ram
void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction)
{ //return an item in auction to its owner by mail
Item *pItem = GetAItem(auction->item_guidlow);
if (!pItem)
return;
+
uint64 owner_guid = MAKE_NEW_GUID(auction->owner, 0, HIGHGUID_PLAYER);
Player *owner = objmgr.GetPlayer(owner_guid);
uint32 owner_accId = objmgr.GetPlayerAccountIdByGUID(owner_guid);
@@ -214,17 +253,22 @@ void AuctionHouseMgr::SendAuctionExpiredMail(AuctionEntry * auction)
{
std::ostringstream subject;
subject << auction->item_template << ":0:" << AUCTION_EXPIRED;
+
if (owner && owner->GetGUIDLow() != auctionbot.GetAHBplayerGUID())
owner->GetSession()->SendAuctionOwnerNotification(auction);
+
MailItemsInfo mi;
mi.AddItem(auction->item_guidlow, auction->item_template, pItem);
+
WorldSession::SendMailTo(owner, MAIL_AUCTION, MAIL_STATIONERY_AUCTION, auction->GetHouseId(), GUID_LOPART(owner_guid), subject.str(), 0, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
}
}
+
void AuctionHouseMgr::LoadAuctionItems()
{
// data needs to be at first place for Item::LoadFromDB
QueryResult *result = CharacterDatabase.Query("SELECT data,itemguid,item_template FROM auctionhouse JOIN item_instance ON itemguid = guid");
+
if (!result)
{
barGoLink bar(1);
@@ -233,34 +277,45 @@ void AuctionHouseMgr::LoadAuctionItems()
sLog.outString(">> Loaded 0 auction items");
return;
}
+
barGoLink bar(result->GetRowCount());
+
uint32 count = 0;
+
Field *fields;
do
{
bar.step();
+
fields = result->Fetch();
uint32 item_guid = fields[1].GetUInt32();
uint32 item_template = fields[2].GetUInt32();
+
ItemPrototype const *proto = objmgr.GetItemPrototype(item_template);
+
if (!proto)
{
sLog.outError("AuctionHouseMgr::LoadAuctionItems: Unknown item (GUID: %u id: #%u) in auction, skipped.", item_guid,item_template);
continue;
}
+
Item *item = NewItemOrBag(proto);
+
if (!item->LoadFromDB(item_guid,0, result))
{
delete item;
continue;
}
AddAItem(item);
+
++count;
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u auction items", count);
}
+
void AuctionHouseMgr::LoadAuctions()
{
QueryResult *result = CharacterDatabase.Query("SELECT COUNT(*) FROM auctionhouse");
@@ -272,9 +327,11 @@ void AuctionHouseMgr::LoadAuctions()
sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
return;
}
+
Field *fields = result->Fetch();
uint32 AuctionCount=fields[0].GetUInt32();
delete result;
+
if (!AuctionCount)
{
barGoLink bar(1);
@@ -283,6 +340,7 @@ void AuctionHouseMgr::LoadAuctions()
sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
return;
}
+
result = CharacterDatabase.Query("SELECT id,auctioneerguid,itemguid,item_template,itemowner,buyoutprice,time,buyguid,lastbid,startbid,deposit FROM auctionhouse");
if (!result)
{
@@ -292,12 +350,17 @@ void AuctionHouseMgr::LoadAuctions()
sLog.outString(">> Loaded 0 auctions. DB table `auctionhouse` is empty.");
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();
@@ -310,6 +373,7 @@ void AuctionHouseMgr::LoadAuctions()
aItem->bid = fields[8].GetUInt32();
aItem->startbid = fields[9].GetUInt32();
aItem->deposit = fields[10].GetUInt32();
+
CreatureData const* auctioneerData = objmgr.GetCreatureData(aItem->auctioneer);
if (!auctioneerData)
{
@@ -318,6 +382,7 @@ void AuctionHouseMgr::LoadAuctions()
delete aItem;
continue;
}
+
CreatureInfo const* auctioneerInfo = objmgr.GetCreatureTemplate(auctioneerData->id);
if (!auctioneerInfo)
{
@@ -326,6 +391,7 @@ void AuctionHouseMgr::LoadAuctions()
delete aItem;
continue;
}
+
aItem->auctionHouseEntry = AuctionHouseMgr::GetAuctionHouseEntry(auctioneerInfo->faction_A);
if (!aItem->auctionHouseEntry)
{
@@ -335,6 +401,7 @@ void AuctionHouseMgr::LoadAuctions()
delete aItem;
continue;
}
+
// check if sold item exists for guid
// and item_template in fact (GetAItem will fail if problematic in result check in AuctionHouseMgr::LoadAuctionItems)
if (!GetAItem(aItem->item_guidlow))
@@ -344,35 +411,44 @@ void AuctionHouseMgr::LoadAuctions()
delete aItem;
continue;
}
+
GetAuctionsMap(auctioneerInfo->faction_A)->AddAuction(aItem);
+
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u auctions", AuctionCount);
}
+
void AuctionHouseMgr::AddAItem(Item* it)
{
ASSERT(it);
ASSERT(mAitems.find(it->GetGUIDLow()) == mAitems.end());
mAitems[it->GetGUIDLow()] = it;
}
+
bool AuctionHouseMgr::RemoveAItem(uint32 id)
{
ItemMap::iterator i = mAitems.find(id);
if (i == mAitems.end())
return false;
+
mAitems.erase(i);
return true;
}
+
void AuctionHouseMgr::Update()
{
mHordeAuctions.Update();
mAllianceAuctions.Update();
mNeutralAuctions.Update();
}
+
AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTemplateId)
{
uint32 houseid = 7; // goblin auction house
+
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_AUCTION))
{
//FIXME: found way for proper auctionhouse selection by another way
@@ -405,6 +481,7 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTem
}
}
}
+
return sAuctionHouseStore.LookupEntry(houseid);
}
void AuctionHouseObject::AddAuction(AuctionEntry *ah)
@@ -413,45 +490,57 @@ AuctionHouseEntry const* AuctionHouseMgr::GetAuctionHouseEntry(uint32 factionTem
AuctionsMap[ah->Id] = ah;
auctionbot.IncrementItemCounts(ah);
}
+
bool AuctionHouseObject::RemoveAuction(AuctionEntry *auction, uint32 item_template)
{
auctionbot.DecrementItemCounts(auction, item_template);
return AuctionsMap.erase(auction->Id) ? true : false;
}
+
void AuctionHouseObject::Update()
{
time_t curTime = sWorld.GetGameTime();
///- Handle expired auctions
+
// If storage is empty, no need to update. next == NULL in this case.
if (AuctionsMap.empty())
return;
+
QueryResult* result = CharacterDatabase.PQuery("SELECT id FROM auctionhouse WHERE time <= %u ORDER BY TIME ASC", (uint32)curTime+60);
+
if (!result)
{
delete result;
return;
}
+
if (result->GetRowCount() == 0)
{
delete result;
return;
}
+
vector<uint32> expiredAuctions;
+
do
{
uint32 tmpdata = result->Fetch()->GetUInt32();
expiredAuctions.push_back(tmpdata);
} while (result->NextRow());
delete result;
+
vector<uint32>::iterator iter = expiredAuctions.begin();
while (!expiredAuctions.empty())
{
// from auctionhousehandler.cpp, creates auction pointer & player pointer
AuctionEntry* auction = GetAuction(*iter);
+
// Erase the auction from the vector.
expiredAuctions.erase(iter);
+
if (!auction)
continue;
+
///- Either cancel the auction if there was no bidder
if (auction->bidder == 0)
auctionmgr.SendAuctionExpiredMail(auction);
@@ -464,6 +553,7 @@ void AuctionHouseObject::Update()
auctionmgr.SendAuctionSuccessfulMail(auction);
auctionmgr.SendAuctionWonMail(auction);
}
+
///- In any case clear the auction
auction->DeleteFromDB();
uint32 item_template = auction->item_template;
@@ -471,6 +561,7 @@ void AuctionHouseObject::Update()
RemoveAuction(auction, item_template);
}
}
+
void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
{
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr)
@@ -480,10 +571,12 @@ void AuctionHouseObject::BuildListBidderItems(WorldPacket& data, Player* player,
{
if (itr->second->BuildAuctionInfo(data))
++count;
+
++totalcount;
}
}
}
+
void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount)
{
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr)
@@ -493,38 +586,50 @@ void AuctionHouseObject::BuildListOwnerItems(WorldPacket& data, Player* player,
{
if (Aentry->BuildAuctionInfo(data))
++count;
+
++totalcount;
}
}
}
+
void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& wsearchedname, uint32 listfrom, uint32 levelmin, uint32 levelmax, uint32 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
uint32& count, uint32& totalcount)
{
int loc_idx = player->GetSession()->GetSessionDbLocaleIndex();
+
for (AuctionEntryMap::const_iterator itr = AuctionsMap.begin();itr != AuctionsMap.end();++itr)
{
AuctionEntry *Aentry = itr->second;
Item *item = auctionmgr.GetAItem(Aentry->item_guidlow);
if (!item)
continue;
+
ItemPrototype const *proto = item->GetProto();
+
if (itemClass != 0xffffffff && proto->Class != itemClass)
continue;
+
if (itemSubClass != 0xffffffff && proto->SubClass != itemSubClass)
continue;
+
if (inventoryType != 0xffffffff && proto->InventoryType != inventoryType)
continue;
+
if (quality != 0xffffffff && proto->Quality != quality)
continue;
+
if (levelmin != 0x00 && (proto->RequiredLevel < levelmin || (levelmax != 0x00 && proto->RequiredLevel > levelmax)))
continue;
+
if (usable != 0x00 && player->CanUseItem(item) != EQUIP_ERR_OK)
continue;
+
std::string name = proto->Name1;
if (name.empty())
continue;
+
// local name
if (loc_idx >= 0)
{
@@ -535,8 +640,10 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
name = il->Name[loc_idx];
}
}
+
if (!wsearchedname.empty() && !Utf8FitTo(name, wsearchedname))
continue;
+
if (count < 50 && totalcount >= listfrom)
{
++count;
@@ -545,6 +652,7 @@ void AuctionHouseObject::BuildListAuctionItems(WorldPacket& data, Player* player
++totalcount;
}
}
+
//this function inserts to WorldPacket auction's data
bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const
{
@@ -556,12 +664,14 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const
}
data << uint32(Id);
data << uint32(pItem->GetEntry());
+
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
@@ -577,6 +687,7 @@ bool AuctionEntry::BuildAuctionInfo(WorldPacket & data) const
data << uint32(bid); //current bid
return true;
}
+
uint32 AuctionEntry::GetAuctionCut() const
{
int32 cut = int32(((double)auctionHouseEntry->cutPercent / 100.0f) * (double)sWorld.getRate(RATE_AUCTION_CUT)) * bid;
@@ -585,6 +696,7 @@ uint32 AuctionEntry::GetAuctionCut() const
else
return 0;
}
+
/// the sum of outbid is (1% from current bid)*5, if bid is very small, it is 1c
uint32 AuctionEntry::GetAuctionOutBid() const
{
@@ -593,11 +705,13 @@ uint32 AuctionEntry::GetAuctionOutBid() const
outbid = 1;
return outbid;
}
+
void AuctionEntry::DeleteFromDB() const
{
//No SQL injection (Id is integer)
CharacterDatabase.PExecute("DELETE FROM auctionhouse WHERE id = '%u'",Id);
}
+
void AuctionEntry::SaveToDB() const
{
//No SQL injection (no strings)
diff --git a/src/game/AuctionHouseMgr.h b/src/game/AuctionHouseMgr.h
index 5d62e16ab85..0e3ead4f608 100644
--- a/src/game/AuctionHouseMgr.h
+++ b/src/game/AuctionHouseMgr.h
@@ -17,15 +17,22 @@
* 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_MGR_H
#define _AUCTION_HOUSE_MGR_H
+
#include "Policies/Singleton.h"
+
#include "SharedDefines.h"
+
#include "AuctionHouseBot.h"
+
class Item;
class Player;
class WorldPacket;
+
#define MIN_AUCTION_TIME (12*HOUR)
+
enum AuctionError
{
AUCTION_OK = 0,
@@ -34,12 +41,14 @@ enum AuctionError
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;
@@ -54,6 +63,7 @@ struct AuctionEntry
uint32 bidder;
uint32 deposit; //deposit can be calculated only when creating auction
AuctionHouseEntry const* auctionHouseEntry; // in AuctionHouse.dbc
+
// helpers
uint32 GetHouseId() const { return auctionHouseEntry->houseId; }
uint32 GetHouseFaction() const { return auctionHouseEntry->faction; }
@@ -63,6 +73,7 @@ struct AuctionEntry
void DeleteFromDB() const;
void SaveToDB() const;
};
+
//this class is used as auctionhouse instance
class AuctionHouseObject
{
@@ -74,37 +85,51 @@ class AuctionHouseObject
for (AuctionEntryMap::iterator itr = AuctionsMap.begin(); itr != AuctionsMap.end(); ++itr)
delete itr->second;
}
+
typedef std::map<uint32, AuctionEntry*> AuctionEntryMap;
+
uint32 Getcount() { return AuctionsMap.size(); }
+
AuctionEntryMap::iterator GetAuctionsBegin() {return AuctionsMap.begin();}
AuctionEntryMap::iterator GetAuctionsEnd() {return AuctionsMap.end();}
+
AuctionEntry* GetAuction(uint32 id) const
{
AuctionEntryMap::const_iterator itr = AuctionsMap.find(id);
return itr != AuctionsMap.end() ? itr->second : NULL;
}
+
void AddAuction(AuctionEntry *ah);
+
bool RemoveAuction(AuctionEntry *auction, uint32 item_template);
+
void Update();
+
void BuildListBidderItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
void BuildListOwnerItems(WorldPacket& data, Player* player, uint32& count, uint32& totalcount);
void BuildListAuctionItems(WorldPacket& data, Player* player,
std::wstring const& searchedname, uint32 listfrom, uint32 levelmin, uint32 levelmax, uint32 usable,
uint32 inventoryType, uint32 itemClass, uint32 itemSubClass, uint32 quality,
uint32& count, uint32& totalcount);
+
private:
AuctionEntryMap AuctionsMap;
+
// storage for "next" auction item for next Update()
AuctionEntryMap::const_iterator next;
};
+
class AuctionHouseMgr
{
public:
AuctionHouseMgr();
~AuctionHouseMgr();
+
typedef UNORDERED_MAP<uint32, Item*> ItemMap;
+
AuctionHouseObject* GetAuctionsMap(uint32 factionTemplateId);
AuctionHouseObject* GetBidsMap(uint32 factionTemplateId);
+
Item* GetAItem(uint32 id)
{
ItemMap::const_iterator itr = mAitems.find(id);
@@ -114,6 +139,7 @@ class AuctionHouseMgr
}
return NULL;
}
+
//auction messages
void SendAuctionWonMail(AuctionEntry * auction);
void SendAuctionSalePendingMail(AuctionEntry * auction);
@@ -121,18 +147,25 @@ class AuctionHouseMgr
void SendAuctionExpiredMail(AuctionEntry * auction);
static uint32 GetAuctionDeposit(AuctionHouseEntry const* entry, uint32 time, Item *pItem);
static AuctionHouseEntry const* GetAuctionHouseEntry(uint32 factionTemplateId);
+
public:
//load first auction items, because of check if item exists, when loading
void LoadAuctionItems();
void LoadAuctions();
+
void AddAItem(Item* it);
bool RemoveAItem(uint32 id);
+
void Update();
+
private:
AuctionHouseObject mHordeAuctions;
AuctionHouseObject mAllianceAuctions;
AuctionHouseObject mNeutralAuctions;
+
ItemMap mAitems;
};
+
#define auctionmgr Trinity::Singleton<AuctionHouseMgr>::Instance()
+
#endif
diff --git a/src/game/Bag.cpp b/src/game/Bag.cpp
index 5b406c2eb08..2d58b63c0a9 100644
--- a/src/game/Bag.cpp
+++ b/src/game/Bag.cpp
@@ -17,19 +17,25 @@
* 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 "ObjectMgr.h"
#include "Database/DatabaseEnv.h"
+
#include "Bag.h"
#include "Log.h"
#include "UpdateData.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);
}
+
Bag::~Bag()
{
for(uint8 i = 0; i < MAX_BAG_SIZE; ++i)
@@ -45,52 +51,68 @@ Bag::~Bag()
delete m_bagslot[i];
}
}
+
void Bag::AddToWorld()
{
Item::AddToWorld();
+
for(uint32 i = 0; i < GetBagSize(); ++i)
if(m_bagslot[i])
m_bagslot[i]->AddToWorld();
}
+
void Bag::RemoveFromWorld()
{
for(uint32 i = 0; i < GetBagSize(); ++i)
if(m_bagslot[i])
m_bagslot[i]->RemoveFromWorld();
+
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 );
+
SetEntry(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);
+
// Cleaning 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 (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
{
@@ -101,34 +123,44 @@ bool Bag::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result)
m_bagslot[i] = NULL;
}
}
+
return true;
}
+
void Bag::DeleteFromDB()
{
for (uint8 i = 0; i < MAX_BAG_SIZE; ++i)
if (m_bagslot[i])
m_bagslot[i]->DeleteFromDB();
+
Item::DeleteFromDB();
}
+
uint32 Bag::GetFreeSlots() const
{
uint32 slots = 0;
for (uint32 i=0; i < GetBagSize(); ++i)
if (!m_bagslot[i])
++slots;
+
return slots;
}
+
void Bag::RemoveItem( uint8 slot, bool /*update*/ )
{
assert(slot < MAX_BAG_SIZE);
+
if (m_bagslot[slot])
m_bagslot[slot]->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;
@@ -139,21 +171,26 @@ void Bag::StoreItem( uint8 slot, Item *pItem, bool /*update*/ )
pItem->SetSlot(slot);
}
}
+
void Bag::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
{
Item::BuildCreateUpdateBlockForPlayer( data, target );
+
for (uint32 i = 0; i < GetBagSize(); ++i)
if(m_bagslot[i])
m_bagslot[i]->BuildCreateUpdateBlockForPlayer( data, target );
}
+
// If the bag is empty returns true
bool Bag::IsEmpty() const
{
for(uint32 i = 0; i < GetBagSize(); ++i)
if (m_bagslot[i])
return false;
+
return true;
}
+
uint32 Bag::GetItemCount( uint32 item, Item* eItem ) const
{
Item *pItem;
@@ -164,6 +201,7 @@ uint32 Bag::GetItemCount( uint32 item, Item* eItem ) const
if( pItem && pItem != eItem && pItem->GetEntry() == item )
count += pItem->GetCount();
}
+
if(eItem && eItem->GetProto()->GemProperties)
{
for(uint32 i=0; i < GetBagSize(); ++i)
@@ -173,20 +211,25 @@ uint32 Bag::GetItemCount( uint32 item, Item* eItem ) const
count += pItem->GetGemCountWithID(item);
}
}
+
return count;
}
+
uint8 Bag::GetSlotByItemGUID(uint64 guid) const
{
for(uint32 i = 0; i < GetBagSize(); ++i)
if(m_bagslot[i] != 0)
if(m_bagslot[i]->GetGUID() == guid)
return i;
+
return NULL_SLOT;
}
+
Item* Bag::GetItemByPos( uint8 slot ) const
{
if( slot < GetBagSize() )
return m_bagslot[slot];
+
return NULL;
}
diff --git a/src/game/Bag.h b/src/game/Bag.h
index 649c2e21a27..3ae786512ce 100644
--- a/src/game/Bag.h
+++ b/src/game/Bag.h
@@ -17,29 +17,40 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_BAG_H
#define TRINITY_BAG_H
+
// Maximum 36 Slots ( (CONTAINER_END - CONTAINER_FIELD_SLOT_1)/2
#define MAX_BAG_SIZE 36 // 2.0.12
+
#include "Item.h"
#include "ItemPrototype.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;
uint32 GetBagSize() const { return GetUInt32Value(CONTAINER_FIELD_NUM_SLOTS); }
+
// DB operations
// overwrite virtual Item::SaveToDB
void SaveToDB();
@@ -47,11 +58,15 @@ class Bag : public Item
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;
diff --git a/src/game/BattleGround.cpp b/src/game/BattleGround.cpp
index 92c82f620f5..3eb699472d8 100644
--- a/src/game/BattleGround.cpp
+++ b/src/game/BattleGround.cpp
@@ -17,10 +17,12 @@
* 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 "ObjectMgr.h"
#include "World.h"
#include "WorldPacket.h"
+
#include "ArenaTeam.h"
#include "BattleGround.h"
#include "BattleGroundMgr.h"
@@ -34,6 +36,7 @@
#include "SpellAuras.h"
#include "Util.h"
+
namespace MaNGOS
{
class BattleGroundChatBuilder
@@ -44,14 +47,17 @@ namespace MaNGOS
void operator()(WorldPacket& data, int32 loc_idx)
{
char const* text = objmgr.GetMangosString(i_textId,loc_idx);
+
if (i_args)
{
// we need copy va_list before use or original va_list will corrupted
va_list ap;
va_copy(ap,*i_args);
+
char str [2048];
vsnprintf(str,2048,text, ap );
va_end(ap);
+
do_helper(data,&str[0]);
}
else
@@ -61,6 +67,7 @@ namespace MaNGOS
void do_helper(WorldPacket& data, char const* text)
{
uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
+
data << uint8(i_msgtype);
data << uint32(LANG_UNIVERSAL);
data << uint64(target_guid); // there 0 for BG messages
@@ -70,11 +77,13 @@ namespace MaNGOS
data << text;
data << uint8(i_source ? i_source->chatTag() : uint8(0));
}
+
ChatMsg i_msgtype;
int32 i_textId;
Player const* i_source;
va_list* i_args;
};
+
class BattleGround2ChatBuilder
{
public:
@@ -85,9 +94,12 @@ namespace MaNGOS
char const* text = objmgr.GetMangosString(i_textId,loc_idx);
char const* arg1str = i_arg1 ? objmgr.GetMangosString(i_arg1,loc_idx) : "";
char const* arg2str = i_arg2 ? objmgr.GetMangosString(i_arg2,loc_idx) : "";
+
char str [2048];
snprintf(str,2048,text, arg1str, arg2str );
+
uint64 target_guid = i_source ? i_source ->GetGUID() : 0;
+
data << uint8(i_msgtype);
data << uint32(LANG_UNIVERSAL);
data << uint64(target_guid); // there 0 for BG messages
@@ -98,6 +110,7 @@ namespace MaNGOS
data << uint8(i_source ? i_source->chatTag() : uint8(0));
}
private:
+
ChatMsg i_msgtype;
int32 i_textId;
Player const* i_source;
@@ -105,6 +118,7 @@ namespace MaNGOS
int32 i_arg2;
};
} // namespace MaNGOS
+
template<class Do>
void BattleGround::BroadcastWorker(Do& _do)
{
@@ -112,6 +126,7 @@ void BattleGround::BroadcastWorker(Do& _do)
if (Player *plr = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER)))
_do(plr);
}
+
BattleGround::BattleGround()
{
m_TypeID = BattleGroundTypeId(0);
@@ -135,32 +150,46 @@ BattleGround::BattleGround()
m_LevelMax = 0;
m_InBGFreeSlotQueue = false;
m_SetDeleteThis = false;
+
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_ArenaTeamIds[BG_TEAM_ALLIANCE] = 0;
m_ArenaTeamIds[BG_TEAM_HORDE] = 0;
+
m_ArenaTeamRatingChanges[BG_TEAM_ALLIANCE] = 0;
m_ArenaTeamRatingChanges[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;
+
m_TeamScores[BG_TEAM_ALLIANCE] = 0;
m_TeamScores[BG_TEAM_HORDE] = 0;
+
m_PrematureCountDown = false;
m_PrematureCountDown = 0;
+
m_HonorMode = BG_NORMAL;
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_2M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_30S;
@@ -171,6 +200,7 @@ BattleGround::BattleGround()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
}
+
BattleGround::~BattleGround()
{
// remove objects and creatures
@@ -178,9 +208,11 @@ BattleGround::~BattleGround()
int size = m_BgCreatures.size();
for(int i = 0; i < size; ++i)
DelCreature(i);
+
size = m_BgObjects.size();
for(int i = 0; i < size; ++i)
DelObject(i);
+
if (GetInstanceID()) // not spam by useless queries in case BG templates
{
// delete creature and go respawn times
@@ -190,6 +222,7 @@ BattleGround::~BattleGround()
CharacterDatabase.PExecute("DELETE FROM instance WHERE id = '%u'",GetInstanceID());
// remove from battlegrounds
}
+
sBattleGroundMgr.RemoveBattleGround(GetInstanceID(), GetTypeID());
// unload map
if (Map * map = MapManager::Instance().FindMap(GetMapId(), GetInstanceID()))
@@ -197,9 +230,11 @@ BattleGround::~BattleGround()
((BattleGroundMap*)map)->SetUnload();
// remove from bg free slot queue
this->RemoveFromBGFreeSlotQueue();
+
for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
delete itr->second;
}
+
void BattleGround::Update(uint32 diff)
{
if (!GetPlayersSize())
@@ -218,6 +253,7 @@ void BattleGround::Update(uint32 diff)
m_SetDeleteThis = true;
return;
}
+
// remove offline players from bg after 5 minutes
if (!m_OfflineQueue.empty())
{
@@ -232,9 +268,11 @@ void BattleGround::Update(uint32 diff)
}
}
}
+
/*********************************************************/
/*** BATTLEGROUND RESSURECTION SYSTEM ***/
/*********************************************************/
+
//this should be handled by spell system
m_LastResurrectTime += diff;
if (m_LastResurrectTime >= RESURRECTION_INTERVAL)
@@ -249,6 +287,7 @@ void BattleGround::Update(uint32 diff)
Player *plr = objmgr.GetPlayer(*itr2);
if (!plr)
continue;
+
if (!sh && plr->IsInWorld())
{
sh = plr->GetMap()->GetCreature(itr->first);
@@ -257,12 +296,14 @@ void BattleGround::Update(uint32 diff)
// Spirit Heal, effect 117
sh->CastSpell(sh, SPELL_SPIRIT_HEAL, true);
}
+
// Resurrection visual
plr->CastSpell(plr, SPELL_RESURRECTION_VISUAL, true);
m_ResurrectQueue.push_back(*itr2);
}
(itr->second).clear();
}
+
m_ReviveQueue.clear();
m_LastResurrectTime = 0;
}
@@ -284,9 +325,11 @@ void BattleGround::Update(uint32 diff)
}
m_ResurrectQueue.clear();
}
+
/*********************************************************/
/*** BATTLEGROUND BALLANCE SYSTEM ***/
/*********************************************************/
+
// if less then minimum players are in on one side, then start premature finish timer
if (GetStatus() == STATUS_IN_PROGRESS && !isArena() && sBattleGroundMgr.GetPrematureFinishTime() && (GetPlayersCountByTeam(ALLIANCE) < GetMinPlayersPerTeam() || GetPlayersCountByTeam(HORDE) < GetMinPlayersPerTeam()))
{
@@ -303,6 +346,7 @@ void BattleGround::Update(uint32 diff)
winner = ALLIANCE;
else if (GetPlayersCountByTeam(HORDE) >= GetMinPlayersPerTeam())
winner = HORDE;
+
EndBattleGround(winner);
m_PrematureCountDown = false;
}
@@ -326,21 +370,26 @@ void BattleGround::Update(uint32 diff)
}
else if (m_PrematureCountDown)
m_PrematureCountDown = false;
+
/*********************************************************/
/*** BATTLEGROUND STARTING SYSTEM ***/
/*********************************************************/
+
if (GetStatus() == STATUS_WAIT_JOIN && GetPlayersSize())
{
ModifyStartDelayTime(diff);
+
if (!(m_Events & BG_STARTING_EVENT_1))
{
m_Events |= BG_STARTING_EVENT_1;
+
// setup here, only when at least one player has ported to the map
if (!SetupBattleGround())
{
EndNow();
return;
}
+
StartingEventCloseDoors();
SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FIRST]);
//first start warning - 2 or 1 minute
@@ -362,14 +411,18 @@ void BattleGround::Update(uint32 diff)
else if (GetStartDelayTime() <= 0 && !(m_Events & BG_STARTING_EVENT_4))
{
m_Events |= BG_STARTING_EVENT_4;
+
StartingEventOpenDoors();
+
SendMessageToAll(m_StartMessageIds[BG_STARTING_EVENT_FOURTH], CHAT_MSG_BG_SYSTEM_NEUTRAL);
SetStatus(STATUS_IN_PROGRESS);
SetStartDelayTime(m_StartDelayTimes[BG_STARTING_EVENT_FOURTH]);
+
//remove preparation
if (isArena())
{
//TODO : add arena sound PlaySoundToAll(SOUND_ARENA_START);
+
for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player *plr = objmgr.GetPlayer(itr->first))
{
@@ -390,11 +443,14 @@ void BattleGround::Update(uint32 diff)
++iter;
}
}
+
CheckArenaWinConditions();
}
else
{
+
PlaySoundToAll(SOUND_BG_START);
+
for(BattleGroundPlayerMap::const_iterator itr = GetPlayers().begin(); itr != GetPlayers().end(); ++itr)
if (Player* plr = objmgr.GetPlayer(itr->first))
plr->RemoveAurasDueToSpell(SPELL_PREPARATION);
@@ -406,9 +462,11 @@ void BattleGround::Update(uint32 diff)
}
}
}
+
/*********************************************************/
/*** BATTLEGROUND ENDING SYSTEM ***/
/*********************************************************/
+
if (GetStatus() == STATUS_WAIT_LEAVE)
{
// remove all players from battleground after 2 minutes
@@ -427,9 +485,11 @@ void BattleGround::Update(uint32 diff)
}
}
}
+
//update start time
m_StartTime += diff;
}
+
void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, float O)
{
BattleGroundTeamId idx = GetTeamIndexByTeamId(TeamID);
@@ -438,6 +498,7 @@ void BattleGround::SetTeamStartLoc(uint32 TeamID, float X, float Y, float Z, flo
m_TeamStartLocZ[idx] = Z;
m_TeamStartLocO[idx] = O;
}
+
void BattleGround::SendPacketToAll(WorldPacket *packet)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
@@ -449,43 +510,54 @@ void BattleGround::SendPacketToAll(WorldPacket *packet)
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
}
}
+
void BattleGround::SendPacketToTeam(uint32 TeamID, WorldPacket *packet, Player *sender, bool self)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = objmgr.GetPlayer(itr->first);
+
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
+
if (!self && sender == plr)
continue;
+
uint32 team = itr->second.Team;
if(!team) team = plr->GetTeam();
+
if (team == 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(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = objmgr.GetPlayer(itr->first);
+
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
+
uint32 team = itr->second.Team;
if(!team) team = plr->GetTeam();
+
if (team == TeamID)
{
sBattleGroundMgr.BuildPlaySoundPacket(&data, SoundID);
@@ -493,22 +565,27 @@ void BattleGround::PlaySoundToTeam(uint32 SoundID, uint32 TeamID)
}
}
}
+
void BattleGround::CastSpellOnTeam(uint32 SpellID, uint32 TeamID)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = objmgr.GetPlayer(itr->first);
+
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
+
uint32 team = itr->second.Team;
if(!team) team = plr->GetTeam();
+
if (team == TeamID)
plr->CastSpell(plr, SpellID, true);
}
}
+
void BattleGround::YellToAll(Creature* creature, const char* text, uint32 language)
{
for(std::map<uint64, BattleGroundPlayer>::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
@@ -525,81 +602,102 @@ void BattleGround::YellToAll(Creature* creature, const char* text, uint32 langua
}
}
+
void BattleGround::RewardHonorToTeam(uint32 Honor, uint32 TeamID)
{
for(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = objmgr.GetPlayer(itr->first);
+
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
+
uint32 team = itr->second.Team;
if(!team) team = plr->GetTeam();
+
if (team == 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(BattleGroundPlayerMap::const_iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = objmgr.GetPlayer(itr->first);
+
if (!plr)
{
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
+
uint32 team = itr->second.Team;
if(!team) team = plr->GetTeam();
+
if (team == TeamID)
plr->GetReputationMgr().ModifyReputation(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)
{
this->RemoveFromBGFreeSlotQueue();
+
ArenaTeam * winner_arena_team = NULL;
ArenaTeam * loser_arena_team = NULL;
uint32 loser_rating = 0;
uint32 winner_rating = 0;
WorldPacket data;
int32 winmsg_id = 0;
+
if (winner == ALLIANCE)
{
winmsg_id = isBattleGround() ? LANG_BG_A_WINS : LANG_ARENA_GOLD_WINS;
+
PlaySoundToAll(SOUND_ALLIANCE_WINS); // alliance wins sound
+
SetWinner(WINNER_ALLIANCE);
}
else if (winner == HORDE)
{
winmsg_id = isBattleGround() ? LANG_BG_H_WINS : LANG_ARENA_GREEN_WINS;
+
PlaySoundToAll(SOUND_HORDE_WINS); // horde wins sound
+
SetWinner(WINNER_HORDE);
}
else
{
SetWinner(3);
}
+
SetStatus(STATUS_WAIT_LEAVE);
//we must set it this way, because end time is sent in packet!
m_EndTime = TIME_TO_AUTOREMOVE;
+
// arena rating calculation
if (isArena() && isRated())
{
@@ -622,10 +720,12 @@ void BattleGround::EndBattleGround(uint32 winner)
SetArenaTeamRatingChangeForTeam(HORDE, 0);
}
}
+
for(BattleGroundPlayerMap::iterator itr = m_Players.begin(); itr != m_Players.end(); ++itr)
{
Player *plr = objmgr.GetPlayer(itr->first);
uint32 team = itr->second.Team;
+
if (!plr)
{
//if rated arena match - make member lost!
@@ -639,9 +739,11 @@ void BattleGround::EndBattleGround(uint32 winner)
sLog.outError("BattleGround: Player (GUID: %u) not found!", GUID_LOPART(itr->first));
continue;
}
+
// should remove spirit of redemption
if(plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
+
if (!plr->isAlive())
{
plr->ResurrectPlayer(1.0f);
@@ -652,8 +754,10 @@ void BattleGround::EndBattleGround(uint32 winner)
plr->CombatStop();
plr->getHostilRefManager().deleteReferences();
}
+
//this line is obsolete - team is set ALWAYS
//if(!team) team = plr->GetTeam();
+
// per player calculation
if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team)
{
@@ -663,15 +767,18 @@ void BattleGround::EndBattleGround(uint32 winner)
ArenaTeamMember* member = winner_arena_team->GetMember(plr->GetGUID());
if (member)
plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, member->personal_rating);
+
winner_arena_team->MemberWon(plr,loser_rating);
}
else
{
loser_arena_team->MemberLost(plr,winner_rating);
+
// Arena lost => reset the win_rated_arena having the "no_loose" condition
plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA, ACHIEVEMENT_CRITERIA_CONDITION_NO_LOOSE);
}
}
+
if (team == winner)
{
RewardMark(plr,ITEM_WINNER_COUNT);
@@ -680,15 +787,20 @@ void BattleGround::EndBattleGround(uint32 winner)
}
else if(winner)
RewardMark(plr,ITEM_LOSER_COUNT);
+
plr->CombatStopWithPets(true);
+
BlockMovement(plr);
+
sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
plr->GetSession()->SendPacket(&data);
+
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, TIME_TO_AUTOREMOVE, GetStartTime(), GetArenaType());
plr->GetSession()->SendPacket(&data);
plr->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND, 1);
}
+
if (isArena() && isRated() && winner_arena_team && loser_arena_team && winner_arena_team != loser_arena_team)
{
// update arena points only after increasing the player's match count!
@@ -702,14 +814,17 @@ void BattleGround::EndBattleGround(uint32 winner)
winner_arena_team->NotifyStatsChanged();
loser_arena_team->NotifyStatsChanged();
}
+
if (winmsg_id)
SendMessageToAll(winmsg_id, CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
+
uint32 BattleGround::GetBonusHonorFromKill(uint32 kills) const
{
//variable kills means how many honorable kills you scored (so we need kills * honor_for_one_kill)
return MaNGOS::Honor::hk_honor_at_level(GetMaxLevel(), kills);
}
+
uint32 BattleGround::GetBattlemasterEntry() const
{
switch(GetTypeID())
@@ -722,6 +837,7 @@ uint32 BattleGround::GetBattlemasterEntry() const
default: return 0;
}
}
+
void BattleGround::RewardMark(Player *plr,uint32 count)
{
BattleGroundMarks mark;
@@ -742,60 +858,75 @@ void BattleGround::RewardMark(Player *plr,uint32 count)
default:
return;
}
+
//if (IsSpell)
// RewardSpellCast(plr,mark);
//else
RewardItem(plr,mark,count);
}
+
void BattleGround::RewardSpellCast(Player *plr, uint32 spell_id)
{
// 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
if (plr->HasAura(SPELL_AURA_PLAYER_INACTIVE))
return;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
if(!spellInfo)
{
sLog.outError("Battleground reward casting spell %u not exist.",spell_id);
return;
}
+
plr->CastSpell(plr, spellInfo, true);
}
+
void BattleGround::RewardItem(Player *plr, uint32 item_id, uint32 count)
{
// 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
if (plr->HasAura(SPELL_AURA_PLAYER_INACTIVE))
return;
+
ItemPosCountVec dest;
uint32 no_space_count = 0;
uint8 msg = plr->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, item_id, count, &no_space_count );
+
if( msg == EQUIP_ERR_ITEM_NOT_FOUND)
{
sLog.outErrorDb("Battleground reward item (Entry %u) not exist in `item_template`.",item_id);
return;
}
+
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, item_id, true, 0))
plr->SendNewItem(item,count,true,false);
+
if (no_space_count > 0)
SendRewardMarkByMail(plr,item_id,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();
@@ -803,14 +934,17 @@ void BattleGround::SendRewardMarkByMail(Player *plr,uint32 mark, uint32 count)
if (ItemLocale const *il = objmgr.GetItemLocale(markProto->ItemId))
if (il->Name.size() > size_t(loc_idx) && !il->Name[loc_idx].empty())
subject = il->Name[loc_idx];
+
// text
std::string textFormat = plr->GetSession()->GetTrinityString(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::RewardQuestComplete(Player *plr)
{
uint32 quest;
@@ -831,12 +965,15 @@ void BattleGround::RewardQuestComplete(Player *plr)
default:
return;
}
+
RewardSpellCast(plr, quest);
}
+
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)
{
uint32 team = GetPlayerTeam(guid);
@@ -850,23 +987,30 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac
// check if the player was a participant of the match, or only entered through gm command (goname)
participant = true;
}
+
BattleGroundScoreMap::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);
+
// should remove spirit of redemption
if(plr && plr->HasAuraType(SPELL_AURA_SPIRIT_OF_REDEMPTION))
plr->RemoveAurasByType(SPELL_AURA_MOD_SHAPESHIFT);
+
if(plr && !plr->isAlive()) // resurrect on exit
{
plr->ResurrectPlayer(1.0f);
plr->SpawnCorpseBones();
}
+
RemovePlayer(plr, guid); // BG subclass specific code
+
if(participant) // if the player was a match participant, remove auras, calc rating, update queue
{
BattleGroundTypeId bgTypeId = GetTypeID();
@@ -874,15 +1018,19 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac
if (plr)
{
plr->ClearAfkReports();
+
if(!team) team = plr->GetTeam();
+
// if arena, remove the specific arena auras
if (isArena())
{
plr->RemoveArenaAuras(true); // removes debuffs / dots etc., we don't want the player to die after porting out
bgTypeId=BATTLEGROUND_AA; // set the bg type to all arenas (it will be used for queue refreshing)
+
// unsummon current and summon old pet if there was one and there isn't a current pet
plr->RemovePet(NULL, PET_SAVE_NOT_IN_SLOT);
plr->ResummonPetTemporaryUnSummonedIfAny();
+
if (isRated() && GetStatus() == STATUS_IN_PROGRESS)
{
//left a rated match while the encounter was in progress, consider as loser
@@ -898,6 +1046,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_NONE, 0, 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(bgQueueTypeId);
}
@@ -913,6 +1062,7 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac
players_arena_team->OfflineMemberLost(guid, others_arena_team->GetRating());
}
}
+
// remove from raid group if player is member
if (Group *group = GetBgRaid(team))
{
@@ -935,18 +1085,23 @@ void BattleGround::RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPac
sBattleGroundMgr.BuildPlayerLeftBattleGroundPacket(&data, guid);
SendPacketToTeam(team, &data, plr, false);
}
+
if (plr)
{
// Do next only if found in battleground
plr->SetBattleGroundId(0, BATTLEGROUND_TYPE_NONE); // We're not in BG.
// reset destination bg team
plr->SetBGTeam(0);
+
if (Transport)
plr->TeleportToBGEntryPoint();
+
sLog.outDetail("BATTLEGROUND: Removed player %s from BattleGround.", plr->GetName());
}
+
//battleground object will be deleted next BattleGround::Update() call
}
+
// this method is called when no players remains in battleground
void BattleGround::Reset()
{
@@ -958,24 +1113,32 @@ void BattleGround::Reset()
SetLastResurrectTime(0);
SetArenaType(0);
SetRated(false);
+
m_Events = 0;
+
if (m_InvitedAlliance > 0 || m_InvitedHorde > 0)
sLog.outError("BattleGround system: bad counter, m_InvitedAlliance: %d, m_InvitedHorde: %d", m_InvitedAlliance, m_InvitedHorde);
+
m_InvitedAlliance = 0;
m_InvitedHorde = 0;
m_InBGFreeSlotQueue = false;
+
m_Players.clear();
+
for(BattleGroundScoreMap::const_iterator itr = m_PlayerScores.begin(); itr != m_PlayerScores.end(); ++itr)
delete itr->second;
m_PlayerScores.clear();
+
ResetBGSubclass();
}
+
void BattleGround::StartBattleGround()
{
SetStartTime(0);
SetLastResurrectTime(0);
// add BG to free slot queue
AddToBGFreeSlotQueue();
+
// add bg to update list
// This must be done here, because we need to have already invited some players when first BG::Update() method is executed
// and it doesn't matter if we call StartBattleGround() more times, because m_BattleGrounds is a map and instance id never changes
@@ -983,23 +1146,31 @@ void BattleGround::StartBattleGround()
if(m_IsRated)
sLog.outArena("Arena match type: %u for Team1Id: %u - Team2Id: %u started.", m_ArenaType, m_ArenaTeamIds[BG_TEAM_ALLIANCE], m_ArenaTeamIds[BG_TEAM_HORDE]);
}
+
void BattleGround::AddPlayer(Player *plr)
{
// remove afk from player
if (plr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_AFK))
plr->ToggleAFK();
+
// score struct must be created in inherited class
+
uint64 guid = plr->GetGUID();
uint32 team = plr->GetBGTeam();
+
BattleGroundPlayer bp;
bp.OfflineRemoveTime = 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);
+
// add arena specific auras
if (isArena())
{
@@ -1020,11 +1191,14 @@ void BattleGround::AddPlayer(Player *plr)
else
plr->CastSpell(plr, SPELL_ALLIANCE_GREEN_FLAG,true);
}
+
plr->DestroyConjuredItems(true);
plr->UnsummonPetTemporaryIfAny();
+
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));
}
@@ -1034,14 +1208,18 @@ void BattleGround::AddPlayer(Player *plr)
if(GetStatus() == STATUS_WAIT_JOIN) // not started yet
plr->CastSpell(plr, SPELL_PREPARATION, true); // reduces all mana cost of spells.
}
+
plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
plr->GetAchievementMgr().ResetAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, ACHIEVEMENT_CRITERIA_CONDITION_MAP, GetMapId());
+
// setup BG group membership
PlayerAddedToBGCheckIfBGIsRunning(plr);
AddOrSetPlayerToCorrectBgGroup(plr, guid, team);
+
// Log
sLog.outDetail("BATTLEGROUND: Player %s joined the battle.", plr->GetName());
}
+
/* this method adds player to his team's bg group, or sets his correct group if player is already in bg group */
void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team)
{
@@ -1068,6 +1246,7 @@ void BattleGround::AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid,
}
}
}
+
// This method should be called when player logs into running battleground
void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
{
@@ -1085,6 +1264,7 @@ void BattleGround::EventPlayerLoggedIn(Player* player, uint64 plr_guid)
// if battleground is starting, then add preparation aura
// we don't have to do that, because preparation aura isn't removed when player logs out
}
+
// This method should be called when player logs out from running battleground
void BattleGround::EventPlayerLoggedOut(Player* player)
{
@@ -1103,6 +1283,7 @@ void BattleGround::EventPlayerLoggedOut(Player* player)
}
}
}
+
/* This method should be called only once ... it adds pointer to queue */
void BattleGround::AddToBGFreeSlotQueue()
{
@@ -1113,6 +1294,7 @@ void BattleGround::AddToBGFreeSlotQueue()
m_InBGFreeSlotQueue = true;
}
}
+
/* This method removes this battleground from free queue - it must be called when deleting battleground - not used now*/
void BattleGround::RemoveFromBGFreeSlotQueue()
{
@@ -1128,6 +1310,7 @@ void BattleGround::RemoveFromBGFreeSlotQueue()
}
}
}
+
// get the number of free slots for team
// returns the number how many players can join battleground to MaxPlayersPerTeam
uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
@@ -1159,6 +1342,7 @@ uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
// allow join more ppl if the other side has more players
else if(otherTeam > GetInvitedCount(Team))
diff = otherTeam - GetInvitedCount(Team);
+
// difference based on max players per team (don't allow inviting more)
uint32 diff2 = (GetInvitedCount(Team) < GetMaxPlayersPerTeam()) ? GetMaxPlayersPerTeam() - GetInvitedCount(Team) : 0;
// difference based on players who already entered
@@ -1173,7 +1357,9 @@ uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
// or other side has less than minPlayersPerTeam
else if (GetInvitedCount(Team) <= GetMinPlayersPerTeam())
diff3 = GetMinPlayersPerTeam() - GetInvitedCount(Team) + 1;
+
// return the minimum of the 3 differences
+
// min of diff and diff 2
diff = diff < diff2 ? diff : diff2;
// min of diff, diff2 and diff3
@@ -1181,16 +1367,20 @@ uint32 BattleGround::GetFreeSlotsForTeam(uint32 Team) const
}
return 0;
}
+
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
BattleGroundScoreMap::const_iterator itr = m_PlayerScores.find(Source->GetGUID());
+
if(itr == m_PlayerScores.end()) // player not found...
return;
+
switch(type)
{
case SCORE_KILLING_BLOWS: // Killing blows
@@ -1223,14 +1413,18 @@ void BattleGround::UpdatePlayerScore(Player *Source, uint32 type, uint32 value)
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);
}
+
void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
{
for(std::map<uint64, std::vector<uint64> >::iterator itr = m_ReviveQueue.begin(); itr != m_ReviveQueue.end(); ++itr)
@@ -1240,20 +1434,25 @@ void BattleGround::RemovePlayerFromResurrectQueue(uint64 player_guid)
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)
{
Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
if (!map)
return false;
+
// must be created this way, adding to godatamap would add it to the base map of the instance
// and when loading it (in go::LoadFromDB()), a new guid would be assigned to the object, and a new object would be created
// so we must create it specific for this instance
@@ -1268,9 +1467,11 @@ bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float
}
/*
uint32 guid = go->GetGUIDLow();
+
// without this, UseButtonOrDoor caused the crash, since it tried to get go info from godata
// iirc that was changed, so adding to go data map is no longer required if that was the only function using godata from GameObject without checking if it existed
GameObjectData& data = objmgr.NewGOData(guid);
+
data.id = entry;
data.mapid = GetMapId();
data.posX = x;
@@ -1291,6 +1492,7 @@ bool BattleGround::AddObject(uint32 type, uint32 entry, float x, float y, float
m_BgObjects[type] = go->GetGUID();
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)
@@ -1311,6 +1513,7 @@ void BattleGround::DoorClose(uint32 type)
sLog.outError("BattleGround: Door object not found (cannot close doors)");
}
}
+
void BattleGround::DoorOpen(uint32 type)
{
GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
@@ -1325,6 +1528,7 @@ void BattleGround::DoorOpen(uint32 type)
sLog.outError("BattleGround: Door object not found! - doors will be closed.");
}
}
+
GameObject* BattleGround::GetBGObject(uint32 type)
{
GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
@@ -1332,6 +1536,7 @@ GameObject* BattleGround::GetBGObject(uint32 type)
sLog.outError("couldn't get gameobject %i",type);
return obj;
}
+
Creature* BattleGround::GetBGCreature(uint32 type)
{
Creature *creature = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
@@ -1339,6 +1544,7 @@ Creature* BattleGround::GetBGCreature(uint32 type)
sLog.outError("couldn't get creature %i",type);
return creature;
}
+
void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
{
Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
@@ -1367,11 +1573,13 @@ void BattleGround::SpawnBGObject(uint32 type, uint32 respawntime)
}
}
}
+
Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, float x, float y, float z, float o, uint32 respawntime)
{
Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceID());
if (!map)
return NULL;
+
Creature* pCreature = new Creature;
if (!pCreature->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), map, PHASEMASK_NORMAL, entry, 0, teamval, x, y, z, o))
{
@@ -1379,10 +1587,14 @@ Creature* BattleGround::AddCreature(uint32 entry, uint32 type, uint32 teamval, f
delete pCreature;
return NULL;
}
+
pCreature->SetHomePosition(x, y, z, o);
+
//pCreature->SetDungeonDifficulty(0);
+
map->Add(pCreature);
m_BgCreatures[type] = pCreature->GetGUID();
+
return pCreature;
}
/*
@@ -1391,6 +1603,7 @@ void BattleGround::SpawnBGCreature(uint32 type, uint32 respawntime)
Map * map = MapManager::Instance().FindMap(GetMapId(),GetInstanceId());
if (!map)
return false;
+
if (respawntime == 0)
{
Creature *obj = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
@@ -1418,6 +1631,7 @@ bool BattleGround::DelCreature(uint32 type)
{
if (!m_BgCreatures[type])
return true;
+
Creature *cr = HashMapHolder<Creature>::Find(m_BgCreatures[type]);
if (!cr)
{
@@ -1428,10 +1642,12 @@ bool BattleGround::DelCreature(uint32 type)
m_BgCreatures[type] = 0;
return true;
}
+
bool BattleGround::DelObject(uint32 type)
{
if (!m_BgObjects[type])
return true;
+
GameObject *obj = HashMapHolder<GameObject>::Find(m_BgObjects[type]);
if (!obj)
{
@@ -1443,13 +1659,16 @@ bool BattleGround::DelObject(uint32 type)
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 = BG_CREATURE_ENTRY_A_SPIRITGUIDE;
else
entry = BG_CREATURE_ENTRY_H_SPIRITGUIDE;
+
Creature* pCreature = AddCreature(entry,type,team,x,y,z,o);
if (!pCreature)
{
@@ -1457,11 +1676,14 @@ bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float
EndNow();
return false;
}
+
pCreature->setDeathState(DEAD);
+
pCreature->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT, pCreature->GetGUID());
// aura
//TODO: Fix display here
//pCreature->SetVisibleAura(0, SPELL_SPIRIT_HEAL_CHANNEL);
+
//pCreature->SetUInt32Value(UNIT_FIELD_AURAFLAGS, 0x00000009);
//pCreature->SetUInt32Value(UNIT_FIELD_AURALEVELS, 0x0000003C);
//pCreature->SetUInt32Value(UNIT_FIELD_AURAAPPLICATIONS, 0x000000FF);
@@ -1469,42 +1691,52 @@ bool BattleGround::AddSpiritGuide(uint32 type, float x, float y, float z, float
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(int32 entry, ChatMsg type, Player const* source)
{
MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source);
MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
BroadcastWorker(bg_do);
}
+
void BattleGround::PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ...)
{
va_list ap;
va_start(ap, source);
+
MaNGOS::BattleGroundChatBuilder bg_builder(type, entry, source, &ap);
MaNGOS::LocalizedPacketDo<MaNGOS::BattleGroundChatBuilder> bg_do(bg_builder);
BroadcastWorker(bg_do);
+
va_end(ap);
}
+
void BattleGround::SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 arg1, int32 arg2)
{
MaNGOS::BattleGround2ChatBuilder bg_builder(type, entry, source, arg1, arg2);
MaNGOS::LocalizedPacketDo<MaNGOS::BattleGround2ChatBuilder> bg_do(bg_builder);
BroadcastWorker(bg_do);
}
+
void BattleGround::EndNow()
{
RemoveFromBGFreeSlotQueue();
SetStatus(STATUS_WAIT_LEAVE);
SetEndTime(0);
}
+
//to be removed
const char *BattleGround::GetTrinityString(int32 entry)
{
// FIXME: now we have different DBC locales and need localized message for each target client
return objmgr.GetTrinityStringForDBCLocale(entry);
}
+
/*
important notice:
buffs aren't spawned/despawned when players captures anything
@@ -1515,6 +1747,7 @@ void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
GameObject *obj = HashMapHolder<GameObject>::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)
@@ -1524,6 +1757,7 @@ void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
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();
@@ -1539,31 +1773,40 @@ void BattleGround::HandleTriggerBuff(uint64 const& go_guid)
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(BattleGroundPlayerMap::const_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 -- ONLY IN BattleGrounds
if (!isArena())
player->SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE );
}
+
// return the player's team based on battlegroundplayer info
// used in same faction arena matches mainly
uint32 BattleGround::GetPlayerTeam(uint64 guid)
@@ -1573,10 +1816,12 @@ uint32 BattleGround::GetPlayerTeam(uint64 guid)
return itr->second.Team;
return 0;
}
+
uint32 BattleGround::GetOtherTeam(uint32 teamId)
{
return (teamId) ? ((teamId == ALLIANCE) ? HORDE : ALLIANCE) : 0;
}
+
bool BattleGround::IsPlayerInBattleGround(uint64 guid)
{
BattleGroundPlayerMap::const_iterator itr = m_Players.find(guid);
@@ -1584,18 +1829,24 @@ bool BattleGround::IsPlayerInBattleGround(uint64 guid)
return true;
return false;
}
+
void BattleGround::PlayerAddedToBGCheckIfBGIsRunning(Player* plr)
{
if (GetStatus() != STATUS_WAIT_LEAVE)
return;
+
WorldPacket data;
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(GetTypeID(), GetArenaType());
+
BlockMovement(plr);
+
sBattleGroundMgr.BuildPvpLogDataPacket(&data, this);
plr->GetSession()->SendPacket(&data);
+
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, this, plr->GetBattleGroundQueueIndex(bgQueueTypeId), STATUS_IN_PROGRESS, GetEndTime(), GetStartTime(), GetArenaType());
plr->GetSession()->SendPacket(&data);
}
+
uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
{
int count = 0;
@@ -1610,6 +1861,7 @@ uint32 BattleGround::GetAlivePlayersCountByTeam(uint32 Team) const
}
return count;
}
+
void BattleGround::SetHoliday(bool is_holiday)
{
if(is_holiday)
@@ -1617,6 +1869,7 @@ void BattleGround::SetHoliday(bool is_holiday)
else
m_HonorMode = BG_NORMAL;
}
+
int32 BattleGround::GetObjectType(uint64 guid)
{
for(uint32 i = 0; i < m_BgObjects.size(); ++i)
@@ -1625,9 +1878,11 @@ int32 BattleGround::GetObjectType(uint64 guid)
sLog.outError("BattleGround: cheating? a player used a gameobject which isnt supposed to be a usable object!");
return -1;
}
+
void BattleGround::HandleKillUnit(Creature *creature, Player *killer)
{
}
+
void BattleGround::CheckArenaWinConditions()
{
if (!GetAlivePlayersCountByTeam(ALLIANCE) && GetPlayersCountByTeam(HORDE))
@@ -1635,6 +1890,7 @@ void BattleGround::CheckArenaWinConditions()
else if (GetPlayersCountByTeam(ALLIANCE) && !GetAlivePlayersCountByTeam(HORDE))
EndBattleGround(ALLIANCE);
}
+
void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
{
Group* &old_raid = TeamID == ALLIANCE ? m_BgRaids[BG_TEAM_ALLIANCE] : m_BgRaids[BG_TEAM_HORDE];
@@ -1642,10 +1898,12 @@ void BattleGround::SetBgRaid( uint32 TeamID, Group *bg_raid )
if(bg_raid) bg_raid->SetBattlegroundGroup(this);
old_raid = bg_raid;
}
+
WorldSafeLocsEntry const* BattleGround::GetClosestGraveYard( Player* player )
{
return objmgr.GetClosestGraveYard( player->GetPositionX(), player->GetPositionY(), player->GetPositionZ(), player->GetMapId(), player->GetTeam() );
}
+
bool BattleGround::IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const
{
BattleGroundTeamId team_idx = GetTeamIndexByTeamId(team);
diff --git a/src/game/BattleGround.h b/src/game/BattleGround.h
index cb19fd92525..007f7a642dd 100644
--- a/src/game/BattleGround.h
+++ b/src/game/BattleGround.h
@@ -17,16 +17,21 @@
* 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 "SharedDefines.h"
+
class Creature;
class GameObject;
class Group;
class Player;
class WorldPacket;
+
struct WorldSafeLocsEntry;
+
enum BattleGroundSounds
{
SOUND_HORDE_WINS = 8454,
@@ -34,6 +39,7 @@ enum BattleGroundSounds
SOUND_BG_START = 3439,
SOUND_BG_START_L70ETC = 11803,
};
+
enum BattleGroundQuests
{
SPELL_WS_QUEST_REWARD = 43483,
@@ -44,6 +50,7 @@ enum BattleGroundQuests
SPELL_AB_QUEST_REWARD_4_BASES = 24061,
SPELL_AB_QUEST_REWARD_5_BASES = 24064
};
+
enum BattleGroundMarks
{
SPELL_WS_MARK_LOSER = 24950,
@@ -57,16 +64,19 @@ enum BattleGroundMarks
ITEM_AB_MARK_OF_HONOR = 20559,
ITEM_EY_MARK_OF_HONOR = 29024
};
+
enum BattleGroundMarksCount
{
ITEM_WINNER_COUNT = 3,
ITEM_LOSER_COUNT = 1
};
+
enum BattleGroundCreatures
{
BG_CREATURE_ENTRY_A_SPIRITGUIDE = 13116, // alliance
BG_CREATURE_ENTRY_H_SPIRITGUIDE = 13117, // horde
};
+
enum BattleGroundSpells
{
SPELL_WAITING_FOR_RESURRECT = 2584, // Waiting to Resurrect
@@ -83,6 +93,7 @@ enum BattleGroundSpells
SPELL_RECENTLY_DROPPED_FLAG = 42792, // Recently Dropped Flag
SPELL_AURA_PLAYER_INACTIVE = 43681 // Inactive
};
+
enum BattleGroundTimeIntervals
{
RESURRECTION_INTERVAL = 30000, // ms
@@ -95,6 +106,7 @@ enum BattleGroundTimeIntervals
RESPAWN_IMMEDIATELY = 0, // secs
BUFF_RESPAWN_TIME = 180, // secs
};
+
enum BattleGroundStartTimeIntervals
{
BG_START_DELAY_2M = 120000, // ms (2 minutes)
@@ -103,13 +115,16 @@ enum BattleGroundStartTimeIntervals
BG_START_DELAY_15S = 15000, // ms (15 seconds) Used only in arena
BG_START_DELAY_NONE = 0, // ms
};
+
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, // first status, should mean bg is not instance
@@ -118,18 +133,22 @@ enum BattleGroundStatus
STATUS_IN_PROGRESS = 3, // means bg is running
STATUS_WAIT_LEAVE = 4 // means some faction has won BG and it is ending
};
+
struct BattleGroundPlayer
{
time_t OfflineRemoveTime; // 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;
};
+
// handle the queue types and bg types separately to enable joining queue for different sized arenas at the same time
enum BattleGroundQueueTypeId
{
@@ -144,6 +163,7 @@ enum BattleGroundQueueTypeId
BATTLEGROUND_QUEUE_5v5 = 8
};
#define MAX_BATTLEGROUND_QUEUE_TYPES 9
+
enum BGQueueIdBasedOnLevel // queue_id for level ranges
{
QUEUE_ID_MAX_LEVEL_19 = 0,
@@ -156,6 +176,7 @@ enum BGQueueIdBasedOnLevel // queue_id for level ranges
QUEUE_ID_MAX_LEVEL_80 = 7
};
#define MAX_BATTLEGROUND_QUEUES 8
+
enum ScoreType
{
SCORE_KILLING_BLOWS = 1,
@@ -181,29 +202,34 @@ enum ScoreType
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
};
#define BG_TEAMS_COUNT 2
+
enum BattleGroundStartingEvents
{
BG_STARTING_EVENT_NONE = 0x00,
@@ -212,6 +238,7 @@ enum BattleGroundStartingEvents
BG_STARTING_EVENT_3 = 0x04,
BG_STARTING_EVENT_4 = 0x08
};
+
enum BattleGroundStartingEventsIds
{
BG_STARTING_EVENT_FIRST = 0,
@@ -220,6 +247,7 @@ enum BattleGroundStartingEventsIds
BG_STARTING_EVENT_FOURTH = 3
};
#define BG_STARTING_EVENT_COUNT 4
+
enum BattleGroundJoinError
{
BG_JOIN_ERR_OK = 0,
@@ -233,6 +261,7 @@ enum BattleGroundJoinError
BG_JOIN_ERR_ALL_QUEUES_USED = 8,
BG_JOIN_ERR_GROUP_NOT_ENOUGH = 9
};
+
class BattleGroundScore
{
public:
@@ -240,6 +269,7 @@ class BattleGroundScore
BonusHonor(0), DamageDone(0), HealingDone(0)
{}
virtual ~BattleGroundScore() {} //virtual destructor is used when deleting score from scores map
+
uint32 KillingBlows;
uint32 Deaths;
uint32 HonorableKills;
@@ -247,12 +277,14 @@ class BattleGroundScore
uint32 DamageDone;
uint32 HealingDone;
};
+
enum BGHonorMode
{
BG_NORMAL = 0,
BG_HOLIDAY,
BG_HONOR_MODE_NUM
};
+
/*
This class is used to:
1. Add player to battleground
@@ -263,6 +295,7 @@ This class is used to:
class BattleGround
{
friend class BattleGroundMgr;
+
public:
/* Construction */
BattleGround();
@@ -279,9 +312,11 @@ class BattleGround
virtual void ResetBGSubclass() // must be implemented in BG subclass
{
}
+
/* achievement req. */
virtual bool IsAllNodesConrolledByTeam(uint32 /*team*/) const { return false; }
bool IsTeamScoreInRange(uint32 team, uint32 minScore, uint32 maxScore) const;
+
/* Battleground */
// Get methods:
char const* GetName() const { return m_Name; }
@@ -295,15 +330,19 @@ class BattleGround
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; }
+
int32 GetStartDelayTime() const { return m_StartDelayTime; }
uint8 GetArenaType() const { return m_ArenaType; }
uint8 GetWinner() const { return m_Winner; }
uint32 GetBattlemasterEntry() const;
uint32 GetBonusHonorFromKill(uint32 kills) const;
+
// Set methods:
void SetName(char const* Name) { m_Name = Name; }
void SetTypeID(BattleGroundTypeId TypeID) { m_TypeID = TypeID; }
@@ -327,12 +366,16 @@ class BattleGround
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
@@ -344,25 +387,33 @@ class BattleGround
}
bool HasFreeSlots() const;
uint32 GetFreeSlotsForTeam(uint32 Team) const;
+
bool isArena() const { return m_IsArena; }
bool isBattleGround() const { return !m_IsArena; }
bool isRated() const { return m_IsRated; }
+
typedef std::map<uint64, BattleGroundPlayer> BattleGroundPlayerMap;
BattleGroundPlayerMap const& GetPlayers() const { return m_Players; }
uint32 GetPlayersSize() const { return m_Players.size(); }
+
typedef std::map<uint64, BattleGroundScore*> BattleGroundScoreMap;
BattleGroundScoreMap::const_iterator GetPlayerScoresBegin() const { return m_PlayerScores.begin(); }
BattleGroundScoreMap::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();
+
GameObject* GetBGObject(uint32 type);
Creature* GetBGCreature(uint32 type);
/* 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
{
@@ -372,14 +423,17 @@ class BattleGround
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 YellToAll(Creature* creature, const char* text, uint32 language);
+
template<class Do>
void BroadcastWorker(Do& _do);
+
void PlaySoundToTeam(uint32 SoundID, uint32 TeamID);
void PlaySoundToAll(uint32 SoundID);
void CastSpellOnTeam(uint32 SpellID, uint32 TeamID);
@@ -394,14 +448,19 @@ class BattleGround
void UpdateWorldStateForPlayer(uint32 Field, uint32 Value, Player *Source);
void EndBattleGround(uint32 winner);
void BlockMovement(Player *plr);
+
void SendMessageToAll(int32 entry, ChatMsg type, Player const* source = NULL);
void PSendMessageToAll(int32 entry, ChatMsg type, Player const* source, ... );
+
// specialized version with 2 string id args
void SendMessage2ToAll(int32 entry, ChatMsg type, Player const* source, int32 strId1 = 0, int32 strId2 = 0);
+
/* 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);
+
virtual void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+
static BattleGroundTeamId GetTeamIndexByTeamId(uint32 Team) { return Team == ALLIANCE ? BG_TEAM_ALLIANCE : BG_TEAM_HORDE; }
uint32 GetPlayersCountByTeam(uint32 Team) const { return m_PlayersCount[GetTeamIndexByTeamId(Team)]; }
uint32 GetAlivePlayersCountByTeam(uint32 Team) const; // used in arenas to correctly handle death in spirit of redemption / last stand etc. (killer = killed) cases
@@ -412,32 +471,41 @@ class BattleGround
else
++m_PlayersCount[GetTeamIndexByTeamId(Team)];
}
+
// used for rated arena battles
void SetArenaTeamIdForTeam(uint32 Team, uint32 ArenaTeamId) { m_ArenaTeamIds[GetTeamIndexByTeamId(Team)] = ArenaTeamId; }
uint32 GetArenaTeamIdForTeam(uint32 Team) const { return m_ArenaTeamIds[GetTeamIndexByTeamId(Team)]; }
void SetArenaTeamRatingChangeForTeam(uint32 Team, int32 RatingChange) { m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)] = RatingChange; }
int32 GetArenaTeamRatingChangeForTeam(uint32 Team) const { return m_ArenaTeamRatingChanges[GetTeamIndexByTeamId(Team)]; }
void CheckArenaWinConditions();
+
/* 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);
virtual void HandleKillUnit(Creature* /*unit*/, Player* /*killer*/);
+
/* Battleground events */
virtual void EventPlayerDroppedFlag(Player* /*player*/) {}
virtual void EventPlayerClickedOnFlag(Player* /*player*/, GameObject* /*target_obj*/) {}
virtual void EventPlayerCapturedFlag(Player* /*player*/) {}
void EventPlayerLoggedIn(Player* player, uint64 plr_guid);
void EventPlayerLoggedOut(Player* player);
+
/* Death related */
virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);
+
virtual void AddPlayer(Player *plr); // must be implemented in BG subclass
+
void AddOrSetPlayerToCorrectBgGroup(Player *plr, uint64 plr_guid, uint32 team);
+
virtual void RemovePlayerAtLeave(uint64 guid, bool Transport, bool SendPacket);
// can be extended in in BG subclass
+
void HandleTriggerBuff(uint64 const& go_guid);
void SetHoliday(bool is_holiday);
+
// TODO: make this protected:
typedef std::vector<uint64> BGObjects;
typedef std::vector<uint64> BGCreatures;
@@ -451,30 +519,40 @@ class BattleGround
bool DelObject(uint32 type);
bool AddSpiritGuide(uint32 type, float x, float y, float z, float o, uint32 team);
int32 GetObjectType(uint64 guid);
+
void DoorOpen(uint32 type);
void DoorClose(uint32 type);
//to be removed
const char *GetTrinityString(int32 entry);
+
virtual bool HandlePlayerUnderMap(Player * /*plr*/) { return false; }
+
// since arenas can be AvA or Hvh, we have to get the "temporary" team of a player
uint32 GetPlayerTeam(uint64 guid);
uint32 GetOtherTeam(uint32 teamId);
bool IsPlayerInBattleGround(uint64 guid);
+
void SetDeleteThis() {m_SetDeleteThis = true;}
+
/* virtual score-array - get's used in bg-subclasses */
int32 m_TeamScores[BG_TEAMS_COUNT];
+
protected:
//this method is called, when BG cannot spawn its own spirit guide, or something is wrong, It correctly ends BattleGround
void EndNow();
void PlayerAddedToBGCheckIfBGIsRunning(Player* plr);
+
/* Scorekeeping */
+
BattleGroundScoreMap m_PlayerScores; // Player scores
// 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<uint64, std::vector<uint64> > m_ReviveQueue;
+
/*
these are important variables used for starting messages
*/
@@ -482,7 +560,9 @@ class BattleGround
BattleGroundStartTimeIntervals m_StartDelayTimes[BG_STARTING_EVENT_COUNT];
//this must be filled in constructors!
uint32 m_StartMessageIds[BG_STARTING_EVENT_COUNT];
+
bool m_BuffChange;
+
BGHonorMode m_HonorMode;
private:
/* Battleground */
@@ -504,21 +584,28 @@ class BattleGround
bool m_PrematureCountDown;
uint32 m_PrematureCountDownTimer;
char const *m_Name;
+
/* Player lists */
std::vector<uint64> m_ResurrectQueue; // Player GUID
std::deque<uint64> m_OfflineQueue; // Player GUID
+
/* 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[BG_TEAMS_COUNT]; // 0 - alliance, 1 - horde
+
/* Players count by team */
uint32 m_PlayersCount[BG_TEAMS_COUNT];
+
/* Arena team ids by team */
uint32 m_ArenaTeamIds[BG_TEAMS_COUNT];
+
int32 m_ArenaTeamRatingChanges[BG_TEAMS_COUNT];
+
/* Limits */
uint32 m_LevelMin;
uint32 m_LevelMax;
@@ -526,6 +613,7 @@ class BattleGround
uint32 m_MaxPlayers;
uint32 m_MinPlayersPerTeam;
uint32 m_MinPlayers;
+
/* Start location */
uint32 m_MapId;
float m_TeamStartLocX[BG_TEAMS_COUNT];
diff --git a/src/game/BattleGroundAA.cpp b/src/game/BattleGroundAA.cpp
index 4122325cd66..6133508ece1 100644
--- a/src/game/BattleGroundAA.cpp
+++ b/src/game/BattleGroundAA.cpp
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundAA.h"
#include "Language.h"
#include "Player.h"
+
BattleGroundAA::BattleGroundAA()
{
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S;
@@ -33,36 +36,47 @@ BattleGroundAA::BattleGroundAA()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN;
}
+
BattleGroundAA::~BattleGroundAA()
{
+
}
+
void BattleGroundAA::Update(uint32 diff)
{
BattleGround::Update(diff);
}
+
void BattleGroundAA::StartingEventCloseDoors()
{
}
+
void BattleGroundAA::StartingEventOpenDoors()
{
}
+
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
index 241a1ba73c1..06793ea38e0 100644
--- a/src/game/BattleGroundAA.h
+++ b/src/game/BattleGroundAA.h
@@ -19,7 +19,9 @@
*/
#ifndef __BATTLEGROUNDAA_H
#define __BATTLEGROUNDAA_H
+
class BattleGround;
+
class BattleGroundAAScore : public BattleGroundScore
{
public:
@@ -27,17 +29,21 @@ class BattleGroundAAScore : public BattleGroundScore
virtual ~BattleGroundAAScore() {};
//TODO fix me
};
+
class BattleGroundAA : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundAA();
~BattleGroundAA();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr, uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
diff --git a/src/game/BattleGroundAB.cpp b/src/game/BattleGroundAB.cpp
index 16eda4b9e94..6609c3400be 100644
--- a/src/game/BattleGroundAB.cpp
+++ b/src/game/BattleGroundAB.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "World.h"
#include "WorldPacket.h"
#include "ObjectMgr.h"
@@ -28,34 +29,42 @@
#include "Object.h"
#include "Player.h"
#include "Util.h"
+
// these variables aren't used outside of this file, so declare them only here
uint32 BG_AB_HonorScoreTicks[BG_HONOR_MODE_NUM] = {
330, // normal honor
200 // holiday
};
+
uint32 BG_AB_ReputationScoreTicks[BG_HONOR_MODE_NUM] = {
200, // normal honor
150 // holiday
};
+
BattleGroundAB::BattleGroundAB()
{
m_BuffChange = true;
m_BgObjects.resize(BG_AB_OBJECT_MAX);
m_BgCreatures.resize(BG_AB_ALL_NODES_COUNT);
+
m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AB_START_TWO_MINUTES;
m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AB_START_ONE_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AB_START_HALF_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AB_HAS_BEGUN;
}
+
BattleGroundAB::~BattleGroundAB()
{
}
+
void BattleGroundAB::Update(uint32 diff)
{
BattleGround::Update(diff);
+
if (GetStatus() == STATUS_IN_PROGRESS)
{
int team_points[BG_TEAMS_COUNT] = { 0, 0 };
+
for (int node = 0; node < BG_AB_DYNAMIC_NODES_COUNT; ++node)
{
// 3 sec delay to spawn new banner instead previous despawned one
@@ -69,6 +78,7 @@ void BattleGroundAB::Update(uint32 diff)
_CreateBanner(node, m_BannerTimers[node].type, m_BannerTimers[node].teamIndex, false);
}
}
+
// 1-minute to occupy a node from contested state
if (m_NodeTimers[node])
{
@@ -88,6 +98,7 @@ void BattleGroundAB::Update(uint32 diff)
_SendNodeUpdate(node);
_NodeOccupied(node,(teamIndex == 0) ? ALLIANCE:HORDE);
// Message to chatlog
+
if (teamIndex == 0)
{
// FIXME: team and node names not localized
@@ -102,10 +113,12 @@ void BattleGroundAB::Update(uint32 diff)
}
}
}
+
for (int team = 0; team < BG_TEAMS_COUNT; ++team)
if (m_Nodes[node] == team + BG_AB_NODE_TYPE_OCCUPIED)
++team_points[team];
}
+
// Accumulate points
for (int team = 0; team < BG_TEAMS_COUNT; ++team)
{
@@ -138,6 +151,7 @@ void BattleGroundAB::Update(uint32 diff)
PlaySoundToAll(BG_AB_SOUND_NEAR_VICTORY);
m_IsInformedNearVictory = true;
}
+
if (m_TeamScores[team] > BG_AB_MAX_TEAM_SCORE)
m_TeamScores[team] = BG_AB_MAX_TEAM_SCORE;
if (team == BG_TEAM_ALLIANCE)
@@ -151,6 +165,7 @@ void BattleGroundAB::Update(uint32 diff)
m_TeamScores500Disadvantage[otherTeam] = true;
}
}
+
// Test win condition
if (m_TeamScores[BG_TEAM_ALLIANCE] >= BG_AB_MAX_TEAM_SCORE)
EndBattleGround(ALLIANCE);
@@ -158,6 +173,7 @@ void BattleGroundAB::Update(uint32 diff)
EndBattleGround(HORDE);
}
}
+
void BattleGroundAB::StartingEventCloseDoors()
{
// despawn banners, auras and buffs
@@ -165,15 +181,18 @@ void BattleGroundAB::StartingEventCloseDoors()
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
DoorClose(BG_AB_OBJECT_GATE_A);
DoorClose(BG_AB_OBJECT_GATE_H);
SpawnBGObject(BG_AB_OBJECT_GATE_A, RESPAWN_IMMEDIATELY);
SpawnBGObject(BG_AB_OBJECT_GATE_H, RESPAWN_IMMEDIATELY);
+
// Starting base spirit guides
_NodeOccupied(BG_AB_SPIRIT_ALIANCE,ALLIANCE);
_NodeOccupied(BG_AB_SPIRIT_HORDE,HORDE);
}
+
void BattleGroundAB::StartingEventOpenDoors()
{
// spawn neutral banners
@@ -188,20 +207,26 @@ void BattleGroundAB::StartingEventOpenDoors()
DoorOpen(BG_AB_OBJECT_GATE_A);
DoorOpen(BG_AB_OBJECT_GATE_H);
}
+
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.
@@ -230,6 +255,7 @@ void BattleGroundAB::HandleAreaTrigger(Player *Source, uint32 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)
@@ -242,24 +268,30 @@ void BattleGroundAB::_CreateBanner(uint8 node, uint8 type, uint8 teamIndex, bool
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);
}
+
int32 BattleGroundAB::_GetNodeNameId(uint8 node)
{
switch (node)
@@ -274,16 +306,20 @@ int32 BattleGroundAB::_GetNodeNameId(uint8 node)
}
return 0;
}
+
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)
@@ -291,25 +327,32 @@ void BattleGroundAB::FillInitialWorldStates(WorldPacket& data)
++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_NEAR_VICTORY_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)
@@ -317,14 +360,17 @@ void BattleGroundAB::_SendNodeUpdate(uint8 node)
++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);
// SpawnBGCreature(node,RESPAWN_IMMEDIATELY);
+
uint8 capturedNodes = 0;
for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
{
@@ -336,10 +382,12 @@ void BattleGroundAB::_NodeOccupied(uint8 node,Team 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<uint64> ghost_list = m_ReviveQueue[m_BgCreatures[node]];
if (!ghost_list.empty())
@@ -350,21 +398,27 @@ void BattleGroundAB::_NodeDeOccupied(uint8 node)
Player* plr = objmgr.GetPlayer(*itr);
if (!plr)
continue;
+
if (!ClosestGrave) // cache
ClosestGrave = GetClosestGraveYard(plr);
+
if (ClosestGrave)
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<GameObject>::Find(m_BgObjects[node*8+7]);
while ( (node < BG_AB_DYNAMIC_NODES_COUNT) && ((!obj) || (!source->IsWithinDistInMap(obj,10))))
@@ -372,15 +426,19 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ
++node;
obj=HashMapHolder<GameObject>::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;
}
+
BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(source->GetTeam());
+
// 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
@@ -395,11 +453,13 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ
_CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true);
_SendNodeUpdate(node);
m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME;
+
// FIXME: team and node names not localized
if (teamIndex == 0)
SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node), LANG_BG_AB_ALLY);
else
SendMessage2ToAll(LANG_BG_AB_NODE_CLAIMED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node), LANG_BG_AB_HORDE);
+
sound = BG_AB_SOUND_NODE_CLAIMED;
}
// If node is contested
@@ -417,6 +477,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ
_CreateBanner(node, BG_AB_NODE_TYPE_CONTESTED, teamIndex, true);
_SendNodeUpdate(node);
m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME;
+
// FIXME: node names not localized
if (teamIndex == BG_TEAM_ALLIANCE)
SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node));
@@ -436,6 +497,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ
_SendNodeUpdate(node);
m_NodeTimers[node] = 0;
_NodeOccupied(node,(teamIndex == BG_TEAM_ALLIANCE) ? ALLIANCE:HORDE);
+
// FIXME: node names not localized
if (teamIndex == BG_TEAM_ALLIANCE)
SendMessage2ToAll(LANG_BG_AB_NODE_DEFENDED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node));
@@ -457,13 +519,16 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ
_SendNodeUpdate(node);
_NodeDeOccupied(node);
m_NodeTimers[node] = BG_AB_FLAG_CAPTURING_TIME;
+
// FIXME: node names not localized
if (teamIndex == BG_TEAM_ALLIANCE)
SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_ALLIANCE, source, _GetNodeNameId(node));
else
SendMessage2ToAll(LANG_BG_AB_NODE_ASSAULTED,CHAT_MSG_BG_SYSTEM_HORDE, source, _GetNodeNameId(node));
+
sound = (teamIndex == BG_TEAM_ALLIANCE) ? BG_AB_SOUND_NODE_ASSAULTED_ALLIANCE : BG_AB_SOUND_NODE_ASSAULTED_HORDE;
}
+
// If node is occupied again, send "X has taken the Y" msg.
if (m_Nodes[node] >= BG_AB_NODE_TYPE_OCCUPIED)
{
@@ -475,6 +540,7 @@ void BattleGroundAB::EventPlayerClickedOnFlag(Player *source, GameObject* /*targ
}
PlaySoundToAll(sound);
}
+
bool BattleGroundAB::SetupBattleGround()
{
for (int i = 0 ; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
@@ -509,12 +575,15 @@ bool BattleGroundAB::SetupBattleGround()
)
sLog.outErrorDb("BatteGroundAB: Failed to spawn buff object!");
}
+
return true;
}
+
void BattleGroundAB::Reset()
{
//call parent's class reset
BattleGround::Reset();
+
m_TeamScores[BG_TEAM_ALLIANCE] = 0;
m_TeamScores[BG_TEAM_HORDE] = 0;
m_lastTick[BG_TEAM_ALLIANCE] = 0;
@@ -529,6 +598,7 @@ void BattleGroundAB::Reset()
m_ReputationTics = (isBGWeekend) ? BG_AB_ABBGWeekendReputationTicks : BG_AB_NotABBGWeekendReputationTicks;
m_TeamScores500Disadvantage[BG_TEAM_ALLIANCE] = false;
m_TeamScores500Disadvantage[BG_TEAM_HORDE] = false;
+
for (uint8 i = 0; i < BG_AB_DYNAMIC_NODES_COUNT; ++i)
{
m_Nodes[i] = 0;
@@ -536,10 +606,12 @@ void BattleGroundAB::Reset()
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);
}
+
void BattleGroundAB::EndBattleGround(uint32 winner)
{
//win reward
@@ -550,22 +622,27 @@ void BattleGroundAB::EndBattleGround(uint32 winner)
//complete map_end rewards (even if no team wins)
RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE);
RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE);
+
BattleGround::EndBattleGround(winner);
}
+
WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player)
{
BattleGroundTeamId teamIndex = GetTeamIndexByTeamId(player->GetTeam());
+
// Is there any occupied node for this team?
std::vector<uint8> 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 plr_x = player->GetPositionX();
float plr_y = player->GetPositionY();
+
float mindist = 999999.0f;
for (uint8 i = 0; i < nodes.size(); ++i)
{
@@ -584,13 +661,16 @@ WorldSafeLocsEntry const* BattleGroundAB::GetClosestGraveYard(Player* player)
// 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)
{
BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID());
if( itr == m_PlayerScores.end() ) // player not found...
return;
+
switch(type)
{
case SCORE_BASES_ASSAULTED:
@@ -604,6 +684,7 @@ void BattleGroundAB::UpdatePlayerScore(Player *Source, uint32 type, uint32 value
break;
}
}
+
bool BattleGroundAB::IsAllNodesConrolledByTeam(uint32 team) const
{
uint32 count = 0;
@@ -611,5 +692,6 @@ bool BattleGroundAB::IsAllNodesConrolledByTeam(uint32 team) const
if ((team == ALLIANCE && m_Nodes[i] == BG_AB_NODE_STATUS_ALLY_OCCUPIED) ||
(team == HORDE && m_Nodes[i] == BG_AB_NODE_STATUS_HORDE_OCCUPIED))
++count;
+
return count == BG_AB_DYNAMIC_NODES_COUNT;
}
diff --git a/src/game/BattleGroundAB.h b/src/game/BattleGroundAB.h
index 7da1c517528..ef151ad187b 100644
--- a/src/game/BattleGroundAB.h
+++ b/src/game/BattleGroundAB.h
@@ -19,7 +19,9 @@
*/
#ifndef __BATTLEGROUNDAB_H
#define __BATTLEGROUNDAB_H
+
class BattleGround;
+
enum BG_AB_WorldStates
{
BG_AB_OP_OCCUPIED_BASES_HORDE = 1778,
@@ -39,6 +41,7 @@ enum BG_AB_WorldStates
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)
@@ -56,8 +59,11 @@ enum BG_AB_WorldStates
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
{
@@ -67,6 +73,7 @@ enum BG_AB_NodeObjectId
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
@@ -99,6 +106,7 @@ enum BG_AB_ObjectType
BG_AB_OBJECT_BERSERKBUFF_GOLD_MINE = 56,
BG_AB_OBJECT_MAX = 57,
};
+
/* Object id templates from DB */
enum BG_AB_ObjectTypes
{
@@ -106,21 +114,26 @@ enum BG_AB_ObjectTypes
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_WARNING_NEAR_VICTORY_SCORE = 1800,
BG_AB_MAX_TEAM_SCORE = 2000
};
+
/* do NOT change the order, else wrong behaviour */
enum BG_AB_BattleGroundNodes
{
@@ -129,11 +142,15 @@ enum BG_AB_BattleGroundNodes
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,
@@ -144,6 +161,7 @@ enum BG_AB_NodeStatus
BG_AB_NODE_STATUS_ALLY_OCCUPIED = 3,
BG_AB_NODE_STATUS_HORDE_OCCUPIED = 4
};
+
enum BG_AB_Sounds
{
BG_AB_SOUND_NODE_CLAIMED = 8192,
@@ -153,10 +171,12 @@ enum BG_AB_Sounds
BG_AB_SOUND_NODE_ASSAULTED_HORDE = 8174,
BG_AB_SOUND_NEAR_VICTORY = 8456
};
+
#define BG_AB_NotABBGWeekendHonorTicks 330
#define BG_AB_ABBGWeekendHonorTicks 200
#define BG_AB_NotABBGWeekendReputationTicks 200
#define BG_AB_ABBGWeekendReputationTicks 150
+
// x, y, z, o
const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = {
{1166.785f, 1200.132f, -56.70859f, 0.9075713f}, // stables
@@ -165,16 +185,20 @@ const float BG_AB_NodePositions[BG_AB_DYNAMIC_NODES_COUNT][4] = {
{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
@@ -183,6 +207,7 @@ const float BG_AB_BuffPositions[BG_AB_DYNAMIC_NODES_COUNT][4] = {
{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
@@ -193,12 +218,14 @@ const float BG_AB_SpiritGuidePos[BG_AB_ALL_NODES_COUNT][4] = {
{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:
@@ -207,12 +234,15 @@ class BattleGroundABScore : public BattleGroundScore
uint32 BasesAssaulted;
uint32 BasesDefended;
};
+
class BattleGroundAB : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundAB();
~BattleGroundAB();
+
void Update(uint32 diff);
void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
@@ -223,11 +253,15 @@ class BattleGroundAB : public BattleGround
virtual void Reset();
void EndBattleGround(uint32 winner);
virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);
+
/* 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);
+
/* achievement req. */
bool IsAllNodesConrolledByTeam(uint32 team) const; // overwrited
bool IsTeamScores500Disadvantage(uint32 team) const { return m_TeamScores500Disadvantage[GetTeamIndexByTeamId(team)]; }
@@ -236,11 +270,14 @@ class BattleGroundAB : public BattleGround
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);
+
int32 _GetNodeNameId(uint8 node);
+
/* Nodes info:
0: neutral
1: ally contested
diff --git a/src/game/BattleGroundAV.cpp b/src/game/BattleGroundAV.cpp
index 4db2f4b3df2..42d15a6ce84 100644
--- a/src/game/BattleGroundAV.cpp
+++ b/src/game/BattleGroundAV.cpp
@@ -17,8 +17,10 @@
* 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"
#include "WorldPacket.h"
+
#include "BattleGround.h"
#include "BattleGroundAV.h"
#include "Formulas.h"
@@ -26,29 +28,36 @@
#include "Language.h"
#include "Player.h"
#include "SpellAuras.h"
+
BattleGroundAV::BattleGroundAV()
{
m_BgObjects.resize(BG_AV_OBJECT_MAX);
m_BgCreatures.resize(AV_CPLACE_MAX+AV_STATICCPLACE_MAX);
+
m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_AV_START_TWO_MINUTES;
m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_AV_START_ONE_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_AV_START_HALF_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_AV_HAS_BEGUN;
}
+
BattleGroundAV::~BattleGroundAV()
{
}
+
const uint16 BattleGroundAV::GetBonusHonor(uint8 kills) //TODO: move this function to Battleground.cpp (needs to find a way to get m_MaxLevel)
{
return Trinity::Honor::hk_honor_at_level(m_MaxLevel, kills);
}
+
void BattleGroundAV::HandleKillPlayer(Player *player, Player *killer)
{
if(GetStatus() != STATUS_IN_PROGRESS)
return;
+
BattleGround::HandleKillPlayer(player, killer);
UpdateScore(player->GetTeam(),-1);
}
+
void BattleGroundAV::HandleKillUnit(Creature *unit, Player *killer)
{
sLog.outDebug("bg_av HandleKillUnit %i",unit->GetEntry());
@@ -86,6 +95,7 @@ void BattleGroundAV::HandleKillUnit(Creature *unit, Player *killer)
Creature* creature = GetBGCreature(AV_CPLACE_HERALD);
if(creature)
YellToAll(creature,GetTrinityString(LANG_BG_AV_A_CAPTAIN_DEAD),LANG_UNIVERSAL);
+
}
else if ( entry == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0] )
{
@@ -110,6 +120,7 @@ void BattleGroundAV::HandleKillUnit(Creature *unit, Player *killer)
else if ( entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_N_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_A_4][0] || entry == BG_AV_CreatureInfo[AV_NPC_S_MINE_H_4][0])
ChangeMineOwner(AV_SOUTH_MINE,killer->GetTeam());
}
+
void BattleGroundAV::HandleQuestComplete(uint32 questid, Player *player)
{
if (GetStatus() != STATUS_IN_PROGRESS)
@@ -213,11 +224,13 @@ void BattleGroundAV::HandleQuestComplete(uint32 questid, Player *player)
}
}
+
void BattleGroundAV::UpdateScore(uint16 team, int16 points )
{ //note: to remove reinforcementpoints points must be negative, for adding reinforcements points must be positive
assert( team == ALLIANCE || team == HORDE);
uint8 teamindex = GetTeamIndexByTeamId(team); //0=ally 1=horde
m_Team_Scores[teamindex] += points;
+
UpdateWorldState(((teamindex==BG_TEAM_HORDE)?AV_Horde_Score:AV_Alliance_Score), m_Team_Scores[teamindex]);
if( points < 0)
{
@@ -234,6 +247,7 @@ void BattleGroundAV::UpdateScore(uint16 team, int16 points )
}
}
}
+
Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type )
{
uint32 level;
@@ -257,6 +271,7 @@ Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type )
return NULL;
if(creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_A_CAPTAIN][0] || creature->GetEntry() == BG_AV_CreatureInfo[AV_NPC_H_CAPTAIN][0])
creature->SetRespawnDelay(RESPAWN_ONE_DAY); // TODO: look if this can be done by database + also add this for the wingcommanders
+
if((isStatic && cinfoid>=10 && cinfoid<=14) || (!isStatic && ((cinfoid >= AV_NPC_A_GRAVEDEFENSE0 && cinfoid<=AV_NPC_A_GRAVEDEFENSE3) ||
(cinfoid>=AV_NPC_H_GRAVEDEFENSE0 && cinfoid<=AV_NPC_H_GRAVEDEFENSE3))))
{
@@ -274,14 +289,17 @@ Creature* BattleGroundAV::AddAVCreature(uint16 cinfoid, uint16 type )
//TODO: find a way to add a motionmaster without killing the creature (i
//just copied this code from a gm-command
}
+
if(level != 0)
level += m_MaxLevel-60; //maybe we can do this more generic for custom level-range.. actually it's blizzlike
creature->SetLevel(level);
return creature;
}
+
void BattleGroundAV::Update(uint32 diff)
{
BattleGround::Update(diff);
+
if(GetStatus() == STATUS_IN_PROGRESS)
{
for(uint8 i=0; i<=1;i++)//0=alliance, 1=horde
@@ -317,6 +335,7 @@ void BattleGroundAV::Update(uint32 diff)
{
if( m_Mine_Timer <= 0)
UpdateScore(m_Mine_Owner[mine],1);
+
if(m_Mine_Reclaim_Timer[mine] > diff)
m_Mine_Reclaim_Timer[mine] -= diff;
else{ //we don't need to set this timer to 0 cause this codepart wont get called when this thing is 0
@@ -326,6 +345,7 @@ void BattleGroundAV::Update(uint32 diff)
}
if( m_Mine_Timer <= 0)
m_Mine_Timer=AV_MINE_TICK_TIMER; //this is at the end, cause we need to update both mines
+
//looks for all timers of the nodes and destroy the building (for graveyards the building wont get destroyed, it goes just to the other team
for(BG_AV_Nodes i = BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i)
if(m_Nodes[i].State == POINT_ASSAULTED) //maybe remove this
@@ -337,11 +357,13 @@ void BattleGroundAV::Update(uint32 diff)
}
}
}
+
void BattleGroundAV::StartingEventCloseDoors()
{
DoorClose(BG_AV_OBJECT_DOOR_A);
DoorClose(BG_AV_OBJECT_DOOR_H);
}
+
void BattleGroundAV::StartingEventOpenDoors()
{
sLog.outDebug("BG_AV: start spawning mine stuff");
@@ -351,11 +373,14 @@ void BattleGroundAV::StartingEventOpenDoors()
SpawnBGObject(i,RESPAWN_IMMEDIATELY);
for(uint8 mine = AV_NORTH_MINE; mine <= AV_SOUTH_MINE; mine++) //mine population
ChangeMineOwner(mine, AV_NEUTRAL_TEAM,true);
+
UpdateWorldState(AV_SHOW_H_SCORE, 1);
UpdateWorldState(AV_SHOW_A_SCORE, 1);
+
DoorOpen(BG_AV_OBJECT_DOOR_H);
DoorOpen(BG_AV_OBJECT_DOOR_A);
}
+
void BattleGroundAV::AddPlayer(Player *plr)
{
BattleGround::AddPlayer(plr);
@@ -364,7 +389,9 @@ void BattleGroundAV::AddPlayer(Player *plr)
m_PlayerScores[plr->GetGUID()] = sc;
if(m_MaxLevel==0)
m_MaxLevel=(plr->getLevel()%10 == 0)? plr->getLevel() : (plr->getLevel()-(plr->getLevel()%10))+10; //TODO: just look at the code \^_^/ --but queue-info should provide this information..
+
}
+
void BattleGroundAV::EndBattleGround(uint32 winner)
{
//calculate bonuskills for both teams:
@@ -387,6 +414,7 @@ void BattleGroundAV::EndBattleGround(uint32 winner)
}
}
}
+
for(int i=0; i<=1; i++) //0=ally 1=horde
{
if(m_CaptainAlive[i])
@@ -399,9 +427,11 @@ void BattleGroundAV::EndBattleGround(uint32 winner)
if(kills[i] != 0)
RewardHonorToTeam(GetBonusHonor(kills[i]),(i == 0)?ALLIANCE:HORDE);
}
+
//TODO add enterevademode for all attacking creatures
BattleGround::EndBattleGround(winner);
}
+
void BattleGroundAV::RemovePlayer(Player* plr,uint64 /*guid*/)
{
if(!plr)
@@ -415,11 +445,13 @@ void BattleGroundAV::RemovePlayer(Player* plr,uint64 /*guid*/)
plr->RemoveAurasDueToSpell(AV_BUFF_H_CAPTAIN);
}
+
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)
{
@@ -449,14 +481,18 @@ void BattleGroundAV::HandleAreaTrigger(Player *Source, uint32 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)
{
+
BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID());
if(itr == m_PlayerScores.end()) // player not found...
return;
+
switch(type)
{
case SCORE_GRAVEYARDS_ASSAULTED:
@@ -486,14 +522,19 @@ void BattleGroundAV::UpdatePlayerScore(Player* Source, uint32 type, uint32 value
}
}
+
+
void BattleGroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node)
{
+
uint32 object = GetObjectThroughNode(node);
sLog.outDebug("bg_av: player destroyed point node %i object %i",node,object);
+
//despawn banner
SpawnBGObject(object, RESPAWN_ONE_DAY);
DestroyNode(node);
UpdateNodeWorldState(node);
+
uint32 owner = m_Nodes[node].Owner;
if( IsTower(node) )
{
@@ -506,9 +547,11 @@ void BattleGroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node)
//spawn destroyed aura
for(uint8 i=0; i<=9; i++)
SpawnBGObject(BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH + i + (tmp * 10),RESPAWN_IMMEDIATELY);
+
UpdateScore((owner == ALLIANCE) ? HORDE : ALLIANCE, (-1)*BG_AV_RES_TOWER);
RewardReputationToTeam((owner == ALLIANCE)?730:729,BG_AV_REP_TOWER,owner);
RewardHonorToTeam(GetBonusHonor(BG_AV_KILL_TOWER),owner);
+
SpawnBGObject(BG_AV_OBJECT_TAURA_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp),RESPAWN_ONE_DAY);
SpawnBGObject(BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH+GetTeamIndexByTeamId(owner)+(2*tmp),RESPAWN_ONE_DAY);
}
@@ -536,10 +579,12 @@ void BattleGroundAV::EventPlayerDestroyedPoint(BG_AV_Nodes node)
sprintf(buf, GetTrinityString(LANG_BG_AV_TOWER_TAKEN) , GetNodeName(node),( owner == ALLIANCE ) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE) );
else
sprintf(buf, GetTrinityString(LANG_BG_AV_GRAVE_TAKEN) , GetNodeName(node),( owner == ALLIANCE ) ? GetTrinityString(LANG_BG_AV_ALLY) :GetTrinityString(LANG_BG_AV_HORDE) );
+
Creature* creature = GetBGCreature(AV_CPLACE_HERALD);
if(creature)
YellToAll(creature,buf,LANG_UNIVERSAL);
}
+
void BattleGroundAV::ChangeMineOwner(uint8 mine, uint32 team, bool initial)
{ //mine=0 northmine mine=1 southmin
//changing the owner results in setting respawntim to infinite for current creatures, spawning new mine owners creatures and changing the chest-objects so that the current owning team can use them
@@ -548,10 +593,12 @@ void BattleGroundAV::ChangeMineOwner(uint8 mine, uint32 team, bool initial)
team = AV_NEUTRAL_TEAM;
else
PlaySoundToAll((team==ALLIANCE)?AV_SOUND_ALLIANCE_GOOD:AV_SOUND_HORDE_GOOD);
+
if(m_Mine_Owner[mine] == team && !initial)
return;
m_Mine_PrevOwner[mine] = m_Mine_Owner[mine];
m_Mine_Owner[mine] = team;
+
if(!initial)
{
sLog.outDebug("bg_av depopulating mine %i (0=north,1=south)",mine);
@@ -564,6 +611,7 @@ void BattleGroundAV::ChangeMineOwner(uint8 mine, uint32 team, bool initial)
DelCreature(i); //TODO here also
}
SendMineWorldStates(mine);
+
sLog.outDebug("bg_av populating mine %i (0=north,1=south)",mine);
uint16 miner;
//also neutral team exists.. after a big time, the neutral team tries to conquer the mine
@@ -626,6 +674,7 @@ void BattleGroundAV::ChangeMineOwner(uint8 mine, uint32 team, bool initial)
}
return;
}
+
bool BattleGroundAV::PlayerCanDoMineQuest(int32 GOId,uint32 team)
{
if(GOId == BG_AV_OBJECTID_MINE_N)
@@ -634,10 +683,12 @@ bool BattleGroundAV::PlayerCanDoMineQuest(int32 GOId,uint32 team)
return (m_Mine_Owner[AV_SOUTH_MINE]==team);
return true; //cause it's no mine'object it is ok if this is true
}
+
void BattleGroundAV::PopulateNode(BG_AV_Nodes node)
{
uint32 owner = m_Nodes[node].Owner;
assert(owner);
+
uint32 c_place = AV_CPLACE_DEFENSE_STORM_AID + ( 4 * node );
uint32 creatureid;
if(IsTower(node))
@@ -658,6 +709,7 @@ void BattleGroundAV::PopulateNode(BG_AV_Nodes node)
DelCreature(node);
if( !AddSpiritGuide(node, BG_AV_CreaturePos[node][0], BG_AV_CreaturePos[node][1], BG_AV_CreaturePos[node][2], BG_AV_CreaturePos[node][3], owner))
sLog.outError("AV: couldn't spawn spiritguide at node %i",node);
+
}
for(uint8 i=0; i<4; i++)
{
@@ -675,6 +727,7 @@ void BattleGroundAV::DePopulateNode(BG_AV_Nodes node)
DelCreature(node);
}
+
const BG_AV_Nodes BattleGroundAV::GetNodeThroughObject(uint32 object)
{
sLog.outDebug("bg_AV getnodethroughobject %i",object);
@@ -696,6 +749,7 @@ const BG_AV_Nodes BattleGroundAV::GetNodeThroughObject(uint32 object)
assert(false);
return BG_AV_Nodes(0);
}
+
const uint32 BattleGroundAV::GetObjectThroughNode(BG_AV_Nodes node)
{ //this function is the counterpart to GetNodeThroughObject()
sLog.outDebug("bg_AV GetObjectThroughNode %i",node);
@@ -732,7 +786,9 @@ const uint32 BattleGroundAV::GetObjectThroughNode(BG_AV_Nodes node)
return 0;
}
+
//called when using banner
+
void BattleGroundAV::EventPlayerClickedOnFlag(Player *source, GameObject* target_obj)
{
if(GetStatus() != STATUS_IN_PROGRESS)
@@ -760,12 +816,15 @@ void BattleGroundAV::EventPlayerClickedOnFlag(Player *source, GameObject* target
break;
}
}
+
void BattleGroundAV::EventPlayerDefendsPoint(Player* player, uint32 object)
{
assert(GetStatus() == STATUS_IN_PROGRESS);
BG_AV_Nodes node = GetNodeThroughObject(object);
+
uint32 owner = m_Nodes[node].Owner; //maybe should name it prevowner
uint32 team = player->GetTeam();
+
if(owner == player->GetTeam() || m_Nodes[node].State != POINT_ASSAULTED)
return;
if(m_Nodes[node].TotalOwner == AV_NEUTRAL_TEAM)
@@ -781,11 +840,13 @@ void BattleGroundAV::EventPlayerDefendsPoint(Player* player, uint32 object)
return;
}
+
//spawn new go :)
if(m_Nodes[node].Owner == ALLIANCE)
SpawnBGObject(object+22, RESPAWN_IMMEDIATELY); //spawn horde banner
else
SpawnBGObject(object-22, RESPAWN_IMMEDIATELY); //spawn alliance banner
+
if(!IsTower(node))
{
SpawnBGObject(BG_AV_OBJECT_AURA_N_FIRSTAID_STATION+3*node,RESPAWN_ONE_DAY);
@@ -793,9 +854,11 @@ void BattleGroundAV::EventPlayerDefendsPoint(Player* player, uint32 object)
}
// despawn old go
SpawnBGObject(object, RESPAWN_ONE_DAY);
+
DefendNode(node,team);
PopulateNode(node);
UpdateNodeWorldState(node);
+
if(IsTower(node))
{
//spawn big flag+aura on top of tower
@@ -825,9 +888,11 @@ void BattleGroundAV::EventPlayerDefendsPoint(Player* player, uint32 object)
else
PlaySoundToAll((team==ALLIANCE)?AV_SOUND_ALLIANCE_GOOD:AV_SOUND_HORDE_GOOD);
}
+
void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object)
{
assert(GetStatus() == STATUS_IN_PROGRESS);
+
BG_AV_Nodes node = GetNodeThroughObject(object);
uint32 owner = m_Nodes[node].Owner; //maybe name it prevowner
uint32 team = player->GetTeam();
@@ -835,6 +900,7 @@ void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object)
if(owner == team || team == m_Nodes[node].TotalOwner)
return; //surely a gm used this object
+
if(node == BG_AV_NODES_SNOWFALL_GRAVE) //snowfall is a bit special in capping + it gets eyecandy stuff
{
if(object == BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE) //initial capping
@@ -872,6 +938,7 @@ void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object)
SpawnBGObject(spawn+i,RESPAWN_IMMEDIATELY);
}
}
+
//if snowfall gots capped it can be handled like all other graveyards
if( m_Nodes[node].TotalOwner != AV_NEUTRAL_TEAM)
{
@@ -913,9 +980,11 @@ void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object)
}
DePopulateNode(node);
}
+
SpawnBGObject(object, RESPAWN_ONE_DAY); //delete old banner
AssaultNode(node,team);
UpdateNodeWorldState(node);
+
//send a nice message to all :)
char buf[256];
sprintf(buf, ( IsTower(node) ) ? GetTrinityString(LANG_BG_AV_TOWER_ASSAULTED) : GetTrinityString(LANG_BG_AV_GRAVE_ASSAULTED), GetNodeName(node), ( team == ALLIANCE ) ? GetTrinityString(LANG_BG_AV_ALLY) : GetTrinityString(LANG_BG_AV_HORDE ));
@@ -926,6 +995,7 @@ void BattleGroundAV::EventPlayerAssaultsPoint(Player* player, uint32 object)
UpdatePlayerScore(player, ( IsTower(node) ) ? SCORE_TOWERS_ASSAULTED : SCORE_GRAVEYARDS_ASSAULTED, 1);
PlaySoundToAll((team==ALLIANCE)?AV_SOUND_ALLIANCE_ASSAULTS:AV_SOUND_HORDE_ASSAULTS);
}
+
void BattleGroundAV::FillInitialWorldStates(WorldPacket& data)
{
bool stateok;
@@ -939,6 +1009,7 @@ void BattleGroundAV::FillInitialWorldStates(WorldPacket& data)
data << uint32(BG_AV_NodeWorldStates[i][GetWorldStateType(j,HORDE)]) << uint32((m_Nodes[i].Owner == HORDE && stateok)?1:0);
}
}
+
//towers
for (uint8 i = BG_AV_NODES_DUNBALDAR_SOUTH; i <= BG_AV_NODES_MAX; i++)
for (uint8 j =1; j <= 3; j+=2)
@@ -963,6 +1034,7 @@ void BattleGroundAV::FillInitialWorldStates(WorldPacket& data)
SendMineWorldStates(AV_NORTH_MINE);
SendMineWorldStates(AV_SOUTH_MINE);
}
+
const uint8 BattleGroundAV::GetWorldStateType(uint8 state, uint16 team) //this is used for node worldstates and returns values which fit good into the worldstatesarray
{
//neutral stuff cant get handled (currently its only snowfall)
@@ -985,6 +1057,7 @@ const uint8 BattleGroundAV::GetWorldStateType(uint8 state, uint16 team) //this i
sLog.outError("BG_AV: should update a strange worldstate state:%i team:%i",state,team);
return 5; //this will crash the game, but i want to know if something is wrong here
}
+
void BattleGroundAV::UpdateNodeWorldState(BG_AV_Nodes node)
{
UpdateWorldState(BG_AV_NodeWorldStates[node][GetWorldStateType(m_Nodes[node].State,m_Nodes[node].Owner)],1);
@@ -993,12 +1066,14 @@ void BattleGroundAV::UpdateNodeWorldState(BG_AV_Nodes node)
else
UpdateWorldState(BG_AV_NodeWorldStates[node][GetWorldStateType(m_Nodes[node].PrevState,m_Nodes[node].PrevOwner)],0);
}
+
void BattleGroundAV::SendMineWorldStates(uint32 mine)
{
assert(mine == AV_NORTH_MINE || mine==AV_SOUTH_MINE);
// currently i'm sure, that this works (:
// assert(m_Mine_PrevOwner[mine] == ALLIANCE || m_Mine_PrevOwner[mine] == HORDE || m_Mine_PrevOwner[mine] == AV_NEUTRAL_TEAM);
// assert(m_Mine_Owner[mine] == ALLIANCE || m_Mine_Owner[mine] == HORDE || m_Mine_Owner[mine] == AV_NEUTRAL_TEAM);
+
uint8 owner,prevowner,mine2; //those variables are needed to access the right worldstate in the BG_AV_MineWorldStates array
mine2 = (mine==AV_NORTH_MINE)?0:1;
if(m_Mine_PrevOwner[mine] == ALLIANCE)
@@ -1013,11 +1088,13 @@ void BattleGroundAV::SendMineWorldStates(uint32 mine)
owner = 2;
else
owner = 1;
+
UpdateWorldState(BG_AV_MineWorldStates[mine2][owner],1);
if( prevowner != owner)
UpdateWorldState(BG_AV_MineWorldStates[mine2][prevowner],0);
}
+
WorldSafeLocsEntry const* BattleGroundAV::GetClosestGraveYard(Player* player)
{
WorldSafeLocsEntry const* good_entry = NULL;
@@ -1043,9 +1120,11 @@ WorldSafeLocsEntry const* BattleGroundAV::GetClosestGraveYard(Player* player)
// If not, place ghost on starting location
if( !good_entry )
good_entry = sWorldSafeLocsStore.LookupEntry( BG_AV_GraveyardIds[GetTeamIndexByTeamId(player->GetTeam())+7] );
+
return good_entry;
}
+
bool BattleGroundAV::SetupBattleGround()
{
// Create starting objects
@@ -1058,6 +1137,7 @@ bool BattleGroundAV::SetupBattleGround()
sLog.outErrorDb("BatteGroundAV: Failed to spawn some object BattleGround not created!1");
return false;
}
+
//spawn node-objects
for (uint8 i = BG_AV_NODES_FIRSTAID_STATION ; i < BG_AV_NODES_MAX; ++i)
{
@@ -1152,6 +1232,7 @@ bool BattleGroundAV::SetupBattleGround()
return false;
}
}
+
if(!AddObject(BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE, BG_AV_OBJECTID_BANNER_SNOWFALL_N ,BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][0],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][1],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][2],BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3],0,0,sin(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), cos(BG_AV_ObjectPos[BG_AV_NODES_SNOWFALL_GRAVE][3]/2), RESPAWN_ONE_DAY))
{
sLog.outError("BatteGroundAV: Failed to spawn some object BattleGround not created!8");
@@ -1168,6 +1249,7 @@ bool BattleGroundAV::SetupBattleGround()
return false;
}
}
+
uint16 i;
sLog.outDebug("Alterac Valley: entering state STATUS_WAIT_JOIN ...");
// Initial Nodes
@@ -1198,6 +1280,7 @@ bool BattleGroundAV::SetupBattleGround()
for(i = BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE; i <= BG_AV_OBJECT_DOOR_A; i++)
SpawnBGObject(i, RESPAWN_IMMEDIATELY);
SpawnBGObject(BG_AV_OBJECT_AURA_N_SNOWFALL_GRAVE,RESPAWN_IMMEDIATELY);
+
//creatures
sLog.outDebug("BG_AV start poputlating nodes");
for(BG_AV_Nodes i= BG_AV_NODES_FIRSTAID_STATION; i < BG_AV_NODES_MAX; ++i )
@@ -1220,6 +1303,7 @@ bool BattleGroundAV::SetupBattleGround()
AddAVCreature(AV_NPC_HERALD,AV_CPLACE_HERALD);
return true;
}
+
const char* BattleGroundAV::GetNodeName(BG_AV_Nodes node)
{
switch (node)
@@ -1247,6 +1331,7 @@ const char* BattleGroundAV::GetNodeName(BG_AV_Nodes node)
}
}
}
+
void BattleGroundAV::AssaultNode(BG_AV_Nodes node, uint16 team)
{
if (m_Nodes[node].TotalOwner == team)
@@ -1276,15 +1361,18 @@ void BattleGroundAV::AssaultNode(BG_AV_Nodes node, uint16 team)
m_Nodes[node].PrevState = m_Nodes[node].State;
m_Nodes[node].State = POINT_ASSAULTED;
}
+
void BattleGroundAV::DestroyNode(BG_AV_Nodes node)
{
assert(m_Nodes[node].State == POINT_ASSAULTED);
+
m_Nodes[node].TotalOwner = m_Nodes[node].Owner;
m_Nodes[node].PrevOwner = m_Nodes[node].Owner;
m_Nodes[node].PrevState = m_Nodes[node].State;
m_Nodes[node].State = (m_Nodes[node].Tower)? POINT_DESTROYED : POINT_CONTROLED;
m_Nodes[node].Timer = 0;
}
+
void BattleGroundAV::InitNode(BG_AV_Nodes node, uint16 team, bool tower)
{
m_Nodes[node].TotalOwner = team;
@@ -1296,6 +1384,7 @@ void BattleGroundAV::InitNode(BG_AV_Nodes node, uint16 team, bool tower)
m_Nodes[node].Timer = 0;
m_Nodes[node].Tower = tower;
}
+
void BattleGroundAV::DefendNode(BG_AV_Nodes node, uint16 team)
{
assert(m_Nodes[node].TotalOwner == team);
@@ -1307,6 +1396,7 @@ void BattleGroundAV::DefendNode(BG_AV_Nodes node, uint16 team)
m_Nodes[node].State = POINT_CONTROLED;
m_Nodes[node].Timer = 0;
}
+
void BattleGroundAV::ResetBGSubclass()
{
m_MaxLevel=0;
@@ -1330,9 +1420,12 @@ void BattleGroundAV::ResetBGSubclass()
for(BG_AV_Nodes i = BG_AV_NODES_ICEBLOOD_TOWER; i <= BG_AV_NODES_FROSTWOLF_WTOWER; ++i) //horde towers
InitNode(i,HORDE,true);
InitNode(BG_AV_NODES_SNOWFALL_GRAVE,AV_NEUTRAL_TEAM,false); //give snowfall neutral owner
+
m_Mine_Timer=AV_MINE_TICK_TIMER;
for(uint16 i = 0; i < AV_CPLACE_MAX+AV_STATICCPLACE_MAX; i++)
if(m_BgCreatures[i])
DelCreature(i);
+
}
+
diff --git a/src/game/BattleGroundAV.h b/src/game/BattleGroundAV.h
index 7e5bd367b70..306c513ae95 100644
--- a/src/game/BattleGroundAV.h
+++ b/src/game/BattleGroundAV.h
@@ -17,32 +17,44 @@
* 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;
+
#define LANG_BG_AV_A_CAPTAIN_BUFF "Begone. Uncouth scum! The Alliance shall prevail in Alterac Valley!"
#define LANG_BG_AV_H_CAPTAIN_BUFF "Now is the time to attack! For the Horde!"
#define LANG_BG_AV_S_MINE_BOSS_CLAIMS "Snivvle is here! Snivvle claims the Coldtooth Mine!"
+
#define BG_AV_CAPTIME 240000 //4:00
#define BG_AV_SNOWFALL_FIRSTCAP 300000 //5:00 but i also have seen 4:05
+
#define BG_AV_SCORE_INITIAL_POINTS 600
#define SEND_MSG_NEAR_LOSE 120
+
#define BG_AV_KILL_BOSS 4
#define BG_AV_REP_BOSS 350
+
#define BG_AV_KILL_CAPTAIN 3
#define BG_AV_REP_CAPTAIN 125
#define BG_AV_RES_CAPTAIN 100
+
#define BG_AV_KILL_TOWER 3
#define BG_AV_REP_TOWER 12
#define BG_AV_RES_TOWER 75
+
#define BG_AV_GET_COMMANDER 1 //for a safely returned wingcommander
//bonushonor at the end
#define BG_AV_KILL_SURVIVING_TOWER 2
#define BG_AV_REP_SURVIVING_TOWER 12
+
#define BG_AV_KILL_SURVIVING_CAPTAIN 2
#define BG_AV_REP_SURVIVING_CAPTAIN 125
+
enum BG_AV_Sounds
{ //TODO: get out if there comes a sound when neutral team captures mine
+
/*
8212:
alliance grave assault
@@ -50,6 +62,7 @@ enum BG_AV_Sounds
drek "mlanzenabschaum! In meiner Burg?! Toetet sie all" - nicht immer der sound
8333:
galv "sterbt fuer euch ist kein platz hier"
+
8332:
bal "Verschwinde, dreckiger Abschaum! Die Allianz wird im Alteractal "
8174:
@@ -72,16 +85,21 @@ horde:
mine capture
horde wins
*/
+
AV_SOUND_NEAR_VICTORY = 8456, //not confirmed yet
+
AV_SOUND_ALLIANCE_ASSAULTS = 8212, //tower,grave + enemy boss if someone tries to attack him
AV_SOUND_HORDE_ASSAULTS = 8174,
AV_SOUND_ALLIANCE_GOOD = 8173, //if something good happens for the team: wins(maybe only through killing the boss), captures mine or grave, destroys tower and defends grave
AV_SOUND_HORDE_GOOD = 8213,
AV_SOUND_BOTH_TOWER_DEFEND = 8192,
+
AV_SOUND_ALLIANCE_CAPTAIN = 8232, //gets called when someone attacks them and at the beginning after 3min+rand(x)*10sec (maybe buff)
AV_SOUND_HORDE_CAPTAIN = 8333,
+
};
+
enum BG_AV_OTHER_VALUES
{
AV_STATICCPLACE_MAX = 123,
@@ -100,21 +118,25 @@ enum BG_AV_ObjectIds
BG_AV_OBJECTID_BANNER_H = 178943, // can only be used by alliance
BG_AV_OBJECTID_BANNER_CONT_A = 178940, // can only be used by horde
BG_AV_OBJECTID_BANNER_CONT_H = 179435, // can only be used by alliance
+
BG_AV_OBJECTID_BANNER_A_B = 178365,
BG_AV_OBJECTID_BANNER_H_B = 178364,
BG_AV_OBJECTID_BANNER_CONT_A_B = 179286,
BG_AV_OBJECTID_BANNER_CONT_H_B = 179287,
BG_AV_OBJECTID_BANNER_SNOWFALL_N = 180418,
+
//snowfall eyecandy banner:
BG_AV_OBJECTID_SNOWFALL_CANDY_A = 179044,
BG_AV_OBJECTID_SNOWFALL_CANDY_PA = 179424,
BG_AV_OBJECTID_SNOWFALL_CANDY_H = 179064,
BG_AV_OBJECTID_SNOWFALL_CANDY_PH = 179425,
+
//banners on top of towers:
BG_AV_OBJECTID_TOWER_BANNER_A = 178927, //[PH] Alliance A1 Tower Banner BIG
BG_AV_OBJECTID_TOWER_BANNER_H = 178955, //[PH] Horde H1 Tower Banner BIG
BG_AV_OBJECTID_TOWER_BANNER_PA = 179446, //[PH] Alliance H1 Tower Pre-Banner BIG
BG_AV_OBJECTID_TOWER_BANNER_PH = 179436, //[PH] Horde A1 Tower Pre-Banner BIG
+
//Auras
BG_AV_OBJECTID_AURA_A = 180421,
BG_AV_OBJECTID_AURA_H = 180422,
@@ -122,14 +144,18 @@ enum BG_AV_ObjectIds
BG_AV_OBJECTID_AURA_A_S = 180100,
BG_AV_OBJECTID_AURA_H_S = 180101,
BG_AV_OBJECTID_AURA_N_S = 180102,
+
BG_AV_OBJECTID_GATE_A = 180424,
BG_AV_OBJECTID_GATE_H = 180424,
+
//mine supplies
BG_AV_OBJECTID_MINE_N = 178785,
BG_AV_OBJECTID_MINE_S = 178784,
+
BG_AV_OBJECTID_FIRE = 179065,
BG_AV_OBJECTID_SMOKE = 179066
};
+
enum BG_AV_Nodes
{
BG_AV_NODES_FIRSTAID_STATION = 0,
@@ -147,8 +173,10 @@ enum BG_AV_Nodes
BG_AV_NODES_TOWER_POINT = 12,
BG_AV_NODES_FROSTWOLF_ETOWER = 13,
BG_AV_NODES_FROSTWOLF_WTOWER = 14,
+
BG_AV_NODES_MAX = 15
};
+
enum BG_AV_ObjectTypes
{
BG_AV_OBJECT_FLAG_A_FIRSTAID_STATION = 0,
@@ -162,6 +190,7 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_FLAG_A_DUNBALDAR_NORTH = 8,
BG_AV_OBJECT_FLAG_A_ICEWING_BUNKER = 9,
BG_AV_OBJECT_FLAG_A_STONEHEART_BUNKER = 10,
+
BG_AV_OBJECT_FLAG_C_A_FIRSTAID_STATION = 11,
BG_AV_OBJECT_FLAG_C_A_STORMPIKE_GRAVE = 12,
BG_AV_OBJECT_FLAG_C_A_STONEHEART_GRAVE = 13,
@@ -173,6 +202,7 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_FLAG_C_A_TOWER_POINT = 19,
BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_ETOWER = 20,
BG_AV_OBJECT_FLAG_C_A_FROSTWOLF_WTOWER = 21,
+
BG_AV_OBJECT_FLAG_C_H_FIRSTAID_STATION = 22,
BG_AV_OBJECT_FLAG_C_H_STORMPIKE_GRAVE = 23,
BG_AV_OBJECT_FLAG_C_H_STONEHEART_GRAVE = 24,
@@ -184,6 +214,7 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_FLAG_C_H_DUNBALDAR_NORTH = 30,
BG_AV_OBJECT_FLAG_C_H_ICEWING_BUNKER = 31,
BG_AV_OBJECT_FLAG_C_H_STONEHEART_BUNKER = 32,
+
BG_AV_OBJECT_FLAG_H_FIRSTAID_STATION = 33,
BG_AV_OBJECT_FLAG_H_STORMPIKE_GRAVE = 34,
BG_AV_OBJECT_FLAG_H_STONEHEART_GRAVE = 35,
@@ -195,7 +226,9 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_FLAG_H_TOWER_POINT = 41,
BG_AV_OBJECT_FLAG_H_FROSTWOLF_ETOWER = 42,
BG_AV_OBJECT_FLAG_H_FROSTWOLF_WTOWER = 43,
+
BG_AV_OBJECT_FLAG_N_SNOWFALL_GRAVE = 44,
+
BG_AV_OBJECT_DOOR_H = 45,
BG_AV_OBJECT_DOOR_A = 46,
//auras for graveyards (3auras per graveyard neutral,alliance,horde)
@@ -220,6 +253,7 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_AURA_N_FROSTWOLF_HUT = 65,
BG_AV_OBJECT_AURA_A_FROSTWOLF_HUT = 66,
BG_AV_OBJECT_AURA_H_FROSTWOLF_HUT = 67,
+
//big flags on top of towers 2 flags on each (contested,(alliance | horde)) + 2 auras
BG_AV_OBJECT_TFLAG_A_DUNBALDAR_SOUTH = 67,
BG_AV_OBJECT_TFLAG_H_DUNBALDAR_SOUTH = 68,
@@ -253,6 +287,7 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_TAURA_H_FROSTWOLF_ETOWER = 96,
BG_AV_OBJECT_TAURA_A_FROSTWOLF_WTOWER = 97,
BG_AV_OBJECT_TAURA_H_FROSTWOLF_WTOWER = 98,
+
BG_AV_OBJECT_BURN_DUNBALDAR_SOUTH = 99,
BG_AV_OBJECT_BURN_DUNBALDAR_NORTH = 109,
BG_AV_OBJECT_BURN_ICEWING_BUNKER = 119,
@@ -271,9 +306,12 @@ enum BG_AV_ObjectTypes
BG_AV_OBJECT_MINE_SUPPLY_N_MAX = 224,
BG_AV_OBJECT_MINE_SUPPLY_S_MIN = 225,
BG_AV_OBJECT_MINE_SUPPLY_S_MAX = 236,
+
BG_AV_OBJECT_MAX = 237
};
+
+
enum BG_AV_OBJECTS
{
AV_OPLACE_FIRSTAID_STATION = 0,
@@ -299,6 +337,7 @@ enum BG_AV_OBJECTS
AV_OPLACE_BIGBANNER_TOWER_POINT = 20,
AV_OPLACE_BIGBANNER_FROSTWOLF_ETOWER = 21,
AV_OPLACE_BIGBANNER_FROSTWOLF_WTOWER = 22,
+
AV_OPLACE_BURN_DUNBALDAR_SOUTH = 23,
AV_OPLACE_BURN_DUNBALDAR_NORTH = 33,
AV_OPLACE_BURN_ICEWING_BUNKER = 43,
@@ -317,6 +356,7 @@ enum BG_AV_OBJECTS
AV_OPLACE_MINE_SUPPLY_N_MAX = 136,
AV_OPLACE_MINE_SUPPLY_S_MIN = 137,
AV_OPLACE_MINE_SUPPLY_S_MAX = 148,
+
AV_OPLACE_MAX = 149
};
const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = {
@@ -433,6 +473,7 @@ const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = {
{-1295.55f,-263.865f,105.033f,0.925024f},
{-1294.71f,-281.466f,107.664f,-1.50098f},
{-1289.69f,-259.521f,107.612f,-2.19912f},
+
//the two buildings of the captains
//alliance
{-64.4987f,-289.33f,33.4616f,-2.82743f},
@@ -456,6 +497,7 @@ const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = {
{-568.04f,-188.707f,81.55f,0},
{-501.775f,-151.581f,81.2027f,0},
{-509.975f,-191.652f,83.2978f,0},
+
//snowfall eyecandy
{-191.153f,-129.868f,78.5595f,-1.25664f },
{-201.282f,-134.319f,78.6753f,-0.942478f },
@@ -487,11 +529,13 @@ const float BG_AV_ObjectPos[AV_OPLACE_MAX][4] = {
{-947.642f,-208.807f,77.0101f,1.36136f},
{-951.394f,-193.695f,67.634f,0.802851f}
};
+
const float BG_AV_DoorPositons[2][4] = {
{780.487f, -493.024f, 99.9553f, 3.0976f}, //alliance
{-1375.193f, -538.981f, 55.2824f, 0.72178f} //horde
};
+
//creaturestuff starts here
//is related to BG_AV_CreaturePos
enum BG_AV_CreaturePlace
@@ -513,6 +557,7 @@ enum BG_AV_CreaturePlace
AV_CPLACE_DEFENSE_FROSTWOLF = 25,
AV_CPLACE_DEFENSE_ICE_GRAVE = 29,
AV_CPLACE_DEFENSE_FROST_HUT = 33,
+
AV_CPLACE_DEFENSE_DUN_S = 37,
AV_CPLACE_DEFENSE_DUN_N = 41,
AV_CPLACE_DEFENSE_ICEWING = 45,
@@ -521,6 +566,7 @@ enum BG_AV_CreaturePlace
AV_CPLACE_DEFENSE_TOWERPOINT = 57,
AV_CPLACE_DEFENSE_FROST_E = 61,
AV_CPLACE_DEFENSE_FROST_t = 65,
+
AV_CPLACE_A_MARSHAL_SOUTH = 69,
AV_CPLACE_A_MARSHAL_NORTH = 70,
AV_CPLACE_A_MARSHAL_ICE = 71,
@@ -550,10 +596,13 @@ enum BG_AV_CreaturePlace
AV_CPLACE_MINE_S_S_MAX = 299,
//boss
AV_CPLACE_MINE_S_3 = 300,
+
//herald
AV_CPLACE_HERALD = 301,
+
AV_CPLACE_MAX = 302
};
+
//x, y, z, o
const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = {
//spiritguides
@@ -643,6 +692,7 @@ const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = {
{-1302.41f,-259.256f,114.065f,1.67602f},
{-1287.97f,-262.087f,114.165f,6.18264f},
{-1291.59f,-271.166f,114.151f,5.28257f},
+
//alliance marshall
{721.104f,-7.64155f,50.7046f,3.45575f},// south
{723.058f,-14.1548f,50.7046f,3.40339f},// north
@@ -653,6 +703,7 @@ const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = {
{-1370.96f,-223.532f,98.4266f,4.93012f},
{-1378.37f,-228.614f,99.3546f,5.38565f},
{-1358.02f,-228.998f,98.868f,3.87768f},
+
//irondeep mine
//Irondeep Trogg
{971.671f,-442.657f,57.6951f,3.1765f},
@@ -888,10 +939,13 @@ const float BG_AV_CreaturePos[AV_CPLACE_MAX][4] = {
{-848.902f,-92.931f,68.6325f,3.33350},
//herald
{-48.459f,-288.802f,55.47f,1.0}
+
};
+
enum BG_AV_CreatureIds
{
+
AV_NPC_A_GRAVEDEFENSE0 = 0, // stormpike Defender
AV_NPC_A_GRAVEDEFENSE1 = 1, // seasoned defender
AV_NPC_A_GRAVEDEFENSE2 = 2, // veteran defender
@@ -899,6 +953,7 @@ enum BG_AV_CreatureIds
AV_NPC_A_TOWERDEFENSE = 4, // stormpike bowman
AV_NPC_A_CAPTAIN = 5, // balinda
AV_NPC_A_BOSS = 6, // vanndar
+
AV_NPC_H_GRAVEDEFENSE0 = 7, // frostwolf guardian
AV_NPC_H_GRAVEDEFENSE1 = 8, // seasoned guardian
AV_NPC_H_GRAVEDEFENSE2 = 9, // veteran guardian
@@ -906,6 +961,7 @@ enum BG_AV_CreatureIds
AV_NPC_H_TOWERDEFENSE = 11, // frostwolf bowman
AV_NPC_H_CAPTAIN = 12, // galvangar
AV_NPC_H_BOSS = 13, // drek thar
+
AV_NPC_A_MARSHAL_SOUTH = 14,
AV_NPC_MARSHAL_NORTH = 15,
AV_NPC_A_MARSHAL_ICE = 16,
@@ -941,7 +997,9 @@ enum BG_AV_CreatureIds
AV_NPC_S_MINE_H_4 = 46,
AV_NPC_HERALD = 47,
AV_NPC_INFO_MAX = 48
+
};
+
//entry, team, minlevel, maxlevel
//TODO this array should be removed, the only needed things are the entrys (for spawning(?) and handlekillunit)
const uint32 BG_AV_CreatureInfo[AV_NPC_INFO_MAX][4] = {
@@ -963,37 +1021,46 @@ const uint32 BG_AV_CreatureInfo[AV_NPC_INFO_MAX][4] = {
{ 14762, 1534, 60, 60 }, //Dun Baldar North Marshal
{ 14764, 1534, 60, 60 }, //Icewing Marshal
{ 14765, 1534, 60, 60 }, //Stonehearth Marshal
+
{ 14773, 1214, 60, 60 }, //Iceblood Warmaster
{ 14776, 1214, 60, 60 }, //Tower Point Warmaster
{ 14772, 1214, 60, 60 }, //East Frostwolf Warmaster
{ 14777, 1214, 60, 60 }, //West Frostwolf Warmaster
+
{ 10987, 59, 52, 53 }, //Irondeep Trogg
{ 11600, 59, 53, 54 }, //Irondeep Shaman
{ 11602, 59, 54, 55 }, //Irondeep Skullthumper
{ 11657, 59, 58, 58 }, //Morloch
+
{13396,469,52,53}, //irondeep alliance TODO: get the right ids
{13080,469,53,54},
{13098,469,54,55},
{13078,469,58,58},
+
{13397,67,52,53}, //irondeep horde
{13099,67,53,54},
{13081,67,54,55},
{13079,67,58,58},
+
{ 11603, 59, 52, 53 }, //south mine neutral
{ 11604, 59, 53, 54 },
{ 11605, 59, 54, 55 },
{ 11677, 59, 58, 58 },
{ 10982, 59, 52, 53 }, //vermin
+
{13317,469,52,53}, //alliance
{13096,469,54,55}, //explorer
{13087,469,54,55}, //invader
{13086,469,58,58},
+
{13316,67,52,53}, //horde
{13097,67,54,55}, //surveypr
{13089,67,54,55}, //guard
{13088,67,58,58},
{14848,67,58,58} //Herald
+
};
+
//x,y,z,o,static_creature_info-id
const float BG_AV_StaticCreaturePos[AV_STATICCPLACE_MAX][5] = { //static creatures
{-1235.31f,-340.777f,60.5088f,3.31613f,0 },//2225 - Zora Guthrek
@@ -1119,7 +1186,9 @@ const float BG_AV_StaticCreaturePos[AV_STATICCPLACE_MAX][5] = { //static creatur
{773.651f,-497.482f,99.0408f,2.11185f,46 },//14284 - Stormpike Battleguard
{949.1f,-506.913f,95.4237f,3.31613f,46 },//14284 - Stormpike Battleguard
{-1370.9f,-219.793f,98.4258f,5.04381f,47}, //drek thar
+
};
+
const uint32 BG_AV_StaticCreatureInfo[51][4] = {
{ 2225, 1215, 55, 55 }, //Zora Guthrek
{ 3343, 1215, 55, 55 }, //Grelkor
@@ -1173,6 +1242,7 @@ const uint32 BG_AV_StaticCreatureInfo[51][4] = {
{ 11947, 1214, 61, 61 }, //Captain Galvangar
{ 11949, 1216, 61, 61 } //Captain Balinda Stonehearth
};
+
enum BG_AV_Graveyards
{
AV_GRAVE_STORM_AID = 751,
@@ -1186,6 +1256,8 @@ enum BG_AV_Graveyards
AV_GRAVE_MAIN_HORDE = 610
};
+
+
const uint32 BG_AV_GraveyardIds[9]= {
AV_GRAVE_STORM_AID,
AV_GRAVE_STORM_GRAVE,
@@ -1197,6 +1269,7 @@ const uint32 BG_AV_GraveyardIds[9]= {
AV_GRAVE_MAIN_ALLIANCE,
AV_GRAVE_MAIN_HORDE
};
+
enum BG_AV_BUFF
{ //TODO add all other buffs here
AV_BUFF_ARMOR = 21163,
@@ -1210,15 +1283,19 @@ enum BG_AV_States
POINT_DESTROYED = 2,
POINT_CONTROLED = 3
};
+
enum BG_AV_WorldStates
{
AV_Alliance_Score = 3127,
AV_Horde_Score = 3128,
AV_SHOW_H_SCORE = 3133,
AV_SHOW_A_SCORE = 3134,
+
/*
//the comments behind the state shows which icon overlaps the other.. but is, until now, unused and maybe not a good solution (but give few performance (: )
+
// Graves
+
// Alliance
//Stormpike first aid station
AV_AID_A_C = 1325,
@@ -1261,6 +1338,7 @@ enum BG_AV_WorldStates
AV_FROSTWOLFHUT_H_C = 1330,
AV_FROSTWOLFHUT_H_A = 1332, //over ac
+
//Towers
//Alliance
//Dunbaldar South Bunker
@@ -1296,30 +1374,38 @@ enum BG_AV_WorldStates
AV_FROSTWOLFE_CONTROLLED = 1383,
AV_FROSTWOLFE_DESTROYED = 1366,
AV_FROSTWOLFE_ASSAULTED = 1388,
+
//mines
+
AV_N_MINE_N = 1360,
AV_N_MINE_A = 1358,
AV_N_MINE_H = 1359,
+
AV_S_MINE_N = 1357,
AV_S_MINE_A = 1355,
AV_S_MINE_H = 1356,
+
//towers assaulted by own team (unused)
AV_STONEH_UNUSED = 1377,
AV_ICEWING_UNUSED = 1376,
AV_DUNS_UNUSED = 1375,
AV_DUNN_UNUSED = 1374,
+
AV_ICEBLOOD_UNUSED = 1395,
AV_TOWERPOINT_UNUSED = 1394,
AV_FROSTWOLFE_UNUSED = 1393,
AV_FROSTWOLFW_UNUSED = 1392
*/
+
};
+
//alliance_control neutral_control horde_control
const uint32 BG_AV_MineWorldStates[2][3] = {
{1358, 1360,1359},
{1355, 1357,1356}
};
+
//alliance_control alliance_assault h_control h_assault
const uint32 BG_AV_NodeWorldStates[16][4] = {
//Stormpike first aid station
@@ -1353,6 +1439,7 @@ const uint32 BG_AV_NodeWorldStates[16][4] = {
//Frostwolf West
{1365,1387,1382,1392},
};
+
enum BG_AV_QuestIds
{
AV_QUEST_A_SCRAPS1 = 7223,
@@ -1378,6 +1465,7 @@ enum BG_AV_QuestIds
AV_QUEST_A_RIDER_TAME = 7027,
AV_QUEST_H_RIDER_TAME = 7001
};
+
struct BG_AV_NodeInfo
{
uint16 TotalOwner;
@@ -1388,7 +1476,9 @@ struct BG_AV_NodeInfo
int Timer;
bool Tower;
};
+
inline BG_AV_Nodes &operator++(BG_AV_Nodes &i){ return i = BG_AV_Nodes(i + 1); }
+
class BattleGroundAVScore : public BattleGroundScore
{
public:
@@ -1402,24 +1492,30 @@ class BattleGroundAVScore : public BattleGroundScore
uint32 LeadersKilled;
uint32 SecondaryObjectives;
};
+
class BattleGroundAV : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundAV();
~BattleGroundAV();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr,uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
virtual void ResetBGSubclass();
+
/*general stuff*/
void UpdateScore(uint16 team, int16 points);
void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
+
/*handlestuff*/ //these are functions which get called from extern
virtual void EventPlayerClickedOnFlag(Player *source, GameObject* target_obj);
void HandleKillPlayer(Player* player, Player *killer);
@@ -1427,47 +1523,62 @@ class BattleGroundAV : public BattleGround
void HandleQuestComplete(uint32 questid, Player *player);
bool PlayerCanDoMineQuest(int32 GOId,uint32 team);
+
void EndBattleGround(uint32 winner);
+
virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);
+
private:
/* Nodes occupying */
void EventPlayerAssaultsPoint(Player* player, uint32 object);
void EventPlayerDefendsPoint(Player* player, uint32 object);
void EventPlayerDestroyedPoint(BG_AV_Nodes node);
+
void AssaultNode(BG_AV_Nodes node,uint16 team);
void DestroyNode(BG_AV_Nodes node);
void InitNode(BG_AV_Nodes node, uint16 team, bool tower);
void DefendNode(BG_AV_Nodes node, uint16 team);
+
void PopulateNode(BG_AV_Nodes node);
void DePopulateNode(BG_AV_Nodes node);
+
const BG_AV_Nodes GetNodeThroughObject(uint32 object);
const uint32 GetObjectThroughNode(BG_AV_Nodes node);
const char* GetNodeName(BG_AV_Nodes node);
const bool IsTower(BG_AV_Nodes node) { return m_Nodes[node].Tower; }
+
/*mine*/
void ChangeMineOwner(uint8 mine, uint32 team, bool initial=false);
+
/*worldstates*/
void FillInitialWorldStates(WorldPacket& data);
const uint8 GetWorldStateType(uint8 state, uint16 team);
void SendMineWorldStates(uint32 mine);
void UpdateNodeWorldState(BG_AV_Nodes node);
+
/*general */
Creature* AddAVCreature(uint16 cinfoid, uint16 type);
const uint16 GetBonusHonor(uint8 kills); //TODO remove this when mangos handles this right
+
/*variables */
int32 m_Team_Scores[2];
uint32 m_Team_QuestStatus[2][9]; //[x][y] x=team y=questcounter
+
BG_AV_NodeInfo m_Nodes[BG_AV_NODES_MAX];
+
uint32 m_Mine_Owner[2];
uint32 m_Mine_PrevOwner[2]; //only for worldstates needed
int32 m_Mine_Timer; //ticks for both teams
uint32 m_Mine_Reclaim_Timer[2];
uint32 m_CaptainBuffTimer[2];
bool m_CaptainAlive[2];
+
uint8 m_MaxLevel; //TODO remove this when battleground-getmaxlevel() returns something usefull
bool m_IsInformedNearVictory[2];
+
};
+
#endif
diff --git a/src/game/BattleGroundBE.cpp b/src/game/BattleGroundBE.cpp
index e79d2b606ff..05475328b01 100644
--- a/src/game/BattleGroundBE.cpp
+++ b/src/game/BattleGroundBE.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundBE.h"
#include "Language.h"
@@ -24,9 +25,11 @@
#include "ObjectMgr.h"
#include "Player.h"
#include "WorldPacket.h"
+
BattleGroundBE::BattleGroundBE()
{
m_BgObjects.resize(BG_BE_OBJECT_MAX);
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S;
@@ -37,72 +40,94 @@ BattleGroundBE::BattleGroundBE()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN;
}
+
BattleGroundBE::~BattleGroundBE()
{
+
}
+
void BattleGroundBE::Update(uint32 diff)
{
BattleGround::Update(diff);
+
/*if (GetStatus() == STATUS_IN_PROGRESS)
{
// update something
}*/
}
+
void BattleGroundBE::StartingEventCloseDoors()
{
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);
}
+
void BattleGroundBE::StartingEventOpenDoors()
{
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);
}
+
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;
+
UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE));
}
+
void BattleGroundBE::RemovePlayer(Player* /*plr*/, uint64 /*guid*/)
{
if (GetStatus() == STATUS_WAIT_LEAVE)
return;
+
UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE));
+
CheckArenaWinConditions();
}
+
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);
+
UpdateWorldState(0x9f1, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0x9f0, GetAlivePlayersCountByTeam(HORDE));
+
CheckArenaWinConditions();
}
+
bool BattleGroundBE::HandlePlayerUnderMap(Player *player)
{
player->TeleportTo(GetMapId(),6238.930176,262.963470,0.889519,player->GetOrientation(),false);
return true;
}
+
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)
@@ -118,20 +143,24 @@ void BattleGroundBE::HandleAreaTrigger(Player *Source, uint32 Trigger)
Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
break;
}
+
//if (buff_guid)
// HandleTriggerBuff(buff_guid,Source);
}
+
void BattleGroundBE::FillInitialWorldStates(WorldPacket &data)
{
data << uint32(0x9f1) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7
data << uint32(0x9f0) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8
data << uint32(0x9f3) << uint32(1); // 9
}
+
void BattleGroundBE::Reset()
{
//call parent's class reset
BattleGround::Reset();
}
+
bool BattleGroundBE::SetupBattleGround()
{
// gates
@@ -146,16 +175,22 @@ bool BattleGroundBE::SetupBattleGround()
sLog.outErrorDb("BatteGroundBE: Failed to spawn some object!");
return false;
}
+
return true;
}
+
void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value)
{
+
BattleGroundScoreMap::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...........
@@ -164,6 +199,7 @@ void BattleGroundBE::UpdatePlayerScore(Player* Source, uint32 type, uint32 value
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
diff --git a/src/game/BattleGroundBE.h b/src/game/BattleGroundBE.h
index b3996498e53..e75e332f44f 100644
--- a/src/game/BattleGroundBE.h
+++ b/src/game/BattleGroundBE.h
@@ -19,7 +19,9 @@
*/
#ifndef __BATTLEGROUNDBE_H
#define __BATTLEGROUNDBE_H
+
class BattleGround;
+
enum BattleGroundBEObjectTypes
{
BG_BE_OBJECT_DOOR_1 = 0,
@@ -30,6 +32,7 @@ enum BattleGroundBEObjectTypes
BG_BE_OBJECT_BUFF_2 = 5,
BG_BE_OBJECT_MAX = 6
};
+
enum BattleGroundBEObjects
{
BG_BE_OBJECT_TYPE_DOOR_1 = 183971,
@@ -39,23 +42,28 @@ enum BattleGroundBEObjects
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(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr, uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
@@ -63,6 +71,7 @@ class BattleGroundBE : public BattleGround
virtual void FillInitialWorldStates(WorldPacket &d);
void HandleKillPlayer(Player* player, Player *killer);
bool HandlePlayerUnderMap(Player * plr);
+
/* Scorekeeping */
void UpdatePlayerScore(Player *Source, uint32 type, uint32 value);
};
diff --git a/src/game/BattleGroundDS.cpp b/src/game/BattleGroundDS.cpp
index e133bc740b0..0be870be175 100644
--- a/src/game/BattleGroundDS.cpp
+++ b/src/game/BattleGroundDS.cpp
@@ -15,12 +15,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundDS.h"
#include "Language.h"
#include "Player.h"
+
BattleGroundDS::BattleGroundDS()
{
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S;
@@ -31,36 +34,47 @@ BattleGroundDS::BattleGroundDS()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN;
}
+
BattleGroundDS::~BattleGroundDS()
{
+
}
+
void BattleGroundDS::Update(uint32 diff)
{
BattleGround::Update(diff);
}
+
void BattleGroundDS::StartingEventCloseDoors()
{
}
+
void BattleGroundDS::StartingEventOpenDoors()
{
}
+
void BattleGroundDS::AddPlayer(Player *plr)
{
BattleGround::AddPlayer(plr);
//create score and add it to map, default values are set in constructor
BattleGroundDSScore* sc = new BattleGroundDSScore;
+
m_PlayerScores[plr->GetGUID()] = sc;
}
+
void BattleGroundDS::RemovePlayer(Player * /*plr*/, uint64 /*guid*/)
{
}
+
void BattleGroundDS::HandleKillPlayer(Player* player, Player* killer)
{
BattleGround::HandleKillPlayer(player, killer);
}
+
void BattleGroundDS::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/)
{
}
+
bool BattleGroundDS::SetupBattleGround()
{
return true;
diff --git a/src/game/BattleGroundDS.h b/src/game/BattleGroundDS.h
index 8b310a67da0..44a6cfbd33a 100644
--- a/src/game/BattleGroundDS.h
+++ b/src/game/BattleGroundDS.h
@@ -17,7 +17,9 @@
*/
#ifndef __BATTLEGROUNDDS_H
#define __BATTLEGROUNDDS_H
+
class BattleGround;
+
class BattleGroundDSScore : public BattleGroundScore
{
public:
@@ -25,17 +27,21 @@ class BattleGroundDSScore : public BattleGroundScore
virtual ~BattleGroundDSScore() {};
//TODO fix me
};
+
class BattleGroundDS : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundDS();
~BattleGroundDS();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr, uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
diff --git a/src/game/BattleGroundEY.cpp b/src/game/BattleGroundEY.cpp
index 5678eeec494..0c7ea66388b 100644
--- a/src/game/BattleGroundEY.cpp
+++ b/src/game/BattleGroundEY.cpp
@@ -17,6 +17,7 @@
* 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"
#include "World.h"
#include "WorldPacket.h"
@@ -28,11 +29,13 @@
#include "Object.h"
#include "Player.h"
#include "Util.h"
+
// these variables aren't used outside of this file, so declare them only here
uint32 BG_EY_HonorScoreTicks[BG_HONOR_MODE_NUM] = {
330, // normal honor
200 // holiday
};
+
BattleGroundEY::BattleGroundEY()
{
m_BuffChange = true;
@@ -42,17 +45,21 @@ BattleGroundEY::BattleGroundEY()
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;
+
m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_EY_START_TWO_MINUTES;
m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_EY_START_ONE_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_EY_START_HALF_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_EY_HAS_BEGUN;
}
+
BattleGroundEY::~BattleGroundEY()
{
}
+
void BattleGroundEY::Update(uint32 diff)
{
BattleGround::Update(diff);
+
if (GetStatus() == STATUS_IN_PROGRESS)
{
m_PointAddingTimer -= diff;
@@ -64,9 +71,11 @@ void BattleGroundEY::Update(uint32 diff)
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;
@@ -76,6 +85,7 @@ void BattleGroundEY::Update(uint32 diff)
RespawnFlagAfterDrop();
}
}
+
m_TowerCapCheckTimer -= diff;
if (m_TowerCapCheckTimer <= 0)
{
@@ -91,17 +101,21 @@ void BattleGroundEY::Update(uint32 diff)
}
}
}
+
void BattleGroundEY::StartingEventCloseDoors()
{
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);
}
+
void BattleGroundEY::StartingEventOpenDoors()
{
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)
@@ -111,6 +125,7 @@ void BattleGroundEY::StartingEventOpenDoors()
SpawnBGObject(BG_EY_OBJECT_SPEEDBUFF_FEL_REALVER + buff + i * 3, RESPAWN_IMMEDIATELY);
}
}
+
void BattleGroundEY::AddPoints(uint32 Team, uint32 Points)
{
BattleGroundTeamId team_index = GetTeamIndexByTeamId(Team);
@@ -123,6 +138,7 @@ void BattleGroundEY::AddPoints(uint32 Team, uint32 Points)
}
UpdateTeamScore(Team);
}
+
void BattleGroundEY::CheckSomeoneJoinedPoint()
{
GameObject *obj = NULL;
@@ -159,6 +175,7 @@ void BattleGroundEY::CheckSomeoneJoinedPoint()
}
}
}
+
void BattleGroundEY::CheckSomeoneLeftPoint()
{
//reset current point counts
@@ -200,6 +217,7 @@ void BattleGroundEY::CheckSomeoneLeftPoint()
}
}
}
+
void BattleGroundEY::UpdatePointStatuses()
{
for(uint8 point = 0; point < EY_POINTS_MAX; ++point)
@@ -208,12 +226,14 @@ void BattleGroundEY::UpdatePointStatuses()
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)
@@ -222,6 +242,7 @@ void BattleGroundEY::UpdatePointStatuses()
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]);
@@ -234,6 +255,7 @@ void BattleGroundEY::UpdatePointStatuses()
//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);
@@ -242,6 +264,7 @@ void BattleGroundEY::UpdatePointStatuses()
}
}
}
+
void BattleGroundEY::UpdateTeamScore(uint32 Team)
{
uint32 score = GetTeamScore(Team);
@@ -255,16 +278,19 @@ void BattleGroundEY::UpdateTeamScore(uint32 Team)
PlaySoundToAll(BG_EY_SOUND_NEAR_VICTORY);
m_IsInformedNearVictory = true;
}*/
+
if (score >= BG_EY_MAX_TEAM_SCORE)
{
score = BG_EY_MAX_TEAM_SCORE;
EndBattleGround(Team);
}
+
if (Team == ALLIANCE)
UpdateWorldState(EY_ALLIANCE_RESOURCES, score);
else
UpdateWorldState(EY_HORDE_RESOURCES, score);
}
+
void BattleGroundEY::EndBattleGround(uint32 winner)
{
//win reward
@@ -275,8 +301,10 @@ void BattleGroundEY::EndBattleGround(uint32 winner)
//complete map reward
RewardHonorToTeam(GetBonusHonorFromKill(1), ALLIANCE);
RewardHonorToTeam(GetBonusHonorFromKill(1), HORDE);
+
BattleGround::EndBattleGround(winner);
}
+
void BattleGroundEY::UpdatePointsCount(uint32 Team)
{
if (Team == ALLIANCE)
@@ -284,6 +312,7 @@ void BattleGroundEY::UpdatePointsCount(uint32 Team)
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!!!
@@ -304,14 +333,18 @@ void BattleGroundEY::UpdatePointsIcons(uint32 Team, uint32 Point)
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 :(
@@ -335,12 +368,15 @@ void BattleGroundEY::RemovePlayer(Player *plr, uint64 guid)
}
}
}
+
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:
@@ -380,6 +416,7 @@ void BattleGroundEY::HandleAreaTrigger(Player *Source, uint32 Trigger)
break;
}
}
+
bool BattleGroundEY::SetupBattleGround()
{
// doors
@@ -440,6 +477,7 @@ bool BattleGroundEY::SetupBattleGround()
sLog.outErrorDb("BatteGroundEY: Failed to spawn some object BattleGround not created!");
return false;
}
+
//buffs
for (int i = 0; i < EY_POINTS_MAX; ++i)
{
@@ -455,6 +493,7 @@ bool BattleGroundEY::SetupBattleGround()
)
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))
@@ -462,18 +501,22 @@ bool BattleGroundEY::SetupBattleGround()
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::Reset()
{
//call parent's class reset
BattleGround::Reset();
+
m_TeamScores[BG_TEAM_ALLIANCE] = 0;
m_TeamScores[BG_TEAM_HORDE] = 0;
m_TeamPointsCount[BG_TEAM_ALLIANCE] = 0;
@@ -488,6 +531,7 @@ void BattleGroundEY::Reset()
m_TowerCapCheckTimer = 0;
bool isBGWeekend = sBattleGroundMgr.IsBGWeekend(GetTypeID());
m_HonorTics = (isBGWeekend) ? BG_EY_EYWeekendHonorTicks : BG_EY_NotEYWeekendHonorTicks;
+
for(uint8 i = 0; i < EY_POINTS_MAX; ++i)
{
m_PointOwnedByTeam[i] = EY_POINT_NO_OWNER;
@@ -499,37 +543,47 @@ void BattleGroundEY::Reset()
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(LANG_BG_EY_RESETED_FLAG, CHAT_MSG_BG_SYSTEM_NEUTRAL);
PlaySoundToAll(BG_EY_SOUND_FLAG_RESET); // flags respawned sound...
}
+
UpdateWorldState(NETHERSTORM_FLAG, 1);
}
+
void BattleGroundEY::RespawnFlagAfterDrop()
{
RespawnFlag(true);
+
GameObject *obj = HashMapHolder<GameObject>::Find(GetDroppedFlagGUID());
if (obj)
obj->Delete();
else
sLog.outError("BattleGroundEY: Unknown dropped flag guid: %u",GUID_LOPART(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)
{
if (GetStatus() != STATUS_IN_PROGRESS)
@@ -543,10 +597,13 @@ void BattleGroundEY::EventPlayerDroppedFlag(Player *Source)
}
return;
}
+
if (!IsFlagPickedup())
return;
+
if (GetFlagPickerGUID() != Source->GetGUID())
return;
+
SetFlagPicker(0);
Source->RemoveAurasDueToSpell(BG_EY_NETHERSTORM_FLAG_SPELL);
m_FlagState = BG_EY_FLAG_STATE_ON_GROUND;
@@ -556,15 +613,18 @@ void BattleGroundEY::EventPlayerDroppedFlag(Player *Source)
//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);
+
if (Source->GetTeam() == ALLIANCE)
SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL);
else
SendMessageToAll(LANG_BG_EY_DROPPED_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL);
}
+
void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target_obj)
{
if (GetStatus() != STATUS_IN_PROGRESS || IsFlagPickedup() || !Source->IsWithinDistInMap(target_obj, 10))
return;
+
if (Source->GetTeam() == ALLIANCE)
{
UpdateWorldState(NETHERSTORM_FLAG_STATE_ALLIANCE, BG_EY_FLAG_STATE_ON_PLAYER);
@@ -575,27 +635,34 @@ void BattleGroundEY::EventPlayerClickedOnFlag(Player *Source, GameObject* target
UpdateWorldState(NETHERSTORM_FLAG_STATE_HORDE, BG_EY_FLAG_STATE_ON_PLAYER);
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);
+
if (Source->GetTeam() == ALLIANCE)
PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_ALLIANCE, NULL, Source->GetName());
else
PSendMessageToAll(LANG_BG_EY_HAS_TAKEN_FLAG, CHAT_MSG_BG_SYSTEM_HORDE, NULL, Source->GetName());
}
+
void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point)
{
if (GetStatus() != STATUS_IN_PROGRESS)
return;
+
//Natural point
uint32 Team = m_PointOwnedByTeam[Point];
+
if (!Team)
return;
+
if (Team == ALLIANCE)
{
m_TeamPointsCount[BG_TEAM_ALLIANCE]--;
@@ -610,27 +677,36 @@ void BattleGroundEY::EventTeamLostPoint(Player *Source, uint32 Point)
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;
+
if (Team == ALLIANCE)
SendMessageToAll(m_LoosingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source);
else
SendMessageToAll(m_LoosingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source);
+
UpdatePointsIcons(Team, Point);
UpdatePointsCount(Team);
}
+
void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point)
{
if (GetStatus() != STATUS_IN_PROGRESS)
return;
+
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]++;
@@ -645,39 +721,53 @@ void BattleGroundEY::EventTeamCapturedPoint(Player *Source, uint32 Point)
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;
+
if (Team == ALLIANCE)
SendMessageToAll(m_CapturingPointTypes[Point].MessageIdAlliance,CHAT_MSG_BG_SYSTEM_ALLIANCE, Source);
else
SendMessageToAll(m_CapturingPointTypes[Point].MessageIdHorde,CHAT_MSG_BG_SYSTEM_HORDE, Source);
+
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);
+
// SpawnBGCreature(Point,RESPAWN_IMMEDIATELY);
+
UpdatePointsIcons(Team, Point);
UpdatePointsCount(Team);
}
+
void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType)
{
if (GetStatus() != STATUS_IN_PROGRESS || GetFlagPickerGUID() != Source->GetGUID())
return;
+
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);
else
PlaySoundToAll(BG_EY_SOUND_FLAG_CAPTURED_HORDE);
+
SpawnBGObject(BgObjectType, RESPAWN_IMMEDIATELY);
+
m_FlagsTimer = BG_EY_FLAG_RESPAWN_TIME;
m_FlagCapturedBgObjectType = BgObjectType;
+
uint8 team_id = 0;
if (Source->GetTeam() == ALLIANCE)
{
@@ -689,15 +779,19 @@ void BattleGroundEY::EventPlayerCapturedFlag(Player *Source, uint32 BgObjectType
team_id = BG_TEAM_HORDE;
SendMessageToAll(LANG_BG_EY_CAPTURED_FLAG_H, CHAT_MSG_BG_SYSTEM_HORDE, Source);
}
+
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)
{
BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID());
if(itr == m_PlayerScores.end()) // player not found
return;
+
switch(type)
{
case SCORE_FLAG_CAPTURES: // flags captured
@@ -708,6 +802,7 @@ void BattleGroundEY::UpdatePlayerScore(Player *Source, uint32 type, uint32 value
break;
}
}
+
void BattleGroundEY::FillInitialWorldStates(WorldPacket& data)
{
data << uint32(EY_HORDE_BASE) << uint32(m_TeamPointsCount[BG_TEAM_HORDE]);
@@ -720,19 +815,33 @@ void BattleGroundEY::FillInitialWorldStates(WorldPacket& data)
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));
@@ -743,31 +852,39 @@ void BattleGroundEY::FillInitialWorldStates(WorldPacket& data)
data << uint32(0xa9e) << uint32(0x0);
data << uint32(0xc0d) << uint32(0x17b);
}
+
WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player)
{
uint32 g_id = 0;
+
switch(player->GetTeam())
{
case ALLIANCE: g_id = EY_GRAVEYARD_MAIN_ALLIANCE; break;
case HORDE: g_id = EY_GRAVEYARD_MAIN_HORDE; break;
default: 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;
}
+
float plr_x = player->GetPositionX();
float plr_y = player->GetPositionY();
float plr_z = player->GetPositionZ();
+
distance = (entry->x - plr_x)*(entry->x - plr_x) + (entry->y - plr_y)*(entry->y - plr_y) + (entry->z - plr_z)*(entry->z - plr_z);
nearestDistance = distance;
+
for(uint8 i = 0; i < EY_POINTS_MAX; ++i)
{
if (m_PointOwnedByTeam[i]==player->GetTeam() && m_PointState[i]==EY_POINT_UNDER_CONTROL)
@@ -786,13 +903,16 @@ WorldSafeLocsEntry const *BattleGroundEY::GetClosestGraveYard(Player* player)
}
}
}
+
return nearestEntry;
}
+
bool BattleGroundEY::IsAllNodesConrolledByTeam(uint32 team) const
{
uint32 count = 0;
for(int i = 0; i < EY_POINTS_MAX; ++i)
if (m_PointOwnedByTeam[i] == team && m_PointState[i] == EY_POINT_UNDER_CONTROL)
++count;
+
return count == EY_POINTS_MAX;
}
diff --git a/src/game/BattleGroundEY.h b/src/game/BattleGroundEY.h
index 086c4d79fbd..3385a06bfb0 100644
--- a/src/game/BattleGroundEY.h
+++ b/src/game/BattleGroundEY.h
@@ -17,12 +17,17 @@
* 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 BG_EY_FLAG_RESPAWN_TIME (10*IN_MILISECONDS) //10 seconds
#define BG_EY_FPOINTS_TICK_TIME (2*IN_MILISECONDS) //2 seconds
+
enum BG_EY_WorldStates
{
EY_ALLIANCE_RESOURCES = 2749,
@@ -49,6 +54,7 @@ enum BG_EY_WorldStates
NETHERSTORM_FLAG_STATE_ALLIANCE = 2769,
NETHERSTORM_FLAG_STATE_HORDE = 2770
};
+
enum BG_EY_ProgressBarConsts
{
BG_EY_POINT_MAX_CAPTURERS_COUNT = 5,
@@ -62,6 +68,7 @@ enum BG_EY_ProgressBarConsts
BG_EY_PROGRESS_BAR_NEUTRAL_HIGH = 70,
BG_EY_PROGRESS_BAR_ALI_CONTROLLED = 100
};
+
enum BG_EY_Sounds
{
//strange ids, but sure about them
@@ -71,11 +78,13 @@ enum BG_EY_Sounds
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
@@ -91,6 +100,7 @@ enum EYBattleGroundObjectEntry
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,
@@ -102,6 +112,7 @@ enum EYBattleGroundPointsTrigger
TR_MAGE_TOWER_BUFF = 4570,
TR_DRAENEI_RUINS_BUFF = 4571
};
+
enum EYBattleGroundGaveyards
{
EY_GRAVEYARD_MAIN_ALLIANCE = 1103,
@@ -111,15 +122,18 @@ enum EYBattleGroundGaveyards
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,
@@ -128,8 +142,10 @@ enum EYBattleGroundCreaturesTypes
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,
@@ -194,13 +210,16 @@ enum EYBattleGroundObjectTypes
BG_EY_OBJECT_BERSERKBUFF_MAGE_TOWER = 58,
BG_EY_OBJECT_MAX = 59
};
+
#define BG_EY_NotEYWeekendHonorTicks 330
#define BG_EY_EYWeekendHonorTicks 200
+
enum BG_EY_Score
{
BG_EY_WARNING_NEAR_VICTORY_SCORE = 1800,
BG_EY_MAX_TEAM_SCORE = 2000
};
+
enum BG_EY_FlagState
{
BG_EY_FLAG_STATE_ON_BASE = 0,
@@ -208,12 +227,14 @@ enum BG_EY_FlagState
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)
@@ -222,6 +243,7 @@ struct BattleGroundEYPointIconsStruct
uint32 WorldStateAllianceControlledIndex;
uint32 WorldStateHordeControlledIndex;
};
+
struct BattleGroundEYLoosingPointStruct
{
BattleGroundEYLoosingPointStruct(uint32 _SpawnNeutralObjectType, uint32 _DespawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _DespawnObjectTypeHorde, uint32 _MessageIdHorde)
@@ -229,12 +251,14 @@ struct BattleGroundEYLoosingPointStruct
DespawnObjectTypeAlliance(_DespawnObjectTypeAlliance), MessageIdAlliance(_MessageIdAlliance),
DespawnObjectTypeHorde(_DespawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde)
{}
+
uint32 SpawnNeutralObjectType;
uint32 DespawnObjectTypeAlliance;
uint32 MessageIdAlliance;
uint32 DespawnObjectTypeHorde;
uint32 MessageIdHorde;
};
+
struct BattleGroundEYCapturingPointStruct
{
BattleGroundEYCapturingPointStruct(uint32 _DespawnNeutralObjectType, uint32 _SpawnObjectTypeAlliance, uint32 _MessageIdAlliance, uint32 _SpawnObjectTypeHorde, uint32 _MessageIdHorde, uint32 _GraveYardId)
@@ -243,6 +267,7 @@ struct BattleGroundEYCapturingPointStruct
SpawnObjectTypeHorde(_SpawnObjectTypeHorde), MessageIdHorde(_MessageIdHorde),
GraveYardId(_GraveYardId)
{}
+
uint32 DespawnNeutralObjectType;
uint32 SpawnObjectTypeAlliance;
uint32 MessageIdAlliance;
@@ -250,8 +275,10 @@ struct BattleGroundEYCapturingPointStruct
uint32 MessageIdHorde;
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] =
{
@@ -274,6 +301,7 @@ const BattleGroundEYCapturingPointStruct m_CapturingPointTypes[EY_POINTS_MAX] =
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:
@@ -281,17 +309,21 @@ class BattleGroundEYScore : public BattleGroundScore
virtual ~BattleGroundEYScore() {};
uint32 FlagCaptures;
};
+
class BattleGroundEY : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundEY();
~BattleGroundEY();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
/* BG Flags */
uint64 GetFlagPickerGUID() const { return m_FlagKeeper; }
void SetFlagPicker(uint64 guid) { m_FlagKeeper = guid; }
@@ -299,6 +331,7 @@ class BattleGroundEY : public BattleGround
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);
@@ -312,9 +345,11 @@ class BattleGroundEY : public BattleGround
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);
+
/* achievement req. */
bool IsAllNodesConrolledByTeam(uint32 team) const;
private:
@@ -323,30 +358,38 @@ class BattleGroundEY : public BattleGround
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_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<uint64> PlayersNearPointType;
PlayersNearPointType m_PlayersNearPoint[EY_POINTS_MAX + 1];
uint8 m_CurrentPointPlayersCount[2*EY_POINTS_MAX];
+
int32 m_PointAddingTimer;
uint32 m_HonorTics;
};
diff --git a/src/game/BattleGroundHandler.cpp b/src/game/BattleGroundHandler.cpp
index 1e685ec1f3a..3e7c453e300 100644
--- a/src/game/BattleGroundHandler.cpp
+++ b/src/game/BattleGroundHandler.cpp
@@ -17,11 +17,13 @@
* 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 "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "WorldPacket.h"
#include "WorldSession.h"
+
#include "ArenaTeam.h"
#include "BattleGroundMgr.h"
#include "BattleGroundWS.h"
@@ -32,33 +34,42 @@
#include "Player.h"
#include "Object.h"
#include "Opcodes.h"
+
void WorldSession::HandleBattlemasterHelloOpcode( WorldPacket & recv_data )
{
uint64 guid;
recv_data >> guid;
sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_HELLO Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid),GuidHigh2TypeId(GUID_HIPART(guid)));
+
Creature *unit = GetPlayer()->GetMap()->GetCreature(guid);
if (!unit)
return;
+
if(!unit->isBattleMaster()) // it's not battlemaster
return;
+
// Stop the npc if moving
unit->StopMoving();
+
BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(unit->GetEntry());
+
if (!_player->GetBGAccessByLevel(bgTypeId))
{
// temp, must be gossip message...
SendNotification(LANG_YOUR_BG_LEVEL_REQ_ERROR);
return;
}
+
SendBattlegGroundList(guid, bgTypeId);
}
+
void WorldSession::SendBattlegGroundList( uint64 guid, BattleGroundTypeId bgTypeId )
{
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundListPacket(&data, guid, _player, bgTypeId, 0);
SendPacket( &data );
}
+
void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data )
{
uint64 guid;
@@ -67,31 +78,40 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data )
uint8 joinAsGroup;
bool isPremade = false;
Group * grp;
+
recv_data >> guid; // battlemaster guid
recv_data >> bgTypeId_; // battleground type id (DBC id)
recv_data >> instanceId; // instance id, 0 if First Available selected
recv_data >> joinAsGroup; // join as group
+
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
{
sLog.outError("Battleground: invalid bgtype (%u) received. possible cheater? player guid %u",bgTypeId_,_player->GetGUIDLow());
return;
}
+
BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_);
+
sLog.outDebug( "WORLD: Recvd CMSG_BATTLEMASTER_JOIN Message from (GUID: %u TypeId:%u)", GUID_LOPART(guid), GuidHigh2TypeId(GUID_HIPART(guid)));
+
// can do this, since it's battleground, not arena
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, 0);
+
// ignore if player is already in BG
if (_player->InBattleGround())
return;
+
// get bg instance or bg template if instance not found
BattleGround *bg = NULL;
if (instanceId)
bg = sBattleGroundMgr.GetBattleGroundThroughClientInstance(instanceId, bgTypeId);
+
if (!bg && !(bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId)))
{
sLog.outError("Battleground: no available bg / template found");
return;
}
+
// check queueing conditions
if (!joinAsGroup)
{
@@ -126,6 +146,7 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data )
}
}
// if we're here, then the conditions to join a bg are met. We can proceed in joining.
+
// _player->GetGroup() was already checked, grp is already initialized
GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, 0, false, isPremade, 0);
uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(ginfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId));
@@ -136,7 +157,9 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data )
{
Player *member = itr->getSource();
if(!member) continue; // this should never happen
+
uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId); // add to queue
+
WorldPacket data;
// send status packet (in queue)
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType);
@@ -152,10 +175,12 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data )
{
// already checked if queueSlot is valid, now just get it
uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId);
+
WorldPacket data;
// send status packet (in queue)
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, ginfo->ArenaType);
SendPacket(&data);
+
sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddPlayer(_player, ginfo);
sLog.outDebug("Battleground: player joined queue for bg queue type %u bg type %u: GUID %u, NAME %s",bgQueueTypeId,bgTypeId,_player->GetGUIDLow(), _player->GetName());
}
@@ -163,25 +188,31 @@ void WorldSession::HandleBattlemasterJoinOpcode( WorldPacket & recv_data )
if (!ginfo->IsInvitedToBGInstanceGUID)
sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AnnounceWorld(ginfo, _player->GetGUID(), true);
}
+
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;
+
switch( bg->GetTypeID() )
{
case BATTLEGROUND_WS:
{
uint32 count1 = 0; //always constant zero?
uint32 count2 = 0; //count of next fields
+
Player *ali_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetAllianceFlagPickerGUID());
if (ali_plr)
++count2;
+
Player *horde_plr = objmgr.GetPlayer(((BattleGroundWS*)bg)->GetHordeFlagPickerGUID());
if (horde_plr)
++count2;
+
WorldPacket data(MSG_BATTLEGROUND_PLAYER_POSITIONS, (4+4+16*count1+16*count2));
data << count1; // alliance flag holders count - obsolete, now always 0
/*for(uint8 i = 0; i < count1; ++i)
@@ -203,6 +234,7 @@ void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv
data << (float)horde_plr->GetPositionX();
data << (float)horde_plr->GetPositionY();
}
+
SendPacket(&data);
}
break;
@@ -224,44 +256,57 @@ void WorldSession::HandleBattleGroundPlayerPositionsOpcode( WorldPacket & /*recv
break;
}
}
+
void WorldSession::HandlePVPLogDataOpcode( 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::HandleBattlefieldListOpcode( WorldPacket &recv_data )
{
sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_LIST Message");
+
uint32 bgTypeId;
recv_data >> bgTypeId; // id from DBC
+
uint8 fromWhere;
recv_data >> fromWhere; // 0 - battlemaster, 1 - UI
+
BattlemasterListEntry const* bl = sBattlemasterListStore.LookupEntry(bgTypeId);
if (!bl)
{
sLog.outError("Battleground: invalid bgtype received.");
return;
}
+
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundListPacket(&data, 0, _player, BattleGroundTypeId(bgTypeId), fromWhere);
SendPacket( &data );
}
+
void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
{
sLog.outDebug( "WORLD: Recvd CMSG_BATTLEFIELD_PORT Message");
+
uint8 type; // arenatype if arena
uint8 unk2; // unk, can be 0x0 (may be if was invited?) and 0x1
uint32 instanceId;
uint32 bgTypeId_; // type id from dbc
uint16 unk; // 0x1F90 constant?
uint8 action; // enter battle 0x1, leave queue 0x0
+
recv_data >> type >> unk2 >> bgTypeId_ >> unk >> action;
+
if (!sBattlemasterListStore.LookupEntry(bgTypeId_))
{
sLog.outError("Battleground: invalid bgtype (%u) received.", bgTypeId_);
@@ -282,10 +327,12 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
// if the player is not in queue, continue or no group information - this should never happen
if (itrPlayerStatus == qpMap.end() || !itrPlayerStatus->second.GroupInfo)
continue;
+
BattleGround * bg = NULL;
// get possibly needed data from groupinfo
uint8 arenatype = itrPlayerStatus->second.GroupInfo->ArenaType;
uint8 status = 0;
+
if (!itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID)
{
// not invited to bg, get template
@@ -298,9 +345,11 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
bg = sBattleGroundMgr.GetBattleGround(itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID, bgTypeId);
status = STATUS_WAIT_JOIN;
}
+
// if bg not found, then continue, don't invite if already in the instance
if (!bg || (_player->InBattleGround() && _player->GetBattleGround() && _player->GetBattleGround()->GetInstanceID() == bg->GetInstanceID()))
continue;
+
// re - invite player with proper data
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, i, status, INVITE_ACCEPT_WAIT_TIME, 0, arenatype);
@@ -309,6 +358,7 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
}
return;
}
+
//get GroupQueueInfo from BattleGroundQueue
BattleGroundTypeId bgTypeId = BattleGroundTypeId(bgTypeId_);
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, type);
@@ -319,6 +369,7 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
sLog.outError("Battleground: itrplayerstatus not found.");
return;
}
+
instanceId = itrPlayerStatus->second.GroupInfo->IsInvitedToBGInstanceGUID;
// if action == 1, then instanceId is required
if (!instanceId && action == 1)
@@ -326,7 +377,9 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
sLog.outError("Battleground: instance not found.");
return;
}
+
BattleGround *bg = sBattleGroundMgr.GetBattleGround(instanceId, bgTypeId);
+
// bg template might and must be used in case of leaving queue, when instance is not created yet
if (!bg && action == 0)
bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
@@ -335,6 +388,7 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
sLog.outError("Battleground: bg_template not found for type id %u.", bgTypeId);
return;
}
+
if (_player->InBattleGroundQueue())
{
//we must use temporary variables, because GroupQueueInfo pointer can be deleted in BattleGroundQueue::RemovePlayer() function!
@@ -343,6 +397,7 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
uint32 isRated = itrPlayerStatus->second.GroupInfo->IsRated;
uint32 rating = itrPlayerStatus->second.GroupInfo->ArenaTeamRating;
uint32 opponentsRating = itrPlayerStatus->second.GroupInfo->OpponentsTeamRating;
+
//some checks if player isn't cheating - it is not exactly cheating, but we cannot allow it
if (action == 1 && arenaType == 0)
{
@@ -370,7 +425,9 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
case 1: // port to battleground
if (!_player->IsInvitedForBattleGroundQueueType(bgQueueTypeId))
return; // cheating?
+
_player->SetBattleGroundEntryPoint();
+
// resurrect the player
if (!_player->isAlive())
{
@@ -382,7 +439,9 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
{
_player->GetMotionMaster()->MovementExpired();
_player->CleanupAfterTaxiFlight();
+
}
+
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_IN_PROGRESS, 0, bg->GetStartTime(), bg->GetArenaType());
_player->GetSession()->SendPacket(&data);
// remove battleground queue status from BGmgr
@@ -391,6 +450,7 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
// also this is required to prevent stuck at old battleground after SetBattleGroundId set to new
if (BattleGround *currentBg = _player->GetBattleGround())
currentBg->RemovePlayerAtLeave(_player->GetGUID(), false, true);
+
// set the destination instance id
_player->SetBattleGroundId(bg->GetInstanceID(), bgTypeId);
// set the destination team
@@ -431,26 +491,34 @@ void WorldSession::HandleBattleFieldPortOpcode( WorldPacket &recv_data )
}
}
}
+
void WorldSession::HandleLeaveBattlefieldOpcode( WorldPacket & /*recv_data*/ )
{
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;
// we must update all queues here
BattleGround *bg = NULL;
@@ -503,56 +571,76 @@ void WorldSession::HandleBattlefieldStatusOpcode( WorldPacket & /*recv_data*/ )
}
}
}
+
void WorldSession::HandleAreaSpiritHealerQueryOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_AREA_SPIRIT_HEALER_QUERY");
+
BattleGround *bg = _player->GetBattleGround();
if (!bg)
return;
+
uint64 guid;
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetMap()->GetCreature(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");
+
BattleGround *bg = _player->GetBattleGround();
if (!bg)
return;
+
uint64 guid;
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetMap()->GetCreature(guid);
if (!unit)
return;
+
if(!unit->isSpiritService()) // it's not spirit service
return;
+
bg->AddPlayerToResurrectQueue(guid, _player->GetGUID());
}
+
void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_BATTLEMASTER_JOIN_ARENA");
//recv_data.hexlike();
+
uint64 guid; // arena Battlemaster guid
uint8 arenaslot; // 2v2, 3v3 or 5v5
uint8 asGroup; // asGroup
uint8 isRated; // isRated
Group * grp;
+
recv_data >> guid >> arenaslot >> asGroup >> isRated;
+
// ignore if we already in BG or BG queue
if (_player->InBattleGround())
return;
+
Creature *unit = GetPlayer()->GetMap()->GetCreature(guid);
if (!unit)
return;
+
if(!unit->isBattleMaster()) // it's not battle master
return;
+
uint8 arenatype = 0;
uint32 arenaRating = 0;
+
switch(arenaslot)
{
case 0:
@@ -568,6 +656,7 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
sLog.outError("Unknown arena slot %u at HandleBattlemasterJoinArena()", arenaslot);
return;
}
+
//check existance
BattleGround* bg = NULL;
if (!(bg = sBattleGroundMgr.GetBattleGroundTemplate(BATTLEGROUND_AA)))
@@ -575,8 +664,10 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
sLog.outError("Battleground: template bg (all arenas) not found");
return;
}
+
BattleGroundTypeId bgTypeId = bg->GetTypeID();
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, arenatype);
+
// check queueing conditions
if (!asGroup)
{
@@ -601,7 +692,9 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
return;
}
}
+
uint32 ateamId = 0;
+
if (isRated)
{
ateamId = _player->GetArenaTeamId(arenaslot);
@@ -620,15 +713,19 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *member = itr->getSource();
+
// calc avg personal rating
avg_pers_rating += member->GetUInt32Value(PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + (arenaslot*6) + 5);
}
+
if (arenatype)
avg_pers_rating /= arenatype;
+
// if avg personal rating is more than 150 points below the teams rating, the team will be queued against an opponent matching or similar to the average personal rating
if (avg_pers_rating + 150 < arenaRating)
arenaRating = avg_pers_rating;
}
+
GroupQueueInfo * ginfo = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].AddGroup(_player, bgTypeId, arenatype, isRated, false, arenaRating, ateamId);
uint32 avgTime = sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].GetAverageQueueWaitTime(ginfo, _player->GetBattleGroundQueueIdFromLevel(bgTypeId));
if (asGroup)
@@ -641,11 +738,14 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
}
else
bg->SetRated(false);
+
for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *member = itr->getSource();
if(!member) continue;
+
uint32 queueSlot = member->AddBattleGroundQueueId(bgQueueTypeId);// add to queue
+
WorldPacket data;
// send status packet (in queue)
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype);
@@ -662,6 +762,7 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
else
{
uint32 queueSlot = _player->AddBattleGroundQueueId(bgQueueTypeId);
+
WorldPacket data;
// send status packet (in queue)
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_QUEUE, avgTime, 0, arenatype);
@@ -671,19 +772,24 @@ void WorldSession::HandleBattlemasterJoinArena( WorldPacket & recv_data )
}
sBattleGroundMgr.m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, _player->GetBattleGroundQueueIdFromLevel(bgTypeId), arenatype, isRated, arenaRating);
}
+
void WorldSession::HandleReportPvPAFK( WorldPacket & recv_data )
{
uint64 playerGuid;
recv_data >> playerGuid;
Player *reportedPlayer = objmgr.GetPlayer(playerGuid);
+
if (!reportedPlayer)
{
sLog.outDebug("WorldSession::HandleReportPvPAFK: player not found");
return;
}
+
sLog.outDebug("WorldSession::HandleReportPvPAFK: %s reported %s", _player->GetName(), reportedPlayer->GetName());
+
reportedPlayer->ReportedAfkBy(_player);
}
+
void WorldSession::SendBattleGroundOrArenaJoinError(uint8 err)
{
WorldPacket data;
diff --git a/src/game/BattleGroundMgr.cpp b/src/game/BattleGroundMgr.cpp
index 99339a835a6..f2143de5bdc 100644
--- a/src/game/BattleGroundMgr.cpp
+++ b/src/game/BattleGroundMgr.cpp
@@ -17,11 +17,13 @@
* 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 "ObjectMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "Policies/SingletonImp.h"
+
#include "ArenaTeam.h"
#include "BattleGroundMgr.h"
#include "BattleGroundAV.h"
@@ -43,10 +45,13 @@
#include "GameEventMgr.h"
#include "ProgressBar.h"
#include "SharedDefines.h"
+
INSTANTIATE_SINGLETON_1( BattleGroundMgr );
+
/*********************************************************/
/*** BATTLEGROUND QUEUE SYSTEM ***/
/*********************************************************/
+
BattleGroundQueue::BattleGroundQueue()
{
for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
@@ -60,6 +65,7 @@ BattleGroundQueue::BattleGroundQueue()
}
}
}
+
BattleGroundQueue::~BattleGroundQueue()
{
m_QueuedPlayers.clear();
@@ -73,15 +79,18 @@ BattleGroundQueue::~BattleGroundQueue()
}
}
}
+
/*********************************************************/
/*** BATTLEGROUND QUEUE SELECTION POOLS ***/
/*********************************************************/
+
// selection pool initialization, used to clean up from prev selection
void BattleGroundQueue::SelectionPool::Init()
{
SelectedGroups.clear();
PlayerCount = 0;
}
+
// remove group info from selection pool
// returns true when we need to try to add new group to selection pool
// returns false when selection pool is ok or when we kicked smaller group than we need to kick
@@ -114,6 +123,7 @@ bool BattleGroundQueue::SelectionPool::KickGroup(uint32 size)
}
return true;
}
+
// add group to selection pool
// used when building selection pools
// returns true if we can invite more players, or when we added group to selection pool
@@ -132,13 +142,16 @@ bool BattleGroundQueue::SelectionPool::AddGroup(GroupQueueInfo *ginfo, uint32 de
return true;
return false;
}
+
/*********************************************************/
/*** BATTLEGROUND QUEUES ***/
/*********************************************************/
+
// add group to bg queue with the given leader and bg specifications
GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId BgTypeId, uint8 ArenaType, bool isRated, bool isPremade, uint32 arenaRating, uint32 arenateamid)
{
BGQueueIdBasedOnLevel queue_id = leader->GetBattleGroundQueueIdFromLevel(BgTypeId);
+
// create new ginfo
// cannot use the method like in addplayer, because that could modify an in-queue group's stats
// (e.g. leader leaving queue then joining as individual again)
@@ -153,7 +166,9 @@ GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId
ginfo->Team = leader->GetTeam();
ginfo->ArenaTeamRating = arenaRating;
ginfo->OpponentsTeamRating = 0;
+
ginfo->Players.clear();
+
//compute index (if group is premade or joined a rated match) to queues
uint32 index = 0;
if (!isRated && !isPremade)
@@ -161,10 +176,13 @@ GroupQueueInfo * BattleGroundQueue::AddGroup(Player *leader, BattleGroundTypeId
if (ginfo->Team == HORDE)
index++;
sLog.outDebug("Adding Group to BattleGroundQueue bgTypeId : %u, queue_id : %u, index : %u", BgTypeId, queue_id, index);
+
m_QueuedGroups[queue_id][index].push_back(ginfo);
+
// return ginfo, because it is needed to add players to this group info
return ginfo;
}
+
//add player to playermap
void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo)
{
@@ -172,9 +190,11 @@ void BattleGroundQueue::AddPlayer(Player *plr, GroupQueueInfo *ginfo)
PlayerQueueInfo& info = m_QueuedPlayers[plr->GetGUID()];
info.LastOnlineTime = getMSTime();
info.GroupInfo = ginfo;
+
// add the pinfo to ginfo's list
ginfo->Players[plr->GetGUID()] = &info;
}
+
void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id)
{
uint32 timeInQueue = getMSTimeDiff(ginfo->JoinTime, getMSTime());
@@ -189,6 +209,7 @@ void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* g
if (ginfo->IsRated)
team_index = BG_TEAM_HORDE; //for rated arenas use BG_TEAM_HORDE
}
+
//store pointer to arrayindex of player that was added first
uint32* lastPlayerAddedPointer = &(m_WaitTimeLastPlayer[team_index][queue_id]);
//remove his time from sum
@@ -201,6 +222,7 @@ void BattleGroundQueue::PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* g
(*lastPlayerAddedPointer)++;
(*lastPlayerAddedPointer) %= COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME;
}
+
uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id)
{
uint8 team_index = BG_TEAM_ALLIANCE; //default set to BG_TEAM_ALLIANCE - or non rated arenas!
@@ -221,12 +243,15 @@ uint32 BattleGroundQueue::GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BGQueue
//if there aren't enough values return 0 - not available
return 0;
}
+
//remove player from queue and from group info, if group info is empty then remove it too
void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCount)
{
//Player *plr = objmgr.GetPlayer(guid);
+
int32 queue_id = -1; // signed for proper for-loop finish
QueuedPlayersMap::iterator itr;
+
//remove player from map, if he's there
itr = m_QueuedPlayers.find(guid);
if (itr == m_QueuedPlayers.end())
@@ -234,12 +259,14 @@ void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCou
sLog.outError("BattleGroundQueue: couldn't find player to remove GUID: %u", GUID_LOPART(guid));
return;
}
+
GroupQueueInfo* group = itr->second.GroupInfo;
GroupsQueueType::iterator group_itr, group_itr_tmp;
// mostly people with the highest levels are in battlegrounds, thats why
// we count from MAX_BATTLEGROUND_QUEUES - 1 to 0
// variable index removes useless searching in other team's queue
uint32 index = (group->Team == HORDE) ? BG_TEAM_HORDE : BG_TEAM_ALLIANCE;
+
for (int32 queue_id_tmp = MAX_BATTLEGROUND_QUEUES - 1; queue_id_tmp >= 0 && queue_id == -1; --queue_id_tmp)
{
//we must check premade and normal team's queue - because when players from premade are joining bg,
@@ -266,14 +293,17 @@ void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCou
return;
}
sLog.outDebug("BattleGroundQueue: Removing player GUID %u, from queue_id %u", GUID_LOPART(guid), (uint32)queue_id);
+
// ALL variables are correctly set
// We can ignore leveling up in queue - it should not cause crash
// remove player from group
// if only one player there, remove group
+
// remove player queue info from group queue info
std::map<uint64, PlayerQueueInfo*>::iterator pitr = group->Players.find(guid);
if (pitr != group->Players.end())
group->Players.erase(pitr);
+
// if invited to bg, and should decrease invited count, then do it
if (decreaseInvitedCount && group->IsInvitedToBGInstanceGUID)
{
@@ -281,11 +311,14 @@ void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCou
if (bg)
bg->DecreaseInvitedCount(group->Team);
}
+
// remove player queue info
m_QueuedPlayers.erase(itr);
+
//if we left BG queue(not porting) OR if arena team left queue for rated match
if ((decreaseInvitedCount && !group->ArenaType) || (group->ArenaType && group->IsRated && group->Players.empty()))
AnnounceWorld(group, guid, false);
+
//if player leaves queue and he is invited to rated arena match, then he have to loose
if (group->IsInvitedToBGInstanceGUID && group->IsRated && decreaseInvitedCount)
{
@@ -301,6 +334,7 @@ void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCou
at->SaveToDB();
}
}
+
// remove group queue info if needed
if (group->Players.empty())
{
@@ -329,6 +363,7 @@ void BattleGroundQueue::RemovePlayer(const uint64& guid, bool decreaseInvitedCou
RemovePlayer(group->Players.begin()->first, decreaseInvitedCount);
}
}
+
//Announce world message
void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue)
{
@@ -339,6 +374,7 @@ void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playe
BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId);
if (!bg)
return;
+
char const* bgName = bg->GetName();
if (isAddedToQueue)
{
@@ -370,6 +406,7 @@ void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playe
BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(ginfo->BgTypeId);
if (!bg || !plr)
return;
+
BGQueueIdBasedOnLevel queue_id = plr->GetBattleGroundQueueIdFromLevel(bg->GetTypeID());
char const* bgName = bg->GetName();
uint32 MinPlayers = bg->GetMinPlayersPerTeam();
@@ -384,6 +421,7 @@ void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playe
for(itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].begin(); itr != m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].end(); ++itr)
if (!(*itr)->IsInvitedToBGInstanceGUID)
qHorde += (*itr)->Players.size();
+
// Show queue status to player only (when joining queue)
if (sWorld.getConfig(CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY))
{
@@ -399,11 +437,13 @@ void BattleGroundQueue::AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playe
}
}
}
+
bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side)
{
// set side if needed
if (side)
ginfo->Team = side;
+
if (!ginfo->IsInvitedToBGInstanceGUID)
{
// not yet invited
@@ -412,10 +452,13 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b
BattleGroundTypeId bgTypeId = bg->GetTypeID();
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bgTypeId, bg->GetArenaType());
BGQueueIdBasedOnLevel queue_id = bg->GetQueueId();
+
// set ArenaTeamId for rated matches
if (bg->isArena() && bg->isRated())
bg->SetArenaTeamIdForTeam(ginfo->Team, ginfo->ArenaTeamId);
+
ginfo->RemoveInviteTime = getMSTime() + INVITE_ACCEPT_WAIT_TIME;
+
// loop through the players
for(std::map<uint64,PlayerQueueInfo*>::iterator itr = ginfo->Players.begin(); itr != ginfo->Players.end(); ++itr)
{
@@ -424,29 +467,39 @@ bool BattleGroundQueue::InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * b
// if offline, skip him, this should not happen - player is removed from queue when he logs out
if (!plr)
continue;
+
// invite the player
PlayerInvitedToBGUpdateAverageWaitTime(ginfo, queue_id);
//sBattleGroundMgr.InvitePlayer(plr, bg, ginfo->Team);
+
// set invited player counters
bg->IncreaseInvitedCount(ginfo->Team);
+
plr->SetInviteForBattleGroundQueueType(bgQueueTypeId, ginfo->IsInvitedToBGInstanceGUID);
+
// create remind invite events
BGQueueInviteEvent* inviteEvent = new BGQueueInviteEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, ginfo->RemoveInviteTime);
plr->m_Events.AddEvent(inviteEvent, plr->m_Events.CalculateTime(INVITATION_REMIND_TIME));
// create automatic remove events
BGQueueRemoveEvent* removeEvent = new BGQueueRemoveEvent(plr->GetGUID(), ginfo->IsInvitedToBGInstanceGUID, bgTypeId, bgQueueTypeId, ginfo->RemoveInviteTime);
plr->m_Events.AddEvent(removeEvent, plr->m_Events.CalculateTime(INVITE_ACCEPT_WAIT_TIME));
+
WorldPacket data;
+
uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
+
sLog.outDebug("Battleground: invited plr %s (%u) to BG instance %u queueindex %u bgtype %u, I can't help it if they don't press the enter battle button.",plr->GetName(),plr->GetGUIDLow(),bg->GetInstanceID(),queueSlot,bg->GetTypeID());
+
// send status packet
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_WAIT_JOIN, INVITE_ACCEPT_WAIT_TIME, 0, ginfo->ArenaType);
plr->GetSession()->SendPacket(&data);
}
return true;
}
+
return false;
}
+
/*
This function is inviting players to already running battlegrounds
Invitation type is based on config file
@@ -456,6 +509,7 @@ void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel
{
int32 hordeFree = bg->GetFreeSlotsForTeam(HORDE);
int32 aliFree = bg->GetFreeSlotsForTeam(ALLIANCE);
+
//iterator for iterating through bg queue
GroupsQueueType::const_iterator Ali_itr = m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].begin();
//count of groups in queue - used to stop cycles
@@ -470,9 +524,11 @@ void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel
uint32 hordeIndex = 0;
for (; hordeIndex < hordeCount && m_SelectionPools[BG_TEAM_HORDE].AddGroup((*Horde_itr), hordeFree); hordeIndex++)
++Horde_itr;
+
//if ofc like BG queue invitation is set in config, then we are happy
if (sWorld.getConfig(CONFIG_BATTLEGROUND_INVITATION_TYPE) == 0)
return;
+
/*
if we reached this code, then we have to solve NP - complete problem called Subset sum problem
So one solution is to check all possible invitation subgroups, or we can use these conditions:
@@ -480,6 +536,7 @@ void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel
that we will invite now whole queue, because only 1 change has been made to queues from the last BattleGroundQueue::Update call
2. Other thing we should consider is group order in queue
*/
+
// At first we need to compare free space in bg and our selection pool
int32 diffAli = aliFree - int32(m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount());
int32 diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount());
@@ -522,6 +579,7 @@ void BattleGroundQueue::FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel
diffHorde = hordeFree - int32(m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount());
}
}
+
// this method checks if premade versus premade battleground is possible
// then after 30 mins (default) in queue it moves premade group to normal queue
// it tries to invite as much players as it can - to MaxPlayersPerTeam, because premade groups have more than MinPlayersPerTeam players
@@ -539,6 +597,7 @@ bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32
for( horde_group = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin(); horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end(); ++horde_group)
if (!(*horde_group)->IsInvitedToBGInstanceGUID)
break;
+
if (ali_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].end() && horde_group != m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].end())
{
m_SelectionPools[BG_TEAM_ALLIANCE].AddGroup((*ali_group), MaxPlayersPerTeam);
@@ -580,6 +639,7 @@ bool BattleGroundQueue::CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32
//selection pools are not set
return false;
}
+
// this method tries to create battleground or arena with MinPlayersPerTeam against MinPlayersPerTeam
bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers)
{
@@ -622,6 +682,7 @@ bool BattleGroundQueue::CheckNormalMatch(BattleGround* bg_template, BGQueueIdBas
//return true if there are enough players in selection pools - enable to work .debug bg command correctly
return m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() >= minPlayers && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount() >= minPlayers;
}
+
// this method will check if we can invite players to same faction skirmish match
bool BattleGroundQueue::CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_id, uint32 minPlayersPerTeam)
{
@@ -658,6 +719,7 @@ bool BattleGroundQueue::CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_
}
if (m_SelectionPools[otherTeam].GetPlayerCount() != minPlayersPerTeam)
return false;
+
//here we have correct 2 selections and we need to change one teams team and move selection pool teams to other team's queue
for(GroupsQueueType::iterator itr = m_SelectionPools[otherTeam].SelectedGroups.begin(); itr != m_SelectionPools[otherTeam].SelectedGroups.end(); ++itr)
{
@@ -679,6 +741,7 @@ bool BattleGroundQueue::CheckSkirmishForSameFaction(BGQueueIdBasedOnLevel queue_
}
return true;
}
+
/*
this method is called when group is inserted, or player / group is removed from BG Queue - there is only one player's status changed, so we don't use while(true) cycles to invite whole queue
it must be called after fully adding the members of a group to ensure group joining
@@ -692,6 +755,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_ALLIANCE].empty() &&
m_QueuedGroups[queue_id][BG_QUEUE_NORMAL_HORDE].empty() )
return;
+
//battleground with free slot for player should be always in the beggining of the queue
// maybe it would be better to create bgfreeslotqueue for each queue_id_based_on_level
BGFreeSlotQueueType::iterator itr, next;
@@ -705,16 +769,20 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
{
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
+
// clear selection pools
m_SelectionPools[BG_TEAM_ALLIANCE].Init();
m_SelectionPools[BG_TEAM_HORDE].Init();
+
// call a function that does the job for us
FillPlayersToBG(bg, queue_id);
+
// now everything is set, invite players
for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg, (*citr)->Team);
for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_HORDE].SelectedGroups.end(); ++citr)
InviteGroupToBG((*citr), bg, (*citr)->Team);
+
if (!bg->HasFreeSlots())
{
// remove BG from BGFreeSlotQueue
@@ -722,7 +790,9 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
}
}
}
+
// finished iterating through the bgs with free slots, maybe we need to create a new bg
+
BattleGround * bg_template = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
if (!bg_template)
{
@@ -763,8 +833,10 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
}*/
}
}
+
m_SelectionPools[BG_TEAM_ALLIANCE].Init();
m_SelectionPools[BG_TEAM_HORDE].Init();
+
if (bg_template->isBattleGround())
{
//check if there is premade against premade match
@@ -788,6 +860,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
m_SelectionPools[BG_TEAM_HORDE].Init();
}
}
+
// now check if there are in queues enough players to start new game of (normal battleground, or non-rated arena)
if (!isRated)
{
@@ -802,6 +875,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
sLog.outError("BattleGroundQueue::Update - Cannot create battleground: %u", bgTypeId);
return;
}
+
// invite those selection pools
for(uint32 i = 0; i < BG_TEAMS_COUNT; i++)
for(GroupsQueueType::const_iterator citr = m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.begin(); citr != m_SelectionPools[BG_TEAM_ALLIANCE + i].SelectedGroups.end(); ++citr)
@@ -837,6 +911,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
else if (!front1 && !front2)
return; //queues are empty
}
+
//set rating range
uint32 arenaMinRating = (arenaRating <= sBattleGroundMgr.GetMaxRatingDifference()) ? 0 : arenaRating - sBattleGroundMgr.GetMaxRatingDifference();
uint32 arenaMaxRating = arenaRating + sBattleGroundMgr.GetMaxRatingDifference();
@@ -845,9 +920,13 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
// the discard time is current_time - time_to_discard, teams that joined after that, will have their ratings taken into account
// else leave the discard time on 0, this way all ratings will be discarded
uint32 discardTime = getMSTime() - sBattleGroundMgr.GetRatingDiscardTimer();
+
// we need to find 2 teams which will play next game
+
GroupsQueueType::iterator itr_team[BG_TEAMS_COUNT];
+
//optimalization : --- we dont need to use selection_pools - each update we select max 2 groups
+
for(uint32 i = BG_QUEUE_PREMADE_ALLIANCE; i < BG_QUEUE_NORMAL_ALLIANCE; i++)
{
// take the group that joined first
@@ -900,6 +979,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
}
}
}
+
//if we have 2 teams, then start new arena and invite players!
if (m_SelectionPools[BG_TEAM_ALLIANCE].GetPlayerCount() && m_SelectionPools[BG_TEAM_HORDE].GetPlayerCount())
{
@@ -909,6 +989,7 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
sLog.outError("BattlegroundQueue::Update couldn't create arena instance for rated arena match!");
return;
}
+
(*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_HORDE]))->ArenaTeamRating;
sLog.outDebug("setting oposite teamrating for team %u to %u", (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamId, (*(itr_team[BG_TEAM_ALLIANCE]))->OpponentsTeamRating);
(*(itr_team[BG_TEAM_HORDE]))->OpponentsTeamRating = (*(itr_team[BG_TEAM_ALLIANCE]))->ArenaTeamRating;
@@ -928,26 +1009,33 @@ void BattleGroundQueue::Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLeve
m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_ALLIANCE].erase(itr_team[BG_TEAM_HORDE]);
itr_team[BG_TEAM_HORDE] = m_QueuedGroups[queue_id][BG_QUEUE_PREMADE_HORDE].begin();
}
+
InviteGroupToBG(*(itr_team[BG_TEAM_ALLIANCE]), arena, ALLIANCE);
InviteGroupToBG(*(itr_team[BG_TEAM_HORDE]), arena, HORDE);
+
sLog.outDebug("Starting rated arena match!");
+
arena->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;
+
BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId);
//if battleground ended and its instance deleted - do nothing
if (!bg)
return true;
+
BattleGroundQueueTypeId bgQueueTypeId = BattleGroundMgr::BGQueueTypeId(bg->GetTypeID(), bg->GetArenaType());
uint32 queueSlot = plr->GetBattleGroundQueueIndex(bgQueueTypeId);
if( queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES ) // player is in queue or in battleground
@@ -966,10 +1054,12 @@ bool BGQueueInviteEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
}
return true; //event will be deleted
}
+
void BGQueueInviteEvent::Abort(uint64 /*e_time*/)
{
//do nothing
}
+
/*
this event has many possibilities when it is executed:
1. player is in battleground ( he clicked enter on invitation window )
@@ -985,9 +1075,11 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
if (!plr)
// player logged off (we should do nothing, he is correctly removed from queue in another procedure)
return true;
+
BattleGround* bg = sBattleGroundMgr.GetBattleGround(m_BgInstanceGUID, m_BgTypeId);
//battleground can be deleted already when we are removing queue info
//bg pointer can be NULL! so use it carefully!
+
uint32 queueSlot = plr->GetBattleGroundQueueIndex(m_BgQueueTypeId);
if( queueSlot < PLAYER_MAX_BATTLEGROUND_QUEUES ) // player is in queue, or in Battleground
{
@@ -999,26 +1091,32 @@ bool BGQueueRemoveEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
&& qMapItr->second.GroupInfo->RemoveInviteTime == m_RemoveTime )
{
sLog.outDebug("Battleground: removing player %u from bg queue for instance %u because of not pressing enter battle in time.",plr->GetGUIDLow(),m_BgInstanceGUID);
+
plr->RemoveBattleGroundQueueId(m_BgQueueTypeId);
sBattleGroundMgr.m_BattleGroundQueues[m_BgQueueTypeId].RemovePlayer(m_PlayerGuid, true);
//update queues if battleground isn't ended
if (bg)
sBattleGroundMgr.ScheduleQueueUpdate(m_BgQueueTypeId, m_BgTypeId, bg->GetQueueId());
+
WorldPacket data;
sBattleGroundMgr.BuildBattleGroundStatusPacket(&data, bg, queueSlot, STATUS_NONE, 0, 0, 0);
plr->GetSession()->SendPacket(&data);
}
}
+
//event will be deleted
return true;
}
+
void BGQueueRemoveEvent::Abort(uint64 /*e_time*/)
{
//do nothing
}
+
/*********************************************************/
/*** BATTLEGROUND MANAGER ***/
/*********************************************************/
+
BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTesting(false)
{
for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++)
@@ -1026,10 +1124,12 @@ BattleGroundMgr::BattleGroundMgr() : m_AutoDistributionTimeChecker(0), m_ArenaTe
m_NextRatingDiscardUpdate = sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
m_Testing=false;
}
+
BattleGroundMgr::~BattleGroundMgr()
{
DeleteAllBattleGrounds();
}
+
void BattleGroundMgr::DeleteAllBattleGrounds()
{
for(uint32 i = BATTLEGROUND_TYPE_NONE; i < MAX_BATTLEGROUND_TYPE_ID; i++)
@@ -1043,6 +1143,7 @@ void BattleGroundMgr::DeleteAllBattleGrounds()
delete bg;
}
}
+
// destroy template battlegrounds that listed only in queues (other already terminated)
for(uint32 bgTypeId = 0; bgTypeId < MAX_BATTLEGROUND_TYPE_ID; ++bgTypeId)
{
@@ -1051,6 +1152,7 @@ void BattleGroundMgr::DeleteAllBattleGrounds()
delete BGFreeSlotQueue[bgTypeId].front();
}
}
+
// used to update running battlegrounds, and delete finished ones
void BattleGroundMgr::Update(uint32 diff)
{
@@ -1078,6 +1180,7 @@ void BattleGroundMgr::Update(uint32 diff)
}
}
}
+
// update scheduled queues
if (!m_QueueUpdateScheduler.empty())
{
@@ -1095,6 +1198,7 @@ void BattleGroundMgr::Update(uint32 diff)
m_BattleGroundQueues[bgQueueTypeId].Update(bgTypeId, queue_id);
}
}
+
// if rating difference counts, maybe force-update queues
if (sWorld.getConfig(CONFIG_ARENA_MAX_RATING_DIFFERENCE) && sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER))
{
@@ -1130,9 +1234,11 @@ void BattleGroundMgr::Update(uint32 diff)
m_AutoDistributionTimeChecker -= diff;
}
}
+
void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype)
{
// we can be in 3 queues in same time...
+
if (StatusID == 0 || !bg)
{
data->Initialize(SMSG_BATTLEFIELD_STATUS, 4*3);
@@ -1140,6 +1246,7 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro
*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
@@ -1188,12 +1295,15 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro
*data << uint8(0);
break;
}
+
if (bg->isArena() && (StatusID == STATUS_WAIT_QUEUE))
*data << uint32(BATTLEGROUND_AA); // all arenas I don't think so.
else
*data << uint32(bg->GetTypeID()); // BG id from DBC
+
*data << uint16(0x1F90); // unk value 8080
*data << uint32(bg->GetInstanceID()); // instance id
+
*data << uint8(bg->isArena()); // minimap-icon 0=faction 1=arena
*/
*data << uint32(StatusID); // status
@@ -1218,12 +1328,14 @@ void BattleGroundMgr::BuildBattleGroundStatusPacket(WorldPacket *data, BattleGro
break;
}
}
+
void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
{
uint8 type = (bg->isArena() ? 1 : 0);
// last check on 3.0.3
data->Initialize(MSG_PVP_LOG_DATA, (1+1+4+40*bg->GetPlayerScoresSize()));
*data << uint8(type); // type (battleground=0/arena=1)
+
if(type) // arena
{
// it seems this must be according to BG_WINNER_A/H and _NOT_ BG_TEAM_A/H
@@ -1244,6 +1356,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
*data << (uint8)0;
}
}
+
if (bg->GetStatus() != STATUS_WAIT_LEAVE)
{
*data << uint8(0); // bg not ended
@@ -1253,7 +1366,9 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
*data << uint8(1); // bg ended
*data << uint8(bg->GetWinner()); // who win
}
+
*data << (int32)(bg->GetPlayerScoresSize());
+
for(BattleGround::BattleGroundScoreMap::const_iterator itr = bg->GetPlayerScoresBegin(); itr != bg->GetPlayerScoresEnd(); ++itr)
{
*data << (uint64)itr->first;
@@ -1317,6 +1432,7 @@ void BattleGroundMgr::BuildPvpLogDataPacket(WorldPacket *data, BattleGround *bg)
}
}
}
+
void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, BattleGroundTypeId bgTypeId)
{
/*bgTypeId is:
@@ -1331,27 +1447,32 @@ void BattleGroundMgr::BuildGroupJoinedBattlegroundPacket(WorldPacket *data, Batt
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, const uint64& guid)
{
data->Initialize(SMSG_BATTLEGROUND_PLAYER_LEFT, 8);
*data << uint64(guid);
}
+
void BattleGroundMgr::BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr)
{
data->Initialize(SMSG_BATTLEGROUND_PLAYER_JOINED, 8);
*data << uint64(plr->GetGUID());
}
+
BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId)
{
//cause at HandleBattleGroundJoinOpcode the clients sends the instanceid he gets from
@@ -1359,8 +1480,10 @@ BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 inst
BattleGround* bg = GetBattleGroundTemplate(bgTypeId);
if (!bg)
return NULL;
+
if (bg->isArena())
return GetBattleGround(instanceId, bgTypeId);
+
for(BattleGroundSet::iterator itr = m_BattleGrounds[bgTypeId].begin(); itr != m_BattleGrounds[bgTypeId].end(); ++itr)
{
if (itr->second->GetClientInstanceID() == instanceId)
@@ -1368,6 +1491,7 @@ BattleGround * BattleGroundMgr::GetBattleGroundThroughClientInstance(uint32 inst
}
return NULL;
}
+
BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId)
{
if (!InstanceID)
@@ -1387,15 +1511,18 @@ BattleGround * BattleGroundMgr::GetBattleGround(uint32 InstanceID, BattleGroundT
itr = m_BattleGrounds[bgTypeId].find(InstanceID);
return ( (itr != m_BattleGrounds[bgTypeId].end()) ? itr->second : NULL );
}
+
BattleGround * BattleGroundMgr::GetBattleGroundTemplate(BattleGroundTypeId bgTypeId)
{
//map is sorted and we can be sure that lowest instance id has only BG template
return m_BattleGrounds[bgTypeId].empty() ? NULL : m_BattleGrounds[bgTypeId].begin()->second;
}
+
uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id)
{
if (IsArenaType(bgTypeId))
return 0; //arenas don't have client-instanceids
+
// we create here an instanceid, which is just for
// displaying this to the client and without any other use..
// the client-instanceIds are unique for each battleground-type
@@ -1412,6 +1539,7 @@ uint32 BattleGroundMgr::CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeI
m_ClientBattleGroundIds[bgTypeId][queue_id].insert(lastId + 1);
return lastId + 1;
}
+
// create a new battleground that will really be used to play
BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated)
{
@@ -1422,6 +1550,7 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI
sLog.outError("BattleGround: CreateNewBattleGround - bg template not found for %u", bgTypeId);
return NULL;
}
+
//for arenas there is random map used
if (bg_template->isArena())
{
@@ -1435,6 +1564,7 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI
return NULL;
}
}
+
BattleGround *bg = NULL;
// create a copy of the BG template
switch(bgTypeId)
@@ -1476,18 +1606,23 @@ BattleGround * BattleGroundMgr::CreateNewBattleGround(BattleGroundTypeId bgTypeI
//error, but it is handled few lines above
return 0;
}
+
// generate a new instance id
bg->SetInstanceID(MapManager::Instance().GenerateInstanceId()); // set instance id
bg->SetClientInstanceID(CreateClientVisibleInstanceId(bgTypeId, queue_id));
+
// reset the new bg (set status to status_wait_queue from status_none)
bg->Reset();
+
// start the joining of the bg
bg->SetStatus(STATUS_WAIT_JOIN);
bg->SetQueueId(queue_id);
bg->SetArenaType(arenaType);
bg->SetRated(isRated);
+
return bg;
}
+
// used to create the BG templates
uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, 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)
{
@@ -1508,6 +1643,7 @@ uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsA
case BATTLEGROUND_RV: bg = new BattleGroundRV; break;
default:bg = new BattleGround; break; // placeholder for non implemented BG
}
+
bg->SetMapId(MapID);
bg->SetTypeID(bgTypeId);
bg->SetInstanceID(0);
@@ -1520,11 +1656,14 @@ uint32 BattleGroundMgr::CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsA
bg->SetTeamStartLoc(ALLIANCE, Team1StartLocX, Team1StartLocY, Team1StartLocZ, Team1StartLocO);
bg->SetTeamStartLoc(HORDE, Team2StartLocX, Team2StartLocY, Team2StartLocZ, Team2StartLocO);
bg->SetLevelRange(LevelMin, LevelMax);
+
// add bg to update list
AddBattleGround(bg->GetInstanceID(), bg->GetTypeID(), bg);
+
// return some not-null value, bgTypeId is good enough for me
return bgTypeId;
}
+
void BattleGroundMgr::CreateInitialBattleGrounds()
{
float AStartLoc[4];
@@ -1533,23 +1672,32 @@ void BattleGroundMgr::CreateInitialBattleGrounds()
BattlemasterListEntry const *bl;
WorldSafeLocsEntry const *start;
bool IsArena;
+
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 overwrite by values from DB
bl = sBattlemasterListStore.LookupEntry(bgTypeID_);
if (!bl)
@@ -1557,7 +1705,9 @@ void BattleGroundMgr::CreateInitialBattleGrounds()
sLog.outError("Battleground ID %u not found in BattlemasterList.dbc. Battleground not created.", bgTypeID_);
continue;
}
+
BattleGroundTypeId bgTypeID = BattleGroundTypeId(bgTypeID_);
+
IsArena = (bl->type == TYPE_ARENA);
MinPlayersPerTeam = fields[1].GetUInt32();
MaxPlayersPerTeam = fields[2].GetUInt32();
@@ -1574,7 +1724,9 @@ void BattleGroundMgr::CreateInitialBattleGrounds()
MinLvl = bl->minlvl;
MaxLvl = bl->maxlvl;
}
+
start1 = fields[5].GetUInt32();
+
start = sWorldSafeLocsStore.LookupEntry(start1);
if (start)
{
@@ -1595,7 +1747,9 @@ void BattleGroundMgr::CreateInitialBattleGrounds()
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)
{
@@ -1616,15 +1770,20 @@ void BattleGroundMgr::CreateInitialBattleGrounds()
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, IsArena, 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::InitAutomaticArenaPointDistribution()
{
if (sWorld.getConfig(CONFIG_ARENA_AUTO_DISTRIBUTE_POINTS))
@@ -1645,17 +1804,22 @@ void BattleGroundMgr::InitAutomaticArenaPointDistribution()
sLog.outDebug("Automatic Arena Point Distribution initialized.");
}
}
+
void BattleGroundMgr::DistributeArenaPoints()
{
// used to distribute arena points based on last week's stats
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_START);
+
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_START);
+
//temporary structure for storing maximum points to add values for all players
std::map<uint32, uint32> PlayerPoints;
+
//at first update all points for all team members
for(ObjectMgr::ArenaTeamMap::iterator team_itr = objmgr.GetArenaTeamMapBegin(); team_itr != objmgr.GetArenaTeamMapEnd(); ++team_itr)
if (ArenaTeam * at = team_itr->second)
at->UpdateArenaPointsHelper(PlayerPoints);
+
//cycle that gives points to all players
for (std::map<uint32, uint32>::iterator plr_itr = PlayerPoints.begin(); plr_itr != PlayerPoints.end(); ++plr_itr)
{
@@ -1673,8 +1837,11 @@ void BattleGroundMgr::DistributeArenaPoints()
std::min(int32(plr_itr->second),int32(sWorld.getConfig(CONFIG_MAX_ARENA_POINTS))));
}
}
+
PlayerPoints.clear();
+
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_ONLINE_END);
+
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_START);
for(ObjectMgr::ArenaTeamMap::iterator titr = objmgr.GetArenaTeamMapBegin(); titr != objmgr.GetArenaTeamMapEnd(); ++titr)
{
@@ -1685,15 +1852,20 @@ void BattleGroundMgr::DistributeArenaPoints()
at->NotifyStatsChanged(); // notify the players of the changes
}
}
+
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_TEAM_END);
+
sWorld.SendWorldText(LANG_DIST_ARENA_POINTS_END);
}
+
void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint64& guid, Player* plr, BattleGroundTypeId bgTypeId, uint8 fromWhere)
{
if (!plr)
return;
+
uint32 PlayerLevel = 10;
PlayerLevel = plr->getLevel();
+
data->Initialize(SMSG_BATTLEFIELD_LIST);
*data << uint64(guid); // battlemaster guid
*data << uint8(fromWhere); // from where you joined
@@ -1706,9 +1878,11 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint6
else // battleground
{
*data << uint8(0x00); // unk, different for each bg type
+
size_t count_pos = data->wpos();
uint32 count = 0;
*data << uint32(0x00); // number of bg instances
+
uint32 queue_id = plr->GetBattleGroundQueueIdFromLevel(bgTypeId);
for(std::set<uint32>::iterator itr = m_ClientBattleGroundIds[bgTypeId][queue_id].begin(); itr != m_ClientBattleGroundIds[bgTypeId][queue_id].end();++itr)
{
@@ -1718,6 +1892,7 @@ void BattleGroundMgr::BuildBattleGroundListPacket(WorldPacket *data, const uint6
data->put<uint32>( count_pos , count);
}
}
+
void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGroundTypeId bgTypeId)
{
BattleGround *bg = GetBattleGround(instanceId, bgTypeId);
@@ -1729,6 +1904,7 @@ void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGr
if (team==0)
team = pl->GetTeam();
bg->GetTeamStartLoc(team, 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);
}
@@ -1737,6 +1913,7 @@ void BattleGroundMgr::SendToBattleGround(Player *pl, uint32 instanceId, BattleGr
sLog.outError("player %u trying to port to non-existent bg instance %u",pl->GetGUIDLow(), instanceId);
}
}
+
void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid)
{
WorldPacket data(SMSG_AREA_SPIRIT_HEALER_TIME, 12);
@@ -1746,6 +1923,7 @@ void BattleGroundMgr::SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *
data << guid << time_;
pl->GetSession()->SendPacket(&data);
}
+
bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId)
{
return ( bgTypeId == BATTLEGROUND_AA ||
@@ -1753,6 +1931,7 @@ bool BattleGroundMgr::IsArenaType(BattleGroundTypeId bgTypeId)
bgTypeId == BATTLEGROUND_NA ||
bgTypeId == BATTLEGROUND_RL );
}
+
BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType)
{
switch(bgTypeId)
@@ -1788,6 +1967,7 @@ BattleGroundQueueTypeId BattleGroundMgr::BGQueueTypeId(BattleGroundTypeId bgType
return BATTLEGROUND_QUEUE_NONE;
}
}
+
BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId)
{
switch(bgQueueTypeId)
@@ -1810,6 +1990,7 @@ BattleGroundTypeId BattleGroundMgr::BGTemplateId(BattleGroundQueueTypeId bgQueue
return BattleGroundTypeId(0); // used for unknown template (it existed and do nothing)
}
}
+
uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId)
{
switch(bgQueueTypeId)
@@ -1824,6 +2005,7 @@ uint8 BattleGroundMgr::BGArenaType(BattleGroundQueueTypeId bgQueueTypeId)
return 0;
}
}
+
void BattleGroundMgr::ToggleTesting()
{
m_Testing = !m_Testing;
@@ -1832,6 +2014,7 @@ void BattleGroundMgr::ToggleTesting()
else
sWorld.SendWorldText(LANG_DEBUG_BG_OFF);
}
+
void BattleGroundMgr::ToggleArenaTesting()
{
m_ArenaTesting = !m_ArenaTesting;
@@ -1840,6 +2023,7 @@ void BattleGroundMgr::ToggleArenaTesting()
else
sWorld.SendWorldText(LANG_DEBUG_ARENA_OFF);
}
+
void BattleGroundMgr::SetHolidayWeekends(uint32 mask)
{
for(uint32 bgtype = 1; bgtype < MAX_BATTLEGROUND_TYPE_ID; ++bgtype)
@@ -1850,6 +2034,7 @@ void BattleGroundMgr::SetHolidayWeekends(uint32 mask)
}
}
}
+
void BattleGroundMgr::ScheduleQueueUpdate(BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id)
{
//This method must be atomic, TODO add mutex
@@ -1867,6 +2052,7 @@ void BattleGroundMgr::ScheduleQueueUpdate(BattleGroundQueueTypeId bgQueueTypeId,
if (!found)
m_QueueUpdateScheduler.push_back(schedule_id);
}
+
uint32 BattleGroundMgr::GetMaxRatingDifference() const
{
// this is for stupid people who can't use brain and set max rating difference to 0
@@ -1875,33 +2061,44 @@ uint32 BattleGroundMgr::GetMaxRatingDifference() const
diff = 5000;
return diff;
}
+
uint32 BattleGroundMgr::GetRatingDiscardTimer() const
{
return sWorld.getConfig(CONFIG_ARENA_RATING_DISCARD_TIMER);
}
+
uint32 BattleGroundMgr::GetPrematureFinishTime() const
{
return sWorld.getConfig(CONFIG_BATTLEGROUND_PREMATURE_FINISH_TIMER);
}
+
void BattleGroundMgr::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();
if (!sBattlemasterListStore.LookupEntry(bgTypeId))
@@ -1909,9 +2106,13 @@ void BattleGroundMgr::LoadBattleMastersEntry()
sLog.outErrorDb("Table `battlemaster_entry` contain entry %u for not existed battleground type %u, ignored.",entry,bgTypeId);
continue;
}
+
mBattleMastersMap[entry] = BattleGroundTypeId(bgTypeId);
+
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u battlemaster entries", count );
}
diff --git a/src/game/BattleGroundMgr.h b/src/game/BattleGroundMgr.h
index 2de067d3639..9100142ebd0 100644
--- a/src/game/BattleGroundMgr.h
+++ b/src/game/BattleGroundMgr.h
@@ -17,23 +17,32 @@
* 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 "Common.h"
#include "Policies/Singleton.h"
+
#include "BattleGround.h"
+
typedef std::map<uint32, BattleGround*> BattleGroundSet;
+
//this container can't be deque, because deque doesn't like removing the last element - if you remove it, it invalidates next iterator and crash appears
typedef std::list<BattleGround*> BGFreeSlotQueueType;
+
typedef UNORDERED_MAP<uint32, BattleGroundTypeId> BattleMastersMap;
+
#define BATTLEGROUND_ARENA_POINT_DISTRIBUTION_DAY 86400 // seconds in a day
#define COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME 10
+
struct GroupQueueInfo; // type predefinition
struct PlayerQueueInfo // stores information for players in queue
{
uint32 LastOnlineTime; // for tracking and removing offline players from queue after 5 minutes
GroupQueueInfo * GroupInfo; // pointer to the associated groupqueueinfo
};
+
struct GroupQueueInfo // stores information about the group in queue (also used when joined as solo!)
{
std::map<uint64, PlayerQueueInfo*> Players; // player queue info map
@@ -48,6 +57,7 @@ struct GroupQueueInfo // stores informatio
uint32 ArenaTeamRating; // if rated match, inited to the rating of the team
uint32 OpponentsTeamRating; // for rated arena matches
};
+
enum BattleGroundQueueGroupTypes
{
BG_QUEUE_PREMADE_ALLIANCE = 0,
@@ -56,13 +66,16 @@ enum BattleGroundQueueGroupTypes
BG_QUEUE_NORMAL_HORDE = 3
};
#define BG_QUEUE_GROUP_TYPES_COUNT 4
+
class BattleGround;
class BattleGroundQueue
{
public:
BattleGroundQueue();
~BattleGroundQueue();
+
void Update(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType = 0, bool isRated = false, uint32 minRating = 0);
+
void FillPlayersToBG(BattleGround* bg, BGQueueIdBasedOnLevel queue_id);
bool CheckPremadeMatch(BGQueueIdBasedOnLevel queue_id, uint32 MinPlayersPerTeam, uint32 MaxPlayersPerTeam);
bool CheckNormalMatch(BattleGround* bg_template, BGQueueIdBasedOnLevel queue_id, uint32 minPlayers, uint32 maxPlayers);
@@ -72,12 +85,16 @@ class BattleGroundQueue
void RemovePlayer(const uint64& guid, bool decreaseInvitedCount);
void PlayerInvitedToBGUpdateAverageWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id);
uint32 GetAverageQueueWaitTime(GroupQueueInfo* ginfo, BGQueueIdBasedOnLevel queue_id);
+
void DecreaseGroupLength(uint32 queueId, uint32 AsGroup);
void AnnounceWorld(GroupQueueInfo *ginfo, const uint64& playerGUID, bool isAddedToQueue);
+
typedef std::map<uint64, PlayerQueueInfo> QueuedPlayersMap;
QueuedPlayersMap m_QueuedPlayers;
+
//we need constant add to begin and constant remove / add from the end, therefore deque suits our problem well
typedef std::list<GroupQueueInfo*> GroupsQueueType;
+
/*
This two dimensional array is used to store All queued groups
First dimension specifies the bgTypeId
@@ -88,6 +105,7 @@ class BattleGroundQueue
BG_QUEUE_NORMAL_HORDE is used for normal (or small) horde groups or non-rated arena matches
*/
GroupsQueueType m_QueuedGroups[MAX_BATTLEGROUND_QUEUES][BG_QUEUE_GROUP_TYPES_COUNT];
+
// class to select and invite groups to bg
class SelectionPool
{
@@ -101,14 +119,18 @@ class BattleGroundQueue
private:
uint32 PlayerCount;
};
+
//one selection pool for horde, other one for alliance
SelectionPool m_SelectionPools[BG_TEAMS_COUNT];
+
private:
+
bool InviteGroupToBG(GroupQueueInfo * ginfo, BattleGround * bg, uint32 side);
uint32 m_WaitTimes[BG_TEAMS_COUNT][MAX_BATTLEGROUND_QUEUES][COUNT_OF_PLAYERS_TO_AVERAGE_WAIT_TIME];
uint32 m_WaitTimeLastPlayer[BG_TEAMS_COUNT][MAX_BATTLEGROUND_QUEUES];
uint32 m_SumOfWaitTimes[BG_TEAMS_COUNT][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
@@ -121,6 +143,7 @@ class BGQueueInviteEvent : public BasicEvent
{
};
virtual ~BGQueueInviteEvent() {};
+
virtual bool Execute(uint64 e_time, uint32 p_time);
virtual void Abort(uint64 e_time);
private:
@@ -129,6 +152,7 @@ class BGQueueInviteEvent : public BasicEvent
BattleGroundTypeId m_BgTypeId;
uint32 m_RemoveTime;
};
+
/*
This class is used to remove player from BG queue after 1 minute 20 seconds from first invitation
We must store removeInvite time in case player left queue and joined and is invited again
@@ -140,7 +164,9 @@ class BGQueueRemoveEvent : public BasicEvent
BGQueueRemoveEvent(const uint64& pl_guid, uint32 bgInstanceGUID, BattleGroundTypeId BgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 removeTime)
: m_PlayerGuid(pl_guid), m_BgInstanceGUID(bgInstanceGUID), m_RemoveTime(removeTime), m_BgTypeId(BgTypeId), m_BgQueueTypeId(bgQueueTypeId)
{}
+
virtual ~BGQueueRemoveEvent() {}
+
virtual bool Execute(uint64 e_time, uint32 p_time);
virtual void Abort(uint64 e_time);
private:
@@ -150,6 +176,7 @@ class BGQueueRemoveEvent : public BasicEvent
BattleGroundTypeId m_BgTypeId;
BattleGroundQueueTypeId m_BgQueueTypeId;
};
+
class BattleGroundMgr
{
public:
@@ -157,6 +184,7 @@ class BattleGroundMgr
BattleGroundMgr();
~BattleGroundMgr();
void Update(uint32 diff);
+
/* Packet Building */
void BuildPlayerJoinedBattleGroundPacket(WorldPacket *data, Player *plr);
void BuildPlayerLeftBattleGroundPacket(WorldPacket *data, const uint64& guid);
@@ -167,30 +195,41 @@ class BattleGroundMgr
void BuildBattleGroundStatusPacket(WorldPacket *data, BattleGround *bg, uint8 QueueSlot, uint8 StatusID, uint32 Time1, uint32 Time2, uint8 arenatype);
void BuildPlaySoundPacket(WorldPacket *data, uint32 soundid);
void SendAreaSpiritHealerQueryOpcode(Player *pl, BattleGround *bg, const uint64& guid);
+
/* Battlegrounds */
BattleGround* GetBattleGroundThroughClientInstance(uint32 instanceId, BattleGroundTypeId bgTypeId);
BattleGround* GetBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId); //there must be uint32 because MAX_BATTLEGROUND_TYPE_ID means unknown
+
BattleGround* GetBattleGroundTemplate(BattleGroundTypeId bgTypeId);
BattleGround* CreateNewBattleGround(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id, uint8 arenaType, bool isRated);
+
uint32 CreateBattleGround(BattleGroundTypeId bgTypeId, bool IsArena, 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);
+
void AddBattleGround(uint32 InstanceID, BattleGroundTypeId bgTypeId, BattleGround* BG) { m_BattleGrounds[bgTypeId][InstanceID] = BG; };
void RemoveBattleGround(uint32 instanceID, BattleGroundTypeId bgTypeId) { m_BattleGrounds[bgTypeId].erase(instanceID); }
uint32 CreateClientVisibleInstanceId(BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id);
+
void CreateInitialBattleGrounds();
void DeleteAllBattleGrounds();
+
void SendToBattleGround(Player *pl, uint32 InstanceID, BattleGroundTypeId bgTypeId);
+
/* Battleground queues */
//these queues are instantiated when creating BattlegroundMrg
BattleGroundQueue m_BattleGroundQueues[MAX_BATTLEGROUND_QUEUE_TYPES]; // public, because we need to access them in BG handler code
+
BGFreeSlotQueueType BGFreeSlotQueue[MAX_BATTLEGROUND_TYPE_ID];
+
void ScheduleQueueUpdate(BattleGroundQueueTypeId bgQueueTypeId, BattleGroundTypeId bgTypeId, BGQueueIdBasedOnLevel queue_id);
uint32 GetMaxRatingDifference() const;
uint32 GetRatingDiscardTimer() const;
uint32 GetPrematureFinishTime() const;
+
void InitAutomaticArenaPointDistribution();
void DistributeArenaPoints();
void ToggleArenaTesting();
void ToggleTesting();
+
void SetHolidayWeekends(uint32 mask);
void LoadBattleMastersEntry();
BattleGroundTypeId GetBattleMasterBG(uint32 entry) const
@@ -200,16 +239,20 @@ class BattleGroundMgr
return itr->second;
return BATTLEGROUND_WS;
}
+
bool isArenaTesting() const { return m_ArenaTesting; }
bool isTesting() const { return m_Testing; }
+
static bool IsArenaType(BattleGroundTypeId bgTypeId);
static bool IsBattleGroundType(BattleGroundTypeId bgTypeId) { return !BattleGroundMgr::IsArenaType(bgTypeId); }
static BattleGroundQueueTypeId BGQueueTypeId(BattleGroundTypeId bgTypeId, uint8 arenaType);
static BattleGroundTypeId BGTemplateId(BattleGroundQueueTypeId bgQueueTypeId);
static uint8 BGArenaType(BattleGroundQueueTypeId bgQueueTypeId);
+
static bool IsBGWeekend(BattleGroundTypeId bgTypeId);
private:
BattleMastersMap mBattleMastersMap;
+
/* Battlegrounds */
BattleGroundSet m_BattleGrounds[MAX_BATTLEGROUND_TYPE_ID];
std::vector<uint32>m_QueueUpdateScheduler;
@@ -220,6 +263,7 @@ class BattleGroundMgr
bool m_ArenaTesting;
bool m_Testing;
};
+
#define sBattleGroundMgr Trinity::Singleton<BattleGroundMgr>::Instance()
#endif
diff --git a/src/game/BattleGroundNA.cpp b/src/game/BattleGroundNA.cpp
index b9f85117f1a..92e71c1c283 100644
--- a/src/game/BattleGroundNA.cpp
+++ b/src/game/BattleGroundNA.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundNA.h"
#include "Language.h"
@@ -24,9 +25,11 @@
#include "ObjectMgr.h"
#include "Player.h"
#include "WorldPacket.h"
+
BattleGroundNA::BattleGroundNA()
{
m_BgObjects.resize(BG_NA_OBJECT_MAX);
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S;
@@ -37,69 +40,90 @@ BattleGroundNA::BattleGroundNA()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN;
}
+
BattleGroundNA::~BattleGroundNA()
{
+
}
+
void BattleGroundNA::Update(uint32 diff)
{
BattleGround::Update(diff);
+
/*if (GetStatus() == STATUS_IN_PROGRESS)
{
// update something
}*/
}
+
void BattleGroundNA::StartingEventCloseDoors()
{
for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_4; ++i)
SpawnBGObject(i, RESPAWN_IMMEDIATELY);
}
+
void BattleGroundNA::StartingEventOpenDoors()
{
for(uint32 i = BG_NA_OBJECT_DOOR_1; i <= BG_NA_OBJECT_DOOR_2; ++i)
DoorOpen(i);
+
for(uint32 i = BG_NA_OBJECT_BUFF_1; i <= BG_NA_OBJECT_BUFF_2; ++i)
SpawnBGObject(i, 60);
}
+
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;
+
UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE));
}
+
void BattleGroundNA::RemovePlayer(Player* /*plr*/, uint64 /*guid*/)
{
if (GetStatus() == STATUS_WAIT_LEAVE)
return;
+
UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE));
+
CheckArenaWinConditions();
}
+
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);
+
UpdateWorldState(0xa0f, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0xa10, GetAlivePlayersCountByTeam(HORDE));
+
CheckArenaWinConditions();
}
+
bool BattleGroundNA::HandlePlayerUnderMap(Player *player)
{
player->TeleportTo(GetMapId(),4055.504395,2919.660645,13.611241,player->GetOrientation(),false);
return true;
}
+
void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger)
{
if (GetStatus() != STATUS_IN_PROGRESS)
return;
+
//uint32 SpellId = 0;
//uint64 buff_guid = 0;
switch(Trigger)
@@ -112,20 +136,24 @@ void BattleGroundNA::HandleAreaTrigger(Player *Source, uint32 Trigger)
Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
break;
}
+
//if (buff_guid)
// HandleTriggerBuff(buff_guid,Source);
}
+
void BattleGroundNA::FillInitialWorldStates(WorldPacket &data)
{
data << uint32(0xa0f) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7
data << uint32(0xa10) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8
data << uint32(0xa11) << uint32(1); // 9
}
+
void BattleGroundNA::Reset()
{
//call parent's class reset
BattleGround::Reset();
}
+
bool BattleGroundNA::SetupBattleGround()
{
// gates
@@ -140,8 +168,10 @@ bool BattleGroundNA::SetupBattleGround()
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...........
diff --git a/src/game/BattleGroundNA.h b/src/game/BattleGroundNA.h
index 00d2e554d7e..56e2cf373c4 100644
--- a/src/game/BattleGroundNA.h
+++ b/src/game/BattleGroundNA.h
@@ -19,7 +19,9 @@
*/
#ifndef __BATTLEGROUNDNA_H
#define __BATTLEGROUNDNA_H
+
class BattleGround;
+
enum BattleGroundNAObjectTypes
{
BG_NA_OBJECT_DOOR_1 = 0,
@@ -30,6 +32,7 @@ enum BattleGroundNAObjectTypes
BG_NA_OBJECT_BUFF_2 = 5,
BG_NA_OBJECT_MAX = 6
};
+
enum BattleGroundNAObjects
{
BG_NA_OBJECT_TYPE_DOOR_1 = 183978,
@@ -39,6 +42,7 @@ enum BattleGroundNAObjects
BG_NA_OBJECT_TYPE_BUFF_1 = 184663,
BG_NA_OBJECT_TYPE_BUFF_2 = 184664
};
+
class BattleGroundNAScore : public BattleGroundScore
{
public:
@@ -46,17 +50,21 @@ class BattleGroundNAScore : public BattleGroundScore
virtual ~BattleGroundNAScore() {};
//TODO fix me
};
+
class BattleGroundNA : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundNA();
~BattleGroundNA();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr, uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
diff --git a/src/game/BattleGroundRL.cpp b/src/game/BattleGroundRL.cpp
index c4ca4b1b66f..b9f9943bbf2 100644
--- a/src/game/BattleGroundRL.cpp
+++ b/src/game/BattleGroundRL.cpp
@@ -18,6 +18,7 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundRL.h"
#include "Language.h"
@@ -25,9 +26,11 @@
#include "ObjectMgr.h"
#include "Player.h"
#include "WorldPacket.h"
+
BattleGroundRL::BattleGroundRL()
{
m_BgObjects.resize(BG_RL_OBJECT_MAX);
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S;
@@ -38,70 +41,91 @@ BattleGroundRL::BattleGroundRL()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN;
}
+
BattleGroundRL::~BattleGroundRL()
{
+
}
+
void BattleGroundRL::Update(uint32 diff)
{
BattleGround::Update(diff);
+
/*if (GetStatus() == STATUS_IN_PROGRESS)
{
// update something
}*/
}
+
void BattleGroundRL::StartingEventCloseDoors()
{
for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; ++i)
SpawnBGObject(i, RESPAWN_IMMEDIATELY);
}
+
void BattleGroundRL::StartingEventOpenDoors()
{
for(uint32 i = BG_RL_OBJECT_DOOR_1; i <= BG_RL_OBJECT_DOOR_2; ++i)
DoorOpen(i);
+
for(uint32 i = BG_RL_OBJECT_BUFF_1; i <= BG_RL_OBJECT_BUFF_2; ++i)
SpawnBGObject(i, 60);
}
+
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;
+
UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE));
}
+
void BattleGroundRL::RemovePlayer(Player* /*plr*/, uint64 /*guid*/)
{
if (GetStatus() == STATUS_WAIT_LEAVE)
return;
+
UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE));
+
CheckArenaWinConditions();
}
+
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);
+
UpdateWorldState(0xbb8, GetAlivePlayersCountByTeam(ALLIANCE));
UpdateWorldState(0xbb9, GetAlivePlayersCountByTeam(HORDE));
+
CheckArenaWinConditions();
}
+
bool BattleGroundRL::HandlePlayerUnderMap(Player *player)
{
player->TeleportTo(GetMapId(),1285.810547,1667.896851,39.957642,player->GetOrientation(),false);
return true;
}
+
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)
@@ -114,20 +138,24 @@ void BattleGroundRL::HandleAreaTrigger(Player *Source, uint32 Trigger)
Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
break;
}
+
//if (buff_guid)
// HandleTriggerBuff(buff_guid,Source);
}
+
void BattleGroundRL::FillInitialWorldStates(WorldPacket &data)
{
data << uint32(0xbb8) << uint32(GetAlivePlayersCountByTeam(ALLIANCE)); // 7
data << uint32(0xbb9) << uint32(GetAlivePlayersCountByTeam(HORDE)); // 8
data << uint32(0xbba) << uint32(1); // 9
}
+
void BattleGroundRL::Reset()
{
//call parent's reset
BattleGround::Reset();
}
+
bool BattleGroundRL::SetupBattleGround()
{
// gates
@@ -140,8 +168,10 @@ bool BattleGroundRL::SetupBattleGround()
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 | <...............
diff --git a/src/game/BattleGroundRL.h b/src/game/BattleGroundRL.h
index 7fb7ef60c2d..772c9dd0879 100644
--- a/src/game/BattleGroundRL.h
+++ b/src/game/BattleGroundRL.h
@@ -19,7 +19,9 @@
*/
#ifndef __BATTLEGROUNDRL_H
#define __BATTLEGROUNDRL_H
+
class BattleGround;
+
enum BattleGroundRLObjectTypes
{
BG_RL_OBJECT_DOOR_1 = 0,
@@ -28,6 +30,7 @@ enum BattleGroundRLObjectTypes
BG_RL_OBJECT_BUFF_2 = 3,
BG_RL_OBJECT_MAX = 4
};
+
enum BattleGroundRLObjects
{
BG_RL_OBJECT_TYPE_DOOR_1 = 185918,
@@ -35,6 +38,7 @@ enum BattleGroundRLObjects
BG_RL_OBJECT_TYPE_BUFF_1 = 184663,
BG_RL_OBJECT_TYPE_BUFF_2 = 184664
};
+
class BattleGroundRLScore : public BattleGroundScore
{
public:
@@ -42,19 +46,23 @@ class BattleGroundRLScore : public BattleGroundScore
virtual ~BattleGroundRLScore() {};
//TODO fix me
};
+
class BattleGroundRL : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundRL();
~BattleGroundRL();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void Reset();
virtual void FillInitialWorldStates(WorldPacket &d);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr, uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
diff --git a/src/game/BattleGroundRV.cpp b/src/game/BattleGroundRV.cpp
index a0fc8377eaf..54070961018 100644
--- a/src/game/BattleGroundRV.cpp
+++ b/src/game/BattleGroundRV.cpp
@@ -15,12 +15,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundRV.h"
#include "Language.h"
#include "Player.h"
+
BattleGroundRV::BattleGroundRV()
{
+
m_StartDelayTimes[BG_STARTING_EVENT_FIRST] = BG_START_DELAY_1M;
m_StartDelayTimes[BG_STARTING_EVENT_SECOND] = BG_START_DELAY_30S;
m_StartDelayTimes[BG_STARTING_EVENT_THIRD] = BG_START_DELAY_15S;
@@ -31,36 +34,47 @@ BattleGroundRV::BattleGroundRV()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_ARENA_FIFTEEN_SECONDS;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_ARENA_HAS_BEGUN;
}
+
BattleGroundRV::~BattleGroundRV()
{
+
}
+
void BattleGroundRV::Update(uint32 diff)
{
BattleGround::Update(diff);
}
+
void BattleGroundRV::StartingEventCloseDoors()
{
}
+
void BattleGroundRV::StartingEventOpenDoors()
{
}
+
void BattleGroundRV::AddPlayer(Player *plr)
{
BattleGround::AddPlayer(plr);
//create score and add it to map, default values are set in constructor
BattleGroundRVScore* sc = new BattleGroundRVScore;
+
m_PlayerScores[plr->GetGUID()] = sc;
}
+
void BattleGroundRV::RemovePlayer(Player * /*plr*/, uint64 /*guid*/)
{
}
+
void BattleGroundRV::HandleKillPlayer(Player* player, Player* killer)
{
BattleGround::HandleKillPlayer(player, killer);
}
+
void BattleGroundRV::HandleAreaTrigger(Player * /*Source*/, uint32 /*Trigger*/)
{
}
+
bool BattleGroundRV::SetupBattleGround()
{
return true;
diff --git a/src/game/BattleGroundRV.h b/src/game/BattleGroundRV.h
index 4cfa8940bc3..e3e94baf101 100644
--- a/src/game/BattleGroundRV.h
+++ b/src/game/BattleGroundRV.h
@@ -17,7 +17,9 @@
*/
#ifndef __BATTLEGROUNDRV_H
#define __BATTLEGROUNDRV_H
+
class BattleGround;
+
class BattleGroundRVScore : public BattleGroundScore
{
public:
@@ -25,17 +27,21 @@ class BattleGroundRVScore : public BattleGroundScore
virtual ~BattleGroundRVScore() {};
//TODO fix me
};
+
class BattleGroundRV : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundRV();
~BattleGroundRV();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
void RemovePlayer(Player *plr, uint64 guid);
void HandleAreaTrigger(Player *Source, uint32 Trigger);
bool SetupBattleGround();
diff --git a/src/game/BattleGroundSA.cpp b/src/game/BattleGroundSA.cpp
index ce5688b7706..acf16ad3af0 100644
--- a/src/game/BattleGroundSA.cpp
+++ b/src/game/BattleGroundSA.cpp
@@ -15,10 +15,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundSA.h"
#include "Language.h"
#include "Player.h"
+
BattleGroundSA::BattleGroundSA()
{
//TODO FIX ME!
@@ -27,39 +29,52 @@ BattleGroundSA::BattleGroundSA()
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
}
+
BattleGroundSA::~BattleGroundSA()
{
+
}
+
void BattleGroundSA::Update(uint32 diff)
{
BattleGround::Update(diff);
}
+
void BattleGroundSA::StartingEventCloseDoors()
{
}
+
void BattleGroundSA::StartingEventOpenDoors()
{
}
+
void BattleGroundSA::AddPlayer(Player *plr)
{
BattleGround::AddPlayer(plr);
//create score and add it to map, default values are set in constructor
BattleGroundSAScore* sc = new BattleGroundSAScore;
+
m_PlayerScores[plr->GetGUID()] = sc;
}
+
void BattleGroundSA::RemovePlayer(Player* /*plr*/,uint64 /*guid*/)
{
+
}
+
void BattleGroundSA::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;
}
+
void BattleGroundSA::UpdatePlayerScore(Player* Source, uint32 type, uint32 value)
{
+
BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID());
if(itr == m_PlayerScores.end()) // player not found...
return;
+
BattleGround::UpdatePlayerScore(Source,type,value);
}
diff --git a/src/game/BattleGroundSA.h b/src/game/BattleGroundSA.h
index 81b87b6e3e7..3ba23d02656 100644
--- a/src/game/BattleGroundSA.h
+++ b/src/game/BattleGroundSA.h
@@ -15,31 +15,40 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef __BATTLEGROUNDSA_H
#define __BATTLEGROUNDSA_H
+
class BattleGround;
+
class BattleGroundSAScore : public BattleGroundScore
{
public:
BattleGroundSAScore() {};
virtual ~BattleGroundSAScore() {};
};
+
class BattleGroundSA : public BattleGround
{
friend class BattleGroundMgr;
+
public:
BattleGroundSA();
~BattleGroundSA();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
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/BattleGroundWS.cpp b/src/game/BattleGroundWS.cpp
index 5edae297872..ec19308e71f 100644
--- a/src/game/BattleGroundWS.cpp
+++ b/src/game/BattleGroundWS.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BattleGround.h"
#include "BattleGroundWS.h"
#include "Creature.h"
@@ -28,6 +29,7 @@
#include "Player.h"
#include "World.h"
#include "WorldPacket.h"
+
// these variables aren't used outside of this file, so declare them only here
enum BG_WSG_Rewards
{
@@ -36,34 +38,42 @@ enum BG_WSG_Rewards
BG_WSG_MAP_COMPLETE,
BG_WSG_REWARD_NUM
};
+
uint32 BG_WSG_Honor[BG_HONOR_MODE_NUM][BG_WSG_REWARD_NUM] = {
{20,40,40}, // normal honor
{60,40,80} // holiday
};
+
uint32 BG_WSG_Reputation[BG_HONOR_MODE_NUM][BG_WSG_REWARD_NUM] = {
{0,35,0}, // normal honor
{0,45,0} // holiday
};
+
BattleGroundWS::BattleGroundWS()
{
m_BgObjects.resize(BG_WS_OBJECT_MAX);
m_BgCreatures.resize(BG_CREATURES_MAX_WS);
+
m_StartMessageIds[BG_STARTING_EVENT_FIRST] = LANG_BG_WS_START_TWO_MINUTES;
m_StartMessageIds[BG_STARTING_EVENT_SECOND] = LANG_BG_WS_START_ONE_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_THIRD] = LANG_BG_WS_START_HALF_MINUTE;
m_StartMessageIds[BG_STARTING_EVENT_FOURTH] = LANG_BG_WS_HAS_BEGUN;
}
+
BattleGroundWS::~BattleGroundWS()
{
}
+
void BattleGroundWS::Update(uint32 diff)
{
BattleGround::Update(diff);
+
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;
@@ -73,6 +83,7 @@ void BattleGroundWS::Update(uint32 diff)
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;
@@ -83,6 +94,7 @@ void BattleGroundWS::Update(uint32 diff)
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;
@@ -92,6 +104,7 @@ void BattleGroundWS::Update(uint32 diff)
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;
@@ -132,6 +145,7 @@ void BattleGroundWS::Update(uint32 diff)
}
}
}
+
void BattleGroundWS::StartingEventCloseDoors()
{
for(uint32 i = BG_WS_OBJECT_DOOR_A_1; i <= BG_WS_OBJECT_DOOR_H_4; ++i)
@@ -142,26 +156,32 @@ void BattleGroundWS::StartingEventCloseDoors()
for(uint32 i = BG_WS_OBJECT_A_FLAG; i <= BG_WS_OBJECT_BERSERKBUFF_2; ++i)
SpawnBGObject(i, RESPAWN_ONE_DAY);
}
+
void BattleGroundWS::StartingEventOpenDoors()
{
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);
}
+
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)
@@ -174,6 +194,7 @@ void BattleGroundWS::RespawnFlag(uint32 Team, bool captured)
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
@@ -184,10 +205,12 @@ void BattleGroundWS::RespawnFlag(uint32 Team, bool captured)
}
m_BothFlagsKept = false;
}
+
void BattleGroundWS::RespawnFlagAfterDrop(uint32 team)
{
if (GetStatus() != STATUS_IN_PROGRESS)
return;
+
RespawnFlag(team,false);
if (team == ALLIANCE)
{
@@ -199,20 +222,26 @@ void BattleGroundWS::RespawnFlagAfterDrop(uint32 team)
SpawnBGObject(BG_WS_OBJECT_H_FLAG, RESPAWN_IMMEDIATELY);
SendMessageToAll(LANG_BG_WS_HORDE_FLAG_RESPAWNED, CHAT_MSG_BG_SYSTEM_NEUTRAL);
}
+
PlaySoundToAll(BG_WS_SOUND_FLAGS_RESPAWNED);
+
GameObject *obj = HashMapHolder<GameObject>::Find(GetDroppedFlagGUID(team));
if (obj)
obj->Delete();
else
sLog.outError("unknown droped flag bg, guid: %u",GUID_LOPART(GetDroppedFlagGUID(team)));
+
SetDroppedFlagGUID(0,team);
m_BothFlagsKept = false;
}
+
void BattleGroundWS::EventPlayerCapturedFlag(Player *Source)
{
if (GetStatus() != STATUS_IN_PROGRESS)
return;
+
uint32 winner = 0;
+
Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
if (Source->GetTeam() == ALLIANCE)
{
@@ -252,26 +281,33 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player *Source)
}
//for flag capture is reward 2 honorable kills
RewardHonorToTeam(GetBonusHonorFromKill(2), Source->GetTeam());
+
SpawnBGObject(BG_WS_OBJECT_H_FLAG, BG_WS_FLAG_RESPAWN_TIME);
SpawnBGObject(BG_WS_OBJECT_A_FLAG, BG_WS_FLAG_RESPAWN_TIME);
+
if (Source->GetTeam() == ALLIANCE)
SendMessageToAll(LANG_BG_WS_CAPTURED_HF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source);
else
SendMessageToAll(LANG_BG_WS_CAPTURED_AF, CHAT_MSG_BG_SYSTEM_HORDE, Source);
+
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);
+
RewardHonorToTeam(BG_WSG_Honor[m_HonorMode][BG_WSG_WIN], winner);
EndBattleGround(winner);
}
@@ -280,6 +316,7 @@ void BattleGroundWS::EventPlayerCapturedFlag(Player *Source)
m_FlagsTimer[GetTeamIndexByTeamId(Source->GetTeam()) ? 0 : 1] = BG_WS_FLAG_RESPAWN_TIME;
}
}
+
void BattleGroundWS::EventPlayerDroppedFlag(Player *Source)
{
if (GetStatus() != STATUS_IN_PROGRESS)
@@ -308,7 +345,9 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source)
}
return;
}
+
bool set = false;
+
if (Source->GetTeam() == ALLIANCE)
{
if (!IsHordeFlagPickedup())
@@ -343,10 +382,12 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source)
set = true;
}
}
+
if (set)
{
Source->CastSpell(Source, SPELL_RECENTLY_DROPPED_FLAG, true);
UpdateFlagState(Source->GetTeam(), 1);
+
if (Source->GetTeam() == ALLIANCE)
{
SendMessageToAll(LANG_BG_WS_DROPPED_HF, CHAT_MSG_BG_SYSTEM_HORDE, Source);
@@ -357,15 +398,19 @@ void BattleGroundWS::EventPlayerDroppedFlag(Player *Source)
SendMessageToAll(LANG_BG_WS_DROPPED_AF, CHAT_MSG_BG_SYSTEM_ALLIANCE, Source);
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;
+
int32 message_id = 0;
ChatMsg type;
+
//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())
@@ -383,6 +428,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target
if(m_FlagState[1] == BG_WS_FLAG_STATE_ON_PLAYER)
m_BothFlagsKept = 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())
@@ -400,6 +446,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target
if(m_FlagState[0] == BG_WS_FLAG_STATE_ON_PLAYER)
m_BothFlagsKept = true;
}
+
//Alliance flag on ground(not in base) (returned or picked up again from ground!)
if (GetFlagState(ALLIANCE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10))
{
@@ -433,6 +480,7 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target
//called in HandleGameObjectUseOpcode:
//target_obj->Delete();
}
+
//Horde flag on ground(not in base) (returned or picked up again)
if (GetFlagState(HORDE) == BG_WS_FLAG_STATE_ON_GROUND && Source->IsWithinDistInMap(target_obj, 10))
{
@@ -466,11 +514,14 @@ void BattleGroundWS::EventPlayerClickedOnFlag(Player *Source, GameObject* target
//called in HandleGameObjectUseOpcode:
//target_obj->Delete();
}
+
if (!message_id)
return;
+
SendMessageToAll(message_id, type, Source);
Source->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
}
+
void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid)
{
// sometimes flag aura not removed :(
@@ -497,6 +548,7 @@ void BattleGroundWS::RemovePlayer(Player *plr, uint64 guid)
this->EventPlayerDroppedFlag(plr);
}
}
+
void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value)
{
if (team == ALLIANCE)
@@ -504,6 +556,7 @@ void BattleGroundWS::UpdateFlagState(uint32 team, uint32 value)
else
UpdateWorldState(BG_WS_FLAG_STATE_HORDE, value);
}
+
void BattleGroundWS::UpdateTeamScore(uint32 team)
{
if (team == ALLIANCE)
@@ -511,11 +564,13 @@ void BattleGroundWS::UpdateTeamScore(uint32 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)
@@ -558,9 +613,11 @@ void BattleGroundWS::HandleAreaTrigger(Player *Source, uint32 Trigger)
Source->GetSession()->SendAreaTriggerMessage("Warning: Unhandled AreaTrigger in Battleground: %u", Trigger);
break;
}
+
//if (buff_guid)
// HandleTriggerBuff(buff_guid,Source);
}
+
bool BattleGroundWS::SetupBattleGround()
{
// flags
@@ -590,25 +647,31 @@ bool BattleGroundWS::SetupBattleGround()
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::Reset()
{
//call parent's class reset
BattleGround::Reset();
+
m_FlagKeepers[BG_TEAM_ALLIANCE] = 0;
m_FlagKeepers[BG_TEAM_HORDE] = 0;
m_DroppedFlagGUID[BG_TEAM_ALLIANCE] = 0;
@@ -621,6 +684,7 @@ void BattleGroundWS::Reset()
m_ReputationCapture = (isBGWeekend) ? 45 : 35;
m_HonorWinKills = (isBGWeekend) ? 3 : 1;
m_HonorEndKills = (isBGWeekend) ? 4 : 2;
+
/* 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);
@@ -628,6 +692,7 @@ void BattleGroundWS::Reset()
DelCreature(WS_SPIRIT_MAIN_HORDE);
*/
}
+
void BattleGroundWS::EndBattleGround(uint32 winner)
{
//win reward
@@ -638,20 +703,27 @@ void BattleGroundWS::EndBattleGround(uint32 winner)
//complete map_end rewards (even if no team wins)
RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), ALLIANCE);
RewardHonorToTeam(GetBonusHonorFromKill(m_HonorEndKills), HORDE);
+
BattleGround::EndBattleGround(winner);
}
+
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)
{
+
BattleGroundScoreMap::iterator itr = m_PlayerScores.find(Source->GetGUID());
if(itr == m_PlayerScores.end()) // player not found
return;
+
switch(type)
{
case SCORE_FLAG_CAPTURES: // flags captured
@@ -665,6 +737,7 @@ void BattleGroundWS::UpdatePlayerScore(Player *Source, uint32 type, uint32 value
break;
}
}
+
WorldSafeLocsEntry const* BattleGroundWS::GetClosestGraveYard(Player* player)
{
//if status in progress, it returns main graveyards with spiritguides
@@ -687,30 +760,37 @@ WorldSafeLocsEntry const* BattleGroundWS::GetClosestGraveYard(Player* player)
return sWorldSafeLocsStore.LookupEntry(WS_GRAVEYARD_FLAGROOM_HORDE);
}
}
+
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_ALLIANCE) << uint32(2);
else
data << uint32(BG_WS_FLAG_STATE_ALLIANCE) << uint32(1);
+
if (m_FlagState[BG_TEAM_ALLIANCE] == 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);
+
}
diff --git a/src/game/BattleGroundWS.h b/src/game/BattleGroundWS.h
index 0b616af24b1..16631afecdc 100644
--- a/src/game/BattleGroundWS.h
+++ b/src/game/BattleGroundWS.h
@@ -17,9 +17,12 @@
* 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"
+
enum BG_WS_TimerOrScore
{
BG_WS_MAX_TEAM_SCORE = 3,
@@ -28,6 +31,7 @@ enum BG_WS_TimerOrScore
BG_WS_SPELL_FORCE_TIME = 600000,
BG_WS_SPELL_BRUTAL_TIME = 900000
};
+
enum BG_WS_Sound
{
BG_WS_SOUND_FLAG_CAPTURED_ALLIANCE = 8173,
@@ -38,6 +42,7 @@ enum BG_WS_Sound
BG_WS_SOUND_ALLIANCE_FLAG_PICKED_UP = 8174,
BG_WS_SOUND_FLAGS_RESPAWNED = 8232
};
+
enum BG_WS_SpellId
{
BG_WS_SPELL_WARSONG_FLAG = 23333,
@@ -47,6 +52,7 @@ enum BG_WS_SpellId
BG_WS_SPELL_FOCUSED_ASSAULT = 46392,
BG_WS_SPELL_BRUTAL_ASSAULT = 46393
};
+
enum BG_WS_WorldStates
{
BG_WS_FLAG_UNK_ALLIANCE = 1545,
@@ -58,6 +64,7 @@ enum BG_WS_WorldStates
BG_WS_FLAG_STATE_HORDE = 2338,
BG_WS_FLAG_STATE_ALLIANCE = 2339
};
+
enum BG_WS_ObjectTypes
{
BG_WS_OBJECT_DOOR_A_1 = 0,
@@ -80,6 +87,7 @@ enum BG_WS_ObjectTypes
BG_WS_OBJECT_BERSERKBUFF_2 = 17,
BG_WS_OBJECT_MAX = 18
};
+
enum BG_WS_ObjectEntry
{
BG_OBJECT_DOOR_A_1_WS_ENTRY = 179918,
@@ -97,6 +105,7 @@ enum BG_WS_ObjectEntry
BG_OBJECT_A_FLAG_GROUND_WS_ENTRY = 179785,
BG_OBJECT_H_FLAG_GROUND_WS_ENTRY = 179786
};
+
enum BG_WS_FlagState
{
BG_WS_FLAG_STATE_ON_BASE = 0,
@@ -104,6 +113,7 @@ enum BG_WS_FlagState
BG_WS_FLAG_STATE_ON_PLAYER = 2,
BG_WS_FLAG_STATE_ON_GROUND = 3
};
+
enum BG_WS_Graveyards
{
WS_GRAVEYARD_FLAGROOM_ALLIANCE = 769,
@@ -111,17 +121,21 @@ 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
};
+
enum BG_WS_CarrierDebuffs
{
WS_SPELL_FOCUSED_ASSAULT = 46392,
WS_SPELL_BRUTAL_ASSAULT = 46393
};
+
class BattleGroundWGScore : public BattleGroundScore
{
public:
@@ -130,18 +144,22 @@ class BattleGroundWGScore : public BattleGroundScore
uint32 FlagCaptures;
uint32 FlagReturns;
};
+
class BattleGroundWS : public BattleGround
{
friend class BattleGroundMgr;
+
public:
/* Construction */
BattleGroundWS();
~BattleGroundWS();
void Update(uint32 diff);
+
/* inherited from BattlegroundClass */
virtual void AddPlayer(Player *plr);
virtual void StartingEventCloseDoors();
virtual void StartingEventOpenDoors();
+
/* BG Flags */
uint64 GetAllianceFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_ALLIANCE]; }
uint64 GetHordeFlagPickerGUID() const { return m_FlagKeepers[BG_TEAM_HORDE]; }
@@ -156,10 +174,12 @@ class BattleGroundWS : public BattleGround
void RemoveTimedAura(uint32 aura);
bool IsBrutalTimerDone;
bool IsForceTimerDone;
+
/* 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);
@@ -167,12 +187,14 @@ class BattleGroundWS : public BattleGround
virtual void Reset();
void EndBattleGround(uint32 winner);
virtual WorldSafeLocsEntry const* GetClosestGraveYard(Player* player);
+
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; }
@@ -184,6 +206,7 @@ class BattleGroundWS : public BattleGround
uint8 m_FlagState[2]; // for checking flag state
int32 m_FlagsTimer[2];
int32 m_FlagsDropTimer[2];
+
uint32 m_ReputationCapture;
uint32 m_HonorWinKills;
uint32 m_HonorEndKills;
diff --git a/src/game/Calendar.h b/src/game/Calendar.h
index 41f75c7aa28..7a86afa7db7 100644
--- a/src/game/Calendar.h
+++ b/src/game/Calendar.h
@@ -15,9 +15,12 @@
* 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_CALENDAR_H
#define MANGOS_CALENDAR_H
+
class Calendar
{
+
};
#endif
diff --git a/src/game/CalendarHandler.cpp b/src/game/CalendarHandler.cpp
index 747f2e9f309..93d3b7eae26 100644
--- a/src/game/CalendarHandler.cpp
+++ b/src/game/CalendarHandler.cpp
@@ -15,27 +15,36 @@
* 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 "InstanceSaveMgr.h"
#include "Log.h"
#include "Opcodes.h"
#include "Player.h"
+
void WorldSession::HandleCalendarGetCalendar(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GET_CALENDAR"); // empty
+
time_t cur_time = time(NULL);
+
WorldPacket data(SMSG_CALENDAR_SEND_CALENDAR,4+4*0+4+4*0+4+4);
+
// TODO: calendar invite event output
data << (uint32) 0; //invite node count
// TODO: calendar event output
data << (uint32) 0; //event count
+
data << (uint32) 0; //wtf??
data << (uint32) secsToTimeBitFields(cur_time); // current time
+
uint32 counter = 0;
size_t p_counter = data.wpos();
data << uint32(counter); // instance save count
+
for(int i = 0; i < TOTAL_DIFFICULTIES; ++i)
{
for (Player::BoundInstancesMap::const_iterator itr = _player->m_boundInstances[i].begin(); itr != _player->m_boundInstances[i].end(); ++itr)
@@ -52,6 +61,7 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket &recv_data)
}
}
data.put<uint32>(p_counter,counter);
+
data << (uint32) 1135753200; //wtf?? (28.12.2005 12:00)
data << (uint32) 0; // unk counter 4
data << (uint32) 0; // unk counter 5
@@ -59,12 +69,14 @@ void WorldSession::HandleCalendarGetCalendar(WorldPacket &recv_data)
//data.hexlike();
SendPacket(&data);
}
+
void WorldSession::HandleCalendarGetEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GET_EVENT");
recv_data.hexlike();
recv_data.read_skip<uint64>(); // unk
}
+
void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GUILD_FILTER");
@@ -73,20 +85,24 @@ void WorldSession::HandleCalendarGuildFilter(WorldPacket &recv_data)
recv_data.read_skip<uint32>(); // unk2
recv_data.read_skip<uint32>(); // unk3
}
+
void WorldSession::HandleCalendarArenaTeam(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_ARENA_TEAM");
recv_data.hexlike();
recv_data.read_skip<uint32>(); // unk
}
+
void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_ADD_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//std::string unk1, unk2;
//recv_data >> (std::string)unk1;
//recv_data >> (std::string)unk2;
+
//uint8 unk3, unk4;
//uint32 unk5, unk6, unk7, unk8, unk9, count = 0;
//recv_data >> (uint8)unk3;
@@ -112,11 +128,13 @@ void WorldSession::HandleCalendarAddEvent(WorldPacket &recv_data)
// }
//}
}
+
void WorldSession::HandleCalendarUpdateEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_UPDATE_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> std::string
@@ -129,88 +147,110 @@ void WorldSession::HandleCalendarUpdateEvent(WorldPacket &recv_data)
//recv_data >> uint32
//recv_data >> uint32
}
+
void WorldSession::HandleCalendarRemoveEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_REMOVE_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
+
}
+
void WorldSession::HandleCalendarCopyEvent(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_COPY_EVENT");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
+
}
+
void WorldSession::HandleCalendarEventInvite(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_INVITE");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> std::string
//recv_data >> uint8
//recv_data >> uint8
+
}
+
void WorldSession::HandleCalendarEventRsvp(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_RSVP");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
+
}
+
void WorldSession::HandleCalendarEventRemoveInvite(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_REMOVE_INVITE");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data.readPackGUID(guid)
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
}
+
void WorldSession::HandleCalendarEventStatus(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_STATUS");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data.readPackGUID(guid)
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
+
void WorldSession::HandleCalendarEventModeratorStatus(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_EVENT_MODERATOR_STATUS");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data.readPackGUID(guid)
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint32
}
+
void WorldSession::HandleCalendarComplain(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_COMPLAIN");
recv_data.hexlike();
recv_data.rpos(recv_data.wpos()); // set to end to avoid warnings spam
+
//recv_data >> uint64
//recv_data >> uint64
//recv_data >> uint64
}
+
void WorldSession::HandleCalendarGetNumPending(WorldPacket & /*recv_data*/)
{
sLog.outDebug("WORLD: CMSG_CALENDAR_GET_NUM_PENDING"); // empty
+
WorldPacket data(SMSG_CALENDAR_SEND_NUM_PENDING, 4);
data << uint32(0); // 0 - no pending invites, 1 - some pending invites
SendPacket(&data);
diff --git a/src/game/Cell.h b/src/game/Cell.h
index 7145df0aacc..9baf4104080 100644
--- a/src/game/Cell.h
+++ b/src/game/Cell.h
@@ -17,14 +17,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CELL_H
#define TRINITY_CELL_H
+
#include <cmath>
+
#include "GameSystem/TypeContainer.h"
#include "GameSystem/TypeContainerVisitor.h"
+
#include "GridDefines.h"
+
class Map;
class WorldObject;
+
enum District
{
UPPER_DISTRICT = 1,
@@ -38,12 +44,15 @@ enum District
LOWER_RIGHT_DISTRICT = (LOWER_DISTRICT | RIGHT_DISTRICT),
ALL_DISTRICT = (UPPER_DISTRICT | LOWER_DISTRICT | LEFT_DISTRICT | RIGHT_DISTRICT | CENTER_DISTRICT)
};
+
template<class T> struct CellLock;
+
struct TRINITY_DLL_DECL CellArea
{
CellArea() : right_offset(0), left_offset(0), upper_offset(0), lower_offset(0) {}
CellArea(int right, int left, int upper, int lower) : right_offset(right), left_offset(left), upper_offset(upper), lower_offset(lower) {}
bool operator!() const { return !right_offset && !left_offset && !upper_offset && !lower_offset; }
+
void ResizeBorders(CellPair& begin_cell, CellPair& end_cell) const
{
begin_cell << left_offset;
@@ -51,17 +60,20 @@ struct TRINITY_DLL_DECL CellArea
end_cell >> right_offset;
end_cell += upper_offset;
}
+
int right_offset;
int left_offset;
int upper_offset;
int lower_offset;
};
+
struct TRINITY_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;
@@ -69,12 +81,14 @@ struct TRINITY_DLL_DECL Cell
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;
@@ -96,38 +110,45 @@ struct TRINITY_DLL_DECL Cell
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;
}
+
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 );
}
+
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
@@ -143,13 +164,17 @@ struct TRINITY_DLL_DECL Cell
} Part;
uint32 All;
} data;
+
template<class LOCK_TYPE, class T, class CONTAINER> void Visit(const CellLock<LOCK_TYPE> &, TypeContainerVisitor<T, CONTAINER> &visitor, Map &) const;
template<class LOCK_TYPE, class T, class CONTAINER> void Visit(const CellLock<LOCK_TYPE> &, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m, const WorldObject &obj, float radius) const;
template<class LOCK_TYPE, class T, class CONTAINER> void Visit(const CellLock<LOCK_TYPE> &, TypeContainerVisitor<T, CONTAINER> &visitor, Map &, float radius, float x_off, float y_off) const;
+
static CellArea CalculateCellArea(const WorldObject &obj, float radius);
+
private:
template<class LOCK_TYPE, class T, class CONTAINER> void VisitCircle(const CellLock<LOCK_TYPE> &, TypeContainerVisitor<T, CONTAINER> &, Map &, const CellPair& , const CellPair& ) const;
};
+
template<class T>
struct TRINITY_DLL_DECL CellLock
{
diff --git a/src/game/CellImpl.h b/src/game/CellImpl.h
index 659ffd48eb6..ff2b0343c69 100644
--- a/src/game/CellImpl.h
+++ b/src/game/CellImpl.h
@@ -17,12 +17,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CELLIMPL_H
#define TRINITY_CELLIMPL_H
+
#include <cmath>
+
#include "Cell.h"
#include "Map.h"
#include "Object.h"
+
inline Cell::Cell(CellPair const& p)
{
data.Part.grid_x = p.x_coord / MAX_NUMBER_OF_CELLS;
@@ -32,6 +36,7 @@ inline Cell::Cell(CellPair const& p)
data.Part.nocreate = 0;
data.Part.reserved = 0;
}
+
template<class LOCK_TYPE,class T, class CONTAINER>
inline void
Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m) const
@@ -39,16 +44,19 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
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:
@@ -109,6 +117,7 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
break;
}
}
+
// loop the cell range
for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++)
{
@@ -122,6 +131,7 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
}
}
}
+
template<class LOCK_TYPE,class T, class CONTAINER>
inline void
Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m, float radius, float x_off, float y_off) const
@@ -129,7 +139,9 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
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;
+
int left = 0, right = 0, upper = 0, lower = 0;
+
// Origin = (CENTER_GRID_CELL_OFFSET, CENTER_GRID_CELL_OFFSET)
if(CENTER_GRID_CELL_OFFSET - x_off < radius)
++right;
@@ -139,17 +151,21 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
++upper;
if(CENTER_GRID_CELL_OFFSET + y_off < radius)
++lower;
+
if(!left && !right && !upper && !lower)
{
m.Visit(l, visitor);
return;
}
+
CellPair begin_cell = standing_cell;
CellPair end_cell = standing_cell;
+
begin_cell << left; //note: need change << if left > 1
begin_cell -= lower;
end_cell >> right;
end_cell += upper;
+
// loop the cell range
for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++)
{
@@ -163,16 +179,20 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
}
}
}
+
inline int CellHelper(const float radius)
{
if(radius < 1.0f)
return 0;
+
return (int)ceilf(radius/SIZE_OF_GRID_CELL);
}
+
inline CellArea Cell::CalculateCellArea(const WorldObject &obj, float radius)
{
if(radius <= 0.0f)
return CellArea();
+
//we should increase search radius by object's radius, otherwise
//we could have problems with huge creatures, which won't attack nearest players etc
radius += obj.GetObjectSize();
@@ -180,18 +200,23 @@ inline CellArea Cell::CalculateCellArea(const WorldObject &obj, float radius)
//TODO: add more correct/generic method for this task
const float x_offset = (obj.GetPositionX() - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL;
const float y_offset = (obj.GetPositionY() - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL;
+
const float x_val = floor(x_offset + CENTER_GRID_CELL_ID + 0.5f);
const float y_val = floor(y_offset + CENTER_GRID_CELL_ID + 0.5f);
+
const float x_off = (x_offset - x_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL;
const float y_off = (y_offset - y_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL;
+
const float tmp_diff = radius - CENTER_GRID_CELL_OFFSET;
//lets calculate upper/lower/right/left corners for cell search
int right = CellHelper(tmp_diff + x_off);
int left = CellHelper(tmp_diff - x_off);
int upper = CellHelper(tmp_diff + y_off);
int lower = CellHelper(tmp_diff - y_off);
+
return CellArea(right, left, upper, lower);
}
+
template<class LOCK_TYPE, class T, class CONTAINER>
inline void
Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m, const WorldObject &obj, float radius) const
@@ -199,6 +224,7 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
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;
+
//no jokes here... Actually placing ASSERT() here was good idea, but
//we had some problems with DynamicObjects, which pass radius = 0.0f (DB issue?)
//maybe it is better to just return when radius <= 0.0f?
@@ -210,6 +236,7 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
//lets limit the upper value for search radius
if(radius > 333.0f)
radius = 333.0f;
+
//lets calculate object coord offsets from cell borders.
CellArea area = Cell::CalculateCellArea(obj, radius);
//if radius fits inside standing cell
@@ -218,8 +245,10 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
m.Visit(l, visitor);
return;
}
+
CellPair begin_cell = standing_cell;
CellPair end_cell = standing_cell;
+
area.ResizeBorders(begin_cell, end_cell);
//visit all cells, found in CalculateCellArea()
//if radius is known to reach cell area more than 4x4 then we should call optimized VisitCircle
@@ -230,9 +259,11 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
VisitCircle(l, visitor, m, begin_cell, end_cell);
return;
}
+
//ALWAYS visit standing cell first!!! Since we deal with small radiuses
//it is very essential to call visitor for standing cell firstly...
m.Visit(l, visitor);
+
// loop the cell range
for(uint32 x = begin_cell.x_coord; x <= end_cell.x_coord; x++)
{
@@ -250,6 +281,7 @@ Cell::Visit(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &vi
}
}
}
+
template<class LOCK_TYPE, class T, class CONTAINER>
inline void
Cell::VisitCircle(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINER> &visitor, Map &m, const CellPair& begin_cell, const CellPair& end_cell) const
@@ -259,6 +291,7 @@ Cell::VisitCircle(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINE
//lets calculate x_start/x_end coords for central strip...
const uint32 x_start = begin_cell.x_coord + x_shift;
const uint32 x_end = end_cell.x_coord - x_shift;
+
//visit central strip with constant width...
for(uint32 x = x_start; x <= x_end; ++x)
{
@@ -271,10 +304,12 @@ Cell::VisitCircle(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINE
m.Visit(lock, visitor);
}
}
+
//if x_shift == 0 then we have too small cell area, which were already
//visited at previous step, so just return from procedure...
if(x_shift == 0)
return;
+
uint32 y_start = end_cell.y_coord;
uint32 y_end = begin_cell.y_coord;
//now we are visiting borders of an octagon...
@@ -292,6 +327,7 @@ Cell::VisitCircle(const CellLock<LOCK_TYPE> &l, TypeContainerVisitor<T, CONTAINE
r_zone_left.data.Part.nocreate = l->data.Part.nocreate;
CellLock<LOCK_TYPE> lock_left(r_zone_left, cell_pair_left);
m.Visit(lock_left, visitor);
+
//right trapezoid cell visit
CellPair cell_pair_right(x_end + step, y);
Cell r_zone_right(cell_pair_right);
diff --git a/src/game/Channel.cpp b/src/game/Channel.cpp
index 5ff403b9484..72e35a6e777 100644
--- a/src/game/Channel.cpp
+++ b/src/game/Channel.cpp
@@ -17,10 +17,12 @@
* 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 "SocialMgr.h"
#include "World.h"
+
Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team)
: m_name(name), m_announce(true), m_moderate(false), m_channelId(channel_id), m_ownerGUID(0), m_password(""), m_flags(0)
{
@@ -31,16 +33,20 @@ Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team)
{
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;
- m_IsSaved = false;
+ m_IsSaved = false;
}
else // it's custom channel
{
@@ -56,7 +62,9 @@ Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team)
m_moderate = fields[3].GetBool();
m_password = fields[4].GetString();
const char* db_BannedList = fields[5].GetString();
+
m_IsSaved = true;
+
if(db_BannedList)
{
Tokens tokens = StrSplit(db_BannedList, " ");
@@ -79,10 +87,11 @@ Channel::Channel(const std::string& name, uint32 channel_id, uint32 Team)
{
sLog.outDebug("New Channel(%s) saved", name.c_str());
m_IsSaved = true;
- }
+ }
}
}
}
+
void Channel::Join(uint64 p, const char *pass)
{
WorldPacket data;
@@ -95,19 +104,23 @@ void Channel::Join(uint64 p, const char *pass)
}
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) &&
@@ -118,28 +131,36 @@ void Channel::Join(uint64 p, const char *pass)
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 = MEMBER_FLAG_NONE;
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);
+ players[p].SetModerator(true);
}
/*
else if(!IsConstant() && m_ownerGUID && plr && m_ownerGUID == plr->GetGUID() ))
@@ -148,6 +169,7 @@ void Channel::Join(uint64 p, const char *pass)
players[p].SetModerator(true);
}*/
}
+
void Channel::Leave(uint64 p, bool send)
{
if(!IsOn(p))
@@ -162,6 +184,7 @@ void Channel::Leave(uint64 p, bool send)
else
{
Player *plr = objmgr.GetPlayer(p);
+
if(send)
{
WorldPacket data;
@@ -171,7 +194,9 @@ void Channel::Leave(uint64 p, bool send)
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) ))
{
@@ -179,7 +204,9 @@ void Channel::Leave(uint64 p, bool send)
MakeLeft(&data, p);
SendToAll(&data);
}
+
LeaveNotify(p);
+
if(changeowner)
{
uint64 newowner = !players.empty() ? players.begin()->second.player : 0;
@@ -188,12 +215,14 @@ void Channel::Leave(uint64 p, bool send)
}
}
}
+
void Channel::KickOrBan(uint64 good, const char *badname, bool ban)
{
AccountTypes sec = SEC_PLAYER;
Player *gplr = objmgr.GetPlayer(good);
if(gplr)
sec = gplr->GetSession()->GetSecurity();
+
if(!IsOn(good))
{
WorldPacket data;
@@ -224,7 +253,9 @@ void Channel::KickOrBan(uint64 good, const char *badname, bool ban)
else
{
bool changeowner = (m_ownerGUID == bad->GetGUID());
+
WorldPacket data;
+
if(ban && !IsBanned(bad->GetGUID()))
{
banned.insert(bad->GetGUID());
@@ -243,14 +274,17 @@ void Channel::KickOrBan(uint64 good, const char *badname, bool ban)
if(CharacterDatabase.PExecute( ss.str( ).c_str( ) ))
{
sLog.outDebug("Channel(%s) BannedList saved", m_name.c_str());
- }
+ }
}
+
}
else
MakePlayerKicked(&data, bad->GetGUID(), good);
+
SendToAll(&data);
players.erase(bad->GetGUID());
bad->LeftChannel(this);
+
if(changeowner)
{
uint64 newowner = !players.empty() ? good : false;
@@ -260,12 +294,14 @@ void Channel::KickOrBan(uint64 good, const char *badname, bool ban)
}
}
}
+
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;
@@ -290,6 +326,7 @@ void Channel::UnBan(uint64 good, const char *badname)
else
{
banned.erase(bad->GetGUID());
+
WorldPacket data;
MakePlayerUnbanned(&data, bad->GetGUID(), good);
SendToAll(&data);
@@ -307,17 +344,19 @@ void Channel::UnBan(uint64 good, const char *badname)
if(CharacterDatabase.PExecute( ss.str( ).c_str( ) ))
{
sLog.outDebug("Channel(%s) BannedList saved", m_name.c_str());
- }
+ }
}
}
}
}
+
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;
@@ -333,6 +372,7 @@ void Channel::Password(uint64 p, const char *pass)
else
{
m_password = pass;
+
WorldPacket data;
MakePasswordChanged(&data, p);
SendToAll(&data);
@@ -343,16 +383,19 @@ void Channel::Password(uint64 p, const char *pass)
if(CharacterDatabase.PExecute( ss.str( ).c_str( ) ))
{
sLog.outDebug("Channel(%s) password saved", m_name.c_str());
- }
+ }
}
}
}
+
void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set)
{
Player *plr = objmgr.GetPlayer(p);
if (!plr)
return;
+
uint32 sec = plr->GetSession()->GetSecurity();
+
if(!IsOn(p))
{
WorldPacket data;
@@ -375,9 +418,11 @@ void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set)
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;
@@ -385,6 +430,7 @@ void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set)
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) &&
@@ -395,6 +441,7 @@ void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set)
SendToOne(&data, p);
return;
}
+
if(m_ownerGUID == newp->GetGUID() && m_ownerGUID != p)
{
WorldPacket data;
@@ -402,18 +449,22 @@ void Channel::SetMode(uint64 p, const char *p2n, bool mod, bool set)
SendToOne(&data, p);
return;
}
+
if(mod)
SetModerator(newp->GetGUID(), set);
else
SetMute(newp->GetGUID(), set);
}
}
+
void Channel::SetOwner(uint64 p, const char *newname)
{
Player *plr = objmgr.GetPlayer(p);
if (!plr)
return;
+
uint32 sec = plr->GetSession()->GetSecurity();
+
if(!IsOn(p))
{
WorldPacket data;
@@ -421,6 +472,7 @@ void Channel::SetOwner(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
if(sec < SEC_GAMEMASTER && p != m_ownerGUID)
{
WorldPacket data;
@@ -428,6 +480,7 @@ void Channel::SetOwner(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
Player *newp = objmgr.GetPlayer(newname);
if(newp == NULL || !IsOn(newp->GetGUID()))
{
@@ -436,6 +489,7 @@ void Channel::SetOwner(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
if(newp->GetTeam() != plr->GetTeam() && !sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))
{
WorldPacket data;
@@ -443,9 +497,11 @@ void Channel::SetOwner(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
players[newp->GetGUID()].SetModerator(true);
SetOwner(newp->GetGUID());
}
+
void Channel::SendWhoOwner(uint64 p)
{
if(!IsOn(p))
@@ -461,9 +517,11 @@ void Channel::SendWhoOwner(uint64 p)
SendToOne(&data, p);
}
}
+
void Channel::List(Player* player)
{
uint64 p = player->GetGUID();
+
if(!IsOn(p))
{
WorldPacket data;
@@ -476,13 +534,17 @@ void Channel::List(Player* player)
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
+
uint32 gmLevelInWhoList = sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST);
+
uint32 count = 0;
for(PlayerList::const_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 && (player->GetSession()->GetSecurity() > SEC_PLAYER || plr->GetSession()->GetSecurity() <= gmLevelInWhoList) &&
@@ -493,16 +555,20 @@ void Channel::List(Player* player)
++count;
}
}
+
data.put<uint32>(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;
@@ -518,6 +584,7 @@ void Channel::Announce(uint64 p)
else
{
m_announce = !m_announce;
+
WorldPacket data;
if(m_announce)
MakeAnnouncementsOn(&data, p);
@@ -531,16 +598,19 @@ void Channel::Announce(uint64 p)
if(CharacterDatabase.PExecute( ss.str( ).c_str( ) ))
{
sLog.outDebug("Channel(%s) announce saved", m_name.c_str());
- }
+ }
}
+
}
}
+
void Channel::Moderate(uint64 p)
{
uint32 sec = 0;
Player *plr = objmgr.GetPlayer(p);
if(plr)
sec = plr->GetSession()->GetSecurity();
+
if(!IsOn(p))
{
WorldPacket data;
@@ -556,6 +626,7 @@ void Channel::Moderate(uint64 p)
else
{
m_moderate = !m_moderate;
+
WorldPacket data;
if(m_moderate)
MakeModerationOn(&data, p);
@@ -569,20 +640,23 @@ void Channel::Moderate(uint64 p)
if(CharacterDatabase.PExecute( ss.str( ).c_str( ) ))
{
sLog.outDebug("Channel(%s) moderate saved", m_name.c_str());
- }
+ }
}
}
}
+
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;
@@ -604,6 +678,7 @@ void Channel::Say(uint64 p, const char *what, uint32 lang)
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;
@@ -614,9 +689,11 @@ void Channel::Say(uint64 p, const char *what, uint32 lang)
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))
@@ -626,6 +703,7 @@ void Channel::Invite(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
Player *newp = objmgr.GetPlayer(newname);
if(!newp)
{
@@ -634,9 +712,11 @@ void Channel::Invite(uint64 p, const char *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;
@@ -644,6 +724,7 @@ void Channel::Invite(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
if(IsOn(newp->GetGUID()))
{
WorldPacket data;
@@ -651,6 +732,7 @@ void Channel::Invite(uint64 p, const char *newname)
SendToOne(&data, p);
return;
}
+
WorldPacket data;
if(!newp->GetSocial()->HasIgnore(GUID_LOPART(p)))
{
@@ -661,6 +743,7 @@ void Channel::Invite(uint64 p, const char *newname)
MakePlayerInvited(&data, newp->GetName());
SendToOne(&data, p);
}
+
void Channel::SetOwner(uint64 guid, bool exclaim)
{
if(m_ownerGUID)
@@ -670,15 +753,18 @@ void Channel::SetOwner(uint64 guid, bool exclaim)
if(p_itr != players.end())
p_itr->second.SetOwner(false);
}
+
m_ownerGUID = guid;
if(m_ownerGUID)
{
uint8 oldFlag = GetPlayerFlags(m_ownerGUID);
players[m_ownerGUID].SetModerator(true);
players[m_ownerGUID].SetOwner(true);
+
WorldPacket data;
MakeModeChange(&data, m_ownerGUID, oldFlag);
SendToAll(&data);
+
if(exclaim)
{
MakeOwnerChanged(&data, m_ownerGUID);
@@ -691,10 +777,12 @@ void Channel::SetOwner(uint64 guid, bool exclaim)
if(CharacterDatabase.PExecute( ss.str( ).c_str( ) ))
{
sLog.outDebug("Channel(%s) owner saved", m_name.c_str());
- }
+ }
}*/
+
}
}
+
void Channel::SendToAll(WorldPacket *data, uint64 p)
{
for(PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
@@ -707,6 +795,7 @@ void Channel::SendToAll(WorldPacket *data, uint64 p)
}
}
}
+
void Channel::SendToAllButOne(WorldPacket *data, uint64 who)
{
for(PlayerList::const_iterator i = players.begin(); i != players.end(); ++i)
@@ -719,18 +808,24 @@ void Channel::SendToAllButOne(WorldPacket *data, uint64 who)
}
}
}
+
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)
{
@@ -738,18 +833,21 @@ void Channel::MakeNotifyPacket(WorldPacket *data, uint8 notify_type)
*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)
{
@@ -758,6 +856,7 @@ void Channel::MakeYouJoined(WorldPacket *data)
*data << uint32(GetChannelId());
*data << uint32(0);
}
+
// done 0x03
void Channel::MakeYouLeft(WorldPacket *data)
{
@@ -765,53 +864,64 @@ void Channel::MakeYouLeft(WorldPacket *data)
*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, const 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)
{
@@ -820,35 +930,41 @@ void Channel::MakeModeChange(WorldPacket *data, uint64 guid, uint8 oldflags)
*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)
{
@@ -856,11 +972,13 @@ void Channel::MakePlayerKicked(WorldPacket *data, uint64 bad, uint64 good)
*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)
{
@@ -868,6 +986,7 @@ void Channel::MakePlayerBanned(WorldPacket *data, uint64 bad, uint64 good)
*data << uint64(bad);
*data << uint64(good);
}
+
// done 0x15
void Channel::MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good)
{
@@ -875,90 +994,107 @@ void Channel::MakePlayerUnbanned(WorldPacket *data, uint64 bad, uint64 good)
*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, const std::string& name)
{
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());
@@ -966,6 +1102,7 @@ void Channel::JoinNotify(uint64 guid)
data << GetName();
SendToAll(&data);
}
+
void Channel::LeaveNotify(uint64 guid)
{
WorldPacket data(SMSG_USERLIST_REMOVE, 8+1+4+GetName().size()+1);
diff --git a/src/game/Channel.h b/src/game/Channel.h
index b97acd52360..69a1e2f66f6 100644
--- a/src/game/Channel.h
+++ b/src/game/Channel.h
@@ -17,15 +17,20 @@
* 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 <list>
#include <map>
#include <string>
+
#include "Common.h"
+
#include "Opcodes.h"
#include "Player.h"
#include "WorldPacket.h"
+
enum ChatNotify
{
CHAT_JOINED_NOTICE = 0x00, //+ "%s joined channel.";
@@ -67,6 +72,7 @@ enum ChatNotify
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,
@@ -84,6 +90,7 @@ enum ChannelFlags
// GuildRecruitment 0x38 = 0x20 | 0x10 | 0x08
// LookingForGroup 0x50 = 0x40 | 0x10
};
+
enum ChannelDBCFlags
{
CHANNEL_DBC_FLAG_NONE = 0x00000,
@@ -97,6 +104,7 @@ enum ChannelDBCFlags
CHANNEL_DBC_FLAG_GUILD_REQ = 0x20000, // GuildRecruitment
CHANNEL_DBC_FLAG_LFG = 0x40000 // LookingForGroup
};
+
enum ChannelMemberFlags
{
MEMBER_FLAG_NONE = 0x00,
@@ -109,12 +117,14 @@ enum ChannelMemberFlags
// 0x40
// 0x80
};
+
class Channel
{
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; }
@@ -136,6 +146,7 @@ class Channel
else flags &= ~MEMBER_FLAG_MUTED;
}
};
+
typedef std::map<uint64, PlayerInfo> PlayerList;
PlayerList players;
typedef std::set<uint64> BannedList;
@@ -148,6 +159,7 @@ class Channel
uint32 m_channelId;
uint64 m_ownerGUID;
bool m_IsSaved;
+
private:
// initial packet data (notify type and channel name)
void MakeNotifyPacket(WorldPacket *data, uint8 notify_type);
@@ -188,40 +200,49 @@ class Channel
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.find(who) != players.end(); }
bool IsBanned(uint64 guid) const { return banned.find(guid) != banned.end(); }
+
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:
uint32 m_Team;
Channel(const std::string& name, uint32 channel_id, uint32 Team = 0);
@@ -236,6 +257,7 @@ class Channel
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);
diff --git a/src/game/ChannelHandler.cpp b/src/game/ChannelHandler.cpp
index d50c579cd9b..abf73e0e274 100644
--- a/src/game/ChannelHandler.cpp
+++ b/src/game/ChannelHandler.cpp
@@ -18,19 +18,26 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "Policies/SingletonImp.h"
+
#include "ObjectMgr.h" // for normalizePlayerName
#include "ChannelMgr.h"
+
void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
+
uint32 channel_id;
uint8 unknown1, unknown2;
std::string channelname, pass;
+
recvPacket >> channel_id >> unknown1 >> unknown2;
recvPacket >> channelname;
+
if(channelname.empty())
return;
+
recvPacket >> pass;
if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
@@ -39,16 +46,20 @@ void WorldSession::HandleJoinChannel(WorldPacket& recvPacket)
chn->Join(_player->GetGUID(), pass.c_str());
}
}
+
void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
+
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))
@@ -56,40 +67,50 @@ void WorldSession::HandleLeaveChannel(WorldPacket& recvPacket)
cMgr->LeftChannel(channelname);
}
}
+
void WorldSession::HandleChannelList(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
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();
std::string channelname, pass;
recvPacket >> channelname;
+
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();
std::string channelname, newp;
recvPacket >> channelname;
+
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());
@@ -100,110 +121,144 @@ void WorldSession::HandleChannelOwner(WorldPacket& recvPacket)
if(Channel *chn = cMgr->GetChannel(channelname, _player))
chn->SendWhoOwner(_player->GetGUID());
}
+
void WorldSession::HandleChannelModerator(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
//recvPacket.hexlike();
std::string channelname, otp;
recvPacket >> channelname;
+
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();
std::string channelname, otp;
recvPacket >> channelname;
+
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();
std::string channelname, otp;
recvPacket >> channelname;
+
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();
+
std::string channelname, otp;
recvPacket >> channelname;
+
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();
std::string channelname, otp;
recvPacket >> channelname;
+
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();
std::string channelname, otp;
recvPacket >> channelname;
+
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();
std::string channelname, otp;
recvPacket >> channelname;
+
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();
+
std::string channelname, otp;
recvPacket >> channelname;
+
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::HandleChannelAnnouncements(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
@@ -214,6 +269,7 @@ void WorldSession::HandleChannelAnnouncements(WorldPacket& recvPacket)
if(Channel *chn = cMgr->GetChannel(channelname, _player))
chn->Announce(_player->GetGUID());
}
+
void WorldSession::HandleChannelModerate(WorldPacket& recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
@@ -224,6 +280,7 @@ void WorldSession::HandleChannelModerate(WorldPacket& recvPacket)
if(Channel *chn = cMgr->GetChannel(channelname, _player))
chn->Moderate(_player->GetGUID());
}
+
void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
@@ -234,6 +291,7 @@ void WorldSession::HandleChannelDisplayListQuery(WorldPacket &recvPacket)
if(Channel *chn = cMgr->GetChannel(channelname, _player))
chn->List(_player);
}
+
void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
@@ -252,6 +310,7 @@ void WorldSession::HandleGetChannelMemberCount(WorldPacket &recvPacket)
}
}
}
+
void WorldSession::HandleSetChannelWatch(WorldPacket &recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
diff --git a/src/game/ChannelMgr.cpp b/src/game/ChannelMgr.cpp
index 8994d84b23f..09d172155cc 100644
--- a/src/game/ChannelMgr.cpp
+++ b/src/game/ChannelMgr.cpp
@@ -15,27 +15,35 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "ChannelMgr.h"
#include "Policies/SingletonImp.h"
#include "World.h"
+
INSTANTIATE_SINGLETON_1( AllianceChannelMgr );
INSTANTIATE_SINGLETON_1( HordeChannelMgr );
+
ChannelMgr* channelMgr(uint32 team)
{
if (sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHANNEL))
return &MaNGOS::Singleton<AllianceChannelMgr>::Instance(); // cross-faction
+
if(team == ALLIANCE)
return &MaNGOS::Singleton<AllianceChannelMgr>::Instance();
if(team == HORDE)
return &MaNGOS::Singleton<HordeChannelMgr>::Instance();
+
return NULL;
}
+
ChannelMgr::~ChannelMgr()
{
for(ChannelMap::iterator itr = channels.begin();itr!=channels.end(); ++itr)
delete itr->second;
+
channels.clear();
}
+
Channel *ChannelMgr::GetJoinChannel(std::string name, uint32 channel_id)
{
if (channels.find(name) == channels.end())
@@ -43,11 +51,14 @@ Channel *ChannelMgr::GetJoinChannel(std::string name, uint32 channel_id)
Channel *nchan = new Channel(name,channel_id, team);
channels[name] = nchan;
}
+
return channels[name];
}
+
Channel *ChannelMgr::GetChannel(std::string name, Player *p, bool pkt)
{
ChannelMap::const_iterator i = channels.find(name);
+
if(i == channels.end())
{
if(pkt)
@@ -56,23 +67,29 @@ Channel *ChannelMgr::GetChannel(std::string name, Player *p, bool pkt)
MakeNotOnPacket(&data,name);
p->GetSession()->SendPacket(&data);
}
+
return NULL;
}
else
return i->second;
}
+
void ChannelMgr::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;
}
}
+
void ChannelMgr::MakeNotOnPacket(WorldPacket *data, std::string name)
{
data->Initialize(SMSG_CHANNEL_NOTIFY, (1+10)); // we guess size
diff --git a/src/game/ChannelMgr.h b/src/game/ChannelMgr.h
index 5bf07d609b9..f65a8520648 100644
--- a/src/game/ChannelMgr.h
+++ b/src/game/ChannelMgr.h
@@ -19,14 +19,19 @@
*/
#ifndef MANGOSSERVER_CHANNELMGR_H
#define MANGOSSERVER_CHANNELMGR_H
+
#include "Common.h"
#include "Channel.h"
#include "Policies/Singleton.h"
+
#include <map>
#include <string>
+
#include "Policies/Singleton.h"
+
#include "Channel.h"
#include "World.h"
+
class ChannelMgr
{
public:
@@ -34,6 +39,7 @@ class ChannelMgr
typedef std::map<std::string,Channel *> ChannelMap;
ChannelMgr() {team = 0;}
~ChannelMgr();
+
Channel *GetJoinChannel(std::string name, uint32 channel_id);
Channel *GetChannel(std::string name, Player *p, bool pkt = true);
void LeftChannel(std::string name);
@@ -41,8 +47,11 @@ class ChannelMgr
ChannelMap channels;
void MakeNotOnPacket(WorldPacket *data, std::string name);
};
+
class AllianceChannelMgr : public ChannelMgr {};
class HordeChannelMgr : public ChannelMgr {};
+
ChannelMgr* channelMgr(uint32 team);
+
#endif
diff --git a/src/game/CharacterHandler.cpp b/src/game/CharacterHandler.cpp
index e348d3b6f55..e1e6eae86a3 100644
--- a/src/game/CharacterHandler.cpp
+++ b/src/game/CharacterHandler.cpp
@@ -17,6 +17,7 @@
* 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 "ObjectAccessor.h"
#include "ObjectMgr.h"
@@ -27,6 +28,7 @@
#include "Auth/md5.h"
#include "Database/DatabaseEnv.h"
#include "Database/DatabaseImpl.h"
+
#include "ArenaTeam.h"
#include "Chat.h"
#include "Group.h"
@@ -41,6 +43,7 @@
#include "UpdateMask.h"
#include "Util.h"
#include "ScriptCalls.h"
+
class LoginQueryHolder : public SqlQueryHolder
{
private:
@@ -53,10 +56,13 @@ class LoginQueryHolder : public SqlQueryHolder
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, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, 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, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points, instance_id, speccount, activespec FROM characters WHERE guid = '%u'", GUID_LOPART(m_guid));
@@ -86,8 +92,10 @@ bool LoginQueryHolder::Initialize()
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 FROM character_glyphs WHERE guid='%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid='%u'", GUID_LOPART(m_guid));
res &= SetPQuery(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA, "SELECT type, time, data FROM character_account_data 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
@@ -116,11 +124,15 @@ class CharacterHandler
session->HandlePlayerLogin((LoginQueryHolder*)holder);
}
} chrHandler;
+
void WorldSession::HandleCharEnum(QueryResult * result)
{
WorldPacket data(SMSG_CHAR_ENUM, 100); // we guess size
+
uint8 num = 0;
+
data << num;
+
if( result )
{
do
@@ -131,11 +143,15 @@ void WorldSession::HandleCharEnum(QueryResult * result)
++num;
}
while( result->NextRow() );
+
delete result;
}
+
data.put<uint8>(0, num);
+
SendPacket( &data );
}
+
void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
{
/// get all the data necessary for loading all characters (along with their pets) on the account
@@ -165,25 +181,32 @@ void WorldSession::HandleCharEnumOpcode( WorldPacket & /*recv_data*/ )
"WHERE characters.account = '%u' ORDER BY characters.guid",
PET_SAVE_AS_CURRENT,GetAccountId());
}
+
void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
{
std::string name;
uint8 race_,class_;
+
recv_data >> name;
+
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;
@@ -192,8 +215,10 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
}
}
}
+
ChrClassesEntry const* classEntry = sChrClassesStore.LookupEntry(class_);
ChrRacesEntry const* raceEntry = sChrRacesStore.LookupEntry(race_);
+
if( !classEntry || !raceEntry )
{
data << (uint8)CHAR_CREATE_FAILED;
@@ -201,6 +226,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_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 (raceEntry->addon > Expansion())
{
@@ -209,6 +235,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
// prevent character creating Expansion class without Expansion account
if (classEntry->addon > Expansion())
{
@@ -217,6 +244,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
// prevent character creating with invalid name
if (!normalizePlayerName(name))
{
@@ -225,6 +253,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
sLog.outError("Account:[%d] but tried to Create character with empty [name] ",GetAccountId());
return;
}
+
// check name limitations
uint8 res = ObjectMgr::CheckPlayerName(name,true);
if (res != CHAR_NAME_SUCCESS)
@@ -233,24 +262,28 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
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;
@@ -258,6 +291,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
return;
}
}
+
QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(guid) FROM characters WHERE account = '%d'", GetAccountId());
uint8 charcount = 0;
if ( result )
@@ -265,6 +299,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
Field *fields=result->Fetch();
charcount = fields[0].GetUInt8();
delete result;
+
if (charcount >= sWorld.getConfig(CONFIG_CHARACTERS_PER_REALM))
{
data << (uint8)CHAR_CREATE_SERVER_LIMIT;
@@ -272,6 +307,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
return;
}
}
+
// speedup check for heroic class disabled case
uint32 heroic_free_slots = sWorld.getConfig(CONFIG_HEROIC_CHARACTERS_PER_REALM);
if(heroic_free_slots==0 && GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
@@ -280,6 +316,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
// speedup check for heroic class disabled case
uint32 req_level_for_heroic = sWorld.getConfig(CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING);
if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && req_level_for_heroic > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
@@ -288,11 +325,15 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
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 0 then allowed creating without any characters
bool have_req_level_for_heroic = (req_level_for_heroic==0);
+
if(!AllowTwoSideAccounts || skipCinematics == 1 || class_ == CLASS_DEATH_KNIGHT)
{
QueryResult *result2 = CharacterDatabase.PQuery("SELECT level,race,class FROM characters WHERE account = '%u' %s",
@@ -300,8 +341,10 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
if(result2)
{
uint32 team_= Player::TeamForRace(race_);
+
Field* field = result2->Fetch();
uint8 acc_race = field[1].GetUInt32();
+
if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
{
uint8 acc_class = field[2].GetUInt32();
@@ -309,6 +352,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
{
if(heroic_free_slots > 0)
--heroic_free_slots;
+
if(heroic_free_slots==0)
{
data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT;
@@ -316,6 +360,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
return;
}
}
+
if(!have_req_level_for_heroic)
{
uint32 acc_level = field[0].GetUInt32();
@@ -323,6 +368,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
have_req_level_for_heroic = true;
}
}
+
// need to check team only for first character
// TODO: what to if account already has characters of both races?
if (!AllowTwoSideAccounts)
@@ -330,6 +376,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
uint32 acc_team=0;
if(acc_race > 0)
acc_team = Player::TeamForRace(acc_race);
+
if(acc_team != team_)
{
data << (uint8)CHAR_CREATE_PVP_TEAMS_VIOLATION;
@@ -338,16 +385,20 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
return;
}
}
+
// search same race for cinematic or same class if need
// TODO: check if cinematic already shown? (already logged in?; cinematic field)
while ((skipCinematics == 1 && !have_same_race) || class_ == CLASS_DEATH_KNIGHT)
{
if(!result2->NextRow())
break;
+
field = result2->Fetch();
acc_race = field[1].GetUInt32();
+
if(!have_same_race)
have_same_race = race_ == acc_race;
+
if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT)
{
uint8 acc_class = field[2].GetUInt32();
@@ -355,6 +406,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
{
if(heroic_free_slots > 0)
--heroic_free_slots;
+
if(heroic_free_slots==0)
{
data << (uint8)CHAR_CREATE_UNIQUE_CLASS_LIMIT;
@@ -362,6 +414,7 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
return;
}
}
+
if(!have_req_level_for_heroic)
{
uint32 acc_level = field[0].GetUInt32();
@@ -373,12 +426,14 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
delete result2;
}
}
+
if(GetSecurity()==SEC_PLAYER && class_ == CLASS_DEATH_KNIGHT && !have_req_level_for_heroic)
{
data << (uint8)CHAR_CREATE_LEVEL_REQUIREMENT;
SendPacket( &data );
return;
}
+
// extract other data required for player creating
uint8 gender, skin, face, hairStyle, hairColor, facialHair, outfitId;
recv_data >> gender;
@@ -388,44 +443,58 @@ void WorldSession::HandleCharCreateOpcode( WorldPacket & recv_data )
recv_data >> hairColor;
recv_data >> facialHair;
recv_data >> outfitId;
+
if(recv_data.rpos() < recv_data.wpos())
{
uint8 unk;
recv_data >> unk;
sLog.outDebug("Character creation %s (account %u) has unhandled tail data: [%u]", name.c_str(), GetAccountId(), unk);
}
+
Player * pNewChar = new Player(this);
if(!pNewChar->Create( objmgr.GenerateLowGuid(HIGHGUID_PLAYER), name, race_, class_, gender, skin, face, hairStyle, hairColor, facialHair, outfitId ))
{
// Player not create (race/class problem?)
delete pNewChar;
+
data << (uint8)CHAR_CREATE_ERROR;
SendPacket( &data );
+
return;
}
+
if ((have_same_race && skipCinematics == 1) || skipCinematics == 2)
pNewChar->setCinematic(1); // not show intro
+
// Player created, save it now
pNewChar->SaveToDB();
charcount+=1;
+
loginDatabase.PExecute("DELETE FROM realmcharacters WHERE acctid= '%d' AND realmid = '%d'", GetAccountId(), realmID);
loginDatabase.PExecute("INSERT INTO realmcharacters (numchars, acctid, realmid) VALUES (%u, %u, %u)", charcount, GetAccountId(), realmID);
+
delete pNewChar; // created only to call SaveToDB()
+
data << (uint8)CHAR_CREATE_SUCCESS;
SendPacket( &data );
+
std::string IP_str = GetRemoteAddress();
sLog.outDetail("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 )
{
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))
{
@@ -434,6 +503,7 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
// is arena team captain
if(objmgr.GetArenaTeamByCaptain(guid))
{
@@ -442,6 +512,7 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
QueryResult *result = CharacterDatabase.PQuery("SELECT account,name FROM characters WHERE guid='%u'", GUID_LOPART(guid));
if(result)
{
@@ -450,22 +521,28 @@ void WorldSession::HandleCharDeleteOpcode( WorldPacket & recv_data )
name = fields[1].GetCppString();
delete result;
}
+
// prevent deleting other players' characters using cheating tools
if(accountId != GetAccountId())
return;
+
std::string IP_str = GetRemoteAddress();
sLog.outDetail("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 )
{
if(PlayerLoading() || GetPlayer() != NULL)
@@ -473,10 +550,14 @@ void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
sLog.outError("Player tryes to login again, AccountId = %d",GetAccountId());
return;
}
+
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())
{
@@ -484,14 +565,18 @@ void WorldSession::HandlePlayerLoginOpcode( WorldPacket & recv_data )
m_playerLoading = false;
return;
}
+
CharacterDatabase.DelayQueryHolder(&chrHandler, &CharacterHandler::HandlePlayerLoginCallback, holder);
}
+
void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
{
uint64 playerGuid = holder->GetGuid();
+
Player* pCurrChar = new Player(this);
// for send server info and strings (config)
ChatHandler chH = ChatHandler(pCurrChar);
+
// "GetAccountId()==db stored account id" checked in LoadFromDB (prevent login not own character using cheating tools)
if(!pCurrChar->LoadFromDB(GUID_LOPART(playerGuid), holder))
{
@@ -501,9 +586,13 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
m_playerLoading = false;
return;
}
+
pCurrChar->GetMotionMaster()->Initialize();
+
SetPlayer(pCurrChar);
+
pCurrChar->SendDungeonDifficulty(false);
+
WorldPacket data( SMSG_LOGIN_VERIFY_WORLD, 20 );
data << pCurrChar->GetMapId();
data << pCurrChar->GetPositionX();
@@ -511,20 +600,25 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
data << pCurrChar->GetPositionZ();
data << pCurrChar->GetOrientation();
SendPacket(&data);
+
// load player specific part before send times
LoadAccountData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACCOUNTDATA),PER_CHARACTER_CACHE_MASK);
SendAccountDataTimes();
+
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 )
{
@@ -535,25 +629,33 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
}
pos = nextpos+1;
}
+
if (pos<str_motd.length())
{
data << str_motd.substr(pos);
++linecount;
}
+
data.put(0, linecount);
+
SendPacket( &data );
DEBUG_LOG( "WORLD: Sent motd (SMSG_MOTD)" );
+
// send server info
if(sWorld.getConfig(CONFIG_ENABLE_SINFO_LOGIN) == 1)
chH.PSendSysMessage(_FULLVERSION);
+
DEBUG_LOG( "WORLD: Sent server info" );
}
+
data.Initialize(SMSG_LEARNED_DANCE_MOVES, 4+4);
data << uint32(0);
data << uint32(0);
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();
@@ -566,6 +668,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
pCurrChar->SetInGuild(0);
pCurrChar->SetRank(0);
}
+
if(pCurrChar->GetGuildId() != 0)
{
Guild* guild = objmgr.GetGuildById(pCurrChar->GetGuildId());
@@ -577,6 +680,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
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;
@@ -584,6 +688,7 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
data<<pCurrChar->GetGUID();
guild->BroadcastPacket(&data);
DEBUG_LOG( "WORLD: Sent guild-signed-on (SMSG_GUILD_EVENT)" );
+
// Increment online members of the guild
guild->IncOnlineMemberCount();
}
@@ -594,24 +699,30 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
pCurrChar->SetInGuild(0);
}
}
+
if(!pCurrChar->isAlive())
pCurrChar->SendCorpseReclaimDelay(true);
+
pCurrChar->SendInitialPacketsBeforeAddToMap();
+
//Show cinematic at the first time that player login
if( !pCurrChar->getCinematic() )
{
pCurrChar->setCinematic(1);
+
if(ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(pCurrChar->getClass()))
{
if (cEntry->CinematicSequence)
pCurrChar->SendCinematicStart(cEntry->CinematicSequence);
else if (ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(pCurrChar->getRace()))
pCurrChar->SendCinematicStart(rEntry->CinematicSequence);
+
// send new char string if not empty
if (!sWorld.GetNewCharString().empty())
chH.PSendSysMessage(sWorld.GetNewCharString().c_str());
}
}
+
if (!pCurrChar->GetMap()->Add(pCurrChar))
{
AreaTrigger const* at = objmgr.GetGoBackTrigger(pCurrChar->GetMapId());
@@ -620,22 +731,29 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
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->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 != ALIVE)
{
@@ -643,57 +761,78 @@ void WorldSession::HandlePlayerLogin(LoginQueryHolder * holder)
if(pCurrChar->getRace() == RACE_NIGHTELF)
pCurrChar->CastSpell(pCurrChar, SPELL_ID_NE_GHOST, 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, SPELL_ID_GHOST, true, 0); // auras SPELL_AURA_GHOST, SPELL_AURA_INCREASE_SPEED(why?), SPELL_AURA_INCREASE_SWIM_SPEED(why?)
+
pCurrChar->SetMovement(MOVE_WATER_WALK);
}
+
pCurrChar->ContinueTaxiFlight();
+
// reset for all pets before pet loading
if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
Pet::resetTalentsForAllPetsOf(pCurrChar);
+
// Load pet if any (if player not alive and in taxi flight or another then pet will remember as temporary unsummoned)
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->SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
+
if(pCurrChar->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_CONTESTED_PVP))
pCurrChar->SetContestedPvP();
+
// Apply at_login requests
if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
{
pCurrChar->resetSpells();
SendNotification(LANG_RESET_SPELLS);
}
+
if(pCurrChar->HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
{
pCurrChar->resetTalents(true);
pCurrChar->SendTalentsInfoData(false); // original talents send already in to SendInitialPacketsBeforeAddToMap, resend reset state
SendNotification(LANG_RESET_TALENTS);
}
+
// show time before shutdown if shutdown planned.
if(sWorld.IsShutdowning())
sWorld.ShutdownMsg(true,pCurrChar);
+
if(sWorld.getConfig(CONFIG_ALL_TAXI_PATHS))
pCurrChar->SetTaxiCheater(true);
+
if(pCurrChar->isGameMaster())
SendNotification(LANG_GM_ON);
+
std::string IP_str = GetRemoteAddress();
sLog.outChar("Account: %d (IP: %s) Login Character:[%s] (guid:%u)",
GetAccountId(),IP_str.c_str(),pCurrChar->GetName() ,pCurrChar->GetGUIDLow());
+
if(!pCurrChar->IsStandState() && !pCurrChar->hasUnitState(UNIT_STAT_STUNNED))
pCurrChar->SetStandState(UNIT_STAND_STATE_STAND);
+
m_playerLoading = false;
+
//Hook for OnLogin Event
Script->OnLogin(pCurrChar);
+
delete holder;
}
+
void WorldSession::HandleSetFactionAtWar( WorldPacket & recv_data )
{
DEBUG_LOG( "WORLD: Received CMSG_SET_FACTION_ATWAR" );
+
uint32 repListID;
uint8 flag;
+
recv_data >> repListID;
recv_data >> flag;
+
GetPlayer()->GetReputationMgr().SetAtWar(repListID,flag);
}
+
//I think this function is never used :/ I dunno, but i guess this opcode not exists
void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
{
@@ -701,9 +840,12 @@ void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
/*
uint32 FactionID;
uint32 Standing;
+
recv_data >> FactionID;
recv_data >> Standing;
+
std::list<struct Factions>::iterator itr;
+
for(itr = GetPlayer()->factions.begin(); itr != GetPlayer()->factions.end(); ++itr)
{
if(itr->ReputationListID == FactionID)
@@ -716,15 +858,19 @@ void WorldSession::HandleSetFactionCheat( WorldPacket & /*recv_data*/ )
*/
GetPlayer()->GetReputationMgr().SendStates();
}
+
void WorldSession::HandleMeetingStoneInfo( WorldPacket & /*recv_data*/ )
{
DEBUG_LOG( "WORLD: Received CMSG_MEETING_STONE_INFO" );
+
SendLfgUpdate(0, 0, 0);
}
+
void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
{
uint32 iFlag;
recv_data >> iFlag;
+
uint32 wInt = (iFlag / 32);
if (wInt >= 8)
{
@@ -732,21 +878,26 @@ void WorldSession::HandleTutorialFlag( WorldPacket & recv_data )
return;
}
uint32 rInt = (iFlag % 32);
+
uint32 tutflag = GetTutorialInt( wInt );
tutflag |= (1 << rInt);
SetTutorialInt( wInt, tutflag );
+
//sLog.outDebug("Received Tutorial Flag Set {%u}.", iFlag);
}
+
void WorldSession::HandleTutorialClear( WorldPacket & /*recv_data*/ )
{
for (int i = 0; i < 8; ++i)
SetTutorialInt( i, 0xFFFFFFFF );
}
+
void WorldSession::HandleTutorialReset( WorldPacket & /*recv_data*/ )
{
for (int i = 0; i < 8; ++i)
SetTutorialInt( i, 0x00000000 );
}
+
void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket & recv_data)
{
DEBUG_LOG("WORLD: Received CMSG_SET_WATCHED_FACTION");
@@ -754,30 +905,37 @@ void WorldSession::HandleSetWatchedFactionOpcode(WorldPacket & recv_data)
recv_data >> fact;
GetPlayer()->SetUInt32Value(PLAYER_FIELD_WATCHED_FACTION_INDEX, fact);
}
+
void WorldSession::HandleSetFactionInactiveOpcode(WorldPacket & recv_data)
{
DEBUG_LOG("WORLD: Received CMSG_SET_FACTION_INACTIVE");
uint32 replistid;
uint8 inactive;
recv_data >> replistid >> inactive;
+
_player->GetReputationMgr().SetInactive(replistid, inactive);
}
+
void WorldSession::HandleShowingHelmOpcode( WorldPacket & /*recv_data*/ )
{
DEBUG_LOG("CMSG_SHOWING_HELM for %s", _player->GetName());
_player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_HELM);
}
+
void WorldSession::HandleShowingCloakOpcode( WorldPacket & /*recv_data*/ )
{
DEBUG_LOG("CMSG_SHOWING_CLOAK for %s", _player->GetName());
_player->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_HIDE_CLOAK);
}
+
void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data)
{
uint64 guid;
std::string newname;
+
recv_data >> guid;
recv_data >> newname;
+
// prevent character rename to invalid name
if (!normalizePlayerName(newname))
{
@@ -786,6 +944,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
uint8 res = ObjectMgr::CheckPlayerName(newname,true);
if (res != CHAR_NAME_SUCCESS)
{
@@ -794,6 +953,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
// check name limitations
if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname))
{
@@ -802,8 +962,10 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
std::string escaped_newname = newname;
CharacterDatabase.escape_string(escaped_newname);
+
// make sure that the character belongs to the current account, that rename at login is enabled
// and that there is no character with the desired new name
CharacterDatabase.AsyncPQuery(&WorldSession::HandleChangePlayerNameOpcodeCallBack,
@@ -812,6 +974,7 @@ void WorldSession::HandleCharRenameOpcode(WorldPacket& recv_data)
GUID_LOPART(guid), GetAccountId(), AT_LOGIN_RENAME, AT_LOGIN_RENAME, escaped_newname.c_str()
);
}
+
void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uint32 accountId, std::string newname)
{
WorldSession * session = sWorld.FindSession(accountId);
@@ -820,6 +983,7 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin
if(result) delete result;
return;
}
+
if (!result)
{
WorldPacket data(SMSG_CHAR_RENAME, 1);
@@ -827,23 +991,31 @@ void WorldSession::HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uin
session->SendPacket( &data );
return;
}
+
uint32 guidLow = result->Fetch()[0].GetUInt32();
uint64 guid = MAKE_NEW_GUID(guidLow, 0, HIGHGUID_PLAYER);
std::string oldname = result->Fetch()[1].GetCppString();
+
delete result;
+
CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_RENAME), guidLow);
CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", guidLow);
+
sLog.outChar("Account: %d (IP: %s) Character:[%s] (guid:%u) Changed name to: %s", session->GetAccountId(), session->GetRemoteAddress().c_str(), oldname.c_str(), guidLow, newname.c_str());
+
WorldPacket data(SMSG_CHAR_RENAME, 1+8+(newname.size()+1));
data << uint8(RESPONSE_SUCCESS);
data << uint64(guid);
data << newname;
session->SendPacket(&data);
}
+
void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
{
uint64 guid;
+
recv_data >> guid;
+
// not accept declined names for unsupported languages
std::string name;
if(!objmgr.GetPlayerNameByGUID(guid, name))
@@ -854,6 +1026,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
SendPacket(&data);
return;
}
+
std::wstring wname;
if(!Utf8toWStr(name, wname))
{
@@ -863,6 +1036,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
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);
@@ -871,9 +1045,12 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
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);
@@ -882,6 +1059,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
SendPacket(&data);
return;
}
+
for(int i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
{
recv_data >> declinedname.name[i];
@@ -894,6 +1072,7 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
return;
}
}
+
if(!ObjectMgr::CheckDeclinedNames(GetMainPartOfName(wname, 0), declinedname))
{
WorldPacket data(SMSG_SET_PLAYER_DECLINED_NAMES_RESULT, 4+8);
@@ -902,30 +1081,41 @@ void WorldSession::HandleSetPlayerDeclinedNames(WorldPacket& recv_data)
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 << uint64(guid);
SendPacket(&data);
}
+
void WorldSession::HandleAlterAppearance( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_ALTER_APPEARANCE");
+
uint32 Hair, Color, FacialHair;
recv_data >> Hair >> Color >> FacialHair;
+
BarberShopStyleEntry const* bs_hair = sBarberShopStyleStore.LookupEntry(Hair);
+
if(!bs_hair || bs_hair->type != 0 || bs_hair->race != _player->getRace() || bs_hair->gender != _player->getGender())
return;
+
BarberShopStyleEntry const* bs_facialHair = sBarberShopStyleStore.LookupEntry(FacialHair);
+
if(!bs_facialHair || bs_facialHair->type != 2 || bs_facialHair->race != _player->getRace() || bs_facialHair->gender != _player->getGender())
return;
+
uint32 Cost = _player->GetBarberShopCost(bs_hair->hair_id, Color, bs_facialHair->hair_id);
+
// 0 - ok
// 1,3 - not enough money
// 2 - you have to seat on barber chair
@@ -942,23 +1132,30 @@ void WorldSession::HandleAlterAppearance( WorldPacket & recv_data )
data << uint32(0); // ok
SendPacket(&data);
}
+
_player->ModifyMoney(-int32(Cost)); // it isn't free
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_AT_BARBER, Cost);
+
_player->SetByteValue(PLAYER_BYTES, 2, uint8(bs_hair->hair_id));
_player->SetByteValue(PLAYER_BYTES, 3, uint8(Color));
_player->SetByteValue(PLAYER_BYTES_2, 0, uint8(bs_facialHair->hair_id));
+
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP, 1);
+
_player->SetStandState(0); // stand up
}
+
void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data )
{
uint32 slot;
recv_data >> slot;
+
if(slot >= MAX_GLYPH_SLOT_INDEX)
{
sLog.outDebug("Client sent wrong glyph slot number in opcode CMSG_REMOVE_GLYPH %u", slot);
return;
}
+
if(uint32 glyph = _player->GetGlyph(slot))
{
if(GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
@@ -969,14 +1166,18 @@ void WorldSession::HandleRemoveGlyph( WorldPacket & recv_data )
}
}
}
+
void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
{
uint64 guid;
std::string newname;
+
recv_data >> guid;
recv_data >> newname;
+
uint8 gender, skin, face, hairStyle, hairColor, facialHair;
recv_data >> gender >> skin >> hairColor >> hairStyle >> facialHair >> face;
+
QueryResult *result = CharacterDatabase.PQuery("SELECT at_login FROM characters WHERE guid ='%u'", GUID_LOPART(guid));
if (!result)
{
@@ -985,9 +1186,11 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
Field *fields = result->Fetch();
uint32 at_loginFlags = fields[0].GetUInt32();
delete result;
+
if (!(at_loginFlags & AT_LOGIN_CUSTOMIZE))
{
WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1);
@@ -995,6 +1198,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
// prevent character rename to invalid name
if (!normalizePlayerName(newname))
{
@@ -1003,6 +1207,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
uint8 res = ObjectMgr::CheckPlayerName(newname,true);
if (res != CHAR_NAME_SUCCESS)
{
@@ -1011,6 +1216,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
// check name limitations
if (GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(newname))
{
@@ -1019,6 +1225,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
SendPacket( &data );
return;
}
+
// character with this name already exist
if (uint64 newguid = objmgr.GetPlayerGUIDByName(newname))
{
@@ -1030,6 +1237,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
return;
}
}
+
CharacterDatabase.escape_string(newname);
if(QueryResult *result = CharacterDatabase.PQuery("SELECT name FROM characters WHERE guid ='%u'", GUID_LOPART(guid)))
{
@@ -1040,6 +1248,7 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
Player::Customize(guid, gender, skin, face, hairStyle, hairColor, facialHair);
CharacterDatabase.PExecute("UPDATE characters set name = '%s', at_login = at_login & ~ %u WHERE guid ='%u'", newname.c_str(), uint32(AT_LOGIN_CUSTOMIZE), GUID_LOPART(guid));
CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid ='%u'", GUID_LOPART(guid));
+
WorldPacket data(SMSG_CHAR_CUSTOMIZE, 1+8+(newname.size()+1)+6);
data << uint8(RESPONSE_SUCCESS);
data << uint64(guid);
@@ -1052,66 +1261,90 @@ void WorldSession::HandleCharCustomize(WorldPacket& recv_data)
data << uint8(facialHair);
SendPacket(&data);
}
+
void WorldSession::HandleEquipmentSetSave(WorldPacket &recv_data)
{
sLog.outDebug("CMSG_EQUIPMENT_SET_SAVE");
+
uint64 setGuid;
if(!recv_data.readPackGUID(setGuid))
return;
+
uint32 index;
recv_data >> index;
if(index >= MAX_EQUIPMENT_SET_INDEX) // client set slots amount
return;
+
std::string name;
recv_data >> name;
+
std::string iconName;
recv_data >> iconName;
+
EquipmentSet eqSet;
+
eqSet.Guid = setGuid;
eqSet.Name = name;
eqSet.IconName = iconName;
eqSet.state = EQUIPMENT_SET_NEW;
+
for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
uint64 itemGuid;
if(!recv_data.readPackGUID(itemGuid))
return;
+
Item *item = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
+
if(!item && itemGuid) // cheating check 1
return;
+
if(item && item->GetGUID() != itemGuid) // cheating check 2
return;
+
eqSet.Items[i] = GUID_LOPART(itemGuid);
}
+
_player->SetEquipmentSet(index, eqSet);
}
+
void WorldSession::HandleEquipmentSetDelete(WorldPacket &recv_data)
{
sLog.outDebug("CMSG_EQUIPMENT_SET_DELETE");
+
uint64 setGuid;
if(!recv_data.readPackGUID(setGuid))
return;
+
_player->DeleteEquipmentSet(setGuid);
}
+
void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data)
{
sLog.outDebug("CMSG_EQUIPMENT_SET_USE");
recv_data.hexlike();
+
for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
uint64 itemGuid;
if(!recv_data.readPackGUID(itemGuid))
return;
+
uint8 srcbag, srcslot;
recv_data >> srcbag >> srcslot;
+
sLog.outDebug("Item " UI64FMTD ": srcbag %u, srcslot %u", itemGuid, srcbag, srcslot);
+
Item *item = _player->GetItemByGuid(itemGuid);
+
uint16 dstpos = i | (INVENTORY_SLOT_BAG_0 << 8);
+
if(!item)
{
Item *uItem = _player->GetItemByPos(INVENTORY_SLOT_BAG_0, i);
if(!uItem)
continue;
+
ItemPosCountVec sDest;
uint8 msg = _player->CanStoreItem( NULL_BAG, NULL_SLOT, sDest, uItem, false );
if(msg == EQUIP_ERR_OK)
@@ -1121,48 +1354,61 @@ void WorldSession::HandleEquipmentSetUse(WorldPacket &recv_data)
}
else
_player->SendEquipError(msg, uItem, NULL);
+
continue;
}
+
if(item->GetPos() == dstpos)
continue;
+
_player->SwapItem(item->GetPos(), dstpos);
}
+
WorldPacket data(SMSG_EQUIPMENT_SET_USE_RESULT, 1);
data << uint8(0); // 4 - equipment swap failed - inventory is full
SendPacket(&data);
}
+
void WorldSession::HandleOnPVPKill(Player *killed)
{
Script->OnPVPKill(GetPlayer(), killed);
}
+
bool WorldSession::HandleOnPlayerChat(const char *text)
{
return Script->OnPlayerChat(GetPlayer(), text);
}
+
uint32 WorldSession::HandleOnGetXP(uint32 amount)
{
return Script->OnGetXP(GetPlayer(), amount);
}
+
int32 WorldSession::HandleOnGetMoney(int32 amount)
{
return Script->OnGetMoney(GetPlayer(), amount);
}
+
void WorldSession::HandleOnAreaChange(AreaTableEntry const *pArea)
{
Script->OnAreaChange(GetPlayer(), pArea);
}
+
bool WorldSession::HandleOnItemClick(Item *pItem)
{
return Script->OnItemClick(GetPlayer(), pItem);
}
+
bool WorldSession::HandleOnItemOpen(Item *pItem)
{
return Script->OnItemOpen(GetPlayer(), pItem);
}
+
bool WorldSession::HandleOnGoClick(GameObject *pGameObject)
{
return Script->OnGoClick(GetPlayer(), pGameObject);
}
+
void WorldSession::HandleOnCreatureKill(Creature *pCreature)
{
Script->OnCreatureKill(GetPlayer(), pCreature);
diff --git a/src/game/Chat.cpp b/src/game/Chat.cpp
index bb8d71e4a3c..32233aae5a9 100644
--- a/src/game/Chat.cpp
+++ b/src/game/Chat.cpp
@@ -17,12 +17,14 @@
* 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 "ObjectMgr.h"
#include "World.h"
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Database/DatabaseEnv.h"
+
#include "AccountMgr.h"
#include "CellImpl.h"
#include "Chat.h"
@@ -34,6 +36,7 @@
#include "UpdateMask.h"
#include "SpellMgr.h"
#include "ScriptCalls.h"
+
// Supported shift-links (client generated and server side)
// |color|Hachievement:achievement_id:player_guid:0:0:0:0:0:0:0:0|h[name]|h|r
// - client, item icon shift click, not used in server currently
@@ -56,7 +59,9 @@
// |color|Htaxinode:id|h[name]|h|r
// |color|Htele:id|h[name]|h|r
// |color|Htrade:spell_id,cur_value,max_value,unk3int,unk3str|h[name]|h|r - client, spellbook profession icon shift-click
+
bool ChatHandler::load_command_table = true;
+
ChatCommand * ChatHandler::getCommandTable()
{
static ChatCommand accountSetCommandTable[] =
@@ -66,6 +71,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "password", SEC_CONSOLE, true, &ChatHandler::HandleAccountSetPasswordCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand accountCommandTable[] =
{
{ "addon", SEC_PLAYER, false, &ChatHandler::HandleAccountAddonCommand, "", NULL },
@@ -78,6 +84,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "", SEC_PLAYER, false, &ChatHandler::HandleAccountCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand banCommandTable[] =
{
{ "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanAccountCommand, "", NULL },
@@ -85,6 +92,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanIPCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand baninfoCommandTable[] =
{
{ "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoAccountCommand, "", NULL },
@@ -92,6 +100,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanInfoIPCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand banlistCommandTable[] =
{
{ "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListAccountCommand, "", NULL },
@@ -99,6 +108,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleBanListIPCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand castCommandTable[] =
{
{ "back", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastBackCommand, "", NULL },
@@ -108,6 +118,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleCastCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand characterCommandTable[] =
{
{ "customize", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterCustomizeCommand, "", NULL },
@@ -117,6 +128,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "reputation", SEC_GAMEMASTER, true, &ChatHandler::HandleCharacterReputationCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand debugPlayCommandTable[] =
{
{ "cinematic", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlayCinematicCommand, "", NULL },
@@ -124,6 +136,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "sound", SEC_MODERATOR, false, &ChatHandler::HandleDebugPlaySoundCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand debugSendCommandTable[] =
{
{ "buyerror", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendBuyErrorCommand, "", NULL },
@@ -140,6 +153,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "spellfail", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSendSpellFailCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand debugCommandTable[] =
{
{ "setbit", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugSet32Bit, "", NULL },
@@ -165,6 +179,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "itemexpire", SEC_ADMINISTRATOR, false, &ChatHandler::HandleDebugItemExpireCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand eventCommandTable[] =
{
{ "activelist", SEC_GAMEMASTER, true, &ChatHandler::HandleEventActiveListCommand, "", NULL },
@@ -173,6 +188,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "", SEC_GAMEMASTER, true, &ChatHandler::HandleEventInfoCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand gmCommandTable[] =
{
{ "chat", SEC_MODERATOR, false, &ChatHandler::HandleGMChatCommand, "", NULL },
@@ -183,6 +199,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "", SEC_MODERATOR, false, &ChatHandler::HandleGMCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand goCommandTable[] =
{
{ "creature", SEC_MODERATOR, false, &ChatHandler::HandleGoCreatureCommand, "", NULL },
@@ -195,9 +212,12 @@ ChatCommand * ChatHandler::getCommandTable()
{ "xy", SEC_MODERATOR, false, &ChatHandler::HandleGoXYCommand, "", NULL },
{ "xyz", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL },
{ "", SEC_MODERATOR, false, &ChatHandler::HandleGoXYZCommand, "", NULL },
+
{ "ticket", SEC_MODERATOR, false, &ChatHandler::HandleGoTicketCommand, "", NULL },
+
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand gobjectCommandTable[] =
{
{ "activate", SEC_GAMEMASTER, false, &ChatHandler::HandleActivateObjectCommand, "", NULL },
@@ -212,6 +232,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "turn", SEC_GAMEMASTER, false, &ChatHandler::HandleGameObjectTurnCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand groupCommandTable[] =
{
{ "leader", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupLeaderCommand, "", NULL },
@@ -219,6 +240,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleGroupRemoveCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand guildCommandTable[] =
{
{ "create", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildCreateCommand, "", NULL },
@@ -228,6 +250,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "rank", SEC_GAMEMASTER, true, &ChatHandler::HandleGuildRankCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand honorCommandTable[] =
{
{ "add", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorAddCommand, "", NULL },
@@ -235,6 +258,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "update", SEC_GAMEMASTER, false, &ChatHandler::HandleHonorUpdateCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand instanceCommandTable[] =
{
{ "listbinds", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceListBindsCommand, "", NULL },
@@ -243,6 +267,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "savedata", SEC_ADMINISTRATOR, false, &ChatHandler::HandleInstanceSaveDataCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand learnCommandTable[] =
{
{ "all", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnAllCommand, "", NULL },
@@ -258,6 +283,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "", SEC_ADMINISTRATOR, false, &ChatHandler::HandleLearnCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand listCommandTable[] =
{
{ "creature", SEC_ADMINISTRATOR, true, &ChatHandler::HandleListCreatureCommand, "", NULL },
@@ -266,6 +292,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "auras", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListAurasCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand lookupPlayerCommandTable[] =
{
{ "ip", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerIpCommand, "", NULL },
@@ -273,6 +300,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "email", SEC_GAMEMASTER, true, &ChatHandler::HandleLookupPlayerEmailCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand lookupCommandTable[] =
{
{ "area", SEC_MODERATOR, true, &ChatHandler::HandleLookupAreaCommand, "", NULL },
@@ -291,6 +319,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "map", SEC_ADMINISTRATOR, true, &ChatHandler::HandleLookupMapCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand modifyCommandTable[] =
{
{ "hp", SEC_MODERATOR, false, &ChatHandler::HandleModifyHPCommand, "", NULL },
@@ -321,6 +350,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "gender", SEC_GAMEMASTER, false, &ChatHandler::HandleModifyGenderCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand npcCommandTable[] =
{
{ "add", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcAddCommand, "", NULL },
@@ -352,13 +382,16 @@ ChatCommand * ChatHandler::getCommandTable()
{ "setdeathstate", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSetDeathStateCommand, "", NULL },
{ "addformation", SEC_MODERATOR, false, &ChatHandler::HandleNpcAddFormationCommand, "", NULL },
{ "setlink", SEC_MODERATOR, false, &ChatHandler::HandleNpcSetLinkCommand, "", NULL },
+
//{ TODO: fix or remove this commands
{ "addweapon", SEC_ADMINISTRATOR, false, &ChatHandler::HandleNpcAddWeaponCommand, "", NULL },
{ "name", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcNameCommand, "", NULL },
{ "subname", SEC_GAMEMASTER, false, &ChatHandler::HandleNpcSubNameCommand, "", NULL },
//}
+
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand petCommandTable[] =
{
{ "create", SEC_GAMEMASTER, false, &ChatHandler::HandleCreatePetCommand, "", NULL },
@@ -367,12 +400,14 @@ ChatCommand * ChatHandler::getCommandTable()
{ "tp", SEC_GAMEMASTER, false, &ChatHandler::HandlePetTpCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand pdumpCommandTable[] =
{
{ "load", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpLoadCommand, "", NULL },
{ "write", SEC_ADMINISTRATOR, true, &ChatHandler::HandlePDumpWriteCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand questCommandTable[] =
{
{ "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestAdd, "", NULL },
@@ -380,6 +415,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "remove", SEC_ADMINISTRATOR, false, &ChatHandler::HandleQuestRemove, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand reloadCommandTable[] =
{
{ "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllCommand, "", NULL },
@@ -393,7 +429,9 @@ ChatCommand * ChatHandler::getCommandTable()
{ "all_quest", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllQuestCommand, "", NULL },
{ "all_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllScriptsCommand, "", NULL },
{ "all_spell", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAllSpellCommand, "", NULL },
+
{ "config", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadConfigCommand, "", NULL },
+
{ "access_requirement", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAccessRequirementCommand, "", NULL },
{ "achievement_criteria_data", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementCriteriaDataCommand, "", NULL },
{ "achievement_reward", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAchievementRewardCommand, "", NULL },
@@ -469,8 +507,10 @@ ChatCommand * ChatHandler::getCommandTable()
{ "auctions", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadAuctionsCommand, "", NULL },
{ "waypoint_scripts", SEC_ADMINISTRATOR, true, &ChatHandler::HandleReloadWpScriptsCommand, "", NULL },
{ "gm_tickets", SEC_ADMINISTRATOR, true, &ChatHandler::HandleGMTicketReloadCommand, "", NULL },
+
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand resetCommandTable[] =
{
{ "achievements", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetAchievementsCommand, "", NULL },
@@ -482,6 +522,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "all", SEC_ADMINISTRATOR, true, &ChatHandler::HandleResetAllCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand sendCommandTable[] =
{
{ "items", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendItemsCommand, "", NULL },
@@ -490,30 +531,35 @@ ChatCommand * ChatHandler::getCommandTable()
{ "money", SEC_ADMINISTRATOR, true, &ChatHandler::HandleSendMoneyCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand serverIdleRestartCommandTable[] =
{
{ "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL },
{ "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleRestartCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand serverIdleShutdownCommandTable[] =
{
{ "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL },
{ "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerIdleShutDownCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand serverRestartCommandTable[] =
{
{ "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL },
{ "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerRestartCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand serverShutdownCommandTable[] =
{
{ "cancel", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCancelCommand,"", NULL },
{ "" , SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerShutDownCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand serverSetCommandTable[] =
{
{ "difftime", SEC_CONSOLE, true, &ChatHandler::HandleServerSetDiffTimeCommand, "", NULL },
@@ -523,6 +569,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "closed", SEC_ADMINISTRATOR, true, &ChatHandler::HandleServerSetClosedCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand serverCommandTable[] =
{
{ "corpses", SEC_GAMEMASTER, true, &ChatHandler::HandleServerCorpsesCommand, "", NULL },
@@ -537,6 +584,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "set", SEC_ADMINISTRATOR, true, NULL, "", serverSetCommandTable },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand teleCommandTable[] =
{
{ "add", SEC_ADMINISTRATOR, false, &ChatHandler::HandleTeleAddCommand, "", NULL },
@@ -546,6 +594,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "", SEC_MODERATOR, false, &ChatHandler::HandleTeleCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand unbanCommandTable[] =
{
{ "account", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanAccountCommand, "", NULL },
@@ -553,6 +602,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "ip", SEC_ADMINISTRATOR, true, &ChatHandler::HandleUnBanIPCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand wpCommandTable[] =
{
{ "show", SEC_GAMEMASTER, false, &ChatHandler::HandleWpShowCommand, "", NULL },
@@ -563,6 +613,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "unload", SEC_GAMEMASTER, false, &ChatHandler::HandleWpUnLoadPathCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand ticketCommandTable[] =
{
{ "list", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketListCommand, "", NULL },
@@ -577,6 +628,7 @@ ChatCommand * ChatHandler::getCommandTable()
{ "comment", SEC_MODERATOR, false, &ChatHandler::HandleGMTicketCommentCommand, "", NULL },
{ NULL, 0, false, NULL, "", NULL }
};
+
static ChatCommand commandTable[] =
{
{ "account", SEC_PLAYER, true, NULL, "", accountCommandTable },
@@ -602,10 +654,12 @@ ChatCommand * ChatHandler::getCommandTable()
{ "reset", SEC_ADMINISTRATOR, true, NULL, "", resetCommandTable },
{ "instance", SEC_ADMINISTRATOR, true, NULL, "", instanceCommandTable },
{ "server", SEC_ADMINISTRATOR, true, NULL, "", serverCommandTable },
+
{ "pet", SEC_GAMEMASTER, false, NULL, "", petCommandTable },
{ "loadpath", SEC_ADMINISTRATOR, false, &ChatHandler::HandleReloadAllPaths, "", NULL },
{ "ahbotoptions", SEC_GAMEMASTER, true, &ChatHandler::HandleAHBotOptionsCommand, "", NULL },
{ "ticket", SEC_MODERATOR, false, NULL, "", ticketCommandTable },
+
{ "aura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleAuraCommand, "", NULL },
{ "unaura", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnAuraCommand, "", NULL },
{ "nameannounce", SEC_MODERATOR, false, &ChatHandler::HandleNameAnnounceCommand, "", NULL },
@@ -666,19 +720,24 @@ ChatCommand * ChatHandler::getCommandTable()
{ "flusharenapoints",SEC_ADMINISTRATOR, false, &ChatHandler::HandleFlushArenaPointsCommand, "", NULL },
{ "repairitems", SEC_GAMEMASTER, true, &ChatHandler::HandleRepairitemsCommand, "", NULL },
{ "waterwalk", SEC_GAMEMASTER, false, &ChatHandler::HandleWaterwalkCommand, "", NULL },
+
{ "freeze", SEC_ADMINISTRATOR, false, &ChatHandler::HandleFreezeCommand, "", NULL },
{ "unfreeze", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnFreezeCommand, "", NULL },
{ "listfreeze", SEC_ADMINISTRATOR, false, &ChatHandler::HandleListFreezeCommand, "", NULL },
+
{ "possess", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePossessCommand, "", NULL },
{ "unpossess", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnPossessCommand, "", NULL },
{ "bindsight", SEC_ADMINISTRATOR, false, &ChatHandler::HandleBindSightCommand, "", NULL },
{ "unbindsight", SEC_ADMINISTRATOR, false, &ChatHandler::HandleUnbindSightCommand, "", NULL },
{ "playall", SEC_ADMINISTRATOR, false, &ChatHandler::HandlePlayAllCommand, "", NULL },
+
{ NULL, 0, false, NULL, "", NULL }
};
+
if (load_command_table)
{
load_command_table = false;
+
QueryResult *result = WorldDatabase.Query("SELECT name,security,help FROM command");
if (result)
{
@@ -686,61 +745,77 @@ ChatCommand * ChatHandler::getCommandTable()
{
Field *fields = result->Fetch();
std::string name = fields[0].GetCppString();
+
SetDataForCommandInTable(commandTable, name.c_str(), fields[1].GetUInt16(), fields[2].GetCppString(), name);
+
} while(result->NextRow());
delete result;
}
}
+
return commandTable;
}
+
const char *ChatHandler::GetTrinityString(int32 entry) const
{
return m_session->GetTrinityString(entry);
}
+
bool ChatHandler::isAvailable(ChatCommand const& cmd) const
{
// check security level only for simple command (without child commands)
return m_session->GetSecurity() >= cmd.SecurityLevel;
}
+
bool ChatHandler::HasLowerSecurity(Player* target, uint64 guid, bool strong)
{
WorldSession* target_session = NULL;
uint32 target_account = 0;
+
if (target)
target_session = target->GetSession();
else if (guid)
target_account = objmgr.GetPlayerAccountIdByGUID(guid);
+
if (!target_session && !target_account)
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
SetSentErrorMessage(true);
return true;
}
+
return HasLowerSecurityAccount(target_session,target_account,strong);
}
+
bool ChatHandler::HasLowerSecurityAccount(WorldSession* target, uint32 target_account, bool strong)
{
uint32 target_sec;
+
// allow everything from console and RA console
if (!m_session)
return false;
+
// ignore only for non-players for non strong checks (when allow apply command at least to same sec level)
if (m_session->GetSecurity() > SEC_PLAYER && !strong && !sWorld.getConfig(CONFIG_GM_LOWER_SECURITY))
return false;
+
if (target)
target_sec = target->GetSecurity();
else if (target_account)
target_sec = accmgr.GetSecurity(target_account);
else
return true; // caller must report error for (target==NULL && target_account==0)
+
if (m_session->GetSecurity() < target_sec || (strong && m_session->GetSecurity() <= target_sec))
{
SendSysMessage(LANG_YOURS_SECURITY_IS_LOW);
SetSentErrorMessage(true);
return true;
}
+
return false;
}
+
bool ChatHandler::hasStringAbbr(const char* name, const char* part)
{
// non "" command
@@ -749,6 +824,7 @@ bool ChatHandler::hasStringAbbr(const char* name, const char* part)
// "" part from non-"" command
if (!*part)
return false;
+
for (;;)
{
if (!*part)
@@ -761,42 +837,54 @@ bool ChatHandler::hasStringAbbr(const char* name, const char* part)
}
}
// allow with any for ""
+
return true;
}
+
void ChatHandler::SendSysMessage(const char *str)
{
WorldPacket data;
+
// need copy to prevent corruption by strtok call in LineFromMessage original string
char* buf = strdup(str);
char* pos = buf;
+
while(char* line = LineFromMessage(pos))
{
FillSystemMessageData(&data, line);
m_session->SendPacket(&data);
}
+
free(buf);
}
+
void ChatHandler::SendGlobalSysMessage(const char *str)
{
// Chat output
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::SendGlobalGMSysMessage(const char *str)
{
// Chat output
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);
@@ -804,10 +892,12 @@ void ChatHandler::SendGlobalGMSysMessage(const char *str)
}
free(buf);
}
+
void ChatHandler::SendSysMessage(int32 entry)
{
SendSysMessage(GetTrinityString(entry));
}
+
void ChatHandler::PSendSysMessage(int32 entry, ...)
{
const char *format = GetTrinityString(entry);
@@ -818,6 +908,7 @@ void ChatHandler::PSendSysMessage(int32 entry, ...)
va_end(ap);
SendSysMessage(str);
}
+
void ChatHandler::PSendSysMessage(const char *format, ...)
{
va_list ap;
@@ -827,20 +918,25 @@ void ChatHandler::PSendSysMessage(const char *format, ...)
va_end(ap);
SendSysMessage(str);
}
+
bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcmd)
{
char const* oldtext = text;
std::string cmd = "";
+
while (*text != ' ' && *text != '\0')
{
cmd += *text;
++text;
}
+
while (*text == ' ') ++text;
+
for (uint32 i = 0; table[i].Name != NULL; ++i)
{
if (!hasStringAbbr(table[i].Name, cmd.c_str()))
continue;
+
// select subcommand from child commands list
if (table[i].ChildCommands != NULL)
{
@@ -850,13 +946,17 @@ bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, co
SendSysMessage(LANG_NO_SUBCMD);
else
SendSysMessage(LANG_CMD_SYNTAX);
+
ShowHelpForCommand(table[i].ChildCommands,text);
}
+
return true;
}
+
// must be available and have handler
if (!table[i].Handler || !isAvailable(table[i]))
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))
@@ -882,24 +982,31 @@ bool ChatHandler::ExecuteCommandInTable(ChatCommand *table, const char* text, co
else
SendSysMessage(LANG_CMD_SYNTAX);
}
+
return true;
}
+
return false;
}
+
bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand)
{
std::string cmd = "";
+
while (*text != ' ' && *text != '\0')
{
cmd += *text;
++text;
}
+
while (*text == ' ') ++text;
+
for (uint32 i = 0; table[i].Name != NULL; i++)
{
// for data fill use full explicit command names
if (table[i].Name != cmd)
continue;
+
// select subcommand from child commands list (including "")
if (table[i].ChildCommands != NULL)
{
@@ -907,6 +1014,7 @@ bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text,
return true;
else if (*text)
return false;
+
// fail with "" subcommands, then use normal level up command instead
}
// expected subcommand by full name DB content
@@ -915,12 +1023,15 @@ bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text,
sLog.outErrorDb("Table `command` have unexpected subcommand '%s' in command '%s', skip.",text,fullcommand.c_str());
return false;
}
+
if (table[i].SecurityLevel != security)
sLog.outDetail("Table `command` overwrite for command '%s' default security (%u) by %u",fullcommand.c_str(),table[i].SecurityLevel,security);
+
table[i].SecurityLevel = security;
table[i].Help = help;
return true;
}
+
// in case "" command let process by caller
if (!cmd.empty())
{
@@ -929,31 +1040,40 @@ bool ChatHandler::SetDataForCommandInTable(ChatCommand *table, const char* text,
else
sLog.outErrorDb("Table `command` have not existed subcommand '%s' in command '%s', skip.",cmd.c_str(),fullcommand.c_str());
}
+
return false;
}
+
int ChatHandler::ParseCommands(const char* text)
{
ASSERT(text);
ASSERT(*text);
+
std::string fullcmd = text;
+
if (m_session && !m_session->HandleOnPlayerChat(text))
return 0;
+
/// chat case (.command or !command format)
if (m_session)
{
if (text[0] != '!' && text[0] != '.')
return 0;
}
+
/// ignore single . and ! in line
if (strlen(text) < 2)
return 0;
// original `text` can't be used. It content destroyed in command code processing.
+
/// ignore messages staring from many dots.
if ((text[0] == '.' && text[1] == '.') || (text[0] == '!' && text[1] == '!'))
return 0;
+
/// skip first . or ! (in console allowed use command with . and ! and without its)
if (text[0] == '!' || text[0] == '.')
++text;
+
if (!ExecuteCommandInTable(getCommandTable(), text, fullcmd))
{
if (m_session && m_session->GetSecurity() == SEC_PLAYER)
@@ -962,9 +1082,11 @@ int ChatHandler::ParseCommands(const char* text)
}
return 1;
}
+
bool ChatHandler::isValidChatMessage(const char* message)
{
/*
+
valid examples:
|cffa335ee|Hitem:812:0:0:0:0:0:0:0:70|h[Glowing Brightwood Staff]|h|r
|cff808080|Hquest:2278:47|h[The Platinum Discs]|h|r
@@ -974,26 +1096,34 @@ valid examples:
|cffffd000|Henchant:3919|h[Engineering: Rough Dynamite]|h|r
|cffffff00|Hachievement:546:0000000000000001:0:0:0:-1:0:0:0:0|h[Safe Deposit]|h|r
|cff66bbff|Hglyph:21:762|h[Glyph of Bladestorm]|h|r
+
| will be escaped to ||
*/
+
if (strlen(message) > 255)
return false;
+
const char validSequence[6] = "cHhhr";
const char* validSequenceIterator = validSequence;
+
// more simple checks
if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) < 3)
{
const std::string validCommands = "cHhr|";
+
while(*message)
{
// find next pipe command
message = strchr(message, '|');
+
if (!message)
return true;
+
++message;
char commandChar = *message;
if (validCommands.find(commandChar) == std::string::npos)
return false;
+
++message;
// validate sequence
if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) == 2)
@@ -1011,13 +1141,17 @@ valid examples:
}
return true;
}
+
std::istringstream reader(message);
char buffer[256];
+
uint32 color;
+
ItemPrototype const* linkedItem;
Quest const* linkedQuest;
SpellEntry const *linkedSpell;
AchievementEntry const* linkedAchievement;
+
while(!reader.eof())
{
if (validSequence == validSequenceIterator)
@@ -1026,6 +1160,7 @@ valid examples:
linkedQuest = NULL;
linkedSpell = NULL;
linkedAchievement = NULL;
+
reader.ignore(255, '|');
}
else if (reader.get() != '|')
@@ -1035,6 +1170,7 @@ valid examples:
#endif
return false;
}
+
// pipe has always to be followed by at least one char
if (reader.peek() == '\0')
{
@@ -1043,11 +1179,14 @@ valid examples:
#endif
return false;
}
+
// no further pipe commands
if (reader.eof())
break;
+
char commandChar;
reader >> commandChar;
+
// | in normal messages is escaped by ||
if (commandChar != '|')
{
@@ -1074,6 +1213,7 @@ valid examples:
#endif
return false;
}
+
switch (commandChar)
{
case 'c':
@@ -1090,6 +1230,7 @@ valid examples:
#endif
return false;
}
+
color <<= 4;
// check for hex char
if (c >= '0' && c <='9')
@@ -1111,10 +1252,12 @@ valid examples:
case 'H':
// read chars up to colon = link type
reader.getline(buffer, 256, ':');
+
if (strcmp(buffer, "item") == 0)
{
// read item entry
reader.getline(buffer, 256, ':');
+
linkedItem= objmgr.GetItemPrototype(atoi(buffer));
if (!linkedItem)
{
@@ -1123,6 +1266,7 @@ valid examples:
#endif
return false;
}
+
if (color != ItemQualityColors[linkedItem->Quality])
{
#ifdef MANGOS_DEBUG
@@ -1131,7 +1275,9 @@ valid examples:
#endif
return false;
}
+
char c = reader.peek();
+
// ignore enchants etc.
while((c >='0' && c <='9') || c==':')
{
@@ -1152,7 +1298,9 @@ valid examples:
questid += c-'0';
c = reader.peek();
}
+
linkedQuest = objmgr.GetQuestTemplate(questid);
+
if (!linkedQuest)
{
#ifdef MANOGS_DEBUG
@@ -1172,11 +1320,13 @@ valid examples:
{
if (color != CHAT_LINK_COLOR_TRADE)
return false;
+
// read spell entry
reader.getline(buffer, 256, ':');
linkedSpell = sSpellStore.LookupEntry(atoi(buffer));
if (!linkedSpell)
return false;
+
char c = reader.peek();
// base64 encoded stuff
while(c !='|' && c!='\0')
@@ -1190,14 +1340,17 @@ valid examples:
// talent links are always supposed to be blue
if (color != CHAT_LINK_COLOR_TALENT)
return false;
+
// read talent entry
reader.getline(buffer, 256, ':');
TalentEntry const *talentInfo = sTalentStore.LookupEntry(atoi(buffer));
if (!talentInfo)
return false;
+
linkedSpell = sSpellStore.LookupEntry(talentInfo->RankID[0]);
if (!linkedSpell)
return false;
+
char c = reader.peek();
// skillpoints? whatever, drop it
while(c !='|' && c!='\0')
@@ -1210,6 +1363,7 @@ valid examples:
{
if (color != CHAT_LINK_COLOR_SPELL)
return false;
+
uint32 spellid = 0;
// read spell entry
char c = reader.peek();
@@ -1228,6 +1382,7 @@ valid examples:
{
if (color != CHAT_LINK_COLOR_ENCHANT)
return false;
+
uint32 spellid = 0;
// read spell entry
char c = reader.peek();
@@ -1249,8 +1404,10 @@ valid examples:
reader.getline(buffer, 256, ':');
uint32 achievementId = atoi(buffer);
linkedAchievement = sAchievementStore.LookupEntry(achievementId);
+
if (!linkedAchievement)
return false;
+
char c = reader.peek();
// skip progress
while(c !='|' && c!='\0')
@@ -1263,6 +1420,7 @@ valid examples:
{
if (color != CHAT_LINK_COLOR_GLYPH)
return false;
+
// first id is slot, drop it
reader.getline(buffer, 256, ':');
uint32 glyphId = 0;
@@ -1277,7 +1435,9 @@ valid examples:
GlyphPropertiesEntry const* glyph = sGlyphPropertiesStore.LookupEntry(glyphId);
if (!glyph)
return false;
+
linkedSpell = sSpellStore.LookupEntry(glyph->SpellId);
+
if (!linkedSpell)
return false;
}
@@ -1302,6 +1462,7 @@ valid examples:
return false;
}
reader.getline(buffer, 256, ']');
+
// verify the link name
if (linkedSpell)
{
@@ -1314,16 +1475,20 @@ valid examples:
{
return false;
}
+
SkillLineAbilityEntry const *skillInfo = bounds.first->second;
+
if (!skillInfo)
{
return false;
}
+
SkillLineEntry const *skillLine = sSkillLineStore.LookupEntry(skillInfo->skillId);
if (!skillLine)
{
return false;
}
+
for (uint8 i=0; i<MAX_LOCALE; ++i)
{
uint32 skillLineNameLength = strlen(skillLine->name[i]);
@@ -1353,6 +1518,7 @@ valid examples:
if (linkedQuest->GetTitle() != buffer)
{
QuestLocale const *ql = objmgr.GetQuestLocale(linkedQuest->GetQuestId());
+
if (!ql)
{
#ifdef MANOGS_DEBUG
@@ -1360,6 +1526,7 @@ valid examples:
#endif
return false;
}
+
bool foundName = false;
for (uint8 i=0; i<ql->Title.size(); i++)
{
@@ -1383,6 +1550,7 @@ valid examples:
if (strcmp(linkedItem->Name1, buffer) != 0)
{
ItemLocale const *il = objmgr.GetItemLocale(linkedItem->ItemId);
+
if (!il)
{
#ifdef MANGOS_DEBUG
@@ -1390,6 +1558,7 @@ valid examples:
#endif
return false;
}
+
bool foundName = false;
for (uint8 i=0; i<il->Name.size(); ++i)
{
@@ -1439,6 +1608,7 @@ valid examples:
return false;
}
}
+
// check if every opened sequence was also closed properly
#ifdef MANGOS_DEBUG
if (validSequence != validSequenceIterator)
@@ -1446,6 +1616,7 @@ valid examples:
#endif
return validSequence == validSequenceIterator;
}
+
bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd)
{
std::string list;
@@ -1454,19 +1625,25 @@ bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, ch
// must be available (ignore handler existence for show command with possibe avalable subcomands
if (!isAvailable(table[i]))
continue;
+
/// for empty subcmd show all available
if (*subcmd && !hasStringAbbr(table[i].Name, subcmd))
continue;
+
if (m_session)
list += "\n ";
else
list += "\n\r ";
+
list += table[i].Name;
+
if (table[i].ChildCommands)
list += " ...";
}
+
if (list.empty())
return false;
+
if (table==getCommandTable())
{
SendSysMessage(LANG_AVIABLE_CMD);
@@ -1474,8 +1651,10 @@ bool ChatHandler::ShowHelpForSubCommands(ChatCommand *table, char const* cmd, ch
}
else
PSendSysMessage(LANG_SUBCMDS_LIST,cmd,list.c_str());
+
return true;
}
+
bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd)
{
if (*cmd)
@@ -1485,20 +1664,26 @@ bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd)
// must be available (ignore handler existence for show command with possibe avalable subcomands
if (!isAvailable(table[i]))
continue;
+
if (!hasStringAbbr(table[i].Name, cmd))
continue;
+
// have subcommand
char const* subcmd = (*cmd) ? strtok(NULL, " ") : "";
+
if (table[i].ChildCommands && subcmd && *subcmd)
{
if (ShowHelpForCommand(table[i].ChildCommands, subcmd))
return true;
}
+
if (!table[i].Help.empty())
SendSysMessage(table[i].Help.c_str());
+
if (table[i].ChildCommands)
if (ShowHelpForSubCommands(table[i].ChildCommands,table[i].Name,subcmd ? subcmd : ""))
return true;
+
return !table[i].Help.empty();
}
}
@@ -1509,28 +1694,36 @@ bool ChatHandler::ShowHelpForCommand(ChatCommand *table, const char* cmd)
// must be available (ignore handler existence for show command with possibe avalable subcomands
if (!isAvailable(table[i]))
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:
@@ -1580,13 +1773,16 @@ void ChatHandler::FillMessageData(WorldPacket *data, WorldSession* session, uint
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;
@@ -1595,116 +1791,153 @@ void ChatHandler::FillMessageData(WorldPacket *data, WorldSession* session, uint
else
*data << uint8(0);
}
+
Player * ChatHandler::getSelectedPlayer()
{
if (!m_session)
return NULL;
+
uint64 guid = m_session->GetPlayer()->GetSelection();
+
if (guid == 0)
return m_session->GetPlayer();
+
return objmgr.GetPlayer(guid);
}
+
Unit* ChatHandler::getSelectedUnit()
{
if (!m_session)
return NULL;
+
uint64 guid = m_session->GetPlayer()->GetSelection();
+
if (guid == 0)
return m_session->GetPlayer();
+
return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid);
}
+
WorldObject *ChatHandler::getSelectedObject()
{
if (!m_session)
return NULL;
+
uint64 guid = m_session->GetPlayer()->GetSelection();
+
if (guid == 0)
return GetNearbyGameObject();
+
return ObjectAccessor::GetUnit(*m_session->GetPlayer(),guid);
}
+
Creature* ChatHandler::getSelectedCreature()
{
if (!m_session)
return NULL;
+
return ObjectAccessor::GetCreatureOrPetOrVehicle(*m_session->GetPlayer(),m_session->GetPlayer()->GetSelection());
}
+
char* ChatHandler::extractKeyFromLink(char* text, char const* linkType, char** something1)
{
// skip empty
if (!text)
return NULL;
+
// skip spaces
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 return 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 spaces
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
// or
// [name] Shift-click form |linkType:key|h[name]|h|r
+
char* tail;
+
if (text[1]=='c')
{
char* check = strtok(text, "|"); // skip color
if (!check)
return NULL; // end of data
+
tail = strtok(NULL, ""); // tail
}
else
tail = text+1; // skip first |
+
char* cLinkType = strtok(tail, ":"); // 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)
@@ -1712,10 +1945,12 @@ char* ChatHandler::extractKeyFromLink(char* text, char const* const* linkTypes,
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;
@@ -1725,25 +1960,34 @@ char const *fmtstring(char const *format, ...)
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::GetNearbyGameObject()
{
if (!m_session)
return NULL;
+
Player* pl = m_session->GetPlayer();
GameObject* obj = NULL;
Trinity::NearestGameObjectCheck check(*pl);
@@ -1751,26 +1995,34 @@ GameObject* ChatHandler::GetNearbyGameObject()
pl->VisitNearbyGridObject(999, searcher);
return obj;
}
+
GameObject* ChatHandler::GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry)
{
if (!m_session)
return NULL;
+
Player* pl = m_session->GetPlayer();
+
GameObject* obj = pl->GetMap()->GetGameObject(MAKE_NEW_GUID(lowguid, entry, HIGHGUID_GAMEOBJECT));
+
if (!obj && objmgr.GetGOData(lowguid)) // guid is DB guid of object
{
// search near player then
CellPair p(Trinity::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::GameObjectWithDbGUIDCheck go_check(*pl,lowguid);
MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(pl,obj,go_check);
+
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *pl->GetMap());
}
+
return obj;
}
+
enum SpellLinkType
{
SPELL_LINK_SPELL = 0,
@@ -1779,6 +2031,7 @@ enum SpellLinkType
SPELL_LINK_TRADE = 3,
SPELL_LINK_GLYPH = 4
};
+
static char const* const spellKeys[] =
{
"Hspell", // normal spell
@@ -1788,6 +2041,7 @@ static char const* const spellKeys[] =
"Hglyph", // glyph
0
};
+
uint32 ChatHandler::extractSpellIdFromLink(char* text)
{
// number or [name] Shift-click form |color|Henchant:recipe_spell_id|h[prof_name: recipe_name]|h|r
@@ -1800,7 +2054,9 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text)
char* idS = extractKeyFromLink(text,spellKeys,&type,&param1_str);
if (!idS)
return 0;
+
uint32 id = (uint32)atol(idS);
+
switch(type)
{
case SPELL_LINK_SPELL:
@@ -1811,11 +2067,14 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text)
TalentEntry const* talentEntry = sTalentStore.LookupEntry(id);
if (!talentEntry)
return 0;
+
int32 rank = param1_str ? (uint32)atol(param1_str) : 0;
if (rank >= MAX_TALENT_RANK)
return 0;
+
if (rank < 0)
rank = 0;
+
return talentEntry->RankID[rank];
}
case SPELL_LINK_ENCHANT:
@@ -1824,33 +2083,41 @@ uint32 ChatHandler::extractSpellIdFromLink(char* text)
case SPELL_LINK_GLYPH:
{
uint32 glyph_prop_id = param1_str ? (uint32)atol(param1_str) : 0;
+
GlyphPropertiesEntry const* glyphPropEntry = sGlyphPropertiesStore.LookupEntry(glyph_prop_id);
if (!glyphPropEntry)
return 0;
+
return glyphPropEntry->SpellId;
}
}
+
// unknown type?
return 0;
}
+
GameTele const* ChatHandler::extractGameTeleFromLink(char* text)
{
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
char* cId = extractKeyFromLink(text,"Htele");
if (!cId)
return false;
+
// id case (explicit or from shift link)
if (cId[0] >= '0' || cId[0] >= '9')
if (uint32 id = atoi(cId))
return objmgr.GetGameTele(id);
+
return objmgr.GetGameTele(cId);
}
+
enum GuidLinkType
{
SPELL_LINK_PLAYER = 0, // must be first for selection in not link case
SPELL_LINK_CREATURE = 1,
SPELL_LINK_GAMEOBJECT = 2
};
+
static char const* const guidKeys[] =
{
"Hplayer",
@@ -1858,15 +2125,18 @@ static char const* const guidKeys[] =
"Hgameobject",
0
};
+
uint64 ChatHandler::extractGuidFromLink(char* text)
{
int type = 0;
+
// |color|Hcreature:creature_guid|h[name]|h|r
// |color|Hgameobject:go_guid|h[name]|h|r
// |color|Hplayer:name|h[name]|h|r
char* idS = extractKeyFromLink(text,guidKeys,&type);
if (!idS)
return 0;
+
switch(type)
{
case SPELL_LINK_PLAYER:
@@ -1874,15 +2144,19 @@ uint64 ChatHandler::extractGuidFromLink(char* text)
std::string name = idS;
if (!normalizePlayerName(name))
return 0;
+
if (Player* player = objmgr.GetPlayer(name.c_str()))
return player->GetGUID();
+
if (uint64 guid = objmgr.GetPlayerGUIDByName(name))
return guid;
+
return 0;
}
case SPELL_LINK_CREATURE:
{
uint32 lowguid = (uint32)atol(idS);
+
if (CreatureData const* data = objmgr.GetCreatureData(lowguid))
return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_UNIT);
else
@@ -1891,26 +2165,32 @@ uint64 ChatHandler::extractGuidFromLink(char* text)
case SPELL_LINK_GAMEOBJECT:
{
uint32 lowguid = (uint32)atol(idS);
+
if (GameObjectData const* data = objmgr.GetGOData(lowguid))
return MAKE_NEW_GUID(lowguid,data->id,HIGHGUID_GAMEOBJECT);
else
return 0;
}
}
+
// unknown type?
return 0;
}
+
std::string ChatHandler::extractPlayerNameFromLink(char* text)
{
// |color|Hplayer:name|h[name]|h|r
char* name_str = extractKeyFromLink(text,"Hplayer");
if (!name_str)
return "";
+
std::string name = name_str;
if (!normalizePlayerName(name))
return "";
+
return name;
}
+
bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* player_guid /*=NULL*/,std::string* player_name /*= NULL*/)
{
if (args && *args)
@@ -1922,15 +2202,20 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* playe
SetSentErrorMessage(true);
return false;
}
+
Player* pl = objmgr.GetPlayer(name.c_str());
+
// if allowed player pointer
if (player)
*player = pl;
+
// if need guid value from DB (in name case for check player existence)
uint64 guid = !pl && (player_guid || player_name) ? objmgr.GetPlayerGUIDByName(name) : 0;
+
// if allowed player guid (if no then only online players allowed)
if (player_guid)
*player_guid = pl ? pl->GetGUID() : guid;
+
if (player_name)
*player_name = pl || guid ? name : "";
}
@@ -1943,9 +2228,11 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* playe
// if allowed player guid (if no then only online players allowed)
if (player_guid)
*player_guid = pl ? pl->GetGUID() : 0;
+
if (player_name)
*player_name = pl ? pl->GetName() : "";
}
+
// some from req. data must be provided (note: name is empty if player not exist)
if ((!player || !*player) && (!player_guid || !*player_guid) && (!player_name || player_name->empty()))
{
@@ -1953,26 +2240,33 @@ bool ChatHandler::extractPlayerTarget(char* args, Player** player, uint64* playe
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
void ChatHandler::extractOptFirstArg(char* args, char** arg1, char** arg2)
{
char* p1 = strtok(args, " ");
char* p2 = strtok(NULL, " ");
+
if (!p2)
{
p2 = p1;
p1 = NULL;
}
+
if (arg1)
*arg1 = p1;
+
if (arg2)
*arg2 = p2;
}
+
char* ChatHandler::extractQuotedArg(char* args)
{
if (!*args)
return NULL;
+
if (*args=='"')
return strtok(args+1, "\"");
else
@@ -1983,45 +2277,55 @@ char* ChatHandler::extractQuotedArg(char* args)
return strtok(NULL, "\"");
}
}
+
bool ChatHandler::needReportToTarget(Player* chr) const
{
Player* pl = m_session->GetPlayer();
return pl != chr && pl->IsVisibleGloballyFor(chr);
}
+
LocaleConstant ChatHandler::GetSessionDbcLocale() const
{
return m_session->GetSessionDbcLocale();
}
+
int ChatHandler::GetSessionDbLocaleIndex() const
{
return m_session->GetSessionDbLocaleIndex();
}
+
const char *CliHandler::GetMangosString(int32 entry) const
{
return objmgr.GetTrinityStringForDBCLocale(entry);
}
+
bool CliHandler::isAvailable(ChatCommand const& cmd) const
{
// skip non-console commands in console case
return cmd.AllowConsole;
}
+
void CliHandler::SendSysMessage(const char *str)
{
m_print(str);
m_print("\r\n");
}
+
std::string CliHandler::GetNameLink() const
{
return GetTrinityString(LANG_CONSOLE_COMMAND);
}
+
bool CliHandler::needReportToTarget(Player* /*chr*/) const
{
return true;
}
+
bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, Group* &group, uint64 &guid, bool offline)
{
plr = NULL;
guid = 0;
+
if (cname)
{
std::string name = cname;
@@ -2033,11 +2337,13 @@ bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, G
SetSentErrorMessage(true);
return false;
}
+
plr = objmgr.GetPlayer(name.c_str());
if (offline)
guid = objmgr.GetPlayerGUIDByName(name.c_str());
}
}
+
if (plr)
{
group = plr->GetGroup();
@@ -2050,17 +2356,21 @@ bool ChatHandler::GetPlayerGroupAndGUIDByName(const char* cname, Player* &plr, G
plr = getSelectedPlayer();
else
plr = m_session->GetPlayer();
+
if (!guid || !offline)
guid = plr->GetGUID();
group = plr->GetGroup();
}
+
return true;
}
+
LocaleConstant CliHandler::GetSessionDbcLocale() const
{
return sWorld.GetDefaultDbcLocale();
}
+
int CliHandler::GetSessionDbLocaleIndex() const
{
return objmgr.GetDBCLocaleIndex();
-}
+} \ No newline at end of file
diff --git a/src/game/Chat.h b/src/game/Chat.h
index 35e39ad8b08..1518cb6d45e 100644
--- a/src/game/Chat.h
+++ b/src/game/Chat.h
@@ -17,15 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_CHAT_H
#define TRINITYCORE_CHAT_H
+
#include "SharedDefines.h"
+
class ChatHandler;
class WorldSession;
class Creature;
class Player;
class Unit;
struct GameTele;
+
class ChatCommand
{
public:
@@ -36,50 +40,66 @@ class ChatCommand
std::string Help;
ChatCommand * ChildCommands;
};
+
class TRINITY_DLL_SPEC 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; }
+
// function with different implementation for chat/console
virtual const char *GetMangosString(int32 entry) const;
virtual void SendSysMessage( const char *str);
+
void SendSysMessage( int32 entry);
void PSendSysMessage( const char *format, ...) ATTR_PRINTF(2,3);
void PSendSysMessage( int32 entry, ... );
std::string PGetParseString(int32 entry, ...);
+
int ParseCommands(const char* text);
+
static ChatCommand* getCommandTable();
+
bool isValidChatMessage(const char* msg);
void SendGlobalSysMessage(const char *str);
protected:
explicit ChatHandler() : m_session(NULL) {} // for CLI subclass
+
bool hasStringAbbr(const char* name, const char* part);
+
// function with different implementation for chat/console
virtual bool isAvailable(ChatCommand const& cmd) const;
virtual std::string GetNameLink() const { return GetNameLink(m_session->GetPlayer()); }
virtual bool needReportToTarget(Player* chr) const;
virtual LocaleConstant GetSessionDbcLocale() const;
virtual int GetSessionDbLocaleIndex() const;
+
bool HasLowerSecurity(Player* target, uint64 guid, bool strong = false);
bool HasLowerSecurityAccount(WorldSession* target, uint32 account, bool strong = false);
+
void SendGlobalGMSysMessage(const char *str);
+
static bool SetDataForCommandInTable(ChatCommand *table, const char* text, uint32 security, std::string const& help, std::string const& fullcommand );
bool ExecuteCommandInTable(ChatCommand *table, const char* text, const std::string& fullcommand);
bool ShowHelpForCommand(ChatCommand *table, const char* cmd);
bool ShowHelpForSubCommands(ChatCommand *table, char const* cmd, char const* subcmd);
+
bool HandleAccountCommand(const char* args);
bool HandleAccountAddonCommand(const char* args);
bool HandleAccountCreateCommand(const char* args);
@@ -90,11 +110,13 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleAccountSetAddonCommand(const char* args);
bool HandleAccountSetGmLevelCommand(const char* args);
bool HandleAccountSetPasswordCommand(const char* args);
+
bool HandleAHBotOptionsCommand(const char * args);
bool HandleNameAnnounceCommand(const char* args);
bool HandleGMNameAnnounceCommand(const char* args);
bool HandleGMAnnounceCommand(const char* args);
bool HandleGMNotifyCommand(const char* args);
+
bool HandleBanAccountCommand(const char* args);
bool HandleBanCharacterCommand(const char* args);
bool HandleBanIPCommand(const char* args);
@@ -104,16 +126,19 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleBanListAccountCommand(const char* args);
bool HandleBanListCharacterCommand(const char* args);
bool HandleBanListIPCommand(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 HandleCharacterCustomizeCommand(const char * args);
bool HandleCharacterDeleteCommand(const char* args);
bool HandleCharacterLevelCommand(const char* args);
bool HandleCharacterRenameCommand(const char * args);
bool HandleCharacterReputationCommand(const char* args);
+
bool HandleDebugAnimCommand(const char* args);
bool HandleDebugArenaCommand(const char * args);
bool HandleDebugBattlegroundCommand(const char * args);
@@ -131,6 +156,7 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleDebugSpellCheckCommand(const char* args);
bool HandleDebugUpdateCommand(const char* args);
bool HandleDebugUpdateWorldStateCommand(const char* args);
+
bool HandleDebugSet32Bit(const char* args);
bool HandleDebugThreatList(const char * args);
bool HandleDebugHostilRefList(const char * args);
@@ -138,9 +164,11 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleUnPossessCommand(const char* args);
bool HandleBindSightCommand(const char* args);
bool HandleUnbindSightCommand(const char* args);
+
bool HandleDebugPlayCinematicCommand(const char* args);
bool HandleDebugPlayMovieCommand(const char* args);
bool HandleDebugPlaySoundCommand(const char* args);
+
bool HandleDebugSendBuyErrorCommand(const char* args);
bool HandleDebugSendChannelNotifyCommand(const char* args);
bool HandleDebugSendChatMsgCommand(const char* args);
@@ -153,10 +181,12 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleDebugSendSellErrorCommand(const char* args);
bool HandleDebugSendSetPhaseShiftCommand(const char * args);
bool HandleDebugSendSpellFailCommand(const char* args);
+
bool HandleEventActiveListCommand(const char* args);
bool HandleEventStartCommand(const char* args);
bool HandleEventStopCommand(const char* args);
bool HandleEventInfoCommand(const char* args);
+
bool HandleGameObjectAddCommand(const char* args);
bool HandleGameObjectDeleteCommand(const char* args);
bool HandleGameObjectMoveCommand(const char* args);
@@ -165,12 +195,14 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleGameObjectStateCommand(const char* args);
bool HandleGameObjectTargetCommand(const char* args);
bool HandleGameObjectTurnCommand(const char* args);
+
bool HandleGMCommand(const char* args);
bool HandleGMChatCommand(const char* args);
bool HandleGMFlyCommand(const char* args);
bool HandleGMListFullCommand(const char* args);
bool HandleGMListIngameCommand(const char* args);
bool HandleGMVisibleCommand(const char* args);
+
bool HandleGoCommand(const char* args);
bool HandleGoCreatureCommand(const char* args);
bool HandleGoGraveyardCommand(const char* args);
@@ -181,19 +213,24 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleGoXYCommand(const char* args);
bool HandleGoXYZCommand(const char* args);
bool HandleGoZoneXYCommand(const char* args);
+
bool HandleGoTicketCommand(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 HandleHonorAddCommand(const char* args);
bool HandleHonorAddKillCommand(const char* args);
bool HandleHonorUpdateCommand(const char* args);
+
bool HandleInstanceListBindsCommand(const char* args);
bool HandleInstanceUnbindCommand(const char* args);
bool HandleInstanceStatsCommand(const char* args);
bool HandleInstanceSaveDataCommand(const char * args);
+
bool HandleLearnCommand(const char* args);
bool HandleLearnAllCommand(const char* args);
bool HandleLearnAllGMCommand(const char* args);
@@ -205,10 +242,12 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleLearnAllMyPetTalentsCommand(const char* args);
bool HandleLearnAllMySpellsCommand(const char* args);
bool HandleLearnAllMyTalentsCommand(const char* args);
+
bool HandleListAurasCommand(const char * args);
bool HandleListCreatureCommand(const char* args);
bool HandleListItemCommand(const char* args);
bool HandleListObjectCommand(const char* args);
+
bool HandleLookupAreaCommand(const char* args);
bool HandleLookupCreatureCommand(const char* args);
bool HandleLookupEventCommand(const char* args);
@@ -225,6 +264,7 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleLookupTaxiNodeCommand(const char * args);
bool HandleLookupTeleCommand(const char * args);
bool HandleLookupMapCommand(const char* args);
+
bool HandleModifyKnownTitlesCommand(const char* args);
bool HandleModifyHPCommand(const char* args);
bool HandleModifyManaCommand(const char* args);
@@ -248,6 +288,7 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleModifyArenaCommand(const char* args);
bool HandleModifyPhaseCommand(const char* args);
bool HandleModifyGenderCommand(const char* args);
+
//-----------------------Npc Commands-----------------------
bool HandleNpcAddCommand(const char* args);
bool HandleNpcAddMoveCommand(const char* args);
@@ -277,16 +318,20 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleNpcYellCommand(const char* args);
bool HandleNpcAddFormationCommand(const char* args);
bool HandleNpcSetLinkCommand(const char* args);
+
//TODO: NpcCommands that needs to be fixed :
bool HandleNpcAddWeaponCommand(const char* args);
bool HandleNpcNameCommand(const char* args);
bool HandleNpcSubNameCommand(const char* args);
//----------------------------------------------------------
+
bool HandlePDumpLoadCommand(const char *args);
bool HandlePDumpWriteCommand(const char *args);
+
bool HandleQuestAdd(const char * args);
bool HandleQuestRemove(const char * args);
bool HandleQuestComplete(const char * args);
+
bool HandleReloadAllCommand(const char* args);
bool HandleReloadAllAchievementCommand(const char* args);
bool HandleReloadAllAreaCommand(const char* args);
@@ -298,7 +343,9 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleReloadAllEventAICommand(const char* args);
bool HandleReloadAllSpellCommand(const char* args);
bool HandleReloadAllLocalesCommand(const char* args);
+
bool HandleReloadConfigCommand(const char* args);
+
bool HandleReloadAccessRequirementCommand(const char* args);
bool HandleReloadAchievementCriteriaDataCommand(const char* args);
bool HandleReloadAchievementRewardCommand(const char* args);
@@ -373,6 +420,7 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleReloadSpellDisabledCommand(const char* args);
bool HandleReloadAuctionsCommand(const char* args);
bool HandleReloadWpScriptsCommand(const char* args);
+
bool HandleResetAchievementsCommand(const char * args);
bool HandleResetAllCommand(const char * args);
bool HandleResetHonorCommand(const char * args);
@@ -380,10 +428,12 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleResetSpellsCommand(const char * args);
bool HandleResetStatsCommand(const char * args);
bool HandleResetTalentsCommand(const char * args);
+
bool HandleSendItemsCommand(const char* args);
bool HandleSendMailCommand(const char* args);
bool HandleSendMessageCommand(const char * args);
bool HandleSendMoneyCommand(const char* args);
+
bool HandleServerCorpsesCommand(const char* args);
bool HandleServerExitCommand(const char* args);
bool HandleServerIdleRestartCommand(const char* args);
@@ -397,16 +447,20 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleServerShutDownCommand(const char* args);
bool HandleServerShutDownCancelCommand(const char* args);
bool HandleServerSetClosedCommand(const char* args);
+
bool HandleServerSetLogFileLevelCommand(const char* args);
bool HandleServerSetDiffTimeCommand(const char* args);
+
bool HandleTeleCommand(const char * args);
bool HandleTeleAddCommand(const char * args);
bool HandleTeleDelCommand(const char * args);
bool HandleTeleGroupCommand(const char* args);
bool HandleTeleNameCommand(const char* args);
+
bool HandleUnBanAccountCommand(const char* args);
bool HandleUnBanCharacterCommand(const char* args);
bool HandleUnBanIPCommand(const char* args);
+
bool HandleWpAddCommand(const char* args);
bool HandleWpLoadPathCommand(const char* args);
bool HandleWpUnLoadPathCommand(const char* args);
@@ -414,11 +468,13 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleWpEventCommand(const char* args);
bool HandleWpShowCommand(const char* args);
bool HandleReloadAllPaths(const char *args);
+
bool HandleHelpCommand(const char* args);
bool HandleCommandsCommand(const char* args);
bool HandleStartCommand(const char* args);
bool HandleDismountCommand(const char* args);
bool HandleSaveCommand(const char* args);
+
bool HandleNamegoCommand(const char* args);
bool HandleGonameCommand(const char* args);
bool HandleGroupgoCommand(const char* args);
@@ -429,7 +485,9 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleTaxiCheatCommand(const char* args);
bool HandleWhispersCommand(const char* args);
bool HandleModifyDrunkCommand(const char* args);
+
bool HandleLoadScriptsCommand(const char* args);
+
bool HandleGUIDCommand(const char* args);
bool HandleItemMoveCommand(const char* args);
bool HandleDeMorphCommand(const char* args);
@@ -440,6 +498,7 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleFreezeCommand(const char *args);
bool HandleUnFreezeCommand(const char *args);
bool HandleListFreezeCommand(const char* args);
+
bool HandleCooldownCommand(const char* args);
bool HandleUnLearnCommand(const char* args);
bool HandleGetDistanceCommand(const char* args);
@@ -466,12 +525,15 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandlePetUnlearnCommand(const char* args);
bool HandlePetLearnCommand(const char* args);
bool HandleCreatePetCommand(const char* args);
+
bool HandleGroupLeaderCommand(const char* args);
bool HandleGroupDisbandCommand(const char* args);
bool HandleGroupRemoveCommand(const char* args);
+
bool HandleBankCommand(const char* args);
bool HandleChangeWeather(const char* args);
bool HandleKickPlayerCommand(const char * args);
+
// GM ticket command handlers
bool HandleGMTicketListCommand(const char* args);
bool HandleGMTicketListOnlineCommand(const char* args);
@@ -484,33 +546,44 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleGMTicketCommentCommand(const char* args);
bool HandleGMTicketDeleteByIdCommand(const char* args);
bool HandleGMTicketReloadCommand(const char*);
+
bool HandleMaxSkillCommand(const char* args);
bool HandleSetSkillCommand(const char* args);
bool HandleRespawnCommand(const char* args);
bool HandleComeToMeCommand(const char *args);
bool HandleCombatStopCommand(const char *args);
+
/*bool HandleCharDeleteCommand(const char *args);
bool HandleSendMessageCommand(const char * args);*/
+
bool HandleFlushArenaPointsCommand(const char *args);
bool HandlePlayAllCommand(const char* args);
bool HandleRepairitemsCommand(const char* args);
+
bool HandleTempGameObjectCommand(const char* args);
bool HandleTempAddSpwCommand(const char* args);
+
//! Development Commands
+
/*bool HandleQuestAdd(const char * args);
bool HandleQuestRemove(const char * args);
bool HandleQuestComplete(const char * args);*/
+
//bool HandleSet32Bit(const char* args);
bool HandleSaveAllCommand(const char* args);
+
Player* getSelectedPlayer();
Creature* getSelectedCreature();
Unit* getSelectedUnit();
WorldObject* getSelectedObject();
+
char* extractKeyFromLink(char* text, char const* linkType, char** something1 = NULL);
char* extractKeyFromLink(char* text, char const* const* linkTypes, int* found_idx, char** something1 = NULL);
+
// if args have single value then it return in arg2 and arg1 == NULL
void extractOptFirstArg(char* args, char** arg1, char** arg2);
char* extractQuotedArg(char* args);
+
uint32 extractSpellIdFromLink(char* text);
uint64 extractGuidFromLink(char* text);
GameTele const* extractGameTeleFromLink(char* text);
@@ -518,10 +591,13 @@ class TRINITY_DLL_SPEC ChatHandler
std::string extractPlayerNameFromLink(char* text);
// select by arg (name/link) or in-game selection online/offline player
bool extractPlayerTarget(char* args, Player** player, uint64* player_guid = NULL, std::string* player_name = NULL);
+
std::string playerLink(std::string const& name) const { return m_session ? "|cffffffff|Hplayer:"+name+"|h["+name+"]|h|r" : name; }
std::string GetNameLink(Player* chr) const { return playerLink(chr->GetName()); }
+
GameObject* GetNearbyGameObject();
GameObject* GetObjectGlobalyWithGuidOrNearWithDbGuid(uint32 lowguid,uint32 entry);
+
// Utility methods for commands
bool LookupPlayerSearchCommand(QueryResult* result, int32 limit);
bool HandleBanListHelper(QueryResult* result);
@@ -530,18 +606,22 @@ class TRINITY_DLL_SPEC ChatHandler
bool HandleUnBanHelper(BanMode mode,char const* args);
void HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel);
void HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id);
+
void SetSentErrorMessage(bool val){ sentErrorMessage = val;};
private:
WorldSession * m_session; // != NULL for chat command call and NULL for CLI command
+
// common global flag
static bool load_command_table;
bool sentErrorMessage;
};
+
class CliHandler : public ChatHandler
{
public:
typedef void Print(char const*);
explicit CliHandler(Print* zprint) : m_print(zprint) {}
+
// overwrite functions
const char *GetTrinityString(int32 entry) const;
bool isAvailable(ChatCommand const& cmd) const;
@@ -550,9 +630,12 @@ class CliHandler : public ChatHandler
bool needReportToTarget(Player* chr) const;
LocaleConstant GetSessionDbcLocale() const;
int GetSessionDbLocaleIndex() const;
+
private:
Print* m_print;
};
+
char const *fmtstring( char const *format, ... );
+
#endif
diff --git a/src/game/ChatHandler.cpp b/src/game/ChatHandler.cpp
index 11b57618d21..e5aaab05c9d 100644
--- a/src/game/ChatHandler.cpp
+++ b/src/game/ChatHandler.cpp
@@ -17,6 +17,7 @@
* 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 "ObjectAccessor.h"
#include "ObjectMgr.h"
@@ -24,6 +25,7 @@
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Database/DatabaseEnv.h"
+
#include "CellImpl.h"
#include "Chat.h"
#include "ChannelMgr.h"
@@ -37,6 +39,7 @@
#include "ScriptCalls.h"
#include "SpellAuras.h"
#include "Util.h"
+
bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg, uint32 lang)
{
if (lang != LANG_ADDON)
@@ -44,6 +47,7 @@ bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg
// strip invisible characters for non-addon messages
if(sWorld.getConfig(CONFIG_CHAT_FAKE_MESSAGE_PREVENTING))
stripLineInvisibleChars(msg);
+
if (sWorld.getConfig(CONFIG_CHAT_STRICT_LINK_CHECKING_SEVERITY) && GetSecurity() < SEC_MODERATOR
&& !ChatHandler(this).isValidChatMessage(msg.c_str()))
{
@@ -54,20 +58,26 @@ bool WorldSession::processChatmessageFurtherAfterSecurityChecks(std::string& msg
return false;
}
}
+
return true;
}
+
void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
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)
@@ -94,20 +104,24 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
return;
}
}
+
if(lang == LANG_ADDON)
{
if(sWorld.getConfig(CONFIG_CHATLOG_ADDON))
{
std::string msg = "";
recv_data >> msg;
+
if(msg.empty())
{
sLog.outDebug("Player %s send empty addon msg", GetPlayer()->GetName());
return;
}
+
sLog.outChat("[ADDON] Player %s sends: %s",
GetPlayer()->GetName(), msg.c_str());
}
+
// Disabled addon channel?
if(!sWorld.getConfig(CONFIG_ADDON_CHANNEL))
return;
@@ -143,27 +157,33 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
break;
}
}
+
// but overwrite it by SPELL_AURA_MOD_LANGUAGE auras (only single case used)
Unit::AuraEffectList const& ModLangAuras = _player->GetAurasByType(SPELL_AURA_MOD_LANGUAGE);
if(!ModLangAuras.empty())
lang = ModLangAuras.front()->GetMiscValue();
}
+
if (!_player->CanSpeak())
{
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
return;
}
+
if (type != CHAT_MSG_AFK && type != CHAT_MSG_DND)
GetPlayer()->UpdateSpeakTime();
}
+
if (GetPlayer()->HasAura(1852) && type != CHAT_MSG_WHISPER)
{
std::string msg="";
recv_data >> msg;
+
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName());
return;
}
+
switch(type)
{
case CHAT_MSG_SAY:
@@ -172,14 +192,19 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
std::string msg = "";
recv_data >> msg;
+
if(msg.empty())
break;
+
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
if(type == CHAT_MSG_SAY)
GetPlayer()->Say(msg, lang);
else if(type == CHAT_MSG_EMOTE)
@@ -187,15 +212,19 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
else if(type == CHAT_MSG_YELL)
GetPlayer()->Yell(msg, lang);
} break;
+
case CHAT_MSG_WHISPER:
{
std::string to, msg;
recv_data >> to;
recv_data >> msg;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
if(!normalizePlayerName(to))
{
WorldPacket data(SMSG_CHAT_PLAYER_NOT_FOUND, (to.size()+1));
@@ -203,6 +232,7 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
SendPacket(&data);
break;
}
+
Player *player = objmgr.GetPlayer(to.c_str());
uint32 tSecurity = GetSecurity();
uint32 pSecurity = player ? player->GetSession()->GetSecurity() : SEC_PLAYER;
@@ -213,6 +243,7 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_CHAT) && tSecurity == SEC_PLAYER && pSecurity == SEC_PLAYER )
{
uint32 sidea = GetPlayer()->GetTeam();
@@ -225,33 +256,43 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
return;
}
}
+
if (GetPlayer()->HasAura(1852) && !player->isGameMaster())
{
SendNotification(GetTrinityString(LANG_GM_SILENCE), GetPlayer()->GetName());
return;
}
+
GetPlayer()->Whisper(msg, lang,player->GetGUID());
} break;
+
case CHAT_MSG_PARTY:
{
std::string msg = "";
recv_data >> msg;
+
if(msg.empty())
break;
+
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
// if player is in battleground, he cannot say to battleground members by /p
Group *group = GetPlayer()->GetOriginalGroup();
// so if player hasn't OriginalGroup and his player->GetGroup() is BG raid, then return
if( !group && (!(group = GetPlayer()->GetGroup()) || group->isBGGroup()) )
return;
+
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_PARTY, lang, NULL, 0, msg.c_str(),NULL);
group->BroadcastPacket(&data, false, group->GetMemberGroup(GetPlayer()->GetGUID()));
+
if(sWorld.getConfig(CONFIG_CHATLOG_PARTY))
sLog.outChat("[PARTY] Player %s tells group with leader %s: %s",
GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str());
@@ -261,19 +302,25 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
std::string msg = "";
recv_data >> msg;
+
if(msg.empty())
break;
+
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
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);
+
if(lang != LANG_ADDON && sWorld.getConfig(CONFIG_CHATLOG_GUILD))
{
sLog.outChat("[GUILD] Player %s tells guild %s: %s",
@@ -285,25 +332,32 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str());
}
}
+
break;
}
case CHAT_MSG_OFFICER:
{
std::string msg = "";
recv_data >> msg;
+
if(msg.empty())
break;
+
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
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);
+
if(sWorld.getConfig(CONFIG_CHATLOG_GUILD))
sLog.outChat("[OFFICER] Player %s tells guild %s officers: %s",
GetPlayer()->GetName(), guild->GetName().c_str(), msg.c_str());
@@ -314,22 +368,29 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
std::string msg="";
recv_data >> msg;
+
if(msg.empty())
break;
+
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
// if player is in battleground, he cannot say to battleground members by /ra
Group *group = GetPlayer()->GetOriginalGroup();
// so if player hasn't OriginalGroup and his player->GetGroup() is BG raid or his group isn't raid, then return
if ((!group && !(group = GetPlayer()->GetGroup())) || group->isBGGroup() || !group->isRaidGroup())
return;
+
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID, lang, "", 0, msg.c_str(),NULL);
group->BroadcastPacket(&data, false);
+
if(sWorld.getConfig(CONFIG_CHATLOG_RAID))
sLog.outChat("[RAID] Player %s tells raid with leader %s: %s",
GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str());
@@ -338,21 +399,28 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
std::string msg="";
recv_data >> msg;
+
if(msg.empty())
break;
+
if (ChatHandler(this).ParseCommands(msg.c_str()) > 0)
break;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
// if player is in battleground, he cannot say to battleground members by /ra
Group *group = GetPlayer()->GetOriginalGroup();
if( !group && !(group = GetPlayer()->GetGroup()) || group->isBGGroup() || !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, false);
+
if(sWorld.getConfig(CONFIG_CHATLOG_RAID))
sLog.outChat("[RAID] Leader player %s tells raid: %s",
GetPlayer()->GetName(), msg.c_str());
@@ -361,74 +429,97 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
{
std::string msg="";
recv_data >> msg;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
Group *group = GetPlayer()->GetGroup();
if(!group || !group->isRaidGroup() || !(group->IsLeader(GetPlayer()->GetGUID()) || group->IsAssistant(GetPlayer()->GetGUID())) || group->isBGGroup())
return;
+
WorldPacket data;
//in battleground, raid warning is sent only to players in battleground - code is ok
ChatHandler::FillMessageData(&data, this, CHAT_MSG_RAID_WARNING, lang, "", 0, msg.c_str(),NULL);
group->BroadcastPacket(&data, false);
+
if(sWorld.getConfig(CONFIG_CHATLOG_RAID))
sLog.outChat("[RAID] Leader player %s warns raid with: %s",
GetPlayer()->GetName(), msg.c_str());
} break;
+
case CHAT_MSG_BATTLEGROUND:
{
std::string msg="";
recv_data >> msg;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
//battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
Group *group = GetPlayer()->GetGroup();
if(!group || !group->isBGGroup())
return;
+
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND, lang, "", 0, msg.c_str(),NULL);
group->BroadcastPacket(&data, false);
+
if(sWorld.getConfig(CONFIG_CHATLOG_BGROUND))
sLog.outChat("[BATTLEGROUND] Player %s tells battleground with leader %s: %s",
GetPlayer()->GetName(), group->GetLeaderName(), msg.c_str());
} break;
+
case CHAT_MSG_BATTLEGROUND_LEADER:
{
std::string msg="";
recv_data >> msg;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
//battleground raid is always in Player->GetGroup(), never in GetOriginalGroup()
Group *group = GetPlayer()->GetGroup();
if(!group || !group->isBGGroup() || !group->IsLeader(GetPlayer()->GetGUID()))
return;
+
WorldPacket data;
ChatHandler::FillMessageData(&data, this, CHAT_MSG_BATTLEGROUND_LEADER, lang, "", 0, msg.c_str(),NULL);
group->BroadcastPacket(&data, false);
+
if(sWorld.getConfig(CONFIG_CHATLOG_BGROUND))
sLog.outChat("[RAID] Leader player %s tells battleground: %s",
GetPlayer()->GetName(), msg.c_str());
} break;
+
case CHAT_MSG_CHANNEL:
{
std::string channel = "", msg = "";
recv_data >> channel;
+
recv_data >> msg;
+
if (!processChatmessageFurtherAfterSecurityChecks(msg, lang))
return;
+
if(msg.empty())
break;
+
if(ChannelMgr* cMgr = channelMgr(_player->GetTeam()))
{
Channel *chn = cMgr->GetChannel(channel,_player);
if(chn)
{
chn->Say(_player->GetGUID(),msg.c_str(),lang);
+
if((chn->HasFlag(CHANNEL_FLAG_TRADE) ||
chn->HasFlag(CHANNEL_FLAG_GENERAL) ||
chn->HasFlag(CHANNEL_FLAG_CITY) ||
@@ -442,10 +533,12 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
}
}
} break;
+
case CHAT_MSG_AFK:
{
std::string msg;
recv_data >> msg;
+
if((msg.empty() || !_player->isAFK()) && !_player->isInCombat() )
{
if(!_player->isAFK())
@@ -459,10 +552,12 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
_player->ToggleDND();
}
} break;
+
case CHAT_MSG_DND:
{
std::string msg;
recv_data >> msg;
+
if(msg.empty() || !_player->isDND())
{
if(!_player->isDND())
@@ -476,19 +571,23 @@ void WorldSession::HandleMessagechatOpcode( WorldPacket & recv_data )
_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;
+
uint32 emote;
recv_data >> emote;
GetPlayer()->HandleEmoteCommand(emote);
}
+
namespace MaNGOS
{
class EmoteChatBuilder
@@ -496,10 +595,12 @@ namespace MaNGOS
public:
EmoteChatBuilder(Player const& pl, uint32 text_emote, uint32 emote_num, Unit const* target)
: i_player(pl), i_text_emote(text_emote), i_emote_num(emote_num), i_target(target) {}
+
void operator()(WorldPacket& data, int32 loc_idx)
{
char const* nam = i_target ? i_target->GetNameForLocaleIdx(loc_idx) : NULL;
uint32 namlen = (nam ? strlen(nam) : 0) + 1;
+
data.Initialize(SMSG_TEXT_EMOTE, (20+namlen));
data << i_player.GetGUID();
data << (uint32)i_text_emote;
@@ -510,6 +611,7 @@ namespace MaNGOS
else
data << (uint8)0x00;
}
+
private:
Player const& i_player;
uint32 i_text_emote;
@@ -517,25 +619,32 @@ namespace MaNGOS
Unit const* i_target;
};
} // namespace MaNGOS
+
void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data )
{
if(!GetPlayer()->isAlive())
return;
+
if (!GetPlayer()->CanSpeak())
{
std::string timeStr = secsToTimeString(m_muteTime - time(NULL));
SendNotification(GetTrinityString(LANG_WAIT_BEFORE_SPEAKING),timeStr.c_str());
return;
}
+
uint32 text_emote, emoteNum;
uint64 guid;
+
recv_data >> text_emote;
recv_data >> emoteNum;
recv_data >> guid;
+
EmotesTextEntry const *em = sEmotesTextStore.LookupEntry(text_emote);
if (!em)
return;
+
uint32 emote_anim = em->textid;
+
switch(emote_anim)
{
case EMOTE_STATE_SLEEP:
@@ -547,36 +656,47 @@ void WorldSession::HandleTextEmoteOpcode( WorldPacket & recv_data )
GetPlayer()->HandleEmoteCommand(emote_anim);
break;
}
+
Unit* unit = ObjectAccessor::GetUnit(*_player, guid);
+
CellPair p = MaNGOS::ComputeCellPair(GetPlayer()->GetPositionX(), GetPlayer()->GetPositionY());
+
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::EmoteChatBuilder emote_builder(*GetPlayer(), text_emote, emoteNum, unit);
MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > emote_do(emote_builder);
MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > > emote_worker(GetPlayer(),sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),emote_do);
TypeContainerVisitor<MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::EmoteChatBuilder > >, WorldTypeMapContainer > message(emote_worker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *GetPlayer()->GetMap(), *GetPlayer(), sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
+
GetPlayer()->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE, text_emote, 0, unit);
+
//Send scripted event call
if (unit && unit->GetTypeId()==TYPEID_UNIT && ((Creature*)unit)->AI())
((Creature*)unit)->AI()->ReceiveEmote(GetPlayer(),text_emote);
}
+
void WorldSession::HandleChatIgnoredOpcode(WorldPacket& recv_data )
{
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);
}
+
void WorldSession::HandleChannelDeclineInvite(WorldPacket &recvPacket)
{
sLog.outDebug("Opcode %u", recvPacket.GetOpcode());
diff --git a/src/game/CombatAI.cpp b/src/game/CombatAI.cpp
index 00d06b9ec33..b18e6dd7532 100644
--- a/src/game/CombatAI.cpp
+++ b/src/game/CombatAI.cpp
@@ -17,51 +17,64 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "CombatAI.h"
#include "SpellMgr.h"
+
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;
}
+
void AggressorAI::UpdateAI(const uint32 /*diff*/)
{
if(!UpdateVictim())
return;
+
DoMeleeAttackIfReady();
}
+
// some day we will delete these useless things
int CombatAI::Permissible(const Creature *creature)
{
return PERMIT_BASE_NO;
}
+
int ArchorAI::Permissible(const Creature *creature)
{
return PERMIT_BASE_NO;
}
+
int TurretAI::Permissible(const Creature *creature)
{
return PERMIT_BASE_NO;
}
+
void CombatAI::InitializeAI()
{
for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
if(me->m_spells[i] && GetSpellStore()->LookupEntry(me->m_spells[i]))
spells.push_back(me->m_spells[i]);
+
CreatureAI::InitializeAI();
}
+
void CombatAI::Reset()
{
events.Reset();
}
+
void CombatAI::JustDied(Unit *killer)
{
for(SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
if(AISpellInfo[*i].condition == AICOND_DIE)
me->CastSpell(killer, *i, true);
}
+
void CombatAI::EnterCombat(Unit *who)
{
for(SpellVct::iterator i = spells.begin(); i != spells.end(); ++i)
@@ -72,13 +85,17 @@ void CombatAI::EnterCombat(Unit *who)
events.ScheduleEvent(*i, AISpellInfo[*i].cooldown + rand()%AISpellInfo[*i].cooldown);
}
}
+
void CombatAI::UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
return;
+
events.Update(diff);
+
if(me->hasUnitState(UNIT_STAT_CASTING))
return;
+
if(uint32 spellId = events.ExecuteEvent())
{
DoCast(spellId);
@@ -88,12 +105,15 @@ void CombatAI::UpdateAI(const uint32 diff)
DoMeleeAttackIfReady();
}
+
/////////////////
//CasterAI
/////////////////
+
void CasterAI::InitializeAI()
{
CombatAI::InitializeAI();
+
float m_attackDist = 30.0f;
for(SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr)
if (AISpellInfo[*itr].condition == AICOND_COMBAT && m_attackDist > GetAISpellInfo(*itr)->maxRange)
@@ -101,10 +121,12 @@ void CasterAI::InitializeAI()
if (m_attackDist == 30.0f)
m_attackDist = MELEE_RANGE;
}
+
void CasterAI::EnterCombat(Unit *who)
{
if (spells.empty())
return;
+
uint32 spell = rand()%spells.size();
uint32 count = 0;
for(SpellVct::iterator itr = spells.begin(); itr != spells.end(); ++itr, ++count)
@@ -123,13 +145,17 @@ void CasterAI::EnterCombat(Unit *who)
}
}
}
+
void CasterAI::UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
return;
+
events.Update(diff);
+
if(me->hasUnitState(UNIT_STAT_CASTING))
return;
+
if(uint32 spellId = events.ExecuteEvent())
{
DoCast(spellId);
@@ -138,9 +164,11 @@ void CasterAI::UpdateAI(const uint32 diff)
}
}
+
//////////////
//ArchorAI
//////////////
+
ArchorAI::ArchorAI(Creature *c) : CreatureAI(c)
{
ASSERT(me->m_spells[0]);
@@ -150,10 +178,12 @@ ArchorAI::ArchorAI(Creature *c) : CreatureAI(c)
me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false);
me->m_SightDistance = me->m_CombatDistance;
}
+
void ArchorAI::AttackStart(Unit *who)
{
if(!who)
return;
+
if(me->IsWithinCombatRange(who, m_minRange))
{
if(me->Attack(who, true) && !who->IsFlying())
@@ -164,22 +194,27 @@ void ArchorAI::AttackStart(Unit *who)
if(me->Attack(who, false) && !who->IsFlying())
me->GetMotionMaster()->MoveChase(who, me->m_CombatDistance);
}
+
if(who->IsFlying())
me->GetMotionMaster()->MoveIdle();
}
+
void ArchorAI::UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
return;
+
if(!me->IsWithinCombatRange(me->getVictim(), m_minRange))
DoSpellAttackIfReady(me->m_spells[0]);
else
DoMeleeAttackIfReady();
}
+
//////////////
//TurretAI
//////////////
+
TurretAI::TurretAI(Creature *c) : CreatureAI(c)
{
ASSERT(me->m_spells[0]);
@@ -187,6 +222,7 @@ TurretAI::TurretAI(Creature *c) : CreatureAI(c)
me->m_CombatDistance = GetSpellMaxRange(me->m_spells[0], false);
me->m_SightDistance = me->m_CombatDistance;
}
+
bool TurretAI::CanAIAttack(const Unit *who) const
{
// TODO: use one function to replace it
@@ -195,16 +231,20 @@ bool TurretAI::CanAIAttack(const Unit *who) const
return false;
return true;
}
+
void TurretAI::AttackStart(Unit *who)
{
if(who)
me->Attack(who, false);
}
+
void TurretAI::UpdateAI(const uint32 diff)
{
if(!UpdateVictim())
return;
+
DoSpellAttackIfReady(me->m_spells[0]);
+
//if(!DoSpellAttackIfReady(me->m_spells[0]))
//if(HostilReference *ref = me->getThreatManager().getCurrentVictim())
//ref->removeReference();
diff --git a/src/game/CombatAI.h b/src/game/CombatAI.h
index d63ee424bf5..5ded95601ae 100644
--- a/src/game/CombatAI.h
+++ b/src/game/CombatAI.h
@@ -17,23 +17,31 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_COMBATAI_H
#define TRINITY_COMBATAI_H
+
#include "CreatureAI.h"
#include "CreatureAIImpl.h"
+
class Creature;
+
class TRINITY_DLL_DECL AggressorAI : public CreatureAI
{
public:
explicit AggressorAI(Creature *c) : CreatureAI(c) {}
+
void UpdateAI(const uint32);
static int Permissible(const Creature *);
};
+
typedef std::vector<uint32> SpellVct;
+
class TRINITY_DLL_SPEC CombatAI : public CreatureAI
{
public:
explicit CombatAI(Creature *c) : CreatureAI(c) {}
+
void InitializeAI();
void Reset();
void EnterCombat(Unit* who);
@@ -44,6 +52,7 @@ class TRINITY_DLL_SPEC CombatAI : public CreatureAI
EventMap events;
SpellVct spells;
};
+
class TRINITY_DLL_SPEC CasterAI : public CombatAI
{
public:
@@ -55,16 +64,19 @@ class TRINITY_DLL_SPEC CasterAI : public CombatAI
private:
float m_attackDist;
};
+
struct TRINITY_DLL_SPEC ArchorAI : public CreatureAI
{
public:
explicit ArchorAI(Creature *c);
void AttackStart(Unit *who);
void UpdateAI(const uint32 diff);
+
static int Permissible(const Creature *);
protected:
float m_minRange;
};
+
struct TRINITY_DLL_SPEC TurretAI : public CreatureAI
{
public:
@@ -72,8 +84,10 @@ struct TRINITY_DLL_SPEC TurretAI : public CreatureAI
bool CanAIAttack(const Unit *who) const;
void AttackStart(Unit *who);
void UpdateAI(const uint32 diff);
+
static int Permissible(const Creature *);
protected:
float m_minRange;
};
+
#endif
diff --git a/src/game/CombatHandler.cpp b/src/game/CombatHandler.cpp
index 80ca6ba3532..095986303f8 100644
--- a/src/game/CombatHandler.cpp
+++ b/src/game/CombatHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,47 +25,61 @@
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "ObjectDefines.h"
+
void WorldSession::HandleAttackSwingOpcode( WorldPacket & recv_data )
{
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->canAttack(pEnemy))
{
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;
}
+
_player->Attack(pEnemy,true);
}
+
void WorldSession::HandleAttackStopOpcode( WorldPacket & /*recv_data*/ )
{
GetPlayer()->AttackStop();
}
+
void WorldSession::HandleSetSheathedOpcode( WorldPacket & recv_data )
{
uint32 sheathed;
recv_data >> sheathed;
+
//sLog.outDebug( "WORLD: Recvd CMSG_SETSHEATHED Message guidlow:%u value1:%u", GetPlayer()->GetGUIDLow(), sheathed );
+
if(sheathed >= MAX_SHEATH_STATE)
{
sLog.outError("Unknown sheath state %u ??",sheathed);
return;
}
+
GetPlayer()->SetSheath(SheathState(sheathed));
}
+
void WorldSession::SendAttackStop(Unit const* enemy)
{
WorldPacket data( SMSG_ATTACKSTOP, (4+20) ); // we guess size
diff --git a/src/game/ConfusedMovementGenerator.cpp b/src/game/ConfusedMovementGenerator.cpp
index 76531d35f01..23477c6e62f 100644
--- a/src/game/ConfusedMovementGenerator.cpp
+++ b/src/game/ConfusedMovementGenerator.cpp
@@ -17,16 +17,19 @@
* 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"
#include "VMapFactory.h"
+
#ifdef MAP_BASED_RAND_GEN
#define rand_norm() unit.rand_norm()
#define urand(a,b) unit.urand(a,b)
#endif
+
template<class T>
void
ConfusedMovementGenerator<T>::Initialize(T &unit)
@@ -36,26 +39,35 @@ ConfusedMovementGenerator<T>::Initialize(T &unit)
x = unit.GetPositionX();
y = unit.GetPositionY();
z = unit.GetPositionZ();
+
Map const* map = unit.GetBaseMap();
+
i_nextMove = 1;
+
bool is_water_ok, is_land_ok;
_InitSpecific(unit, is_water_ok, is_land_ok);
+
VMAP::IVMapManager *vMaps = VMAP::VMapFactory::createOrGetVMapManager();
+
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;
- const bool isInLoS = vMaps->isInLineOfSight(unit.GetMapId(), x, y, z + 2.0f, i_waypoints[idx][0], i_waypoints[idx][1], z + 2.0f);
+
+ const bool isInLoS = vMaps->isInLineOfSight(unit.GetMapId(), x, y, z + 2.0f, i_waypoints[idx][0], i_waypoints[idx][1], z + 2.0f);
if (!isInLoS)
{
- i_waypoints[idx][0] = x;
+ i_waypoints[idx][0] = x;
i_waypoints[idx][1] = y;
}
+
i_waypoints[idx][0] = x + wanderX;
i_waypoints[idx][1] = y + wanderY;
+
// prevent invalid coordinates generation
Trinity::NormalizeMapCoord(i_waypoints[idx][0]);
Trinity::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))
@@ -63,9 +75,11 @@ ConfusedMovementGenerator<T>::Initialize(T &unit)
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.SetUInt64Value(UNIT_FIELD_TARGET, 0);
unit.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_CONFUSED);
unit.CastStop();
@@ -73,6 +87,7 @@ ConfusedMovementGenerator<T>::Initialize(T &unit)
unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
unit.addUnitState(UNIT_STAT_CONFUSED);
}
+
template<>
void
ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_water_ok, bool &is_land_ok)
@@ -80,6 +95,7 @@ ConfusedMovementGenerator<Creature>::_InitSpecific(Creature &creature, bool &is_
is_water_ok = creature.canSwim();
is_land_ok = creature.canWalk();
}
+
template<>
void
ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bool &is_land_ok)
@@ -87,6 +103,7 @@ ConfusedMovementGenerator<Player>::_InitSpecific(Player &, bool &is_water_ok, bo
is_water_ok = true;
is_land_ok = true;
}
+
template<class T>
void
ConfusedMovementGenerator<T>::Reset(T &unit)
@@ -96,14 +113,17 @@ ConfusedMovementGenerator<T>::Reset(T &unit)
i_destinationHolder.ResetUpdate();
unit.StopMoving();
}
+
template<class T>
bool
ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
{
if(!&unit)
return true;
+
if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
return true;
+
if( i_nextMoveTime.Passed() )
{
// currently moving, update location
@@ -114,6 +134,7 @@ ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
{
// arrived, stop and wait a bit
unit.clearUnitState(UNIT_STAT_MOVE);
+
i_nextMove = urand(1,MAX_CONF_WAYPOINTS);
i_nextMoveTime.Reset(urand(0, 1500-1)); // TODO: check the minimum reset time, should be probably higher
}
@@ -136,6 +157,7 @@ ConfusedMovementGenerator<T>::Update(T &unit, const uint32 &diff)
}
return true;
}
+
template<class T>
void
ConfusedMovementGenerator<T>::Finalize(T &unit)
@@ -145,6 +167,7 @@ ConfusedMovementGenerator<T>::Finalize(T &unit)
if(unit.GetTypeId() == TYPEID_UNIT && unit.getVictim())
unit.SetUInt64Value(UNIT_FIELD_TARGET, unit.getVictim()->GetGUID());
}
+
template void ConfusedMovementGenerator<Player>::Initialize(Player &player);
template void ConfusedMovementGenerator<Creature>::Initialize(Creature &creature);
template void ConfusedMovementGenerator<Player>::Finalize(Player &player);
diff --git a/src/game/ConfusedMovementGenerator.h b/src/game/ConfusedMovementGenerator.h
index 0675ea49e4c..ad47b13cb39 100644
--- a/src/game/ConfusedMovementGenerator.h
+++ b/src/game/ConfusedMovementGenerator.h
@@ -17,28 +17,35 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CONFUSEDGENERATOR_H
#define TRINITY_CONFUSEDGENERATOR_H
+
#include "MovementGenerator.h"
#include "DestinationHolder.h"
#include "Traveller.h"
+
#define MAX_CONF_WAYPOINTS 24
+
template<class T>
class TRINITY_DLL_SPEC ConfusedMovementGenerator
: public MovementGeneratorMedium< T, ConfusedMovementGenerator<T> >
{
public:
explicit ConfusedMovementGenerator() : i_nextMoveTime(0) {}
+
void Initialize(T &);
void Finalize(T &);
void Reset(T &);
bool Update(T &, const uint32 &);
+
bool GetDestination(float &x, float &y, float &z) const
{
if(i_destinationHolder.HasArrived()) return false;
i_destinationHolder.GetDestination(x,y,z);
return true;
}
+
MovementGeneratorType GetMovementGeneratorType() { return CONFUSED_MOTION_TYPE; }
private:
void _InitSpecific(T &, bool &, bool &);
diff --git a/src/game/Corpse.cpp b/src/game/Corpse.cpp
index 350e314152d..16e52ef46aa 100644
--- a/src/game/Corpse.cpp
+++ b/src/game/Corpse.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -26,65 +27,87 @@
#include "Opcodes.h"
#include "GossipDef.h"
#include "World.h"
+
Corpse::Corpse(CorpseType type) : WorldObject()
, m_type(type)
{
m_objectType |= TYPEMASK_CORPSE;
m_objectTypeId = TYPEID_CORPSE;
+
m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_POSITION);
+
m_valuesCount = CORPSE_END;
+
m_time = time(NULL);
+
lootForBody = false;
+
if(type != CORPSE_BONES)
m_isWorldObject = true;
}
+
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, Map *map )
{
SetMap(map);
Object::_Create(guidlow, 0, HIGHGUID_CORPSE);
return true;
}
+
bool Corpse::Create( uint32 guidlow, Player *owner)
{
ASSERT(owner);
+
Relocate(owner->GetPositionX(), owner->GetPositionY(), owner->GetPositionZ(), owner->GetOrientation());
+
if(!IsPositionValid())
{
sLog.outError("Corpse (guidlow %d, owner %s) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
guidlow, owner->GetName(), owner->GetPositionX(), owner->GetPositionY());
return false;
}
+
//we need to assign owner's map for corpse
//in other way we will get a crash in Corpse::SaveToDB()
SetMap(owner->GetMap());
+
WorldObject::_Create(guidlow, HIGHGUID_CORPSE, owner->GetPhaseMask());
+
SetFloatValue( OBJECT_FIELD_SCALE_X, 1 );
SetUInt64Value( CORPSE_FIELD_OWNER, owner->GetGUID() );
+
m_grid = Trinity::ComputeGridPair(GetPositionX(), GetPositionY());
+
return true;
}
+
void Corpse::SaveToDB()
{
// prevent DB data inconsistence 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,phaseMask) VALUES ("
<< GetGUIDLow() << ", "
@@ -105,17 +128,21 @@ void Corpse::SaveToDB()
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)
@@ -125,6 +152,7 @@ void Corpse::DeleteFromDB()
// 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)
{
@@ -132,22 +160,29 @@ bool Corpse::LoadFromDB(uint32 guid, QueryResult *result, uint32 InstanceId)
if (!external)
// 0 1 2 3 4 5 6 7 8 9
result = CharacterDatabase.PQuery("SELECT position_x,position_y,position_z,orientation,map,data,time,corpse_type,instance,phaseMask FROM corpse WHERE guid = '%u'",guid);
+
if( !result )
{
sLog.outError("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 9
@@ -157,39 +192,52 @@ bool Corpse::LoadFromDB(uint32 guid, Field *fields)
float positionZ = fields[2].GetFloat();
float ort = fields[3].GetFloat();
uint32 mapid = fields[4].GetUInt32();
+
Object::_Create(guid, 0, HIGHGUID_CORPSE);
+
if(!LoadValues( fields[5].GetString() ))
{
sLog.outError("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("Corpse (guidlow %d, owner %d) have wrong corpse type, not load.",GetGUIDLow(),GUID_LOPART(GetOwnerGUID()));
return false;
}
+
if(m_type != CORPSE_BONES)
m_isWorldObject = true;
+
uint32 instanceid = fields[8].GetUInt32();
+
uint32 phaseMask = fields[9].GetUInt32();
+
// overwrite possible wrong/corrupted guid
SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_CORPSE));
+
// place
SetLocationInstanceId(instanceid);
SetLocationMapId(mapid);
SetPhaseMask(phaseMask, false);
Relocate(positionX, positionY, positionZ, ort);
+
if(!IsPositionValid())
{
sLog.outError("Corpse (guidlow %d, owner %d) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
GetGUIDLow(), GUID_LOPART(GetOwnerGUID()), GetPositionX(), GetPositionY());
return false;
}
+
m_grid = Trinity::ComputeGridPair(GetPositionX(), GetPositionY());
+
return true;
}
+
bool Corpse::isVisibleForInState(Player const* u, bool inVisibleList) const
{
return IsInWorld() && u->IsInWorld() && IsWithinDistInMap(u->m_seer, World::GetMaxVisibleDistanceForObject() + (inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false);
diff --git a/src/game/Corpse.h b/src/game/Corpse.h
index ca9a16d8ced..e0374a03470 100644
--- a/src/game/Corpse.h
+++ b/src/game/Corpse.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_CORPSE_H
#define TRINITYCORE_CORPSE_H
+
#include "Object.h"
#include "Database/DatabaseEnv.h"
#include "GridDefines.h"
#include "LootMgr.h"
+
enum CorpseType
{
CORPSE_BONES = 0,
@@ -30,8 +33,10 @@ enum CorpseType
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,
@@ -42,38 +47,51 @@ enum CorpseFlags
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, Map *map );
bool Create( uint32 guidlow, Player *owner );
+
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(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 YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); }
+
GridReference<Corpse> &GetGridRef() { return m_gridRef; }
private:
GridReference<Corpse> m_gridRef;
+
CorpseType m_type;
time_t m_time;
GridPair m_grid; // gride for corpse position for fast search
diff --git a/src/game/Creature.cpp b/src/game/Creature.cpp
index 63c9d34d157..a3a2e5c1690 100644
--- a/src/game/Creature.cpp
+++ b/src/game/Creature.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -48,13 +49,16 @@
#include "Vehicle.h"
// apply implementation of the singletons
#include "Policies/SingletonImp.h"
+
TrainerSpell const* TrainerSpellData::Find(uint32 spell_id) const
{
TrainerSpellMap::const_iterator itr = spellList.find(spell_id);
if (itr != spellList.end())
return &itr->second;
+
return NULL;
}
+
bool VendorItemData::RemoveItem( uint32 item_id )
{
for(VendorItemList::iterator i = m_items.begin(); i != m_items.end(); ++i )
@@ -67,6 +71,7 @@ bool VendorItemData::RemoveItem( uint32 item_id )
}
return false;
}
+
size_t VendorItemData::FindItemSlot(uint32 item_id) const
{
for(size_t i = 0; i < m_items.size(); ++i )
@@ -74,6 +79,7 @@ size_t VendorItemData::FindItemSlot(uint32 item_id) const
return i;
return m_items.size();
}
+
VendorItem const* VendorItemData::FindItem(uint32 item_id) const
{
for(VendorItemList::const_iterator i = m_items.begin(); i != m_items.end(); ++i )
@@ -81,16 +87,20 @@ VendorItem const* VendorItemData::FindItem(uint32 item_id) const
return *i;
return NULL;
}
+
uint32 CreatureInfo::GetRandomValidModelId() const
{
uint8 c = 0;
uint32 modelIDs[4];
+
if (Modelid1) modelIDs[c++] = Modelid1;
if (Modelid2) modelIDs[c++] = Modelid2;
if (Modelid3) modelIDs[c++] = Modelid3;
if (Modelid4) modelIDs[c++] = Modelid4;
+
return ((c>0) ? modelIDs[urand(0,c-1)] : 0);
}
+
uint32 CreatureInfo::GetFirstValidModelId() const
{
if(Modelid1) return Modelid1;
@@ -99,6 +109,7 @@ uint32 CreatureInfo::GetFirstValidModelId() const
if(Modelid4) return Modelid4;
return 0;
}
+
bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
if(Unit* victim = Unit::GetUnit(m_owner, m_victim))
@@ -107,6 +118,7 @@ bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
{
Creature* assistant = Unit::GetCreature(m_owner, *m_assistants.begin());
m_assistants.pop_front();
+
if (assistant && assistant->CanAssistTo(&m_owner, victim))
{
assistant->SetNoCallAssistance(true);
@@ -118,6 +130,7 @@ bool AssistDelayEvent::Execute(uint64 /*e_time*/, uint32 /*p_time*/)
}
return true;
}
+
Creature::Creature() :
Unit(),
lootForPickPocketed(false), lootForBody(false), m_groupLootTimer(0), lootingGroupLeaderGUID(0),
@@ -132,27 +145,34 @@ m_creatureInfo(NULL), m_reactState(REACT_AGGRESSIVE), m_formation(NULL)
{
m_regenTimer = 200;
m_valuesCount = UNIT_END;
+
for(uint8 i =0; i<CREATURE_MAX_SPELLS; ++i)
m_spells[i] = 0;
+
m_CreatureSpellCooldowns.clear();
m_CreatureCategoryCooldowns.clear();
m_GlobalCooldown = 0;
DisableReputationGain = false;
//m_unit_movement_flags = MONSTER_MOVE_WALK;
+
m_SightDistance = sWorld.getConfig(CONFIG_SIGHT_MONSTER);
m_CombatDistance = 0;//MELEE_RANGE;
}
+
Creature::~Creature()
{
m_vendorItemCounts.clear();
+
if(i_AI)
{
delete i_AI;
i_AI = NULL;
}
+
//if(m_uint32Values)
// sLog.outError("Deconstruct Creature Entry = %u", GetEntry());
}
+
void Creature::AddToWorld()
{
///- Register the creature for guid lookup
@@ -168,6 +188,7 @@ void Creature::AddToWorld()
GetVehicleKit()->Install();
}
}
+
void Creature::RemoveFromWorld()
{
if(IsInWorld())
@@ -180,6 +201,7 @@ void Creature::RemoveFromWorld()
ObjectAccessor::Instance().RemoveObject(this);
}
}
+
void Creature::DisappearAndDie()
{
DestroyForNearbyPlayers();
@@ -189,21 +211,26 @@ void Creature::DisappearAndDie()
setDeathState(JUST_DIED);
RemoveCorpse();
}
+
void Creature::SearchFormation()
{
if(isSummon())
return;
+
uint32 lowguid = GetDBTableGUIDLow();
if(!lowguid)
return;
+
CreatureGroupInfoType::iterator frmdata = CreatureGroupMap.find(lowguid);
if(frmdata != CreatureGroupMap.end())
formation_mgr.AddCreatureToGroup(frmdata->second->leaderGUID, this);
}
+
void Creature::RemoveCorpse()
{
if ((getDeathState()!=CORPSE && !m_isDeadByDefault) || (getDeathState()!=ALIVE && m_isDeadByDefault))
return;
+
m_deathTimer = 0;
setDeathState(DEAD);
ObjectAccessor::UpdateObjectVisibility(this);
@@ -211,12 +238,15 @@ void Creature::RemoveCorpse()
uint32 respawnDelay = m_respawnDelay;
if (AI())
AI()->CorpseRemoved(respawnDelay);
+
m_respawnTime = time(NULL) + m_respawnDelay;
+
float x,y,z,o;
GetRespawnCoord(x, y, z, &o);
SetHomePosition(x,y,z,o);
GetMap()->CreatureRelocation(this,x,y,z,o);
}
+
/**
* change the entry of creature until respawn
*/
@@ -228,6 +258,7 @@ bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
sLog.outErrorDb("Creature::UpdateEntry creature entry %u does not exist.", Entry);
return false;
}
+
// get heroic mode entry
uint32 actualEntry = Entry;
CreatureInfo const *cinfo = normalInfo;
@@ -244,18 +275,23 @@ bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
}
}
}
+
SetEntry(Entry); // normal entry always
m_creatureInfo = cinfo; // map mode related always
+
// equal to player Race field, but creature does not have race
SetByteValue(UNIT_FIELD_BYTES_0, 0, 0);
+
// known valid are: CLASS_WARRIOR,CLASS_PALADIN,CLASS_ROGUE,CLASS_MAGE
SetByteValue(UNIT_FIELD_BYTES_0, 1, uint8(cinfo->unit_class));
+
// Cancel load if no model defined
if (!(cinfo->GetFirstValidModelId()))
{
sLog.outErrorDb("Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ",Entry);
return false;
}
+
uint32 display_id = objmgr.ChooseDisplayId(0, GetCreatureInfo(), data);
CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
if (!minfo) // Cancel load if no model defined
@@ -263,10 +299,13 @@ bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
sLog.outErrorDb("Creature (Entry: %u) has no model defined in table `creature_template`, can't load. ",Entry);
return false;
}
+
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
@@ -276,44 +315,61 @@ bool Creature::InitEntry(uint32 Entry, uint32 team, const CreatureData *data )
{ // 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;
+
for(uint8 i=0; i < CREATURE_MAX_SPELLS; ++i)
m_spells[i] = GetCreatureInfo()->spells[i];
+
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
SetSheath(SHEATH_STATE_MELEE);
+
SelectLevel(GetCreatureInfo());
if (team == HORDE)
setFaction(GetCreatureInfo()->faction_H);
else
setFaction(GetCreatureInfo()->faction_A);
+
if(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT)
SetUInt32Value(UNIT_NPC_FLAGS,GetCreatureInfo()->npcflag | gameeventmgr.GetNPCFlag(this));
else
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()->unit_flags);
SetUInt32Value(UNIT_DYNAMIC_FLAGS,GetCreatureInfo()->dynamicflags);
+
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
SetMeleeDamageSchool(SpellSchools(GetCreatureInfo()->dmgschool));
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(GetCreatureInfo()->armor));
SetModifierValue(UNIT_MOD_RESISTANCE_HOLY, BASE_VALUE, float(GetCreatureInfo()->resistance1));
@@ -322,8 +378,10 @@ bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
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
{
@@ -333,9 +391,11 @@ bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
(factionEntry->team == ALLIANCE || factionEntry->team == HORDE) )
SetPvP(true);
}
+
// HACK: trigger creature is always not selectable
if(isTrigger())
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
+
if(isTotem() || isTrigger()
|| GetCreatureType() == CREATURE_TYPE_CRITTER)
SetReactState(REACT_PASSIVE);
@@ -343,21 +403,26 @@ bool Creature::UpdateEntry(uint32 Entry, uint32 team, const CreatureData *data )
SetReactState(REACT_DEFENSIVE);*/
else
SetReactState(REACT_AGGRESSIVE);
+
if(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_TAUNT)
{
ApplySpellImmune(0, IMMUNITY_STATE, SPELL_AURA_MOD_TAUNT, true);
ApplySpellImmune(0, IMMUNITY_EFFECT,SPELL_EFFECT_ATTACK_ME, true);
}
+
if(GetCreatureInfo()->InhabitType & INHABIT_AIR)
AddUnitMovementFlag(MOVEMENTFLAG_FLY_MODE + MOVEMENTFLAG_FLYING);
+
return true;
}
+
void Creature::Update(uint32 diff)
{
if(m_GlobalCooldown <= diff)
m_GlobalCooldown = 0;
else
m_GlobalCooldown -= diff;
+
switch( m_deathState )
{
case JUST_ALIVED:
@@ -394,6 +459,7 @@ void Creature::Update(uint32 diff)
{
if (m_isDeadByDefault)
break;
+
if( m_deathTimer <= diff )
{
RemoveCorpse();
@@ -403,6 +469,7 @@ void Creature::Update(uint32 diff)
{
// for delayed spells
m_Events.Update( diff );
+
m_deathTimer -= diff;
if (m_groupLootTimer && lootingGroupLeaderGUID)
{
@@ -420,6 +487,7 @@ void Creature::Update(uint32 diff)
}
}
}
+
break;
}
case ALIVE:
@@ -436,11 +504,14 @@ void Creature::Update(uint32 diff)
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 creature is charmed, switch to charmed AI
if(NeedChangeAI)
{
@@ -448,6 +519,7 @@ void Creature::Update(uint32 diff)
NeedChangeAI = false;
IsAIEnabled = true;
}
+
if(!IsInEvadeMode() && IsAIEnabled)
{
// do not allow the AI to be changed during update
@@ -455,10 +527,12 @@ void Creature::Update(uint32 diff)
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)
@@ -466,16 +540,20 @@ void Creature::Update(uint32 diff)
else
m_regenTimer -= diff;
}
+
if (m_regenTimer != 0)
break;
+
bool bIsPolymorphed = IsPolymorphed();
bool bInCombat = isInCombat() && (!getVictim() || // if isInCombat() is true and this has no victim
!getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself() || // or the victim/owner/charmer is not a player
!getVictim()->GetCharmerOrOwnerPlayerOrPlayerItself()->isGameMaster()); // or the victim/owner/charmer is not a GameMaster
- /*if(m_regenTimer <= diff)
+
+ /*if(m_regenTimer <= diff)
{*/
if(!bInCombat || bIsPolymorphed) // regenerate health if not in combat or if polymorphed
RegenerateHealth();
+
if(getPowerType() == POWER_ENERGY)
{
if(!IsVehicle() || GetVehicleKit()->GetVehicleInfo()->m_powerType != POWER_PYRITE)
@@ -483,9 +561,10 @@ void Creature::Update(uint32 diff)
}
else
RegenerateMana();
+
/*if(!bIsPolymorphed) // only increase the timer if not polymorphed
m_regenTimer += 2000 - diff;
- }
+ }
else
if(!bIsPolymorphed) // if polymorphed, skip the timer
m_regenTimer -= diff;*/
@@ -498,13 +577,17 @@ void Creature::Update(uint32 diff)
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())
{
@@ -512,27 +595,35 @@ void Creature::RegenerateMana()
{
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 controlled 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
@@ -540,27 +631,35 @@ void Creature::RegenerateHealth()
}
else
addvalue = maxValue/3;
+
ModifyHealth(addvalue);
}
+
void Creature::DoFleeToGetAssistance()
{
if (!getVictim())
return;
+
if(HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
return;
+
float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_FLEE_ASSISTANCE_RADIUS);
if (radius >0)
{
Creature* pCreature = NULL;
+
CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
MaNGOS::NearestAssistCreatureInCreatureRangeCheck u_check(this, getVictim(), radius);
MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck> searcher(this, pCreature, u_check);
+
TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestAssistCreatureInCreatureRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_creature_searcher, *GetMap(), *this, radius);
+
SetNoSearchAssistance(true);
if(!pCreature)
//SetFeared(true, getVictim()->GetGUID(), 0 ,sWorld.getConfig(CONFIG_CREATURE_FAMILY_FLEE_DELAY));
@@ -570,6 +669,7 @@ void Creature::DoFleeToGetAssistance()
GetMotionMaster()->MoveSeekAssistance(pCreature->GetPositionX(), pCreature->GetPositionY(), pCreature->GetPositionZ());
}
}
+
bool Creature::AIM_Initialize(CreatureAI* ai)
{
// make sure nothing can change the AI during AI update
@@ -578,20 +678,24 @@ bool Creature::AIM_Initialize(CreatureAI* ai)
sLog.outDebug("AIM_Initialize: failed to init, locked.");
return false;
}
+
UnitAI *oldAI = i_AI;
+
Motion_Initialize();
+
i_AI = ai ? ai : FactorySelector::selectAI(this);
if(oldAI) delete oldAI;
IsAIEnabled = true;
i_AI->InitializeAI();
return true;
}
+
void Creature::Motion_Initialize()
{
if(!m_formation)
i_motionMaster.Initialize();
else if(m_formation->getLeader() == this)
- {
+ {
m_formation->FormationReset(false);
i_motionMaster.Initialize();
}
@@ -600,19 +704,24 @@ void Creature::Motion_Initialize()
else
i_motionMaster.Initialize();
}
+
bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 vehId, uint32 team, float x, float y, float z, float ang, const CreatureData *data)
{
ASSERT(map);
SetMap(map);
SetPhaseMask(phaseMask,false);
+
Relocate(x, y, z, ang);
+
if(!IsPositionValid())
{
sLog.outError("Creature (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,Entry,x,y);
return false;
}
+
//oX = x; oY = y; dX = x; dY = y; m_moveTime = 0; m_startMove = 0;
const bool bResult = CreateFromProto(guidlow, Entry, vehId, team, data);
+
if (bResult)
{
switch (GetCreatureInfo()->rank)
@@ -638,6 +747,7 @@ bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry,
if (minfo && !isTotem()) // Cancel load if no model defined or if totem
{
uint32 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);
@@ -645,17 +755,21 @@ bool Creature::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry,
}
return bResult;
}
+
bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
{
if(!isTrainer())
return false;
+
TrainerSpellData const* trainer_spells = GetTrainerSpells();
+
if(!trainer_spells || trainer_spells->spellList.empty())
{
sLog.outErrorDb("Creature %u (Entry: %u) have UNIT_NPC_FLAG_TRAINER but have empty trainer spell list.",
GetGUIDLow(),GetEntry());
return false;
}
+
switch(GetCreatureInfo()->trainer_type)
{
case TRAINER_TYPE_CLASS:
@@ -727,13 +841,16 @@ bool Creature::isCanTrainingOf(Player* pPlayer, bool msg) const
}
return true;
}
+
bool Creature::isCanInteractWithBattleMaster(Player* pPlayer, bool msg) const
{
if(!isBattleMaster())
return false;
+
BattleGroundTypeId bgTypeId = sBattleGroundMgr.GetBattleMasterBG(GetEntry());
if(!msg)
return pPlayer->GetBGAccessByLevel(bgTypeId);
+
if(!pPlayer->GetBGAccessByLevel(bgTypeId))
{
pPlayer->PlayerTalkClass->ClearMenus();
@@ -756,22 +873,27 @@ bool Creature::isCanInteractWithBattleMaster(Player* pPlayer, bool msg) const
}
return true;
}
+
bool Creature::isCanTrainingAndResetTalentsOf(Player* pPlayer) const
{
return pPlayer->getLevel() >= 10
&& GetCreatureInfo()->trainer_type == TRAINER_TYPE_CLASS
&& pPlayer->getClass() == GetCreatureInfo()->trainer_class;
}
+
void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
{
//Prevent gossip from NPCs that are possessed.
Unit* Charmed = Unit::GetCharmer();
if (Charmed)
return;
+
PlayerMenu* pm=pPlayer->PlayerTalkClass;
pm->ClearMenus();
+
// lazy loading single time at use
LoadGossipOptions();
+
for( GossipOptionList::iterator i = m_goptions.begin( ); i != m_goptions.end( ); ++i )
{
GossipOption* gso=&*i;
@@ -854,6 +976,7 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
break;
}
}
+
//note for future dev: should have database fields for BoxMessage & BoxMoney
if(!gso->OptionText.empty() && cantalking)
{
@@ -875,6 +998,7 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
}
}
}
+
///some gossips aren't handled in normal way ... so we need to do it this way .. TODO: handle it in normal way ;-)
if(pm->Empty())
{
@@ -888,30 +1012,38 @@ void Creature::prepareGossipMenu( Player *pPlayer,uint32 gossipid )
}
}
}
+
void Creature::sendPreparedGossip(Player* player)
{
if(!player)
return;
+
if(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT) // if world event npc then
gameeventmgr.HandleWorldEventGossip(player, this); // update world state with progress
+
// in case no gossip flag and quest menu not empty, open quest menu (client expect gossip menu with this flag)
if (!HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_GOSSIP) && !player->PlayerTalkClass->GetQuestMenu().Empty())
{
player->SendPreparedQuest(GetGUID());
return;
}
+
// in case non empty gossip menu (that not included quests list size) show it
// (quest entries from quest menu will be included in list)
player->PlayerTalkClass->SendGossipMenu(GetNpcTextId(), GetGUID());
}
+
void Creature::OnGossipSelect(Player* player, uint32 option)
{
GossipMenu& gossipmenu = player->PlayerTalkClass->GetGossipMenu();
+
if(option >= gossipmenu.MenuItemCount())
return;
+
uint32 action=gossipmenu.GetItem(option).m_gAction;
uint32 zoneid=GetZoneId();
uint64 guid=GetGUID();
+
GossipOption const *gossip=GetGossipOption( action );
if(!gossip)
{
@@ -920,6 +1052,7 @@ void Creature::OnGossipSelect(Player* player, uint32 option)
if(!gossip)
return;
}
+
switch (gossip->Action)
{
case GOSSIP_OPTION_GOSSIP:
@@ -927,6 +1060,7 @@ void Creature::OnGossipSelect(Player* player, uint32 option)
uint32 textid = GetGossipTextId(action, zoneid);
if (textid == 0)
textid=GetNpcTextId();
+
player->PlayerTalkClass->CloseGossip();
player->PlayerTalkClass->SendTalking(textid);
break;
@@ -968,10 +1102,12 @@ void Creature::OnGossipSelect(Player* player, uint32 option)
else
{
player->ModifyMoney(-10000000);
+
// Cast spells that teach dual spec
// Both are also ImplicitTarget self and must be cast by player
player->CastSpell(player,63680,true,NULL,NULL,player->GetGUID());
player->CastSpell(player,63624,true,NULL,NULL,player->GetGUID());
+
// Should show another Gossip text with "Congratulations..."
player->PlayerTalkClass->CloseGossip();
}
@@ -1018,7 +1154,9 @@ void Creature::OnGossipSelect(Player* player, uint32 option)
OnPoiSelect( player, gossip );
break;
}
+
}
+
void Creature::OnPoiSelect(Player* player, GossipOption const *gossip)
{
if(gossip->GossipId==GOSSIP_GUARD_SPELLTRAINER || gossip->GossipId==GOSSIP_GUARD_SKILLTRAINER)
@@ -1046,28 +1184,38 @@ void Creature::OnPoiSelect(Player* player, GossipOption const *gossip)
// 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()
{
// don't cache / use cache in case it's a world event announcer
if(GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_WORLDEVENT)
if(uint32 textid = gameeventmgr.GetNpcTextId(m_DBTableGuid))
return textid;
+
if (!m_DBTableGuid)
return DEFAULT_GOSSIP_MESSAGE;
+
if(uint32 pos = objmgr.GetNpcGossip(m_DBTableGuid))
return pos;
+
return DEFAULT_GOSSIP_MESSAGE;
}
+
GossipOption const* Creature::GetGossipOption( uint32 id ) const
{
for( GossipOptionList::const_iterator i = m_goptions.begin( ); i != m_goptions.end( ); ++i )
@@ -1077,22 +1225,28 @@ GossipOption const* Creature::GetGossipOption( uint32 id ) const
}
return NULL;
}
+
void Creature::ResetGossipOptions()
{
m_gossipOptionLoaded = false;
m_goptions.clear();
}
+
void Creature::LoadGossipOptions()
{
if(m_gossipOptionLoaded)
return;
+
uint32 npcflags=GetUInt32Value(UNIT_NPC_FLAGS);
+
CacheNpcOptionList const& noList = objmgr.GetNpcOptions ();
for (CacheNpcOptionList::const_iterator i = noList.begin (); i != noList.end (); ++i)
if(i->NpcFlag & npcflags)
addGossipOption(*i);
+
m_gossipOptionLoaded = true;
}
+
void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type)
{
/* uint32 timeElap = getMSTime();
@@ -1106,23 +1260,28 @@ void Creature::AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint3
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, 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;
@@ -1130,12 +1289,15 @@ void Creature::SetLootRecipient(Unit *unit)
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
@@ -1146,15 +1308,19 @@ void Creature::SaveToDB()
sLog.outError("Creature::SaveToDB failed, cannot get creature data!");
return;
}
+
SaveToDB(GetMapId(), data->spawnMask,GetPhaseMask());
}
+
void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
{
// update in loaded data
if (!m_DBTableGuid)
m_DBTableGuid = GetGUIDLow();
CreatureData& data = objmgr.NewOrExistCreatureData(m_DBTableGuid);
+
uint32 displayId = GetNativeDisplayId();
+
// check if it's a custom model and if not, use 0 for displayId
CreatureInfo const *cinfo = GetCreatureInfo();
if (cinfo)
@@ -1163,6 +1329,7 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
displayId == cinfo->Modelid3 || displayId == cinfo->Modelid4)
displayId = 0;
}
+
// data->guid = guid don't must be update at save
data.id = GetEntry();
data.mapid = mapid;
@@ -1184,9 +1351,12 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
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 << ","
@@ -1207,46 +1377,64 @@ void Creature::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
<< 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);
ResetPlayerDamageReq();
+
// 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);
+
// TODO: set UNIT_FIELD_POWER*, for some creature class case (energy, etc)
+
SetModifierValue(UNIT_MOD_HEALTH, BASE_VALUE, health);
SetModifierValue(UNIT_MOD_MANA, BASE_VALUE, mana);
+
//damage
float damagemod = 1.0f;//_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
@@ -1265,6 +1453,7 @@ float Creature::_GetHealthMod(int32 Rank)
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_HP);
}
}
+
float Creature::_GetDamageMod(int32 Rank)
{
switch (Rank) // define rates for each elite rank
@@ -1283,6 +1472,7 @@ float Creature::_GetDamageMod(int32 Rank)
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_DAMAGE);
}
}
+
float Creature::GetSpellDamageMod(int32 Rank)
{
switch (Rank) // define rates for each elite rank
@@ -1301,6 +1491,7 @@ float Creature::GetSpellDamageMod(int32 Rank)
return sWorld.getRate(RATE_CREATURE_ELITE_ELITE_SPELLDAMAGE);
}
}
+
bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData *data)
{
SetZoneScript();
@@ -1310,41 +1501,56 @@ bool Creature::CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint3
if(!Entry)
return false;
}
+
CreatureInfo const *cinfo = objmgr.GetCreatureTemplate(Entry);
if(!cinfo)
{
sLog.outErrorDb("Creature entry %u does not exist.", Entry);
return false;
}
+
SetOriginalEntry(Entry);
+
if(!vehId)
vehId = cinfo->VehicleId;
+
if(vehId && !CreateVehicleKit(vehId))
vehId = 0;
+
Object::_Create(guidlow, Entry, vehId ? HIGHGUID_VEHICLE : HIGHGUID_UNIT);
+
if(!UpdateEntry(Entry, team, data))
return false;
+
return true;
}
+
bool Creature::LoadFromDB(uint32 guid, Map *map)
{
CreatureData const* data = objmgr.GetCreatureData(guid);
+
if(!data)
{
sLog.outErrorDb("Creature (GUID: %u) not found in table `creature`, can't load. ",guid);
return false;
}
+
m_DBTableGuid = guid;
if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_UNIT);
+
uint16 team = 0;
if(!Create(guid,map,data->phaseMask,data->id,0,team,data->posX,data->posY,data->posZ,data->orientation,data))
return false;
+
//We should set first home position, because then AI calls home movement
SetHomePosition(data->posX,data->posY,data->posZ,data->orientation);
+
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) // respawn on Update
{
@@ -1356,6 +1562,7 @@ bool Creature::LoadFromDB(uint32 guid, Map *map)
Relocate(data->posX,data->posY,tz);
}
}
+
uint32 curhealth = data->curhealth;
if(curhealth)
{
@@ -1363,13 +1570,18 @@ bool Creature::LoadFromDB(uint32 guid, Map *map)
if(curhealth < 1)
curhealth = 1;
}
+
SetHealth(m_deathState == ALIVE ? curhealth : 0);
SetPower(POWER_MANA,data->curmana);
+
// checked at creature_template loading
m_defaultMovementType = MovementGeneratorType(data->movementType);
+
m_creatureData = data;
+
return true;
}
+
void Creature::LoadEquipment(uint32 equip_entry, bool force)
{
if(equip_entry == 0)
@@ -1382,13 +1594,16 @@ void Creature::LoadEquipment(uint32 equip_entry, bool force)
}
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_ID + i, einfo->equipentry[i]);
}
+
bool Creature::hasQuest(uint32 quest_id) const
{
QuestRelations const& qr = objmgr.mCreatureQuestRelations;
@@ -1399,6 +1614,7 @@ bool Creature::hasQuest(uint32 quest_id) const
}
return false;
}
+
bool Creature::hasInvolvedQuest(uint32 quest_id) const
{
QuestRelations const& qr = objmgr.mCreatureQuestInvolvedRelations;
@@ -1409,6 +1625,7 @@ bool Creature::hasInvolvedQuest(uint32 quest_id) const
}
return false;
}
+
void Creature::DeleteFromDB()
{
if (!m_DBTableGuid)
@@ -1416,8 +1633,10 @@ void Creature::DeleteFromDB()
sLog.outDebug("Trying to delete not saved creature!");
return;
}
+
objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
objmgr.DeleteCreatureData(m_DBTableGuid);
+
WorldDatabase.BeginTransaction();
WorldDatabase.PExecuteLog("DELETE FROM creature WHERE guid = '%u'", m_DBTableGuid);
WorldDatabase.PExecuteLog("DELETE FROM creature_addon WHERE guid = '%u'", m_DBTableGuid);
@@ -1425,31 +1644,40 @@ void Creature::DeleteFromDB()
WorldDatabase.PExecuteLog("DELETE FROM game_event_model_equip WHERE guid = '%u'", m_DBTableGuid);
WorldDatabase.CommitTransaction();
}
+
bool Creature::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const
{
// not in world
if(!IsInWorld() || !u->IsInWorld())
return false;
+
// all dead creatures/players not visible for any creatures
if(!u->isAlive() || !isAlive())
return false;
+
// Always can see self
if (u == this)
return true;
+
// phased visibility (both must phased in same way)
if(!InSamePhase(u))
return false;
+
// always seen by owner
if(GetGUID() == u->GetCharmerOrOwnerGUID())
return true;
+
if(u->GetVisibility() == VISIBILITY_OFF) //GM
return false;
+
// invisible aura
if((m_invisibilityMask || u->m_invisibilityMask) && !canDetectInvisibilityOf(u))
return false;
+
// 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(u->GetVisibility() == VISIBILITY_GROUP_STEALTH)
{
@@ -1457,86 +1685,113 @@ bool Creature::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bo
if(!detect || !canDetectStealthOf(u, GetDistance(u)))
return false;
}
+
// Now check is target visible with LoS
//return u->IsWithinLOS(GetPositionX(),GetPositionY(),GetPositionZ());
return true;
}
+
bool Creature::canStartAttack(Unit const* who, bool force) const
{
if(isCivilian())
return false;
+
if(!canFly() && (GetDistanceZ(who) > CREATURE_Z_ATTACK_RANGE + m_CombatDistance))
//|| who->IsControlledByPlayer() && who->IsFlying()))
// we cannot check flying for other creatures, too much map/vmap calculation
// TODO: should switch to range attack
return false;
+
if(!force)
{
if(!_IsTargetAcceptable(who))
return false;
+
if(who->isInCombat())
if(Unit *victim = who->getAttackerForHelper())
if(IsWithinDistInMap(victim, sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS)))
force = true;
+
if(!force && (IsNeutralToAll() || !IsWithinDistInMap(who, GetAttackDistance(who) + m_CombatDistance)))
return false;
}
+
if(!canCreatureAttack(who, force))
return false;
+
return IsWithinLOSInMap(who);
}
+
float Creature::GetAttackDistance(Unit const* pl) const
{
float aggroRate = sWorld.getRate(RATE_CREATURE_AGGRO);
if(aggroRate==0)
return 0.0f;
+
uint32 playerlevel = pl->getLevelForTarget(this);
uint32 creaturelevel = getLevelForTarget(pl);
+
int32 leveldif = int32(playerlevel) - int32(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 varies 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*IN_MILISECONDS;
+
// always save boss respawn time at death to prevent crash cheating
if(sWorld.getConfig(CONFIG_SAVE_RESPAWN_TIME_IMMEDIATELY) || isWorldBoss())
SaveRespawnTime();
}
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, UNIT_NPC_FLAG_NONE);
+
setActive(false);
+
if(!isPet() && GetCreatureInfo()->SkinLootId)
if ( LootTemplates_Skinning.HaveLootFor(GetCreatureInfo()->SkinLootId) )
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE);
+
SetNoSearchAssistance(false);
+
//Dismiss group if is leader
if(m_formation && m_formation->getLeader() == this)
m_formation->FormationReset(true);
+
if ((canFly() || IsFlying()) && FallGround())
return;
+
Unit::setDeathState(CORPSE);
}
else if(s == JUST_ALIVED)
@@ -1561,23 +1816,28 @@ void Creature::setDeathState(DeathState s)
Unit::setDeathState(ALIVE);
}
}
+
bool Creature::FallGround()
{
// Let's abort after we called this function one time
if (getDeathState() == DEAD_FALLING)
return false;
+
float x, y, z;
GetPosition(x, y, z);
float ground_Z = GetMap()->GetVmapHeight(x, y, z, true);
if (fabs(ground_Z - z) < 0.1f)
return false;
+
GetMotionMaster()->MoveFall(ground_Z, EVENT_FALL_GROUND);
Unit::setDeathState(DEAD_FALLING);
return true;
}
+
void Creature::Respawn(bool force)
{
DestroyForNearbyPlayers();
+
if(force)
{
if(isAlive())
@@ -1585,19 +1845,25 @@ void Creature::Respawn(bool force)
else if(getDeathState() != CORPSE)
setDeathState(CORPSE);
}
+
RemoveCorpse();
+
if(getDeathState()==DEAD)
{
if (m_DBTableGuid)
objmgr.SaveCreatureRespawnTime(m_DBTableGuid,GetInstanceId(),0);
+
DEBUG_LOG("Respawning...");
m_respawnTime = 0;
lootForPickPocketed = false;
lootForBody = false;
+
if(m_originalEntry != GetEntry())
UpdateEntry(m_originalEntry);
+
CreatureInfo const *cinfo = GetCreatureInfo();
SelectLevel(cinfo);
+
if (m_isDeadByDefault)
{
setDeathState(JUST_DIED);
@@ -1608,46 +1874,59 @@ void Creature::Respawn(bool force)
}
else
setDeathState( JUST_ALIVED );
+
CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(GetNativeDisplayId());
if (minfo) // Cancel load if no model defined
{
uint32 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);
}
+
//Call AI respawn virtual function
AI()->JustRespawned();
+
uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), GetTypeId());
if (poolid)
poolhandler.UpdatePool(poolid, GetGUIDLow(), GetTypeId());
}
+
SetToNotify();
}
+
void Creature::ForcedDespawn()
{
setDeathState(JUST_DIED);
RemoveCorpse();
SetHealth(0); // just for nice GM-mode view
}
+
bool Creature::IsImmunedToSpell(SpellEntry const* spellInfo)
{
if (!spellInfo)
return false;
+
if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->Mechanic - 1)))
return true;
+
return Unit::IsImmunedToSpell(spellInfo);
}
+
bool Creature::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const
{
if (GetCreatureInfo()->MechanicImmuneMask & (1 << (spellInfo->EffectMechanic[index] - 1)))
return true;
+
return Unit::IsImmunedToSpellEffect(spellInfo, index);
}
+
SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
{
if(!pVictim)
return NULL;
+
for(uint32 i=0; i < CREATURE_MAX_SPELLS; ++i)
{
if(!m_spells[i])
@@ -1658,6 +1937,7 @@ SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
sLog.outError("WORLD: unknown spell id %i", m_spells[i]);
continue;
}
+
bool bcontinue = true;
for(uint32 j=0;j<3;j++)
{
@@ -1672,6 +1952,7 @@ SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
}
}
if(bcontinue) continue;
+
if(spellInfo->manaCost > GetPower(POWER_MANA))
continue;
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
@@ -1690,10 +1971,12 @@ SpellEntry const *Creature::reachWithSpellAttack(Unit *pVictim)
}
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])
@@ -1704,6 +1987,7 @@ SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
sLog.outError("WORLD: unknown spell id %i", m_spells[i]);
continue;
}
+
bool bcontinue = true;
for(uint32 j=0;j<3;j++)
{
@@ -1714,6 +1998,7 @@ SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
}
}
if(bcontinue) continue;
+
if(spellInfo->manaCost > GetPower(POWER_MANA))
continue;
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
@@ -1732,14 +2017,17 @@ SpellEntry const *Creature::reachWithSpellCure(Unit *pVictim)
}
return NULL;
}
+
bool Creature::IsVisibleInGridForPlayer(Player const* pl) const
{
// gamemaster in GM mode see all, including ghosts
if(pl->isGameMaster())
return true;
+
// Trigger shouldn't be visible for players
if(isTrigger())
return false;
+
// Live player (or with not release body see live creatures or death creatures with corpse disappearing time > 0
if(pl->isAlive() || pl->GetDeathTimer() > 0)
{
@@ -1747,6 +2035,7 @@ bool Creature::IsVisibleInGridForPlayer(Player const* pl) const
return false;
return (isAlive() || m_deathTimer > 0 || (m_isDeadByDefault && m_deathState==CORPSE));
}
+
// Dead player see live creatures near own corpse
if(isAlive())
{
@@ -1758,50 +2047,66 @@ bool Creature::IsVisibleInGridForPlayer(Player const* pl) const
return true;
}
}
+
// Dead player see Spirit Healer or Spirit Guide
if(isSpiritService())
return true;
+
// and not see any other
return false;
}
+
Unit* Creature::SelectNearestTarget(float dist) const
{
CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Unit *target = NULL;
+
{
Trinity::NearestHostileUnitInAttackDistanceCheck u_check(this, dist);
Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck> searcher(this, target, u_check);
+
TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck>, WorldTypeMapContainer > world_unit_searcher(searcher);
TypeContainerVisitor<Trinity::UnitLastSearcher<Trinity::NearestHostileUnitInAttackDistanceCheck>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, world_unit_searcher, *GetMap(), *this, ATTACK_DISTANCE);
cell_lock->Visit(cell_lock, grid_unit_searcher, *GetMap(), *this, ATTACK_DISTANCE);
}
+
return target;
}
+
void Creature::CallAssistance()
{
if( !m_AlreadyCallAssistance && getVictim() && !isPet() && !isCharmed())
{
SetNoCallAssistance(true);
+
float radius = sWorld.getConfig(CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS);
+
if(radius > 0)
{
std::list<Creature*> assistList;
+
{
CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::AnyAssistCreatureInRangeCheck u_check(this, getVictim(), radius);
MaNGOS::CreatureListSearcher<MaNGOS::AnyAssistCreatureInRangeCheck> searcher(this, assistList, u_check);
+
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AnyAssistCreatureInRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_creature_searcher, *GetMap(), *this, radius);
}
+
if (!assistList.empty())
{
AssistDelayEvent *e = new AssistDelayEvent(getVictim()->GetGUID(), *this);
@@ -1816,37 +2121,48 @@ void Creature::CallAssistance()
}
}
}
+
void Creature::CallForHelp(float fRadius)
{
if (fRadius <= 0.0f || !getVictim() || isPet() || isCharmed())
return;
+
CellPair p(MaNGOS::ComputeCellPair(GetPositionX(), GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::CallOfHelpCreatureInRangeDo u_do(this, getVictim(), fRadius);
MaNGOS::CreatureWorker<MaNGOS::CallOfHelpCreatureInRangeDo> worker(this, u_do);
+
TypeContainerVisitor<MaNGOS::CreatureWorker<MaNGOS::CallOfHelpCreatureInRangeDo>, GridTypeMapContainer > grid_creature_searcher(worker);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_creature_searcher, *GetMap(), *this, fRadius);
}
+
bool Creature::CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction /*= true*/) const
{
// is it true?
if(!HasReactState(REACT_AGGRESSIVE))
return false;
+
// we don't need help from zombies :)
if (!isAlive())
return false;
+
// we don't need help from non-combatant ;)
if (isCivilian())
return false;
+
// skip fighting creature
if (isInCombat())
return false;
+
// only free creature
if (GetCharmerOrOwnerGUID())
return false;
+
// only from same creature faction
if (checkfaction)
{
@@ -1858,61 +2174,80 @@ bool Creature::CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction /
if (!IsFriendlyTo(u))
return false;
}
+
// skip non hostile to caster enemy creatures
if (!IsHostileTo(enemy))
return false;
+
return true;
}
+
// use this function to avoid having hostile creatures attack
// friendlies and other mobs they shouldn't attack
bool Creature::_IsTargetAcceptable(const Unit *target) const
{
const Unit *myVictim = getAttackerForHelper();
+
// if I'm already fighting target, the target is acceptable
if (myVictim == target || target->getVictim() == this)
return true;
+
// if I'm hostile towards the target, the target is acceptable
if (IsHostileTo(target))
return true;
+
const Unit *targetVictim = target->getAttackerForHelper();
+
// if the target does not have a victim, the target is not acceptable
if (!targetVictim)
return false;
+
// if the target's victim is friendly, and the target is neutral, the target is acceptable
if (IsFriendlyTo(targetVictim) && !IsFriendlyTo(target))
return true;
+
// if the target's victim is not friendly, or the target is friendly, the target is not acceptable
return false;
}
+
void Creature::SaveRespawnTime()
{
if(isSummon() || !m_DBTableGuid || m_creatureData && !m_creatureData->dbData)
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/IN_MILISECONDS);
}
-// this should not be called by petAI or
+
+// this should not be called by petAI or
bool Creature::canCreatureAttack(Unit const *pVictim, bool force) const
{
if(!pVictim->IsInMap(this))
return false;
+
if(!canAttack(pVictim, force))
return false;
+
if(!pVictim->isInAccessiblePlaceFor(this))
return false;
+
if(IsAIEnabled && !AI()->CanAIAttack(pVictim))
return false;
+
if(sMapStore.LookupEntry(GetMapId())->IsDungeon())
return true;
+
//Use AttackDistance in distance check if threat radius is lower. This prevents creature bounce in and out of combat every update tick.
float dist = std::max(GetAttackDistance(pVictim), (float)sWorld.getConfig(CONFIG_THREAT_RADIUS)) + m_CombatDistance;
+
if(Unit *unit = GetCharmerOrOwner())
return pVictim->IsWithinDist(unit, dist);
else
return pVictim->IsInDist(&m_homePosition, dist);
}
+
CreatureDataAddon const* Creature::GetCreatureAddon() const
{
if (m_DBTableGuid)
@@ -1920,35 +2255,42 @@ 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->bytes1 != 0)
{
// 0 StandState
// 1 FreeTalentPoints Pet only, so always 0 for default creature
// 2 StandFlags
// 3 StandMiscFlags
+
SetByteValue(UNIT_FIELD_BYTES_1, 0, uint8(cainfo->bytes1 & 0xFF));
//SetByteValue(UNIT_FIELD_BYTES_1, 1, uint8((cainfo->bytes1 >> 8) & 0xFF));
SetByteValue(UNIT_FIELD_BYTES_1, 1, 0);
SetByteValue(UNIT_FIELD_BYTES_1, 2, uint8((cainfo->bytes1 >> 16) & 0xFF));
SetByteValue(UNIT_FIELD_BYTES_1, 3, uint8((cainfo->bytes1 >> 24) & 0xFF));
}
+
if (cainfo->bytes2 != 0)
{
// 0 SheathState
// 1 Bytes2Flags
// 2 UnitRename Pet only, so always 0 for default creature
// 3 ShapeshiftForm Must be determined/set by shapeshift spell/aura
+
SetByteValue(UNIT_FIELD_BYTES_2, 0, uint8(cainfo->bytes2 & 0xFF));
SetByteValue(UNIT_FIELD_BYTES_2, 1, uint8((cainfo->bytes2 >> 8) & 0xFF));
//SetByteValue(UNIT_FIELD_BYTES_2, 2, uint8((cainfo->bytes2 >> 16) & 0xFF));
@@ -1956,15 +2298,20 @@ bool Creature::LoadCreaturesAddon(bool reload)
//SetByteValue(UNIT_FIELD_BYTES_2, 3, uint8((cainfo->bytes2 >> 24) & 0xFF));
SetByteValue(UNIT_FIELD_BYTES_2, 3, 0);
}
+
if (cainfo->emote != 0)
SetUInt32Value(UNIT_NPC_EMOTESTATE, cainfo->emote);
+
if (cainfo->move_flags != 0)
SetUnitMovementFlags(cainfo->move_flags);
+
if (cainfo->move_flags & (MOVEMENTFLAG_FLY_MODE | MOVEMENTFLAG_FLYING))
sLog.outErrorDb("Creature (GUID: %u Entry: %u) has improper `moveflags` value in `creature_template_addon`. Use `InhabitType` = 4 (Flying) in `creature_template`, not MOVEMENTFLAG_FLY_MODE or MOVEMENTFLAG_FLYING in `creature_template_addon`.",GetGUIDLow(),GetEntry());
+
//Load Path
if (cainfo->path_id != 0)
m_path_id = cainfo->path_id;
+
if(cainfo->auras)
{
for (CreatureDataAddonAura const* cAura = cainfo->auras; cAura->spell_id; ++cAura)
@@ -1975,27 +2322,33 @@ bool Creature::LoadCreaturesAddon(bool reload)
sLog.outErrorDb("Creature (GUID: %u Entry: %u) has wrong spell %u defined in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id);
continue;
}
+
// skip already applied aura
if(HasAuraEffect(cAura->spell_id,cAura->effect_idx))
{
if(!reload)
sLog.outErrorDb("Creature (GUID: %u Entry: %u) has duplicate aura (spell %u effect %u) in `auras` field.",GetGUIDLow(),GetEntry(),cAura->spell_id,cAura->effect_idx);
+
continue;
}
+
AddAuraEffect(AdditionalSpellInfo, cAura->effect_idx, this, this);
sLog.outDebug("Spell: %u with Aura %u added to creature (GUID: %u Entry: %u)", cAura->spell_id, AdditionalSpellInfo->EffectApplyAuraName[cAura->effect_idx],GetGUIDLow(),GetEntry());
}
}
return true;
}
+
/// Send a message to LocalDefense channel for players opposition team in the zone
void Creature::SendZoneUnderAttackMessage(Player* attacker)
{
uint32 enemy_team = attacker->GetTeam();
+
WorldPacket data(SMSG_ZONE_UNDER_ATTACK,4);
data << (uint32)GetAreaId();
sWorld.SendGlobalMessage(&data,NULL,(enemy_team==ALLIANCE ? HORDE : ALLIANCE));
}
+
void Creature::SetInCombatWithZone()
{
if (!CanHaveThreatList())
@@ -2003,21 +2356,27 @@ void Creature::SetInCombatWithZone()
sLog.outError("Creature entry %u call SetInCombatWithZone but creature cannot have threat list.", GetEntry());
return;
}
+
Map* pMap = GetMap();
+
if (!pMap->IsDungeon())
{
sLog.outError("Creature entry %u call SetInCombatWithZone for map (id: %u) that isn't an instance.", GetEntry(), pMap->GetId());
return;
}
+
Map::PlayerList const &PlList = pMap->GetPlayers();
+
if (PlList.isEmpty())
return;
+
for(Map::PlayerList::const_iterator i = PlList.begin(); i != PlList.end(); ++i)
{
if (Player* pPlayer = i->getSource())
{
if (pPlayer->isGameMaster())
continue;
+
if (pPlayer->isAlive())
{
pPlayer->SetInCombatWith(this);
@@ -2026,44 +2385,56 @@ void Creature::SetInCombatWithZone()
}
}
}
+
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(Player *modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellid, SPELLMOD_COOLDOWN, cooldown);
+
if(cooldown)
_AddCreatureSpellCooldown(spellid, time(NULL) + cooldown/IN_MILISECONDS);
+
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 / IN_MILISECONDS)) > 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::HasSpell(uint32 spellID) const
{
uint8 i;
@@ -2072,6 +2443,7 @@ bool Creature::HasSpell(uint32 spellID) const
break;
return i < CREATURE_MAX_SPELLS; //broke before end of iteration of known spells
}
+
time_t Creature::GetRespawnTimeEx() const
{
time_t now = time(NULL);
@@ -2082,6 +2454,7 @@ time_t Creature::GetRespawnTimeEx() const
else
return now;
}
+
void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float* dist ) const
{
if (m_DBTableGuid)
@@ -2095,9 +2468,11 @@ void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float*
*ori = data->orientation;
if(dist)
*dist = data->spawndist;
+
return;
}
}
+
x = GetPositionX();
y = GetPositionY();
z = GetPositionZ();
@@ -2106,27 +2481,33 @@ void Creature::GetRespawnCoord( float &x, float &y, float &z, float* ori, float*
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 * IN_MILISECONDS) * 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;
@@ -2134,81 +2515,104 @@ uint32 Creature::getLevelForTarget( Unit const* target ) const
return 255;
return level;
}
+
std::string Creature::GetAIName() const
{
return ObjectMgr::GetCreatureTemplate(GetEntry())->AIName;
}
+
std::string Creature::GetScriptName() const
{
return objmgr.GetScriptName(GetScriptId());
}
+
uint32 Creature::GetScriptId() const
{
return ObjectMgr::GetCreatureTemplate(GetEntry())->ScriptID;
}
+
VendorItemData const* Creature::GetVendorItems() const
{
return objmgr.GetNpcVendorItemList(GetEntry());
}
+
uint32 Creature::GetVendorItemCurrentCount(VendorItem const* vItem)
{
if(!vItem->maxcount)
return vItem->maxcount;
+
VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
for(; itr != m_vendorItemCounts.end(); ++itr)
if(itr->itemId==vItem->item)
break;
+
if(itr == m_vendorItemCounts.end())
return vItem->maxcount;
+
VendorItemCount* vCount = &*itr;
+
time_t ptime = time(NULL);
+
if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
{
ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
if((vCount->count + diff * pProto->BuyCount) >= vItem->maxcount )
{
m_vendorItemCounts.erase(itr);
return vItem->maxcount;
}
+
vCount->count += diff * pProto->BuyCount;
vCount->lastIncrementTime = ptime;
}
+
return vCount->count;
}
+
uint32 Creature::UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count)
{
if(!vItem->maxcount)
return 0;
+
VendorItemCounts::iterator itr = m_vendorItemCounts.begin();
for(; itr != m_vendorItemCounts.end(); ++itr)
if(itr->itemId==vItem->item)
break;
+
if(itr == m_vendorItemCounts.end())
{
uint32 new_count = vItem->maxcount > used_count ? vItem->maxcount-used_count : 0;
m_vendorItemCounts.push_back(VendorItemCount(vItem->item,new_count));
return new_count;
}
+
VendorItemCount* vCount = &*itr;
+
time_t ptime = time(NULL);
+
if( vCount->lastIncrementTime + vItem->incrtime <= ptime )
{
ItemPrototype const* pProto = objmgr.GetItemPrototype(vItem->item);
+
uint32 diff = uint32((ptime - vCount->lastIncrementTime)/vItem->incrtime);
if((vCount->count + diff * pProto->BuyCount) < vItem->maxcount )
vCount->count += diff * pProto->BuyCount;
else
vCount->count = vItem->maxcount;
}
+
vCount->count = vCount->count > used_count ? vCount->count-used_count : 0;
vCount->lastIncrementTime = ptime;
return vCount->count;
}
+
TrainerSpellData const* Creature::GetTrainerSpells() const
{
return objmgr.GetNpcTrainerSpells(GetEntry());
}
+
// overwrite WorldObject function for proper name localization
const char* Creature::GetNameForLocaleIdx(int32 loc_idx) const
{
@@ -2221,21 +2625,27 @@ const char* Creature::GetNameForLocaleIdx(int32 loc_idx) const
return cl->Name[loc_idx].c_str();
}
}
+
return GetName();
}
+
const CreatureData* Creature::GetLinkedRespawnCreatureData() const
{
if(!m_DBTableGuid) // only hard-spawned creatures from DB can have a linked master
return NULL;
+
if(uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid))
return objmgr.GetCreatureData(targetGuid);
+
return NULL;
}
+
// returns master's remaining respawn time if any
time_t Creature::GetLinkedCreatureRespawnTime() const
{
if(!m_DBTableGuid) // only hard-spawned creatures from DB can have a linked master
return 0;
+
if(uint32 targetGuid = objmgr.GetLinkedRespawnGuid(m_DBTableGuid))
{
Map* targetMap = NULL;
@@ -2249,6 +2659,7 @@ time_t Creature::GetLinkedCreatureRespawnTime() const
if(targetMap)
return objmgr.GetCreatureRespawnTime(targetGuid,targetMap->GetInstanceId());
}
+
return 0;
}
diff --git a/src/game/Creature.h b/src/game/Creature.h
index f66d485d183..5fbe4a07a28 100644
--- a/src/game/Creature.h
+++ b/src/game/Creature.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_CREATURE_H
#define TRINITYCORE_CREATURE_H
+
#include "Common.h"
#include "Unit.h"
#include "UpdateMask.h"
@@ -26,13 +28,17 @@
#include "LootMgr.h"
#include "Database/DatabaseEnv.h"
#include "Cell.h"
+
#include <list>
+
struct SpellEntry;
+
class CreatureAI;
class Quest;
class Player;
class WorldSession;
class CreatureGroup;
+
enum Gossip_Option
{
GOSSIP_OPTION_NONE = 0, //UNIT_NPC_FLAG_NONE = 0,
@@ -56,6 +62,7 @@ enum Gossip_Option
GOSSIP_OPTION_LEARNDUALSPEC = 18, //UNIT_NPC_FLAG_TRAINER (bonus option for GOSSIP_OPTION_TRAINER)
GOSSIP_OPTION_OUTDOORPVP = 19 //added by code (option for outdoor pvp creatures)
};
+
enum Gossip_Guard
{
GOSSIP_GUARD_BANK = 32,
@@ -70,6 +77,7 @@ enum Gossip_Guard
GOSSIP_GUARD_SPELLTRAINER = 41,
GOSSIP_GUARD_SKILLTRAINER = 42
};
+
enum Gossip_Guard_Spell
{
GOSSIP_GUARD_SPELL_WARRIOR = 64,
@@ -84,6 +92,7 @@ enum Gossip_Guard_Spell
GOSSIP_GUARD_SPELL_UNKNOWN2 = 73,
GOSSIP_GUARD_SPELL_DRUID = 74
};
+
enum Gossip_Guard_Skill
{
GOSSIP_GUARD_SKILL_ALCHEMY = 80,
@@ -99,6 +108,7 @@ enum Gossip_Guard_Skill
GOSSIP_GUARD_SKILL_TAILORING = 90,
GOSSIP_GUARD_SKILL_ENGINERING = 91
};
+
enum GossipOptionIcon
{
GOSSIP_ICON_CHAT = 0, //white chat bubble
@@ -113,6 +123,7 @@ enum GossipOptionIcon
GOSSIP_ICON_BATTLE = 9, //two swords
GOSSIP_ICON_DOT = 10 //yellow dot
};
+
struct GossipOption
{
uint32 Id;
@@ -125,6 +136,7 @@ struct GossipOption
std::string OptionText;
std::string BoxText;
};
+
enum CreatureFlagsExtra
{
CREATURE_FLAG_EXTRA_INSTANCE_BIND = 0x00000001, // creature kill bind instance with killer and killer's group
@@ -141,13 +153,16 @@ enum CreatureFlagsExtra
CREATURE_FLAG_EXTRA_NO_CRIT = 0x00020000, // creature can't do critical strikes
CREATURE_FLAG_EXTRA_NO_SKILLGAIN = 0x00040000, // creature won't increase weapon skills
};
+
// 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
+
#define MAX_KILL_CREDIT 2
+
// from `creature_template` table
struct CreatureInfo
{
@@ -223,6 +238,7 @@ struct CreatureInfo
uint32 ScriptID;
uint32 GetRandomValidModelId() const;
uint32 GetFirstValidModelId() const;
+
// helpers
SkillType GetRequiredLootSkill() const
{
@@ -235,33 +251,40 @@ struct CreatureInfo
else
return SKILL_SKINNING; // normal case
}
+
bool isTameable(bool exotic) const
{
if(type != CREATURE_TYPE_BEAST || family == 0 || (type_flags & CREATURE_TYPEFLAGS_TAMEABLE)==0)
return false;
+
// if can tame exotic then can tame any temable
return exotic || (type_flags & CREATURE_TYPEFLAGS_EXOTIC)==0;
}
};
+
struct CreatureLocale
{
std::vector<std::string> Name;
std::vector<std::string> SubName;
};
+
struct NpcOptionLocale
{
std::vector<std::string> OptionText;
std::vector<std::string> BoxText;
};
+
struct PointOfInterestLocale
{
std::vector<std::string> IconName;
};
+
struct EquipmentInfo
{
uint32 entry;
uint32 equipentry[3];
};
+
// from `creature` table
struct CreatureData
{
@@ -285,11 +308,13 @@ struct CreatureData
uint8 spawnMask;
bool dbData;
};
+
struct CreatureDataAddonAura
{
uint32 spell_id;
uint8 effect_idx;
};
+
// from `creature_addon` table
struct CreatureDataAddon
{
@@ -302,6 +327,7 @@ struct CreatureDataAddon
uint32 move_flags;
CreatureDataAddonAura const* auras; // loaded as char* "spell1 eff1 spell2 eff2 ... "
};
+
struct CreatureModelInfo
{
uint32 modelid;
@@ -310,6 +336,7 @@ struct CreatureModelInfo
uint8 gender;
uint32 modelid_other_gender;
};
+
enum InhabitTypeValues
{
INHABIT_GROUND = 1,
@@ -317,6 +344,7 @@ enum InhabitTypeValues
INHABIT_AIR = 4,
INHABIT_ANYWHERE = INHABIT_GROUND | INHABIT_WATER | INHABIT_AIR
};
+
// Enums used by StringTextData::Type (CreatureEventAI)
enum ChatType
{
@@ -328,6 +356,7 @@ enum ChatType
CHAT_TYPE_BOSS_WHISPER = 5,
CHAT_TYPE_ZONE_YELL = 6
};
+
//Selection method used by SelectTarget (CreatureEventAI)
enum AttackingTarget
{
@@ -340,26 +369,31 @@ enum AttackingTarget
ATTACKING_TARGET_BOTTOMAGGRO_PLAYER, //Selects targets from bottom aggro to top (player only)
*/
};
+
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
+
// Vendors
struct VendorItem
{
VendorItem(uint32 _item, uint32 _maxcount, uint32 _incrtime, uint32 _ExtendedCost)
: item(_item), maxcount(_maxcount), incrtime(_incrtime), ExtendedCost(_ExtendedCost) {}
+
uint32 item;
uint32 maxcount; // 0 for infinity item amount
uint32 incrtime; // time for restore items amount if maxcount != 0
uint32 ExtendedCost;
};
typedef std::vector<VendorItem*> VendorItemList;
+
struct VendorItemData
{
VendorItemList m_items;
+
VendorItem* GetItem(uint32 slot) const
{
if(slot>=m_items.size()) return NULL;
@@ -374,6 +408,7 @@ struct VendorItemData
bool RemoveItem( uint32 item_id );
VendorItem const* FindItem(uint32 item_id) const;
size_t FindItemSlot(uint32 item_id) const;
+
void Clear()
{
for (VendorItemList::const_iterator itr = m_items.begin(); itr != m_items.end(); ++itr)
@@ -381,62 +416,84 @@ struct VendorItemData
m_items.clear();
}
};
+
struct VendorItemCount
{
explicit VendorItemCount(uint32 _item, uint32 _count)
: itemId(_item), count(_count), lastIncrementTime(time(NULL)) {}
+
uint32 itemId;
uint32 count;
time_t lastIncrementTime;
};
+
typedef std::list<VendorItemCount> VendorItemCounts;
+
struct TrainerSpell
{
TrainerSpell() : spell(0), spellCost(0), reqSkill(0), reqSkillValue(0), reqLevel(0), learnedSpell(0) {}
+
TrainerSpell(uint32 _spell, uint32 _spellCost, uint32 _reqSkill, uint32 _reqSkillValue, uint32 _reqLevel, uint32 _learnedspell)
: spell(_spell), spellCost(_spellCost), reqSkill(_reqSkill), reqSkillValue(_reqSkillValue), reqLevel(_reqLevel), learnedSpell(_learnedspell)
{}
+
uint32 spell;
uint32 spellCost;
uint32 reqSkill;
uint32 reqSkillValue;
uint32 reqLevel;
uint32 learnedSpell;
+
// helpers
bool IsCastable() const { return learnedSpell != spell; }
};
+
typedef UNORDERED_MAP<uint32 /*spellid*/, TrainerSpell> TrainerSpellMap;
+
struct TrainerSpellData
{
TrainerSpellData() : trainerType(0) {}
+
TrainerSpellMap spellList;
uint32 trainerType; // trainer type based at trainer spells, can be different from creature_template value.
// req. for correct show non-prof. trainers like weaponmaster, allowed values 0 and 2.
TrainerSpell const* Find(uint32 spell_id) const;
void Clear() { spellList.clear(); }
};
+
typedef std::list<GossipOption> GossipOptionList;
+
typedef std::map<uint32,time_t> CreatureSpellCooldowns;
+
// max different by z coordinate for creature aggro reaction
#define CREATURE_Z_ATTACK_RANGE 3
+
#define MAX_VENDOR_ITEMS 150 // Limitation in 3.x.x item count in SMSG_LIST_INVENTORY
+
class TRINITY_DLL_SPEC Creature : public Unit
{
public:
+
explicit Creature();
virtual ~Creature();
+
void AddToWorld();
void RemoveFromWorld();
+
void DisappearAndDie();
+
bool Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 vehId, uint32 team, float x, float y, float z, float ang, 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 GetCreatureInfo()->equipmentId; }
+
void SetCorpseDelay(uint32 delay) { m_corpseDelay = delay; }
bool isRacialLeader() const { return GetCreatureInfo()->RacialLeader; }
bool isCivilian() const { return GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_CIVILIAN; }
@@ -460,33 +517,45 @@ class TRINITY_DLL_SPEC Creature : public Unit
{
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 { return hasUnitState(UNIT_STAT_EVADE); }
+
bool AIM_Initialize(CreatureAI* ai = NULL);
void Motion_Initialize();
+
void AI_SendMoveToPacket(float x, float y, float z, uint32 time, uint32 MovementFlags, uint8 type);
CreatureAI * AI() const { return (CreatureAI*)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();
@@ -498,56 +567,72 @@ class TRINITY_DLL_SPEC Creature : public Unit
void UpdateDamagePhysical(WeaponAttackType attType);
uint32 GetCurrentEquipmentId() { return m_equipmentId; }
float GetSpellDamageMod(int32 Rank);
+
VendorItemData const* GetVendorItems() const;
uint32 GetVendorItemCurrentCount(VendorItem const* vItem);
uint32 UpdateVendorItemCurrentCount(VendorItem const* vItem, uint32 used_count);
+
TrainerSpellData const* GetTrainerSpells() const;
+
CreatureInfo const *GetCreatureInfo() const { return m_creatureInfo; }
CreatureData const *GetCreatureData() const { return m_creatureData; }
CreatureDataAddon const* GetCreatureAddon() const;
+
std::string GetAIName() const;
std::string GetScriptName() const;
uint32 GetScriptId() const;
+
void prepareGossipMenu( Player *pPlayer, uint32 gossipid = 0 );
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();
void ResetGossipOptions();
GossipOption const* GetGossipOption( uint32 id ) const;
void addGossipOption(GossipOption const& gso) { m_goptions.push_back(gso); }
+
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 YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); }
+
// overwrite WorldObject function for proper name localization
const char* GetNameForLocaleIdx(int32 locale_idx) const;
+
void setDeathState(DeathState s); // overwrite virtual Unit::setDeathState
bool FallGround();
+
bool LoadFromDB(uint32 guid, Map *map);
void SaveToDB();
// overwrited in Pet
virtual void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
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;
+
bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const;
bool canStartAttack(Unit const* u, bool force) const;
float GetAttackDistance(Unit const* pl) const;
+
Unit* SelectNearestTarget(float dist = 0) const;
void DoFleeToGetAssistance();
void CallForHelp(float fRadius);
@@ -557,33 +642,47 @@ class TRINITY_DLL_SPEC Creature : public Unit
bool HasSearchedAssistance() { return m_AlreadySearchedAssistance; }
bool CanAssistTo(const Unit* u, const Unit* enemy, bool checkfaction = true) const;
bool _IsTargetAcceptable(const Unit* target) const;
+
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 const* pl) const;
+
void RemoveCorpse();
bool isDeadByDefault() const { return m_isDeadByDefault; };
+
void ForcedDespawn();
+
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(bool force = false);
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; }
+
// Linked Creature Respawning System
time_t GetLinkedCreatureRespawnTime() const;
const CreatureData* GetLinkedRespawnCreatureData() const;
+
uint32 m_groupLootTimer; // (msecs)timer used for group loot
uint64 lootingGroupLeaderGUID; // used to find group which is looting corpse
+
void SendZoneUnderAttackMessage(Player* attacker);
+
void SetInCombatWithZone();
+
bool hasQuest(uint32 quest_id) const;
bool hasInvolvedQuest(uint32 quest_id) const;
+
GridReference<Creature> &GetGridRef() { return m_gridRef; }
bool isRegeneratingHealth() { return m_regenHealth; }
virtual uint8 GetPetAutoSpellSize() const { return CREATURE_MAX_SPELLS; }
@@ -594,19 +693,26 @@ class TRINITY_DLL_SPEC Creature : public Unit
else
return m_charmInfo->GetCharmSpell(pos)->GetAction();
}
+
void SetHomePosition(float x, float y, float z, float o) { m_homePosition.Relocate(x, y, z, o); }
void SetHomePosition(const Position &pos) { m_homePosition.Relocate(pos);}
void GetHomePosition(float &x, float &y, float &z, float &ori) { m_homePosition.GetPosition(x, y, z, ori); }
+
uint32 GetGlobalCooldown() const { return m_GlobalCooldown; }
+
uint32 GetWaypointPath(){return m_path_id;}
void LoadPath(uint32 pathid) { m_path_id = pathid; }
+
uint32 GetCurrentWaypointID(){return m_waypointID;}
void UpdateWaypointID(uint32 wpID){m_waypointID = wpID;}
+
void SearchFormation();
CreatureGroup *GetFormation() {return m_formation;}
void SetFormation(CreatureGroup *formation) {m_formation = formation;}
+
Unit *SelectVictim();
void SetDeadByDefault (bool death_state) {m_isDeadByDefault = death_state;}
+
void SetDisableReputationGain(bool disable) { DisableReputationGain = disable; }
bool IsReputationGainDisabled() { return DisableReputationGain; }
bool IsDamageEnoughForLootingAndReward() { return m_PlayerDamageReq == 0; }
@@ -617,26 +723,36 @@ class TRINITY_DLL_SPEC Creature : public Unit
}
void ResetPlayerDamageReq() { m_PlayerDamageReq = GetHealth() / 2; }
uint32 m_PlayerDamageReq;
+
void SetOriginalEntry(uint32 entry) { m_originalEntry = entry; }
+
static float _GetDamageMod(int32 Rank);
+
float m_SightDistance, m_CombatDistance;
protected:
bool CreateFromProto(uint32 guidlow, uint32 Entry, uint32 vehId, uint32 team, const CreatureData *data = NULL);
bool InitEntry(uint32 entry, uint32 team=ALLIANCE, const CreatureData* data=NULL);
+
// vendor items
VendorItemCounts m_vendorItemCounts;
+
void _RealtimeSetCreatureInfo();
+
static float _GetHealthMod(int32 Rank);
+
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;
+
ReactStates m_reactState; // for AI, not charmInfo
void RegenerateMana();
void RegenerateHealth();
@@ -645,36 +761,48 @@ class TRINITY_DLL_SPEC Creature : public Unit
Cell m_currentCell; // store current cell where creature listed
uint32 m_DBTableGuid; ///< For new or temporary creatures is 0 for saved it is lowguid
uint32 m_equipmentId;
+
bool m_AlreadyCallAssistance;
bool m_AlreadySearchedAssistance;
bool m_regenHealth;
bool m_AI_locked;
bool m_isDeadByDefault;
+
SpellSchoolMask m_meleeDamageSchoolMask;
uint32 m_originalEntry;
+
Position m_homePosition;
+
bool DisableReputationGain;
+
CreatureInfo const* m_creatureInfo; // in heroic mode can different from ObjMgr::GetCreatureTemplate(GetEntry())
CreatureData const* m_creatureData;
+
private:
//WaypointMovementGenerator vars
uint32 m_waypointID;
uint32 m_path_id;
+
//Formation var
CreatureGroup *m_formation;
+
GridReference<Creature> m_gridRef;
};
+
class AssistDelayEvent : public BasicEvent
{
public:
AssistDelayEvent(const uint64& victim, Unit& owner) : BasicEvent(), m_victim(victim), m_owner(owner) { }
+
bool Execute(uint64 e_time, uint32 p_time);
void AddAssistant(const uint64& guid) { m_assistants.push_back(guid); }
private:
AssistDelayEvent();
+
uint64 m_victim;
std::list<uint64> m_assistants;
Unit& m_owner;
};
+
#endif
diff --git a/src/game/CreatureAI.cpp b/src/game/CreatureAI.cpp
index 6727575e920..27fe428efd1 100644
--- a/src/game/CreatureAI.cpp
+++ b/src/game/CreatureAI.cpp
@@ -17,12 +17,14 @@
* 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 "CreatureAIImpl.h"
#include "Creature.h"
#include "World.h"
#include "SpellMgr.h"
#include "Vehicle.h"
+
//Disable CreatureAI when charmed
void CreatureAI::OnCharmed(bool apply)
{
@@ -30,20 +32,25 @@ void CreatureAI::OnCharmed(bool apply)
me->NeedChangeAI = true;
me->IsAIEnabled = false;
}
+
AISpellInfoType * UnitAI::AISpellInfo;
TRINITY_DLL_SPEC AISpellInfoType * GetAISpellInfo(uint32 i) { return &CreatureAI::AISpellInfo[i]; }
+
void CreatureAI::DoZoneInCombat(Creature* creature)
{
if (!creature)
creature = me;
+
if(!creature->CanHaveThreatList())
return;
+
Map *map = creature->GetMap();
if (!map->IsDungeon()) //use IsDungeon instead of Instanceable, in case battlegrounds will be instantiated
{
sLog.outError("DoZoneInCombat call for map that isn't an instance (creature entry = %d)", creature->GetTypeId() == TYPEID_UNIT ? ((Creature*)creature)->GetEntry() : 0);
return;
}
+
if(!creature->HasReactState(REACT_PASSIVE) && !creature->getVictim())
{
if(Unit *target = creature->SelectNearestTarget(50))
@@ -60,26 +67,32 @@ void CreatureAI::DoZoneInCombat(Creature* creature)
}
}
}
+
if(!creature->HasReactState(REACT_PASSIVE) && !creature->getVictim())
{
sLog.outError("DoZoneInCombat called for creature that has empty threat list (creature entry = %u)", creature->GetEntry());
return;
}
+
Map::PlayerList const &PlList = map->GetPlayers();
+
if(PlList.isEmpty())
return;
+
for(Map::PlayerList::const_iterator i = PlList.begin(); i != PlList.end(); ++i)
{
if(Player* pPlayer = i->getSource())
{
if(pPlayer->isGameMaster())
continue;
+
if(pPlayer->isAlive())
{
creature->SetInCombatWith(pPlayer);
pPlayer->SetInCombatWith(creature);
creature->AddThreat(pPlayer, 0.0f);
}
+
/* Causes certain things to never leave the threat list (Priest Lightwell, etc):
for(Unit::ControlList::const_iterator itr = pPlayer->m_Controlled.begin(); itr != pPlayer->m_Controlled.end(); ++itr)
{
@@ -90,12 +103,15 @@ void CreatureAI::DoZoneInCombat(Creature* creature)
}
}
}
+
void CreatureAI::MoveInLineOfSight(Unit *who)
{
if(me->getVictim())
return;
+
if (me->GetTypeId() == CREATURE_TYPE_NON_COMBAT_PET) // non-combat pets should just stand there and look good;)
return;
+
if(me->canStartAttack(who, false))
AttackStart(who);
//else if(who->getVictim() && me->IsFriendlyTo(who)
@@ -103,6 +119,7 @@ void CreatureAI::MoveInLineOfSight(Unit *who)
// && me->canStartAttack(who->getVictim(), true)) // TODO: if we use true, it will not attack it when it arrives
// me->GetMotionMaster()->MoveChase(who->getVictim());
}
+
void CreatureAI::SelectNearestTarget(Unit *who)
{
if(me->getVictim() && me->GetDistanceOrder(who, me->getVictim()) && me->canAttack(who))
@@ -111,11 +128,14 @@ void CreatureAI::SelectNearestTarget(Unit *who)
me->AddThreat(who, 1000000.0f);
}
}
+
void CreatureAI::EnterEvadeMode()
{
if(!_EnterEvadeMode())
return;
+
sLog.outDebug("Creature %u enters evade mode.", me->GetEntry());
+
if(!me->GetVehicle()) // otherwise me will be in evade mode forever
{
if(Unit *owner = me->GetCharmerOrOwner())
@@ -126,10 +146,13 @@ void CreatureAI::EnterEvadeMode()
else
me->GetMotionMaster()->MoveTargetedHome();
}
+
Reset();
+
if(me->IsVehicle()) // use the same sequence of addtoworld, aireset may remove all summons!
me->GetVehicleKit()->Reset();
}
+
/*void CreatureAI::AttackedBy( Unit* attacker )
{
if(!m_creature->getVictim())
diff --git a/src/game/CreatureAI.h b/src/game/CreatureAI.h
index 98a145b79e0..556e9be14e0 100644
--- a/src/game/CreatureAI.h
+++ b/src/game/CreatureAI.h
@@ -17,29 +17,38 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CREATUREAI_H
#define TRINITY_CREATUREAI_H
+
#include "UnitAI.h"
#include "Common.h"
+
class WorldObject;
class Unit;
class Creature;
class Player;
struct SpellEntry;
+
#define TIME_INTERVAL_LOOK 5000
#define VISIBILITY_RANGE 10000
+
//Spell targets used by SelectSpell
enum SelectTargetType
{
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
{
@@ -48,84 +57,123 @@ enum SelectEffect
SELECT_EFFECT_HEALING, //Spell does healing
SELECT_EFFECT_AURA, //Spell applies an aura
};
+
enum SCEquip
{
EQUIP_NO_CHANGE = -1,
EQUIP_UNEQUIP = 0
};
+
class TRINITY_DLL_SPEC CreatureAI : public UnitAI
{
protected:
Creature * const me;
Creature * const m_creature;
+
bool UpdateVictim();
bool UpdateVictimWithGaze();
bool UpdateCombatState();
+
void SelectNearestTarget(Unit *who);
+
void SetGazeOn(Unit *target);
+
Creature *DoSummon(uint32 uiEntry, const Position &pos, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature *DoSummon(uint32 uiEntry, WorldObject *obj, float fRadius = 5.0f, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
Creature *DoSummonFlyer(uint32 uiEntry, WorldObject *obj, float fZ, float fRadius = 5.0f, uint32 uiDespawntime = 30000, TempSummonType uiType = TEMPSUMMON_CORPSE_TIMED_DESPAWN);
+
public:
explicit CreatureAI(Creature *c) : UnitAI((Unit*)c), me(c), m_creature(c) {}
+
virtual ~CreatureAI() {}
+
///== Reactions At =================================
+
// Called if IsVisible(Unit *who) is true at each *who move, reaction at visibility zone enter
virtual void MoveInLineOfSight(Unit *);
+
// Called for reaction at stopping attack at no attackers or targets
virtual void EnterEvadeMode();
+
// Called for reaction at enter to combat if not in combat yet (enemy can be NULL)
virtual void EnterCombat(Unit* /*enemy*/) {}
+
// Called at any Damage from any attacker (before damage apply)
// Note: it for recalculation damage or special reaction at damage
// for attack reaction use AttackedBy called for not DOT damage in Unit::DealDamage also
virtual void DamageTaken(Unit *done_by, uint32 & /*damage*/) {}
+
// 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 IsSummonedBy(Unit *summoner) {}
+
virtual void SummonedCreatureDespawn(Creature* /*unit*/) {}
+
// Called when hit by a spell
virtual void SpellHit(Unit*, const SpellEntry*) {}
+
// Called when spell hits a target
virtual void SpellHitTarget(Unit* target, const SpellEntry*) {}
+
// Called when the creature is target of hostile action: swing, hostile spell landed, fear/etc)
//virtual void AttackedBy(Unit* attacker);
virtual bool IsEscorted() {return false;}
+
// Called when creature is spawned or respawned (for reseting variables)
virtual void JustRespawned() { Reset(); }
+
// Called at waypoint reached or point movement finished
virtual void MovementInform(uint32 /*MovementType*/, uint32 /*Data*/) {}
+
void OnCharmed(bool apply);
+
//virtual void SpellClick(Player *player) {}
+
// Called at reaching home after evade
virtual void JustReachedHome() {}
+
void DoZoneInCombat(Creature* pUnit = NULL);
- // Called at text emote receive from player
+
+ // Called at text emote receive from player
virtual void ReceiveEmote(Player* pPlayer, uint32 text_emote) {}
+
///== Triggered Actions Requested ==================
+
// Called when creature attack expected (if creature can and no have current victim)
// Note: for reaction at hostile action must be called AttackedBy function.
//virtual void AttackStart(Unit *) {}
+
// Called at World update tick
//virtual void UpdateAI(const uint32 diff ) {}
+
///== State checks =================================
+
// Is unit visible for MoveInLineOfSight
//virtual bool IsVisible(Unit *) const { return false; }
+
// called when the corpse of this creature gets removed
virtual void CorpseRemoved(uint32 & /*respawnDelay*/) {}
+
// Called when victim entered water and creature can not enter water
//virtual bool canReachByRangeAttack(Unit*) { return false; }
+
///== Fields =======================================
+
// Pointer to controlled by AI creature
//Creature* const m_creature;
+
virtual void PassengerBoarded(Unit *who, int8 seatId, bool apply) {}
+
protected:
bool _EnterEvadeMode();
};
+
enum Permitions
{
PERMIT_BASE_NO = -1,
@@ -135,4 +183,5 @@ enum Permitions
PERMIT_BASE_FACTION_SPECIFIC = 400,
PERMIT_BASE_SPECIAL = 800
};
+
#endif
diff --git a/src/game/CreatureAIFactory.h b/src/game/CreatureAIFactory.h
index a2396fc5117..d546c2b1720 100644
--- a/src/game/CreatureAIFactory.h
+++ b/src/game/CreatureAIFactory.h
@@ -19,20 +19,26 @@
*/
#ifndef TRINITY_CREATUREAIFACTORY_H
#define TRINITY_CREATUREAIFACTORY_H
+
//#include "Policies/Singleton.h"
#include "Dynamic/ObjectRegistry.h"
#include "Dynamic/FactoryHolder.h"
+
struct SelectableAI : public FactoryHolder<CreatureAI>, public Permissible<Creature>
{
SelectableAI(const char *id) : FactoryHolder<CreatureAI>(id) {}
};
+
template<class REAL_AI>
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); }
};
+
template<class REAL_AI>
inline CreatureAI*
CreatureAIFactory<REAL_AI>::Create(void *data) const
@@ -40,6 +46,7 @@ CreatureAIFactory<REAL_AI>::Create(void *data) const
Creature* creature = reinterpret_cast<Creature *>(data);
return (new REAL_AI(creature));
}
+
typedef FactoryHolder<CreatureAI> CreatureAICreator;
typedef FactoryHolder<CreatureAI>::FactoryHolderRegistry CreatureAIRegistry;
typedef FactoryHolder<CreatureAI>::FactoryHolderRepository CreatureAIRepository;
diff --git a/src/game/CreatureAIImpl.h b/src/game/CreatureAIImpl.h
index 4fddcc6392d..89d5bbc2a9e 100644
--- a/src/game/CreatureAIImpl.h
+++ b/src/game/CreatureAIImpl.h
@@ -17,17 +17,21 @@
*/
#ifndef CREATUREAIIMPL_H
#define CREATUREAIIMPL_H
+
#include "Common.h"
#include "Platform/Define.h"
#include "TemporarySummon.h"
#include "CreatureAI.h"
+
#define HEROIC(n,h) (HeroicMode ? h : n)
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2)
{
return (rand()%2) ? v1 : v2;
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3)
@@ -40,6 +44,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3)
case 2: return v3;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4)
@@ -53,6 +58,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4)
case 3: return v4;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
@@ -67,6 +73,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
case 4: return v5;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6)
@@ -82,6 +89,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 5: return v6;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7)
@@ -98,6 +106,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 6: return v7;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8)
@@ -115,6 +124,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 7: return v8;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -134,6 +144,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 8: return v9;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -154,6 +165,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 9: return v10;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -175,6 +187,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 10: return v11;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -197,6 +210,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 11: return v12;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -220,6 +234,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 12: return v13;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -244,6 +259,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 13: return v14;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -269,6 +285,7 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 14: return v15;
}
}
+
template<class T>
inline
const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, const T& v6, const T& v7, const T& v8,
@@ -295,20 +312,26 @@ const T& RAND(const T& v1, const T& v2, const T& v3, const T& v4, const T& v5, c
case 15: return v16;
}
}
+
class EventMap : private std::map<uint32, uint32>
{
private:
uint32 m_time, m_phase;
public:
explicit EventMap() : m_phase(0), m_time(0) {}
+
uint32 GetTimer() const { return m_time; }
+
void Reset() { clear(); m_time = 0; m_phase = 0; }
+
void Update(uint32 time) { m_time += time; }
+
void SetPhase(uint32 phase)
{
if(phase && phase < 9)
m_phase = (1 << (phase + 24));
}
+
void ScheduleEvent(uint32 eventId, uint32 time, uint32 gcd = 0, uint32 phase = 0)
{
time += m_time;
@@ -324,11 +347,13 @@ class EventMap : private std::map<uint32, uint32>
}
insert(std::make_pair(time, eventId));
}
+
void RescheduleEvent(uint32 eventId, uint32 time, uint32 gcd = 0, uint32 phase = 0)
{
CancelEvent(eventId);
ScheduleEvent(eventId, time, gcd, phase);
}
+
void RepeatEvent(uint32 time)
{
if(empty())
@@ -344,10 +369,12 @@ class EventMap : private std::map<uint32, uint32>
}
insert(std::make_pair(time, eventId));
}
+
void PopEvent()
{
erase(begin());
}
+
uint32 ExecuteEvent()
{
while(!empty())
@@ -365,6 +392,7 @@ class EventMap : private std::map<uint32, uint32>
}
return 0;
}
+
uint32 GetEvent()
{
while(!empty())
@@ -380,6 +408,7 @@ class EventMap : private std::map<uint32, uint32>
}
return 0;
}
+
void DelayEvents(uint32 time, uint32 gcd)
{
time += m_time;
@@ -397,6 +426,7 @@ class EventMap : private std::map<uint32, uint32>
++itr;
}
}
+
void CancelEvent(uint32 eventId)
{
for(iterator itr = begin(); itr != end();)
@@ -407,6 +437,7 @@ class EventMap : private std::map<uint32, uint32>
++itr;
}
}
+
void CancelEventsByGCD(uint32 gcd)
{
for(iterator itr = begin(); itr != end();)
@@ -418,6 +449,7 @@ class EventMap : private std::map<uint32, uint32>
}
}
};
+
enum AITarget
{
AITARGET_SELF,
@@ -427,13 +459,16 @@ enum AITarget
AITARGET_BUFF,
AITARGET_DEBUFF,
};
+
enum AICondition
{
AICOND_AGGRO,
AICOND_COMBAT,
AICOND_DIE,
};
+
#define AI_DEFAULT_COOLDOWN 5000
+
struct AISpellInfoType
{
AISpellInfoType() : target(AITARGET_SELF), condition(AICOND_COMBAT)
@@ -444,8 +479,10 @@ struct AISpellInfoType
uint32 realCooldown;
float maxRange;
};
+
TRINITY_DLL_SPEC AISpellInfoType * GetAISpellInfo(uint32 i);
+
inline void CreatureAI::SetGazeOn(Unit *target)
{
if(me->canAttack(target))
@@ -454,10 +491,12 @@ inline void CreatureAI::SetGazeOn(Unit *target)
me->SetReactState(REACT_PASSIVE);
}
}
+
inline bool CreatureAI::UpdateVictimWithGaze()
{
if(!me->isInCombat())
return false;
+
if(me->HasReactState(REACT_PASSIVE))
{
if(me->getVictim())
@@ -465,14 +504,17 @@ inline bool CreatureAI::UpdateVictimWithGaze()
else
me->SetReactState(REACT_AGGRESSIVE);
}
+
if(Unit *victim = me->SelectVictim())
AttackStart(victim);
return me->getVictim();
}
+
inline bool CreatureAI::UpdateCombatState()
{
if(!me->isInCombat())
return false;
+
if(!me->HasReactState(REACT_PASSIVE))
{
if(Unit *victim = me->SelectVictim())
@@ -484,12 +526,15 @@ inline bool CreatureAI::UpdateCombatState()
EnterEvadeMode();
return false;
}
+
return true;
}
+
inline bool CreatureAI::UpdateVictim()
{
if(!me->isInCombat())
return false;
+
if(!me->HasReactState(REACT_PASSIVE))
{
if(Unit *victim = me->SelectVictim())
@@ -501,8 +546,10 @@ inline bool CreatureAI::UpdateVictim()
EnterEvadeMode();
return false;
}
+
return true;
}
+
/*
inline bool CreatureAI::UpdateVictim()
{
@@ -513,10 +560,12 @@ inline bool CreatureAI::UpdateVictim()
return me->getVictim();
}
*/
+
inline bool CreatureAI::_EnterEvadeMode()
{
if(!me->isAlive())
return false;
+
// sometimes bosses stuck in combat?
me->RemoveAllAuras();
me->DeleteThreatList();
@@ -524,36 +573,46 @@ inline bool CreatureAI::_EnterEvadeMode()
me->LoadCreaturesAddon();
me->SetLootRecipient(NULL);
me->ResetPlayerDamageReq();
+
if(me->IsInEvadeMode())
return false;
+
return true;
}
+
inline void UnitAI::DoCast(Unit* victim, uint32 spellId, bool triggered)
{
if(!victim || me->hasUnitState(UNIT_STAT_CASTING) && !triggered)
return;
+
me->CastSpell(victim, spellId, triggered);
}
+
inline void UnitAI::DoCastVictim(uint32 spellId, bool triggered)
{
me->CastSpell(me->getVictim(), spellId, triggered);
}
+
inline void UnitAI::DoCastAOE(uint32 spellId, bool triggered)
{
if(!triggered && me->hasUnitState(UNIT_STAT_CASTING))
return;
+
me->CastSpell((Unit*)NULL, spellId, triggered);
}
+
inline Creature *CreatureAI::DoSummon(uint32 uiEntry, const Position &pos, uint32 uiDespawntime, TempSummonType uiType)
{
return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime);
}
+
inline Creature *CreatureAI::DoSummon(uint32 uiEntry, WorldObject* obj, float fRadius, uint32 uiDespawntime, TempSummonType uiType)
{
Position pos;
obj->GetRandomNearPosition(pos, fRadius);
return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime);
}
+
inline Creature *CreatureAI::DoSummonFlyer(uint32 uiEntry, WorldObject *obj, float _fZ, float fRadius, uint32 uiDespawntime, TempSummonType uiType)
{
Position pos;
@@ -561,5 +620,6 @@ inline Creature *CreatureAI::DoSummonFlyer(uint32 uiEntry, WorldObject *obj, flo
pos.m_positionZ += _fZ;
return me->SummonCreature(uiEntry, pos, uiType, uiDespawntime);
}
+
#endif
diff --git a/src/game/CreatureAIRegistry.cpp b/src/game/CreatureAIRegistry.cpp
index 900f89da3b4..67c1f857a56 100644
--- a/src/game/CreatureAIRegistry.cpp
+++ b/src/game/CreatureAIRegistry.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "PassiveAI.h"
#include "ReactorAI.h"
#include "CombatAI.h"
@@ -29,6 +30,7 @@
#include "CreatureAIRegistry.h"
#include "WaypointMovementGenerator.h"
#include "CreatureAIFactory.h"
+
//#include "CreatureAIImpl.h"
namespace AIRegistry
{
@@ -47,6 +49,7 @@ namespace AIRegistry
(new CreatureAIFactory<ArchorAI>("ArchorAI"))->RegisterSelf();
(new CreatureAIFactory<TurretAI>("TurretAI"))->RegisterSelf();
(new CreatureAIFactory<CreatureEventAI>("EventAI"))->RegisterSelf();
+
(new MovementGeneratorFactory<RandomMovementGenerator<Creature> >(RANDOM_MOTION_TYPE))->RegisterSelf();
(new MovementGeneratorFactory<WaypointMovementGenerator<Creature> >(WAYPOINT_MOTION_TYPE))->RegisterSelf();
}
diff --git a/src/game/CreatureAIRegistry.h b/src/game/CreatureAIRegistry.h
index c75167d74af..2b92a096731 100644
--- a/src/game/CreatureAIRegistry.h
+++ b/src/game/CreatureAIRegistry.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CREATUREAIREGISTRY_H
#define TRINITY_CREATUREAIREGISTRY_H
+
namespace AIRegistry
{
void Initialize(void);
diff --git a/src/game/CreatureAISelector.cpp b/src/game/CreatureAISelector.cpp
index 2f7bd0ff211..9f2828bf776 100644
--- a/src/game/CreatureAISelector.cpp
+++ b/src/game/CreatureAISelector.cpp
@@ -17,6 +17,7 @@
* 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 "CreatureAISelector.h"
#include "PassiveAI.h"
@@ -26,24 +27,30 @@
#include "Pet.h"
#include "TemporarySummon.h"
#include "CreatureAIFactory.h"
+
INSTANTIATE_SINGLETON_1(CreatureAIRegistry);
INSTANTIATE_SINGLETON_1(MovementGeneratorRegistry);
+
namespace FactorySelector
{
CreatureAI* selectAI(Creature *creature)
{
const CreatureAICreator *ai_factory = NULL;
CreatureAIRegistry &ai_registry(CreatureAIRepository::Instance());
+
if(creature->isPet())
ai_factory = ai_registry.GetRegistryItem("PetAI");
+
//scriptname in db
if(!ai_factory)
if(CreatureAI* scriptedAI = Script->GetAI(creature))
return scriptedAI;
+
// AIname in db
std::string ainame=creature->GetAIName();
if(!ai_factory && !ainame.empty())
ai_factory = ai_registry.GetRegistryItem( ainame.c_str() );
+
// select by NPC flags
if(!ai_factory)
{
@@ -67,6 +74,7 @@ namespace FactorySelector
else if(creature->GetCreatureType() == CREATURE_TYPE_CRITTER && !creature->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
ai_factory = ai_registry.GetRegistryItem("CritterAI");
}
+
if(!ai_factory)
{
for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
@@ -78,6 +86,7 @@ namespace FactorySelector
}
}
}
+
// select by permit check
if(!ai_factory)
{
@@ -97,16 +106,20 @@ namespace FactorySelector
}
}
}
+
// 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(creature) : 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;
@@ -125,7 +138,9 @@ namespace FactorySelector
}
}
}*/
+
return ( mv_factory == NULL ? NULL : mv_factory->Create(creature) );
+
}
}
diff --git a/src/game/CreatureAISelector.h b/src/game/CreatureAISelector.h
index e7e980518dd..436233b5b21 100644
--- a/src/game/CreatureAISelector.h
+++ b/src/game/CreatureAISelector.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_CREATUREAISELECTOR_H
#define TRINITY_CREATUREAISELECTOR_H
+
class CreatureAI;
class Creature;
class MovementGenerator;
+
namespace FactorySelector
{
CreatureAI* selectAI(Creature *);
diff --git a/src/game/CreatureEventAI.cpp b/src/game/CreatureEventAI.cpp
index 9a6e907f33b..b2f5dc354d2 100644
--- a/src/game/CreatureEventAI.cpp
+++ b/src/game/CreatureEventAI.cpp
@@ -17,6 +17,7 @@
* 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 "CreatureEventAI.h"
#include "CreatureEventAIMgr.h"
@@ -31,6 +32,7 @@
#include "InstanceData.h"
#include "SpellMgr.h"
#include "CreatureAIImpl.h"
+
bool CreatureEventAIHolder::UpdateRepeatTimer( Creature* creature, uint32 repeatMin, uint32 repeatMax )
{
if (repeatMin == repeatMax)
@@ -43,14 +45,17 @@ bool CreatureEventAIHolder::UpdateRepeatTimer( Creature* creature, uint32 repeat
Enabled = false;
return false;
}
+
return true;
}
+
int CreatureEventAI::Permissible(const Creature *creature)
{
if( creature->GetAIName() == "EventAI" )
return PERMIT_BASE_SPECIAL;
return PERMIT_BASE_NO;
}
+
CreatureEventAI::CreatureEventAI(Creature *c ) : CreatureAI(c)
{
// Need make copy for filter unneeded steps and safe in case table reload
@@ -60,6 +65,7 @@ CreatureEventAI::CreatureEventAI(Creature *c ) : CreatureAI(c)
std::vector<CreatureEventAI_Event>::const_iterator i;
for (i = (*CreatureEvents).second.begin(); i != (*CreatureEvents).second.end(); ++i)
{
+
//Debug check
#ifndef TRINITY_DEBUG
if ((*i).event_flags & EFLAG_DEBUG_ONLY)
@@ -83,13 +89,16 @@ CreatureEventAI::CreatureEventAI(Creature *c ) : CreatureAI(c)
}
else
sLog.outError("CreatureEventAI: EventMap for Creature %u is empty but creature is using CreatureEventAI.", m_creature->GetEntry());
+
bEmptyList = CreatureEventAIList.empty();
Phase = 0;
CombatMovementEnabled = true;
MeleeEnabled = true;
AttackDistance = 0.0f;
AttackAngle = 0.0f;
+
InvinceabilityHpLevel = 0;
+
//Handle Spawned Events
if (!bEmptyList)
{
@@ -98,26 +107,32 @@ CreatureEventAI::CreatureEventAI(Creature *c ) : CreatureAI(c)
ProcessEvent(*i);
}
}
+
bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker)
{
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;
+
CreatureEventAI_Event const& event = pHolder.Event;
+
//Check event conditions based on the event type, also reset events
switch (event.event_type)
{
case EVENT_T_TIMER:
if (!m_creature->isInCombat())
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.timer.repeatMin,event.timer.repeatMax);
break;
case EVENT_T_TIMER_OOC:
if (m_creature->isInCombat())
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.timer.repeatMin,event.timer.repeatMax);
break;
@@ -125,9 +140,12 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
if (!m_creature->isInCombat() || !m_creature->GetMaxHealth())
return false;
+
uint32 perc = (m_creature->GetHealth()*100) / m_creature->GetMaxHealth();
+
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
@@ -136,9 +154,12 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
if (!m_creature->isInCombat() || !m_creature->GetMaxPower(POWER_MANA))
return false;
+
uint32 perc = (m_creature->GetPower(POWER_MANA)*100) / m_creature->GetMaxPower(POWER_MANA);
+
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
@@ -154,6 +175,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
break;
case EVENT_T_SPELLHIT:
//Spell hit is special case, param1 and param2 handled within CreatureEventAI::SpellHit
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.spell_hit.repeatMin,event.spell_hit.repeatMax);
break;
@@ -171,9 +193,12 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxHealth())
return false;
+
uint32 perc = (m_creature->getVictim()->GetHealth()*100) / m_creature->getVictim()->GetMaxHealth();
+
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
@@ -181,6 +206,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
case EVENT_T_TARGET_CASTING:
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->IsNonMeleeSpellCasted(false, false, true))
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.target_casting.repeatMin,event.target_casting.repeatMax);
break;
@@ -188,10 +214,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
if (!m_creature->isInCombat())
return false;
+
Unit* pUnit = DoSelectLowestHpFriendly(event.friendly_hp.radius, event.friendly_hp.hpDeficit);
if (!pUnit)
return false;
+
pActionInvoker = pUnit;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.friendly_hp.repeatMin,event.friendly_hp.repeatMax);
break;
@@ -200,13 +229,17 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
if (!m_creature->isInCombat())
return false;
+
std::list<Creature*> pList;
DoFindFriendlyCC(pList, event.friendly_is_cc.radius);
+
//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
pHolder.UpdateRepeatTimer(m_creature,event.friendly_is_cc.repeatMin,event.friendly_is_cc.repeatMax);
break;
@@ -215,11 +248,14 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
std::list<Creature*> pList;
DoFindFriendlyMissingBuff(pList, event.friendly_buff.radius, event.friendly_buff.spellId);
+
//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
pHolder.UpdateRepeatTimer(m_creature,event.friendly_buff.repeatMin,event.friendly_buff.repeatMax);
break;
@@ -229,9 +265,11 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
//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 (((Creature*)pActionInvoker)->GetEntry() != event.summon_unit.creatureId)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.summon_unit.repeatMin,event.summon_unit.repeatMax);
break;
@@ -240,9 +278,12 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
{
if (!m_creature->isInCombat() || !m_creature->getVictim() || !m_creature->getVictim()->GetMaxPower(POWER_MANA))
return false;
+
uint32 perc = (m_creature->getVictim()->GetPower(POWER_MANA)*100) / m_creature->getVictim()->GetMaxPower(POWER_MANA);
+
if (perc > event.percent_range.percentMax || perc < event.percent_range.percentMin)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.percent_range.repeatMin,event.percent_range.repeatMax);
break;
@@ -257,6 +298,7 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
Aura* aura = m_creature->GetAura(event.buffed.spellId,0);
if(!aura || aura->GetStackAmount() < event.buffed.amount)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.buffed.repeatMin,event.buffed.repeatMax);
break;
@@ -266,11 +308,13 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
//Prevent event from occuring on no unit
if (!pActionInvoker)
return false;
+
//Note: checked only aura for effect 0, if need check aura for effect 1/2 then
// possible way: pack in event.buffed.amount 2 uint16 (ammount+effectIdx)
Aura* aura = pActionInvoker->GetAura(event.buffed.spellId,0);
if(!aura || aura->GetStackAmount() < event.buffed.amount)
return false;
+
//Repeat Timers
pHolder.UpdateRepeatTimer(m_creature,event.buffed.repeatMin,event.buffed.repeatMax);
break;
@@ -279,19 +323,25 @@ bool CreatureEventAI::ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pAction
sLog.outErrorDb("CreatureEventAI: 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;
+
//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;
+
//Process actions
for (uint32 j = 0; j < MAX_ACTIONS; j++)
ProcessAction(pHolder.Event.action[j], rnd, pHolder.Event.event_id, pActionInvoker);
+
return true;
}
+
void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker)
{
switch (action.type)
@@ -300,16 +350,20 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
{
if (!action.text.TextId1)
return;
+
int32 temp = 0;
+
if (action.text.TextId2 && action.text.TextId3)
temp = RAND(action.text.TextId1,action.text.TextId2,action.text.TextId3);
else if (action.text.TextId2 && urand(0,1))
temp = action.text.TextId2;
else
temp = action.text.TextId1;
+
if (temp)
{
Unit* target = NULL;
+
if (pActionInvoker)
{
if (pActionInvoker->GetTypeId() == TYPEID_PLAYER)
@@ -327,6 +381,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
if (owner->GetTypeId() == TYPEID_PLAYER)
target = owner;
}
+
DoScriptText(temp, m_creature, target);
}
break;
@@ -391,21 +446,27 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
{
Unit* target = GetTargetByType(action.cast.target, pActionInvoker);
Unit* caster = m_creature;
+
if (!target)
return;
+
if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)
caster = target;
+
//Allowed to cast only if not casting (unless we interrupt ourself) or if spell is triggered
bool canCast = !caster->IsNonMeleeSpellCasted(false) || (action.cast.castFlags & (CAST_TRIGGERED | CAST_INTURRUPT_PREVIOUS));
+
// If cast flag CAST_AURA_NOT_PRESENT is active, check if target already has aura on them
if(action.cast.castFlags & CAST_AURA_NOT_PRESENT)
{
if(target->HasAura(action.cast.spellId))
return;
}
+
if (canCast)
{
const SpellEntry* tSpell = GetSpellStore()->LookupEntry(action.cast.spellId);
+
//Verify that spell exists
if (tSpell)
{
@@ -420,17 +481,21 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
{
AttackDistance = 0.0f;
AttackAngle = 0.0f;
+
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
}
}
+
}
else
{
//Interrupt any previous spell
if (caster->IsNonMeleeSpellCasted(false) && action.cast.castFlags & CAST_INTURRUPT_PREVIOUS)
caster->InterruptNonMeleeSpells(false);
+
caster->CastSpell(target, action.cast.spellId, (action.cast.castFlags & CAST_TRIGGERED));
}
+
}
else
sLog.outErrorDb("CreatureEventAI: event %d creature %d attempt to cast spell that doesn't exist %d", EventId, m_creature->GetEntry(), action.cast.spellId);
@@ -440,11 +505,14 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
case ACTION_T_SUMMON:
{
Unit* target = GetTargetByType(action.summon.target, pActionInvoker);
+
Creature* pCreature = NULL;
+
if (action.summon.duration)
pCreature = m_creature->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, action.summon.duration);
else
pCreature = m_creature->SummonCreature(action.summon.creatureId, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
+
if (!pCreature)
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Spawn event %d is on creature %d", action.summon.creatureId, EventId, m_creature->GetEntry());
else if (action.summon.target != TARGET_T_SELF && target)
@@ -476,11 +544,14 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
case ACTION_T_SET_UNIT_FIELD:
{
Unit* target = GetTargetByType(action.set_unit_field.target, pActionInvoker);
+
// not allow modify important for integrity object fields
if (action.set_unit_field.field < OBJECT_END || action.set_unit_field.field >= UNIT_END)
return;
+
if (target)
target->SetUInt32Value(action.set_unit_field.field, action.set_unit_field.value);
+
break;
}
case ACTION_T_SET_UNIT_FLAG:
@@ -498,13 +569,16 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
// ignore no affect case
if(CombatMovementEnabled==(action.combat_movement.state!=0))
return;
+
CombatMovementEnabled = action.combat_movement.state != 0;
+
//Allow movement (create new targeted movement gen only if idle)
if (CombatMovementEnabled)
{
if(action.combat_movement.melee && m_creature->isInCombat())
if(Unit* victim = m_creature->getVictim())
m_creature->SendMeleeAttackStart(victim);
+
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
}
else
@@ -512,6 +586,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
if(action.combat_movement.melee && m_creature->isInCombat())
if(Unit* victim = m_creature->getVictim())
m_creature->SendMeleeAttackStop(victim);
+
m_creature->GetMotionMaster()->MoveIdle();
}
break;
@@ -533,6 +608,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
}
else
Phase = new_phase;
+
break;
}
case ACTION_T_EVADE:
@@ -565,6 +641,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
case ACTION_T_RANGED_MOVEMENT:
AttackDistance = (float)action.ranged_movement.distance;
AttackAngle = action.ranged_movement.angle/180.0f*M_PI;
+
if (CombatMovementEnabled)
{
m_creature->GetMotionMaster()->MoveChase(m_creature->getVictim(), AttackDistance, AttackAngle);
@@ -582,21 +659,25 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
case ACTION_T_SUMMON_ID:
{
Unit* target = GetTargetByType(action.summon_id.target, pActionInvoker);
+
CreatureEventAI_Summon_Map::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAISummonMap().find(action.summon_id.spawnId);
if (i == CreatureEAI_Mgr.GetCreatureEventAISummonMap().end())
{
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. Summon map index %u does not exist. EventID %d. CreatureID %d", action.summon_id.creatureId, action.summon_id.spawnId, EventId, m_creature->GetEntry());
return;
}
+
Creature* pCreature = NULL;
if ((*i).second.SpawnTimeSecs)
pCreature = m_creature->SummonCreature(action.summon_id.creatureId, (*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(action.summon_id.creatureId, (*i).second.position_x, (*i).second.position_y, (*i).second.position_z, (*i).second.orientation, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 0);
+
if (!pCreature)
sLog.outErrorDb( "CreatureEventAI: failed to spawn creature %u. EventId %d.Creature %d", action.summon_id.creatureId, EventId, m_creature->GetEntry());
else if (action.summon_id.target != TARGET_T_SELF && target)
pCreature->AI()->AttackStart(target);
+
break;
}
case ACTION_T_KILLED_MONSTER:
@@ -619,6 +700,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data without instance script. Creature %d", EventId, m_creature->GetEntry());
return;
}
+
pInst->SetData(action.set_inst_data.field, action.set_inst_data.value);
break;
}
@@ -630,26 +712,31 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 but Target == NULL. Creature %d", EventId, m_creature->GetEntry());
return;
}
+
InstanceData* pInst = (InstanceData*)m_creature->GetInstanceData();
if (!pInst)
{
sLog.outErrorDb("CreatureEventAI: Event %d attempt to set instance data64 without instance script. Creature %d", EventId, m_creature->GetEntry());
return;
}
+
pInst->SetData64(action.set_inst_data64.field, target->GetGUID());
break;
}
case ACTION_T_UPDATE_TEMPLATE:
if (m_creature->GetEntry() == action.update_template.creatureId)
{
+
sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_UPDATE_TEMPLATE call with param1 == current entry. Creature %d", EventId, m_creature->GetEntry());
return;
}
+
m_creature->UpdateEntry(action.update_template.creatureId, action.update_template.team ? HORDE : ALLIANCE);
break;
case ACTION_T_DIE:
if (m_creature->isDead())
{
+
sLog.outErrorDb("CreatureEventAI: Event %d ACTION_T_DIE on dead creature. Creature %d", EventId, m_creature->GetEntry());
return;
}
@@ -666,6 +753,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
break;
}
break;
+
// TRINITY ONLY
case ACTION_T_SET_ACTIVE:
me->setActive(action.raw.param1 ? true : false);
@@ -679,6 +767,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
case ACTION_T_SUMMON_GO:
{
GameObject* pObject = NULL;
+
float x,y,z;
m_creature->GetPosition(x,y,z);
pObject = m_creature->SummonGameObject(action.raw.param1, x, y, z, 0, 0, 0, 0, 0, action.raw.param2);
@@ -688,6 +777,7 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
}
break;
}
+
case ACTION_T_SET_SHEATH:
{
m_creature->SetSheath(SheathState(action.set_sheath.sheath));
@@ -708,22 +798,28 @@ void CreatureEventAI::ProcessAction(CreatureEventAI_Action const& action, uint32
}
}
}
+
void CreatureEventAI::JustRespawned()
{
Reset();
+
if (bEmptyList)
return;
+
//Handle Spawned Events
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
if (SpawnedEventConditionsCheck((*i).Event))
ProcessEvent(*i);
}
+
void CreatureEventAI::Reset()
{
EventUpdateTime = EVENT_UPDATE_TIME;
EventDiff = 0;
+
if (bEmptyList)
return;
+
//Reset all events to enabled
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
@@ -745,9 +841,11 @@ void CreatureEventAI::Reset()
}
}
}
+
void CreatureEventAI::JustReachedHome()
{
m_creature->LoadCreaturesAddon();
+
if (!bEmptyList)
{
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
@@ -756,13 +854,17 @@ void CreatureEventAI::JustReachedHome()
ProcessEvent(*i);
}
}
+
Reset();
}
+
void CreatureEventAI::EnterEvadeMode()
{
CreatureAI::EnterEvadeMode();
+
if (bEmptyList)
return;
+
//Handle Evade events
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
@@ -770,40 +872,49 @@ void CreatureEventAI::EnterEvadeMode()
ProcessEvent(*i);
}
}
+
void CreatureEventAI::JustDied(Unit* killer)
{
Reset();
+
if (bEmptyList)
return;
+
//Handle Evade events
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
if ((*i).Event.event_type == EVENT_T_DEATH)
ProcessEvent(*i, killer);
}
+
// reset phase after any death state events
Phase = 0;
}
+
void CreatureEventAI::KilledUnit(Unit* victim)
{
if (bEmptyList || victim->GetTypeId() != TYPEID_PLAYER)
return;
+
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
if ((*i).Event.event_type == EVENT_T_KILL)
ProcessEvent(*i, victim);
}
}
+
void CreatureEventAI::JustSummoned(Creature* pUnit)
{
if (bEmptyList || !pUnit)
return;
+
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
if ((*i).Event.event_type == EVENT_T_SUMMONED_UNIT)
ProcessEvent(*i, pUnit);
}
}
+
void CreatureEventAI::EnterCombat(Unit *enemy)
{
//Check for on combat start events
@@ -831,13 +942,16 @@ void CreatureEventAI::EnterCombat(Unit *enemy)
}
}
}
+
EventUpdateTime = EVENT_UPDATE_TIME;
EventDiff = 0;
}
+
void CreatureEventAI::AttackStart(Unit *who)
{
if (!who)
return;
+
if (m_creature->Attack(who, MeleeEnabled))
{
if (CombatMovementEnabled)
@@ -850,10 +964,12 @@ void CreatureEventAI::AttackStart(Unit *who)
}
}
}
+
void CreatureEventAI::MoveInLineOfSight(Unit *who)
{
if(me->getVictim())
return;
+
//Check for OOC LOS Event
if (!bEmptyList)
{
@@ -863,6 +979,7 @@ void CreatureEventAI::MoveInLineOfSight(Unit *who)
{
//can trigger if closer than fMaxAllowedRange
float fMaxAllowedRange = (*itr).Event.ooc_los.maxRange;
+
//if range is ok and we are actually in LOS
if (m_creature->IsWithinDistInMap(who, fMaxAllowedRange) && m_creature->IsWithinLOSInMap(who))
{
@@ -874,12 +991,16 @@ void CreatureEventAI::MoveInLineOfSight(Unit *who)
}
}
}
+
CreatureAI::MoveInLineOfSight(who);
}
+
void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell)
{
+
if (bEmptyList)
return;
+
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
if ((*i).Event.event_type == EVENT_T_SPELLHIT)
//If spell id matches (or no spell id) & if spell school matches (or no spell school)
@@ -887,16 +1008,19 @@ void CreatureEventAI::SpellHit(Unit* pUnit, const SpellEntry* pSpell)
if (pSpell->SchoolMask & (*i).Event.spell_hit.schoolMask)
ProcessEvent(*i, pUnit);
}
+
void CreatureEventAI::UpdateAI(const uint32 diff)
{
//Check if we are in combat (also updates calls threat update code)
bool Combat = UpdateVictim();
+
if (!bEmptyList)
{
//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 time based events
for (std::list<CreatureEventAIHolder>::iterator i = CreatureEventAIList.begin(); i != CreatureEventAIList.end(); ++i)
{
@@ -908,11 +1032,13 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
//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;
}
+
//Events that are updated every EVENT_UPDATE_TIME
switch ((*i).Event.event_type)
{
@@ -936,6 +1062,7 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
break;
}
}
+
EventDiff = 0;
EventUpdateTime = EVENT_UPDATE_TIME;
}
@@ -945,18 +1072,22 @@ void CreatureEventAI::UpdateAI(const uint32 diff)
EventUpdateTime -= diff;
}
}
+
//Melee Auto-Attack
if (Combat && MeleeEnabled)
DoMeleeAttackIfReady();
}
+
inline Unit* CreatureEventAI::SelectUnit(AttackingTarget target, uint32 position)
{
//ThreatList m_threatlist;
std::list<HostilReference*>& m_threatlist = m_creature->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator i = m_threatlist.begin();
std::list<HostilReference*>::reverse_iterator r = m_threatlist.rbegin();
+
if (position >= m_threatlist.size() || !m_threatlist.size())
return NULL;
+
switch (target)
{
case ATTACKING_TARGET_RANDOM:
@@ -977,6 +1108,7 @@ inline Unit* CreatureEventAI::SelectUnit(AttackingTarget target, uint32 position
}
return NULL;
}
+
inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3)
{
switch (rnd % 3)
@@ -987,6 +1119,7 @@ inline uint32 CreatureEventAI::GetRandActionParam(uint32 rnd, uint32 param1, uin
}
return 0;
}
+
inline int32 CreatureEventAI::GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3)
{
switch (rnd % 3)
@@ -997,6 +1130,7 @@ inline int32 CreatureEventAI::GetRandActionParam(uint32 rnd, int32 param1, int32
}
return 0;
}
+
inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoker)
{
switch (Target)
@@ -1019,50 +1153,65 @@ inline Unit* CreatureEventAI::GetTargetByType(uint32 Target, Unit* pActionInvoke
return NULL;
};
}
+
Unit* CreatureEventAI::DoSelectLowestHpFriendly(float range, uint32 MinHPDiff)
{
CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Unit* pUnit = NULL;
+
MaNGOS::MostHPMissingInRange u_check(m_creature, range, MinHPDiff);
MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange> searcher(m_creature, pUnit, u_check);
+
/*
typedef TYPELIST_4(GameObject, Creature*except pets*, DynamicObject, Corpse*Bones*) AllGridObjectTypes;
This means that if we only search grid then we cannot possibly return pets or players so this is safe
*/
TypeContainerVisitor<MaNGOS::UnitLastSearcher<MaNGOS::MostHPMissingInRange>, GridTypeMapContainer > grid_unit_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_unit_searcher, *m_creature->GetMap(), *m_creature, range);
return pUnit;
}
+
void CreatureEventAI::DoFindFriendlyCC(std::list<Creature*>& _list, float range)
{
CellPair p(MaNGOS::ComputeCellPair(m_creature->GetPositionX(), m_creature->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::FriendlyCCedInRange u_check(m_creature, range);
MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange> searcher(m_creature, _list, u_check);
+
TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyCCedInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature->GetMap());
}
+
void CreatureEventAI::DoFindFriendlyMissingBuff(std::list<Creature*>& _list, 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();
+
MaNGOS::FriendlyMissingBuffInRange u_check(m_creature, range, spellid);
MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange> searcher(m_creature, _list, u_check);
+
TypeContainerVisitor<MaNGOS::CreatureListSearcher<MaNGOS::FriendlyMissingBuffInRange>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_creature_searcher, *m_creature->GetMap());
}
+
//*********************************
//*** Functions used globally ***
+
void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target)
{
if (!pSource)
@@ -1070,18 +1219,23 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i, invalid Source pointer.",textEntry);
return;
}
+
if (textEntry >= 0)
{
sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) attempts to process text entry %i, but text entry must be negative.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry);
return;
}
+
CreatureEventAI_TextMap::const_iterator i = CreatureEAI_Mgr.GetCreatureEventAITextMap().find(textEntry);
+
if (i == CreatureEAI_Mgr.GetCreatureEventAITextMap().end())
{
sLog.outErrorDb("CreatureEventAI: DoScriptText with source entry %u (TypeId=%u, guid=%u) could not find text entry %i.",pSource->GetEntry(),pSource->GetTypeId(),pSource->GetGUIDLow(),textEntry);
return;
}
+
sLog.outDebug("CreatureEventAI: DoScriptText: text entry=%i, Sound=%u, Type=%u, Language=%u, Emote=%u",textEntry,(*i).second.SoundId,(*i).second.Type,(*i).second.Language,(*i).second.Emote);
+
if((*i).second.SoundId)
{
if (GetSoundEntriesStore()->LookupEntry((*i).second.SoundId))
@@ -1089,6 +1243,7 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
else
sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process invalid sound id %u.",textEntry,(*i).second.SoundId);
}
+
if((*i).second.Emote)
{
if (pSource->GetTypeId() == TYPEID_UNIT || pSource->GetTypeId() == TYPEID_PLAYER)
@@ -1098,6 +1253,7 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
else
sLog.outErrorDb("CreatureEventAI: DoScriptText entry %i tried to process emote for invalid TypeId (%u).",textEntry,pSource->GetTypeId());
}
+
switch((*i).second.Type)
{
case CHAT_TYPE_SAY:
@@ -1129,37 +1285,48 @@ void CreatureEventAI::DoScriptText(int32 textEntry, WorldObject* pSource, Unit*
break;
}
}
+
bool CreatureEventAI::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 && me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED))
return false;
+
//Check for power
if (!Triggered && me->GetPower((Powers)Spell->powerType) < CalculatePowerCost(Spell, me, GetSpellSchoolMask(Spell)))
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->IsInRange(Target,TempRange->minRangeHostile,TempRange->maxRangeHostile))
return false;
+
return true;
}
+
void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
{
if (bEmptyList)
return;
+
for (std::list<CreatureEventAIHolder>::iterator itr = CreatureEventAIList.begin(); itr != CreatureEventAIList.end(); ++itr)
{
if ((*itr).Event.event_type == EVENT_T_RECEIVE_EMOTE)
{
if ((*itr).Event.receive_emote.emoteId != text_emote)
return;
+
PlayerCondition pcon((*itr).Event.receive_emote.condition,(*itr).Event.receive_emote.conditionValue1,(*itr).Event.receive_emote.conditionValue2);
if (pcon.Meets(pPlayer))
{
@@ -1169,6 +1336,7 @@ void CreatureEventAI::ReceiveEmote(Player* pPlayer, uint32 text_emote)
}
}
}
+
void CreatureEventAI::DamageTaken( Unit* done_by, uint32& damage )
{
if(InvinceabilityHpLevel > 0 && m_creature->GetHealth() < InvinceabilityHpLevel+damage)
@@ -1179,10 +1347,12 @@ void CreatureEventAI::DamageTaken( Unit* done_by, uint32& damage )
damage = m_creature->GetHealth() - InvinceabilityHpLevel;
}
}
+
bool CreatureEventAI::SpawnedEventConditionsCheck(CreatureEventAI_Event const& event)
{
if(event.event_type != EVENT_T_SPAWNED)
return false;
+
switch (event.spawned.condition)
{
case SPAWNED_EVENT_ALWAY:
@@ -1201,5 +1371,6 @@ bool CreatureEventAI::SpawnedEventConditionsCheck(CreatureEventAI_Event const& e
default:
break;
}
+
return false;
}
diff --git a/src/game/CreatureEventAI.h b/src/game/CreatureEventAI.h
index b26dfe2e055..7882e7542ec 100644
--- a/src/game/CreatureEventAI.h
+++ b/src/game/CreatureEventAI.h
@@ -15,17 +15,22 @@
* 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_CREATURE_EAI_H
#define MANGOS_CREATURE_EAI_H
+
#include "Common.h"
#include "Creature.h"
#include "CreatureAI.h"
#include "Unit.h"
+
class Player;
class WorldObject;
+
#define EVENT_UPDATE_TIME 500
#define MAX_ACTIONS 3
#define MAX_PHASE 32
+
enum EventAI_Type
{
EVENT_T_TIMER = 0, // InitialMin, InitialMax, RepeatMin, RepeatMax
@@ -53,8 +58,10 @@ enum EventAI_Type
EVENT_T_RECEIVE_EMOTE = 22, // EmoteId, Condition, CondValue1, CondValue2
EVENT_T_BUFFED = 23, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max
EVENT_T_TARGET_BUFFED = 24, // Param1 = SpellID, Param2 = Number of Time STacked, Param3/4 Repeat Min/Max
+
EVENT_T_END,
};
+
enum EventAI_ActionType
{
ACTION_T_NONE = 0, // No action
@@ -98,35 +105,44 @@ enum EventAI_ActionType
ACTION_T_ZONE_COMBAT_PULSE = 38, // No Params
ACTION_T_CALL_FOR_HELP = 39, // Radius
ACTION_T_SET_SHEATH = 40, // Sheath (0-passive,1-melee,2-ranged)
+
ACTION_T_SET_ACTIVE = 101, //Apply
ACTION_T_SET_AGGRESSIVE = 102, //Apply
ACTION_T_ATTACK_START_PULSE = 103, //Distance
ACTION_T_SUMMON_GO = 104, //GameObjectID, DespawnTime in ms
+
ACTION_T_FORCE_DESPAWN = 41, // No Params
ACTION_T_END = 105,
ACTION_T_SET_INVINCIBILITY_HP_LEVEL = 42, // MinHpValue, format(0-flat,1-percent from max health)
};
+
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
@@ -136,6 +152,7 @@ enum CastFlags
CAST_FORCE_TARGET_SELF = 0x10, //Forces the target to cast this spell on itself
CAST_AURA_NOT_PRESENT = 0x20, //Only casts the spell if the target does not have an aura from the spell
};
+
enum EventFlags
{
EFLAG_REPEATABLE = 0x01, //Event repeats
@@ -147,12 +164,14 @@ enum EventFlags
EFLAG_RESERVED_6 = 0x40,
EFLAG_DEBUG_ONLY = 0x80, //Event only occurs in debug build
};
+
enum SpawnedEventMode
{
SPAWNED_EVENT_ALWAY = 0,
SPAWNED_EVENT_MAP = 1,
SPAWNED_EVENT_ZONE = 2
};
+
// String text additional data, used in (CreatureEventAI)
struct StringTextData
{
@@ -163,6 +182,7 @@ struct StringTextData
};
// Text Maps
typedef UNORDERED_MAP<int32, StringTextData> CreatureEventAI_TextMap;
+
struct CreatureEventAI_Action
{
EventAI_ActionType type: 16;
@@ -375,14 +395,19 @@ struct CreatureEventAI_Action
} raw;
};
};
+
struct CreatureEventAI_Event
{
uint32 event_id;
+
uint32 creature_id;
+
uint32 event_inverse_phase_mask;
+
EventAI_Type event_type : 16;
uint8 event_chance : 8;
uint8 event_flags : 8;
+
union
{
// EVENT_T_TIMER = 0
@@ -501,6 +526,7 @@ struct CreatureEventAI_Event
uint32 repeatMin;
uint32 repeatMax;
} buffed;
+
// RAW
struct
{
@@ -510,32 +536,41 @@ struct CreatureEventAI_Event
uint32 param4;
} raw;
};
+
CreatureEventAI_Action action[MAX_ACTIONS];
};
//Event_Map
typedef UNORDERED_MAP<uint32, std::vector<CreatureEventAI_Event> > CreatureEventAI_Event_Map;
+
struct CreatureEventAI_Summon
{
uint32 id;
+
float position_x;
float position_y;
float position_z;
float orientation;
uint32 SpawnTimeSecs;
};
+
//EventSummon_Map
typedef UNORDERED_MAP<uint32, CreatureEventAI_Summon> CreatureEventAI_Summon_Map;
+
struct CreatureEventAIHolder
{
CreatureEventAIHolder(CreatureEventAI_Event p) : Event(p), Time(0), Enabled(true){}
+
CreatureEventAI_Event Event;
uint32 Time;
bool Enabled;
+
// helper
bool UpdateRepeatTimer(Creature* creature, uint32 repeatMin, uint32 repeatMax);
};
+
class TRINITY_DLL_SPEC CreatureEventAI : public CreatureAI
{
+
public:
explicit CreatureEventAI(Creature *c);
~CreatureEventAI()
@@ -557,23 +592,29 @@ class TRINITY_DLL_SPEC CreatureEventAI : public CreatureAI
void UpdateAI(const uint32 diff);
void ReceiveEmote(Player* pPlayer, uint32 text_emote);
static int Permissible(const Creature *);
+
bool ProcessEvent(CreatureEventAIHolder& pHolder, Unit* pActionInvoker = NULL);
void ProcessAction(CreatureEventAI_Action const& action, uint32 rnd, uint32 EventId, Unit* pActionInvoker);
inline uint32 GetRandActionParam(uint32 rnd, uint32 param1, uint32 param2, uint32 param3);
inline int32 GetRandActionParam(uint32 rnd, int32 param1, int32 param2, int32 param3);
inline Unit* GetTargetByType(uint32 Target, Unit* pActionInvoker);
inline Unit* SelectUnit(AttackingTarget target, uint32 position);
+
void DoScriptText(int32 textEntry, WorldObject* pSource, Unit* target);
bool CanCast(Unit* Target, SpellEntry const *Spell, bool Triggered);
+
bool SpawnedEventConditionsCheck(CreatureEventAI_Event const& event);
+
Unit* DoSelectLowestHpFriendly(float range, uint32 MinHPDiff);
void DoFindFriendlyMissingBuff(std::list<Creature*>& _list, float range, uint32 spellid);
void DoFindFriendlyCC(std::list<Creature*>& _list, float range);
+
//Holder for events (stores enabled, time, and eventid)
std::list<CreatureEventAIHolder> CreatureEventAIList;
uint32 EventUpdateTime; //Time between event updates
uint32 EventDiff; //Time between the last event call
bool bEmptyList;
+
//Variables used by Events themselves
uint8 Phase; // Current phase, max 32 phases
bool CombatMovementEnabled; // If we allow targeted movment gen (movement twoards top threat)
diff --git a/src/game/CreatureEventAIMgr.cpp b/src/game/CreatureEventAIMgr.cpp
index 18c4ca97de4..5325edff4a9 100644
--- a/src/game/CreatureEventAIMgr.cpp
+++ b/src/game/CreatureEventAIMgr.cpp
@@ -15,6 +15,7 @@
* 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"
@@ -25,61 +26,77 @@
#include "Policies/SingletonImp.h"
#include "ObjectDefines.h"
#include "GridDefines.h"
+
INSTANTIATE_SINGLETON_1(CreatureEventAIMgr);
+
// -------------------
void CreatureEventAIMgr::LoadCreatureEventAI_Texts()
{
// Drop Existing Text Map, only done once and we are ready to add data from multiple sources.
m_CreatureEventAI_TextMap.clear();
+
// Load EventAI Text
objmgr.LoadTrinityStrings(WorldDatabase,"creature_ai_texts",MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID);
+
// Gather Additional data from EventAI Texts
QueryResult *result = WorldDatabase.Query("SELECT entry, sound, type, language, emote FROM creature_ai_texts");
+
sLog.outString("Loading EventAI Texts additional data...");
if (result)
{
barGoLink bar(result->GetRowCount());
uint32 count = 0;
+
do
{
bar.step();
Field* fields = result->Fetch();
StringTextData temp;
+
int32 i = fields[0].GetInt32();
temp.SoundId = fields[1].GetInt32();
temp.Type = fields[2].GetInt32();
temp.Language = fields[3].GetInt32();
temp.Emote = fields[4].GetInt32();
+
// range negative
if (i > MIN_CREATURE_AI_TEXT_STRING_ID || i <= MAX_CREATURE_AI_TEXT_STRING_ID)
{
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` is not in valid range(%d-%d)",i,MIN_CREATURE_AI_TEXT_STRING_ID,MAX_CREATURE_AI_TEXT_STRING_ID);
continue;
}
+
// range negative (don't must be happen, loaded from same table)
if (!objmgr.GetTrinityStringLocale(i))
{
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` not found",i);
continue;
}
+
if (temp.SoundId)
{
if (!sSoundEntriesStore.LookupEntry(temp.SoundId))
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Sound %u but sound does not exist.",i,temp.SoundId);
}
+
if (!GetLanguageDescByID(temp.Language))
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` using Language %u but Language does not exist.",i,temp.Language);
+
if (temp.Type > CHAT_TYPE_ZONE_YELL)
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Type %u but this Chat Type does not exist.",i,temp.Type);
+
if (temp.Emote)
{
if (!sEmotesStore.LookupEntry(temp.Emote))
sLog.outErrorDb("CreatureEventAI: Entry %i in table `creature_ai_texts` has Emote %u but emote does not exist.",i,temp.Emote);
}
+
m_CreatureEventAI_TextMap[i] = temp;
++count;
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u additional CreatureEventAI Texts data.", count);
}
@@ -90,39 +107,50 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Texts()
sLog.outString();
sLog.outString(">> Loaded 0 additional CreatureEventAI Texts data. DB table `creature_ai_texts` is empty.");
}
+
}
+
// -------------------
void CreatureEventAIMgr::LoadCreatureEventAI_Summons()
{
+
//Drop Existing EventSummon Map
m_CreatureEventAI_Summon_Map.clear();
+
// Gather additional data for EventAI
QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, spawntimesecs FROM creature_ai_summons");
if (result)
{
barGoLink bar(result->GetRowCount());
uint32 Count = 0;
+
do
{
bar.step();
Field *fields = result->Fetch();
+
CreatureEventAI_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();
+
if(!MaNGOS::IsValidMapCoord(temp.position_x,temp.position_y,temp.position_z,temp.orientation))
{
sLog.outErrorDb("CreatureEventAI: Summon id %u have wrong coordinates (%f,%f,%f,%f), skipping.", i,temp.position_x,temp.position_y,temp.position_z,temp.orientation);
continue;
}
+
//Add to map
m_CreatureEventAI_Summon_Map[i] = temp;
++Count;
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u CreatureEventAI summon definitions", Count);
}
@@ -133,12 +161,15 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Summons()
sLog.outString();
sLog.outString(">> Loaded 0 CreatureEventAI Summon definitions. DB table `creature_ai_summons` is empty.");
}
+
}
+
// -------------------
void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
{
//Drop Existing EventAI List
m_CreatureEventAI_Event_Map.clear();
+
// Gather event data
QueryResult *result = WorldDatabase.Query("SELECT id, creature_id, event_type, event_inverse_phase_mask, event_chance, event_flags, "
"event_param1, event_param2, event_param3, event_param4, "
@@ -150,15 +181,19 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
{
barGoLink bar(result->GetRowCount());
uint32 Count = 0;
+
do
{
bar.step();
Field *fields = result->Fetch();
+
CreatureEventAI_Event temp;
temp.event_id = EventAI_Type(fields[0].GetUInt32());
uint32 i = temp.event_id;
+
temp.creature_id = fields[1].GetUInt32();
uint32 creature_id = temp.creature_id;
+
uint32 e_type = fields[2].GetUInt32();
//Report any errors in event
if (e_type >= EVENT_T_END)
@@ -167,6 +202,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
continue;
}
temp.event_type = EventAI_Type(e_type);
+
temp.event_inverse_phase_mask = fields[3].GetUInt32();
temp.event_chance = fields[4].GetUInt8();
temp.event_flags = fields[5].GetUInt8();
@@ -174,12 +210,14 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
temp.raw.param2 = fields[7].GetUInt32();
temp.raw.param3 = fields[8].GetUInt32();
temp.raw.param4 = fields[9].GetUInt32();
+
//Creature does not exist in database
if (!sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id))
{
sLog.outErrorDb("CreatureEventAI: Event %u has script for non-existing creature entry (%u), skipping.", i, temp.creature_id);
continue;
}
+
//No chance of this event occuring
if (temp.event_chance == 0)
sLog.outErrorDb("CreatureEventAI: Event %u has 0 percent chance. Event will never trigger!", i);
@@ -189,6 +227,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: 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)
{
@@ -205,8 +244,10 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
case EVENT_T_TARGET_MANA:
if (temp.percent_range.percentMax > 100)
sLog.outErrorDb("CreatureEventAI: Creature %u are using percentage event(%u) with param2 (MinPercent) > 100. Event will never trigger! ", temp.creature_id, i);
+
if (temp.percent_range.percentMax <= temp.percent_range.percentMin)
sLog.outErrorDb("CreatureEventAI: 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.percent_range.repeatMin && !temp.percent_range.repeatMax)
{
sLog.outErrorDb("CreatureEventAI: 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);
@@ -222,11 +263,14 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: Creature %u has non-existant SpellID(%u) defined in event %u.", temp.creature_id, temp.spell_hit.spellId, i);
continue;
}
+
if ((temp.spell_hit.schoolMask & pSpell->SchoolMask) != pSpell->SchoolMask)
sLog.outErrorDb("CreatureEventAI: 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.spell_hit.schoolMask, i);
}
+
if (!temp.spell_hit.schoolMask)
sLog.outErrorDb("CreatureEventAI: Creature %u is using invalid SpellSchoolMask(%u) defined in event %u.", temp.creature_id, temp.spell_hit.schoolMask, i);
+
if (temp.spell_hit.repeatMax < temp.spell_hit.repeatMin)
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
@@ -297,6 +341,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: Creature %u are using event(%u) with not existed qyest id (%u) in param1, skipped.", temp.creature_id, i, temp.quest.questId);
sLog.outErrorDb("CreatureEventAI: Creature %u using not implemented event (%u) in event %u.", temp.creature_id, temp.event_id, i);
continue;
+
case EVENT_T_AGGRO:
case EVENT_T_DEATH:
case EVENT_T_EVADE:
@@ -307,8 +352,10 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: 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;
}
+
case EVENT_T_RECEIVE_EMOTE:
{
if (!sEmotesTextStore.LookupEntry(temp.receive_emote.emoteId))
@@ -316,18 +363,22 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param1 (EmoteTextId: %u) are not valid.",temp.creature_id, i, temp.receive_emote.emoteId);
continue;
}
+
if (!PlayerCondition::IsValid(ConditionType(temp.receive_emote.condition), temp.receive_emote.conditionValue1, temp.receive_emote.conditionValue2))
{
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: param2 (Condition: %u) are not valid.",temp.creature_id, i, temp.receive_emote.condition);
continue;
}
+
if (!(temp.event_flags & EFLAG_REPEATABLE))
{
sLog.outErrorDb("CreatureEventAI: Creature %u using event %u: EFLAG_REPEATABLE not set. Event must always be repeatable. Flag applied.", temp.creature_id, i);
temp.event_flags |= EFLAG_REPEATABLE;
}
+
break;
}
+
case EVENT_T_BUFFED:
case EVENT_T_TARGET_BUFFED:
{
@@ -341,10 +392,12 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: Creature %u are using repeatable event(%u) with param4 < param3 (RepeatMax < RepeatMin). Event will never repeat.", temp.creature_id, i);
break;
}
+
default:
sLog.outErrorDb("CreatureEventAI: Creature %u using not checked at load event (%u) in event %u. Need check code update?", temp.creature_id, temp.event_id, i);
break;
}
+
for (uint32 j = 0; j < MAX_ACTIONS; j++)
{
uint16 action_type = fields[10+(j*4)].GetUInt16();
@@ -354,11 +407,14 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
temp.action[j].type = ACTION_T_NONE;
continue;
}
+
CreatureEventAI_Action& action = temp.action[j];
+
action.type = EventAI_ActionType(action_type);
action.raw.param1 = fields[11+(j*4)].GetUInt32();
action.raw.param2 = fields[12+(j*4)].GetUInt32();
action.raw.param3 = fields[13+(j*4)].GetUInt32();
+
//Report any errors in actions
switch (action.type)
{
@@ -375,6 +431,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
{
if (m_CreatureEventAI_TextMap.find(action.text.TextId2) == m_CreatureEventAI_TextMap.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param2 refrences non-existing entry in texts table.", i, j+1);
+
if (!action.text.TextId1)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param2, but param1 is not set. Required for randomized text.", i, j+1);
}
@@ -382,6 +439,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
{
if (m_CreatureEventAI_TextMap.find(action.text.TextId3) == m_CreatureEventAI_TextMap.end())
sLog.outErrorDb("CreatureEventAI: Event %u Action %u param3 refrences non-existing entry in texts table.", i, j+1);
+
if (!action.text.TextId1 || !action.text.TextId2)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u has param3, but param1 and/or param2 is not set. Required for randomized text.", i, j+1);
}
@@ -402,6 +460,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existant Creature entry %u.", i, j+1, action.morph.creatureId);
action.morph.creatureId = 0;
}
+
if (action.morph.modelId)
{
if (action.morph.creatureId)
@@ -457,9 +516,11 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
}
}
*/
+
//Cast is always triggered if target is forced to cast on self
if (action.cast.castFlags & CAST_FORCE_TARGET_SELF)
action.cast.castFlags |= CAST_TRIGGERED;
+
if (action.cast.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
@@ -467,6 +528,7 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
case ACTION_T_SUMMON:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.summon.creatureId))
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent creature entry %u.", i, j+1, action.summon.creatureId);
+
if (action.summon.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
break;
@@ -488,8 +550,10 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
}
else
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses non-existent Quest entry %u.", i, j+1, action.quest_event.questId);
+
if (action.quest_event.target >= TARGET_T_END)
sLog.outErrorDb("CreatureEventAI: Event %u Action %u uses incorrect Target type", i, j+1);
+
break;
case ACTION_T_CAST_EVENT:
if (!sCreatureStorage.LookupEntry<CreatureInfo>(action.cast_event.creatureId))
@@ -618,24 +682,29 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
case ACTION_T_RANGED_MOVEMENT: //Distance, Angle
case ACTION_T_CALL_FOR_HELP: //Distance
break;
+
case ACTION_T_RANDOM_SAY:
case ACTION_T_RANDOM_YELL:
case ACTION_T_RANDOM_TEXTEMOTE:
sLog.outErrorDb("CreatureEventAI: Event %u Action %u currently unused ACTION type. Did you forget to update database?", i, j+1);
break;
+
case ACTION_T_SET_ACTIVE:
case ACTION_T_SET_AGGRESSIVE:
case ACTION_T_ATTACK_START_PULSE:
case ACTION_T_SUMMON_GO:
break;
+
default:
sLog.outErrorDb("CreatureEventAI: Event %u Action %u have currently not checked at load action type (%u). Need check code update?", i, j+1, temp.action[j].type);
break;
}
}
+
//Add to list
m_CreatureEventAI_Event_Map[creature_id].push_back(temp);
++Count;
+
if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(temp.creature_id))
{
if(!cInfo->AIName || !cInfo->AIName[0])
@@ -653,9 +722,11 @@ void CreatureEventAIMgr::LoadCreatureEventAI_Scripts()
{
//sLog.outErrorDb("CreatureEventAI: Creature Entry %u has EventAI script but it also has C++ script. EventAI script will be overriden.", cInfo->Entry);
}
- }
+ }
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u CreatureEventAI scripts", Count);
}
diff --git a/src/game/CreatureEventAIMgr.h b/src/game/CreatureEventAIMgr.h
index 821cdb35c6a..b4672460cc6 100644
--- a/src/game/CreatureEventAIMgr.h
+++ b/src/game/CreatureEventAIMgr.h
@@ -15,25 +15,32 @@
* 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_CREATURE_EAI_MGR_H
#define MANGOS_CREATURE_EAI_MGR_H
+
#include "Common.h"
#include "CreatureEventAI.h"
+
class CreatureEventAIMgr
{
public:
CreatureEventAIMgr(){};
~CreatureEventAIMgr(){};
+
void LoadCreatureEventAI_Texts();
void LoadCreatureEventAI_Summons();
void LoadCreatureEventAI_Scripts();
+
CreatureEventAI_Event_Map const& GetCreatureEventAIMap() const { return m_CreatureEventAI_Event_Map; }
CreatureEventAI_Summon_Map const& GetCreatureEventAISummonMap() const { return m_CreatureEventAI_Summon_Map; }
CreatureEventAI_TextMap const& GetCreatureEventAITextMap() const { return m_CreatureEventAI_TextMap; }
+
private:
CreatureEventAI_Event_Map m_CreatureEventAI_Event_Map;
CreatureEventAI_Summon_Map m_CreatureEventAI_Summon_Map;
CreatureEventAI_TextMap m_CreatureEventAI_TextMap;
};
+
#define CreatureEAI_Mgr MaNGOS::Singleton<CreatureEventAIMgr>::Instance()
#endif
diff --git a/src/game/CreatureGroups.cpp b/src/game/CreatureGroups.cpp
index c0816f36804..97610754e69 100644
--- a/src/game/CreatureGroups.cpp
+++ b/src/game/CreatureGroups.cpp
@@ -17,21 +17,28 @@
* 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 "CreatureGroups.h"
#include "ObjectMgr.h"
#include "ProgressBar.h"
#include "Policies/SingletonImp.h"
#include "CreatureAI.h"
+
#define MAX_DESYNC 5.0f
+
INSTANTIATE_SINGLETON_1(CreatureGroupManager);
+
CreatureGroupInfoType CreatureGroupMap;
+
void CreatureGroupManager::AddCreatureToGroup(uint32 groupId, Creature *member)
{
Map *map = member->FindMap();
if(!map)
return;
+
CreatureGroupHolderType::iterator itr = map->CreatureGroupHolder.find(groupId);
+
//Add member to an existing group
if(itr != map->CreatureGroupHolder.end())
{
@@ -47,47 +54,58 @@ void CreatureGroupManager::AddCreatureToGroup(uint32 groupId, Creature *member)
group->AddMember(member);
}
}
+
void CreatureGroupManager::RemoveCreatureFromGroup(CreatureGroup *group, Creature *member)
{
sLog.outDebug("Deleting member pointer to GUID: %u from group %u", group->GetId(), member->GetDBTableGUIDLow());
group->RemoveMember(member);
+
if(group->isEmpty())
{
Map *map = member->FindMap();
if(!map)
return;
+
sLog.outDebug("Deleting group with InstanceID %u", member->GetInstanceId());
map->CreatureGroupHolder.erase(group->GetId());
delete group;
}
}
+
void CreatureGroupManager::LoadCreatureFormations()
{
//Clear existing map
CreatureGroupMap.clear();
+
//Check Integrity of the table
QueryResult *result = WorldDatabase.PQuery("SELECT MAX(leaderGUID) FROM creature_formations");
+
if(!result)
{
sLog.outErrorDb(" ...an error occured while loading the table creature_formations ( maybe it doesn't exist ?)\n");
return;
}
delete result;
+
//Get group data
result = WorldDatabase.PQuery("SELECT leaderGUID, memberGUID, dist, angle, groupAI FROM creature_formations ORDER BY leaderGUID");
+
if(!result)
{
sLog.outErrorDb("The table creature_formations is empty or corrupted");
return;
}
+
uint32 total_records = result->GetRowCount();
barGoLink bar( total_records);
Field *fields;
+
FormationInfo *group_member;
//Loading data...
do
{
fields = result->Fetch();
+
bar.step();
//Load group member data
group_member = new FormationInfo;
@@ -105,6 +123,7 @@ void CreatureGroupManager::LoadCreatureFormations()
group_member->follow_dist = 0;
group_member->follow_angle = 0;
}
+
// check data correctness
{
QueryResult* result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE guid = %u", group_member->leaderGUID);
@@ -113,6 +132,7 @@ void CreatureGroupManager::LoadCreatureFormations()
sLog.outErrorDb("creature_formations table leader guid %u incorrect (not exist)", group_member->leaderGUID);
continue;
}
+
result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE guid = %u", memberGUID);
if(!result)
{
@@ -120,56 +140,71 @@ void CreatureGroupManager::LoadCreatureFormations()
continue;
}
}
+
CreatureGroupMap[memberGUID] = group_member;
- }
+ }
while(result->NextRow()) ;
+
sLog.outString();
sLog.outString( ">> Loaded %u creatures in formations", total_records );
sLog.outString();
//Free some heap
delete result;
}
+
void CreatureGroup::AddMember(Creature *member)
{
sLog.outDebug("CreatureGroup::AddMember: Adding unit GUID: %u.", member->GetGUIDLow());
+
//Check if it is a leader
if(member->GetDBTableGUIDLow() == m_groupID)
{
sLog.outDebug("Unit GUID: %u is formation leader. Adding group.", member->GetGUIDLow());
m_leader = member;
}
+
m_members[member] = CreatureGroupMap.find(member->GetDBTableGUIDLow())->second;
member->SetFormation(this);
}
+
void CreatureGroup::RemoveMember(Creature *member)
{
if(m_leader == member)
m_leader = NULL;
+
m_members.erase(member);
member->SetFormation(NULL);
}
+
void CreatureGroup::MemberAttackStart(Creature *member, Unit *target)
{
uint8 groupAI = CreatureGroupMap[member->GetDBTableGUIDLow()]->groupAI;
if(!groupAI)
return;
+
if(groupAI == 1 && member != m_leader)
return;
+
for(CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
{
sLog.outDebug("GROUP ATTACK: group instance id %u calls member instid %u", m_leader->GetInstanceId(), member->GetInstanceId());
//sLog.outDebug("AI:%u:Group member found: %u, attacked by %s.", groupAI, itr->second->GetGUIDLow(), member->getVictim()->GetName());
+
//Skip one check
if(itr->first == member)
continue;
+
if(!itr->first->isAlive())
continue;
+
if(itr->first->getVictim())
continue;
+
if(itr->first->canAttack(target))
itr->first->AI()->AttackStart(target);
}
}
+
void CreatureGroup::FormationReset(bool dismiss)
{
for(CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
@@ -185,28 +220,37 @@ void CreatureGroup::FormationReset(bool dismiss)
}
m_Formed = !dismiss;
}
+
void CreatureGroup::LeaderMoveTo(float x, float y, float z)
{
if(!m_leader)
return;
+
float pathangle = atan2(m_leader->GetPositionY() - y, m_leader->GetPositionX() - x);
+
for(CreatureGroupMemberType::iterator itr = m_members.begin(); itr != m_members.end(); ++itr)
{
Creature *member = itr->first;
if(member == m_leader || !member->isAlive() || member->getVictim())
continue;
+
float angle = itr->second->follow_angle;
- float dist = itr->second->follow_dist;
+ float dist = itr->second->follow_dist;
+
float dx = x + cos(angle + pathangle) * dist;
float dy = y + sin(angle + pathangle) * dist;
float dz = z;
+
Trinity::NormalizeMapCoord(dx);
Trinity::NormalizeMapCoord(dy);
+
member->UpdateGroundPositionZ(dx, dy, dz);
+
if(member->IsWithinDist(m_leader, dist + MAX_DESYNC))
member->SetUnitMovementFlags(m_leader->GetUnitMovementFlags());
else
member->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
member->GetMotionMaster()->MovePoint(0, dx, dy, dz);
member->SetHomePosition(dx, dy, dz, pathangle);
}
diff --git a/src/game/CreatureGroups.h b/src/game/CreatureGroups.h
index 1cdbc5c3695..cc3eacfe185 100644
--- a/src/game/CreatureGroups.h
+++ b/src/game/CreatureGroups.h
@@ -1,4 +1,4 @@
-/*
+/*
* Copyright (C) 2005-2009 MaNGOS <http://getmangos.com/>
*
* Copyright (C) 2008-2009 Trinity <http://www.trinitycore.org/>
@@ -17,17 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _FORMATIONS_H
#define _FORMATIONS_H
+
#include "Common.h"
+
class CreatureGroup;
+
struct FormationInfo
{
uint32 leaderGUID;
- float follow_dist;
- float follow_angle;
+ float follow_dist;
+ float follow_angle;
uint8 groupAI;
};
+
class CreatureGroupManager
{
public:
@@ -35,29 +40,39 @@ class CreatureGroupManager
void RemoveCreatureFromGroup(CreatureGroup *group, Creature *creature);
void LoadCreatureFormations();
};
+
typedef UNORDERED_MAP<uint32/*memberDBGUID*/, FormationInfo*> CreatureGroupInfoType;
+
extern CreatureGroupInfoType CreatureGroupMap;
+
class CreatureGroup
{
private:
Creature *m_leader; //Important do not forget sometimes to work with pointers instead synonims :D:D
typedef std::map<Creature*, FormationInfo*> CreatureGroupMemberType;
CreatureGroupMemberType m_members;
+
uint32 m_groupID;
bool m_Formed;
+
public:
//Group cannot be created empty
explicit CreatureGroup(uint32 id) : m_groupID(id), m_leader(NULL), m_Formed(false) {}
~CreatureGroup() { sLog.outDebug("Destroying group"); }
+
Creature* getLeader() const { return m_leader; }
uint32 GetId() const { return m_groupID; }
bool isEmpty() const { return m_members.empty(); }
bool isFormed() const { return m_Formed; }
+
void AddMember(Creature *member);
void RemoveMember(Creature *member);
void FormationReset(bool dismiss);
+
void LeaderMoveTo(float x, float y, float z);
void MemberAttackStart(Creature* member, Unit *target);
};
+
#define formation_mgr Trinity::Singleton<CreatureGroupManager>::Instance()
+
#endif
diff --git a/src/game/DBCEnums.h b/src/game/DBCEnums.h
index 21d23dca840..a0e030bf7cb 100644
--- a/src/game/DBCEnums.h
+++ b/src/game/DBCEnums.h
@@ -15,29 +15,36 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DBCENUMS_H
#define DBCENUMS_H
+
// Client expected level limitation, like as used in DBC item max levels for "until max player level"
// use as default max player level, must be fit max level for used client
// also see MAX_LEVEL and STRONG_MAX_LEVEL define
#define DEFAULT_MAX_LEVEL 80
+
// client supported max level for player/pets/etc. Avoid overflow or client stability affected.
// also see GT_MAX_LEVEL define
#define MAX_LEVEL 100
+
// Server side limitation. Base at used code requirements.
// also see MAX_LEVEL and GT_MAX_LEVEL define
#define STRONG_MAX_LEVEL 255
+
enum AreaTeams
{
AREATEAM_NONE = 0,
AREATEAM_ALLY = 2,
AREATEAM_HORDE = 4
};
+
enum AchievementFactionFlags
{
ACHIEVEMENT_FACTION_FLAG_HORDE = 0x00000000,
ACHIEVEMENT_FACTION_FLAG_ALLIANCE = 0x00000001,
};
+
enum AchievementFlags
{
ACHIEVEMENT_FLAG_COUNTER = 0x00000001, // Just count statistic (never stop and complete)
@@ -51,6 +58,7 @@ enum AchievementFlags
ACHIEVEMENT_FLAG_REALM_FIRST_REACH = 0x00000100, //
ACHIEVEMENT_FLAG_REALM_FIRST_KILL = 0x00000200, //
};
+
enum AchievementCriteriaCondition
{
ACHIEVEMENT_CRITERIA_CONDITION_NONE = 0,
@@ -61,6 +69,7 @@ enum AchievementCriteriaCondition
ACHIEVEMENT_CRITERIA_CONDITION_UNK2 = 9, // unk
ACHIEVEMENT_CRITERIA_CONDITION_UNK3 = 13, // unk
};
+
enum AchievementCriteriaCompletionFlags
{
ACHIEVEMENT_CRITERIA_FLAG_SHOW_PROGRESS_BAR = 0x00000001, // Show progress as bar
@@ -70,11 +79,13 @@ enum AchievementCriteriaCompletionFlags
ACHIEVEMENT_CRITERIA_FLAG_UNK5 = 0x00000010, // not used
ACHIEVEMENT_CRITERIA_FLAG_MONEY_COUNTER = 0x00000020, // Displays counter as money
};
+
enum AchievementCriteriaGroupFlags
{
// you mustn't be in a group while fulfilling this achievement
ACHIEVEMENT_CRITERIA_GROUP_NOT_IN_GROUP = 2,
};
+
enum AchievementCriteriaTypes
{
ACHIEVEMENT_CRITERIA_TYPE_KILL_CREATURE = 0,
@@ -188,6 +199,7 @@ enum AchievementCriteriaTypes
// 0..114 => 115 criteria types total
ACHIEVEMENT_CRITERIA_TYPE_TOTAL = 115,
};
+
enum AreaFlags
{
AREA_FLAG_SNOW = 0x00000001, // snow (only Dun Morogh, Naxxramas, Razorfen Downs and Winterspring)
@@ -219,10 +231,12 @@ enum AreaFlags
AREA_FLAG_UNK10 = 0x04000000, // unknown
AREA_FLAG_OUTDOOR_PVP2 = 0x08000000 // Wintergrasp and it's subzones
};
+
enum FactionTemplateFlags
{
FACTION_TEMPLATE_FLAG_CONTESTED_GUARD = 0x00001000, // faction will attack players that were involved in PvP combats
};
+
enum FactionMasks
{
FACTION_MASK_PLAYER = 1, // any player
@@ -231,6 +245,7 @@ enum FactionMasks
FACTION_MASK_MONSTER = 8 // aggressive creature from monster team
// if none flags set then non-aggressive creature
};
+
enum MapTypes
{
MAP_COMMON = 0,
@@ -239,11 +254,13 @@ enum MapTypes
MAP_BATTLEGROUND = 3,
MAP_ARENA = 4
};
+
enum AbilytyLearnType
{
ABILITY_LEARNED_ON_GET_PROFESSION_SKILL = 1,
ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL = 2
};
+
enum ItemEnchantmentType
{
ITEM_ENCHANTMENT_TYPE_NONE = 0,
@@ -256,6 +273,7 @@ enum ItemEnchantmentType
ITEM_ENCHANTMENT_TYPE_USE_SPELL = 7,
ITEM_ENCHANTMENT_TYPE_PRISMATIC_SOCKET = 8
};
+
enum TotemCategoryType
{
TOTEM_CATEGORY_TYPE_KNIFE = 1,
@@ -266,6 +284,7 @@ enum TotemCategoryType
TOTEM_CATEGORY_TYPE_HAMMER = 23,
TOTEM_CATEGORY_TYPE_SPANNER = 24
};
+
// SummonProperties.dbc, col 1
enum SummonPropGroup
{
@@ -275,6 +294,7 @@ enum SummonPropGroup
SUMMON_PROP_GROUP_CONTROLLABLE = 3, // 13 spells in 3.0.3, mostly controllable
SUMMON_PROP_GROUP_UNKNOWN3 = 4 // 86 spells in 3.0.3, taxi/mounts
};
+
// SummonProperties.dbc, col 3
enum SummonPropType
{
@@ -291,6 +311,7 @@ enum SummonPropType
SUMMON_PROP_TYPE_DRAKE_VEH = 10, // summon drake (vehicle), 3 spells
SUMMON_PROP_TYPE_LIGHTWELL = 11 // summon lightwell, 6 spells in 3.0.3
};
+
// SummonProperties.dbc, col 5
enum SummonPropFlags
{
@@ -310,5 +331,6 @@ enum SummonPropFlags
SUMMON_PROP_FLAG_UNK13 = 0x1000, // 8 spells in 3.0.3, siege vehicle
SUMMON_PROP_FLAG_UNK14 = 0x2000, // 2 spells in 3.0.3, escort?
};
+
#endif
diff --git a/src/game/DBCStores.cpp b/src/game/DBCStores.cpp
index fa117a169c0..5dae4cb6e17 100644
--- a/src/game/DBCStores.cpp
+++ b/src/game/DBCStores.cpp
@@ -17,21 +17,27 @@
* 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 "Policies/SingletonImp.h"
#include "Log.h"
#include "ProgressBar.h"
#include "SharedDefines.h"
#include "SpellMgr.h"
+
#include "DBCfmt.h"
+
#include <map>
+
typedef std::map<uint16,uint32> AreaFlagByAreaID;
typedef std::map<uint32,uint32> AreaFlagByMapID;
+
DBCStorage <AreaTableEntry> sAreaStore(AreaTableEntryfmt);
DBCStorage <AreaGroupEntry> sAreaGroupStore(AreaGroupEntryfmt);
DBCStorage <AreaPOIEntry> sAreaPOIStore(AreaPOIEntryfmt);
static AreaFlagByAreaID sAreaFlagByAreaID;
static AreaFlagByMapID sAreaFlagByMapID; // for instances without generated *.map files
+
DBCStorage <AchievementEntry> sAchievementStore(Achievementfmt);
DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore(AchievementCriteriafmt);
DBCStorage <AreaTriggerEntry> sAreaTriggerStore(AreaTriggerEntryfmt);
@@ -50,18 +56,23 @@ DBCStorage <CreatureFamilyEntry> sCreatureFamilyStore(CreatureFamilyfmt);
DBCStorage <CreatureSpellDataEntry> sCreatureSpellDataStore(CreatureSpellDatafmt);
DBCStorage <CreatureTypeEntry> sCreatureTypeStore(CreatureTypefmt);
DBCStorage <CurrencyTypesEntry> sCurrencyTypesStore(CurrencyTypesfmt);
+
DBCStorage <DurabilityQualityEntry> sDurabilityQualityStore(DurabilityQualityfmt);
DBCStorage <DurabilityCostsEntry> sDurabilityCostsStore(DurabilityCostsfmt);
+
DBCStorage <EmotesEntry> sEmotesStore(EmotesEntryfmt);
DBCStorage <EmotesTextEntry> sEmotesTextStore(EmotesTextEntryfmt);
+
typedef std::map<uint32,SimpleFactionsList> FactionTeamMap;
static FactionTeamMap sFactionTeamMap;
DBCStorage <FactionEntry> sFactionStore(FactionEntryfmt);
DBCStorage <FactionTemplateEntry> sFactionTemplateStore(FactionTemplateEntryfmt);
+
DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore(GameObjectDisplayInfofmt);
DBCStorage <GemPropertiesEntry> sGemPropertiesStore(GemPropertiesEntryfmt);
DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore(GlyphPropertiesfmt);
DBCStorage <GlyphSlotEntry> sGlyphSlotStore(GlyphSlotfmt);
+
DBCStorage <GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore(GtBarberShopCostBasefmt);
DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore(GtCombatRatingsfmt);
DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore(GtChanceToMeleeCritBasefmt);
@@ -72,7 +83,9 @@ DBCStorage <GtOCTRegenHPEntry> sGtOCTRegenHPStore(GtOCTRegenHPfmt);
//DBCStorage <GtOCTRegenMPEntry> sGtOCTRegenMPStore(GtOCTRegenMPfmt); -- not used currently
DBCStorage <GtRegenHPPerSptEntry> sGtRegenHPPerSptStore(GtRegenHPPerSptfmt);
DBCStorage <GtRegenMPPerSptEntry> sGtRegenMPPerSptStore(GtRegenMPPerSptfmt);
+
DBCStorage <HolidaysEntry> sHolidaysStore(Holidaysfmt);
+
DBCStorage <ItemEntry> sItemStore(Itemfmt);
DBCStorage <ItemBagFamilyEntry> sItemBagFamilyStore(ItemBagFamilyfmt);
//DBCStorage <ItemCondExtCostsEntry> sItemCondExtCostsStore(ItemCondExtCostsEntryfmt);
@@ -82,22 +95,30 @@ DBCStorage <ItemLimitCategoryEntry> sItemLimitCategoryStore(ItemLimitCategoryEnt
DBCStorage <ItemRandomPropertiesEntry> sItemRandomPropertiesStore(ItemRandomPropertiesfmt);
DBCStorage <ItemRandomSuffixEntry> sItemRandomSuffixStore(ItemRandomSuffixfmt);
DBCStorage <ItemSetEntry> sItemSetStore(ItemSetEntryfmt);
+
DBCStorage <LockEntry> sLockStore(LockEntryfmt);
+
DBCStorage <MailTemplateEntry> sMailTemplateStore(MailTemplateEntryfmt);
DBCStorage <MapEntry> sMapStore(MapEntryfmt);
DBCStorage <MovieEntry> sMovieStore(MovieEntryfmt);
+
DBCStorage <QuestSortEntry> sQuestSortStore(QuestSortEntryfmt);
+
DBCStorage <RandomPropertiesPointsEntry> sRandomPropertiesPointsStore(RandomPropertiesPointsfmt);
DBCStorage <ScalingStatDistributionEntry> sScalingStatDistributionStore(ScalingStatDistributionfmt);
DBCStorage <ScalingStatValuesEntry> sScalingStatValuesStore(ScalingStatValuesfmt);
+
DBCStorage <SkillLineEntry> sSkillLineStore(SkillLinefmt);
DBCStorage <SkillLineAbilityEntry> sSkillLineAbilityStore(SkillLineAbilityfmt);
+
DBCStorage <SoundEntriesEntry> sSoundEntriesStore(SoundEntriesfmt);
+
DBCStorage <SpellItemEnchantmentEntry> sSpellItemEnchantmentStore(SpellItemEnchantmentfmt);
DBCStorage <SpellItemEnchantmentConditionEntry> sSpellItemEnchantmentConditionStore(SpellItemEnchantmentConditionfmt);
DBCStorage <SpellEntry> sSpellStore(SpellEntryfmt);
SpellCategoryStore sSpellCategoryStore;
PetFamilySpellsStore sPetFamilySpellsStore;
+
DBCStorage <SpellCastTimesEntry> sSpellCastTimesStore(SpellCastTimefmt);
DBCStorage <SpellDurationEntry> sSpellDurationStore(SpellDurationfmt);
DBCStorage <SpellFocusObjectEntry> sSpellFocusObjectStore(SpellFocusObjectfmt);
@@ -110,16 +131,21 @@ DBCStorage <SummonPropertiesEntry> sSummonPropertiesStore(SummonPropertiesfmt);
DBCStorage <TalentEntry> sTalentStore(TalentEntryfmt);
TalentSpellPosMap sTalentSpellPosMap;
DBCStorage <TalentTabEntry> sTalentTabStore(TalentTabEntryfmt);
+
// store absolute bit position for first rank for talent inspect
static uint32 sTalentTabPages[12/*MAX_CLASSES*/][3];
+
DBCStorage <TaxiNodesEntry> sTaxiNodesStore(TaxiNodesEntryfmt);
TaxiMask sTaxiNodesMask;
TaxiMask sOldContinentsNodesMask;
+
// DBC used only for initialization sTaxiPathSetBySource at startup.
TaxiPathSetBySource sTaxiPathSetBySource;
DBCStorage <TaxiPathEntry> sTaxiPathStore(TaxiPathEntryfmt);
+
// DBC used only for initialization sTaxiPathSetBySource at startup.
TaxiPathNodesByPath sTaxiPathNodesByPath;
+
static DBCStorage <TaxiPathNodeEntry> sTaxiPathNodeStore(TaxiPathNodeEntryfmt);
DBCStorage <TotemCategoryEntry> sTotemCategoryStore(TotemCategoryEntryfmt);
DBCStorage <VehicleEntry> sVehicleStore(VehicleEntryfmt);
@@ -127,22 +153,28 @@ DBCStorage <VehicleSeatEntry> sVehicleSeatStore(VehicleSeatEntryfmt);
DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore(WorldMapAreaEntryfmt);
DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore(WorldMapOverlayEntryfmt);
DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore(WorldSafeLocsEntryfmt);
+
typedef std::list<std::string> StoreProblemList;
+
static bool LoadDBC_assert_print(uint32 fsize,uint32 rsize, const 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<class T>
inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList& errlist, DBCStorage<T>& storage, const std::string& dbc_path, const std::string& filename, const std::string * custom_entries = NULL, const std::string * idname = NULL)
{
// compatibility format and C++ structure sizes
assert(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()) == sizeof(T) || LoadDBC_assert_print(DBCFileLoader::GetFormatRecordSize(storage.GetFormat()),sizeof(T),filename));
+
std::string dbc_filename = dbc_path + filename;
SqlDbc * sql = NULL;
if (custom_entries)
sql = new SqlDbc(&filename,custom_entries, idname,storage.GetFormat());
+
if(storage.Load(dbc_filename.c_str(), sql))
{
bar.step();
@@ -150,6 +182,7 @@ inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList
{
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<<i); // mark as not available for speedup next checks
@@ -172,14 +205,20 @@ inline void LoadDBC(uint32& availableDbcLocales,barGoLink& bar, StoreProblemList
if (sql)
delete sql;
}
+
void LoadDBCStores(const std::string& dataPath)
{
std::string dbcPath = dataPath+"dbc/";
+
const uint32 DBCFilesCount = 79;
+
barGoLink bar( DBCFilesCount );
+
StoreProblemList bad_dbc_files;
uint32 availableDbcLocales = 0xFFFFFFFF;
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaStore, dbcPath,"AreaTable.dbc");
+
// must be after sAreaStore loading
for(uint32 i = 0; i < sAreaStore.GetNumRows(); ++i) // areaflag numbered from 0
{
@@ -187,11 +226,13 @@ void LoadDBCStores(const std::string& dataPath)
{
// fill AreaId->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 && area->mapid != 571 )
sAreaFlagByMapID.insert(AreaFlagByMapID::value_type(area->mapid,area->exploreFlag));
}
}
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAchievementStore, dbcPath,"Achievement.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAchievementCriteriaStore, dbcPath,"Achievement_Criteria.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sAreaTriggerStore, dbcPath,"AreaTrigger.dbc");
@@ -202,6 +243,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBattlemasterListStore, dbcPath,"BattlemasterList.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sBarberShopStyleStore, dbcPath,"BarberShopStyle.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sCharStartOutfitStore, dbcPath,"CharStartOutfit.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");
@@ -226,6 +268,7 @@ void LoadDBCStores(const std::string& dataPath)
flist.push_back(i);
}
}
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sFactionTemplateStore, dbcPath,"FactionTemplate.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGameObjectDisplayInfoStore, dbcPath,"GameObjectDisplayInfo.dbc");
for(uint32 i = 0; i < sGameObjectDisplayInfoStore.GetNumRows(); ++i)
@@ -240,15 +283,20 @@ void LoadDBCStores(const std::string& dataPath)
std::swap(*(float*)(&info->maxZ), *(float*)(&info->minZ));
}
}
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGemPropertiesStore, dbcPath,"GemProperties.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGlyphPropertiesStore, dbcPath,"GlyphProperties.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGlyphSlotStore, dbcPath,"GlyphSlot.dbc");
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sGtBarberShopCostBaseStore,dbcPath,"gtBarberShopCostBase.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");
@@ -281,12 +329,16 @@ void LoadDBCStores(const std::string& dataPath)
if(spell && spell->Category)
sSpellCategoryStore[spell->Category].insert(i);
}
+
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 && IsPassiveSpell(spellInfo->Id))
{
for (uint32 i = 1; i < sCreatureFamilyStore.GetNumRows(); ++i)
@@ -294,16 +346,20 @@ void LoadDBCStores(const std::string& dataPath)
CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(i);
if(!cFamily)
continue;
+
if(skillLine->skillId != cFamily->skillLine[0] && skillLine->skillId != cFamily->skillLine[1])
continue;
if(spellInfo->spellLevel)
continue;
+
if(skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
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");
@@ -316,6 +372,7 @@ void LoadDBCStores(const std::string& dataPath)
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sStableSlotPricesStore, dbcPath,"StableSlotPrices.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sSummonPropertiesStore, dbcPath,"SummonProperties.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentStore, dbcPath,"Talent.dbc");
+
// create talent spells set
for (unsigned int i = 0; i < sTalentStore.GetNumRows(); ++i)
{
@@ -325,7 +382,9 @@ void LoadDBCStores(const std::string& dataPath)
if(talentInfo->RankID[j])
sTalentSpellPosMap[talentInfo->RankID[j]] = TalentSpellPos(i,j);
}
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTalentTabStore, dbcPath,"TalentTab.dbc");
+
// prepare fast data access to bit pos of talent ranks for use at inspecting
{
// now have all max ranks (and then bit amount used for store talent ranks in inspect)
@@ -334,21 +393,27 @@ void LoadDBCStores(const std::string& dataPath)
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId );
if(!talentTabInfo)
continue;
+
// prevent memory corruption; otherwise cls will become 12 below
if ((talentTabInfo->ClassMask & CLASSMASK_ALL_PLAYABLE)==0)
continue;
+
// store class talent tab pages
uint32 cls = 1;
for(uint32 m=1;!(m & talentTabInfo->ClassMask) && cls < MAX_CLASSES;m <<=1, ++cls) {}
+
sTalentTabPages[cls][talentTabInfo->tabpage]=talentTabId;
}
}
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTaxiNodesStore, dbcPath,"TaxiNodes.dbc");
+
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
@@ -369,6 +434,7 @@ void LoadDBCStores(const std::string& dataPath)
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();
+
// Initialize global taxinodes mask
// include existed nodes that have at least single not spell base (scripted) path
{
@@ -378,6 +444,7 @@ void LoadDBCStores(const std::string& dataPath)
for(int j=0; j < 3; ++j)
if(sInfo->Effect[j]==123 /*SPELL_EFFECT_SEND_TAXI*/)
spellPaths.insert(sInfo->EffectMiscValue[j]);
+
memset(sTaxiNodesMask,0,sizeof(sTaxiNodesMask));
memset(sOldContinentsNodesMask,0,sizeof(sTaxiNodesMask));
for(uint32 i = 1; i < sTaxiNodesStore.GetNumRows(); ++i)
@@ -385,6 +452,7 @@ void LoadDBCStores(const std::string& dataPath)
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i);
if(!node)
continue;
+
TaxiPathSetBySource::const_iterator src_i = sTaxiPathSetBySource.find(i);
if(src_i!=sTaxiPathSetBySource.end() && !src_i->second.empty())
{
@@ -398,24 +466,29 @@ void LoadDBCStores(const std::string& dataPath)
break;
}
}
+
if(!ok)
continue;
}
+
// valid taxi network node
uint8 field = (uint8)((i - 1) / 32);
uint32 submask = 1<<((i-1)%32);
sTaxiNodesMask[field] |= submask;
+
// old continent node (+ nodes virtually at old continents, check explicitly to avoid loading map files for zone info)
if (node->map_id < 2 || i == 82 || i == 83 || i == 93 || i == 94)
sOldContinentsNodesMask[field] |= submask;
}
}
+
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sTotemCategoryStore, dbcPath,"TotemCategory.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleStore, dbcPath,"Vehicle.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sVehicleSeatStore, dbcPath,"VehicleSeat.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapAreaStore, dbcPath,"WorldMapArea.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldMapOverlayStore, dbcPath,"WorldMapOverlay.dbc");
LoadDBC(availableDbcLocales,bar,bad_dbc_files,sWorldSafeLocsStore, dbcPath,"WorldSafeLocs.dbc");
+
// error checks
if(bad_dbc_files.size() >= DBCFilesCount )
{
@@ -427,9 +500,11 @@ void LoadDBCStores(const std::string& dataPath)
std::string str;
for(std::list<std::string>::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",(uint32)bad_dbc_files.size(),DBCFilesCount,str.c_str());
exit(1);
}
+
// Check loaded DBC files proper version
if( !sSpellStore.LookupEntry(66530) || // last added spell in 3.1.3
!sMapStore.LookupEntry(624) || // last map added in 3.1.3
@@ -442,9 +517,11 @@ void LoadDBCStores(const std::string& dataPath)
sLog.outError("\nYou have _outdated_ DBC files. Please extract correct versions from current using client.");
exit(1);
}
+
sLog.outString();
sLog.outString( ">> Initialized %d data stores", DBCFilesCount );
}
+
SimpleFactionsList const* GetFactionTeamList(uint32 faction, bool &isTeamMember)
{
for(FactionTeamMap::const_iterator itr = sFactionTeamMap.begin(); itr != sFactionTeamMap.end(); ++itr)
@@ -465,6 +542,7 @@ SimpleFactionsList const* GetFactionTeamList(uint32 faction, bool &isTeamMember)
}
return NULL;
}
+
char* GetPetName(uint32 petfamily, uint32 dbclang)
{
if(!petfamily)
@@ -474,41 +552,53 @@ char* GetPetName(uint32 petfamily, uint32 dbclang)
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;
}
+
int32 GetAreaFlagByAreaID(uint32 area_id)
{
AreaFlagByAreaID::iterator i = sAreaFlagByAreaID.find(area_id);
if(i == sAreaFlagByAreaID.end())
return -1;
+
return i->second;
}
+
AreaTableEntry const* GetAreaEntryByAreaID(uint32 area_id)
{
int32 areaflag = GetAreaFlagByAreaID(area_id);
if(areaflag < 0)
return NULL;
+
return sAreaStore.LookupEntry(areaflag );
}
+
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);
@@ -517,22 +607,28 @@ uint32 GetAreaFlagByMapId(uint32 mapid)
else
return i->second;
}
+
uint32 GetVirtualMapForMapAndZone(uint32 mapid, uint32 zoneId)
{
if(mapid != 530 && mapid != 571) // 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);
if(!mapEntry)
return CONTENT_1_60;
+
switch(mapEntry->Expansion())
{
default: return CONTENT_1_60;
@@ -540,6 +636,7 @@ ContentLevels GetContentLevelsForMapAndZone(uint32 mapid, uint32 zoneId)
case 2: return CONTENT_71_80;
}
}
+
ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id)
{
// not sorted, numbering index from 0
@@ -551,46 +648,58 @@ ChatChannelsEntry const* GetChannelEntryFor(uint32 channel_id)
}
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 const* GetTalentTabPages(uint32 cls)
{
return sTalentTabPages[cls];
}
+
// script support functions
TRINITY_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore() { return &sSoundEntriesStore; }
TRINITY_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore() { return &sSpellStore; }
diff --git a/src/game/DBCStores.h b/src/game/DBCStores.h
index 90ac6dd97c8..4fd5b6bcb59 100644
--- a/src/game/DBCStores.h
+++ b/src/game/DBCStores.h
@@ -15,22 +15,30 @@
* 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_DBCSTORES_H
#define MANGOS_DBCSTORES_H
+
#include "Common.h"
#include "Database/DBCStore.h"
#include "DBCStructure.h"
+
#include <list>
+
typedef std::list<uint32> SimpleFactionsList;
+
SimpleFactionsList const* GetFactionTeamList(uint32 faction, bool &isTeamMember);
char* GetPetName(uint32 petfamily, uint32 dbclang);
uint32 GetTalentSpellCost(uint32 spellId);
TalentSpellPos const* GetTalentSpellPos(uint32 spellId);
+
int32 GetAreaFlagByAreaID(uint32 area_id); // -1 if not found
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,
@@ -38,11 +46,16 @@ enum ContentLevels
CONTENT_71_80
};
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 const* /*[3]*/ GetTalentTabPages(uint32 cls);
+
extern DBCStorage <AchievementEntry> sAchievementStore;
extern DBCStorage <AchievementCriteriaEntry> sAchievementCriteriaStore;
extern DBCStorage <AreaTableEntry> sAreaStore;// recommend access using functions
@@ -74,6 +87,7 @@ extern DBCStorage <GameObjectDisplayInfoEntry> sGameObjectDisplayInfoStore;
extern DBCStorage <GemPropertiesEntry> sGemPropertiesStore;
extern DBCStorage <GlyphPropertiesEntry> sGlyphPropertiesStore;
extern DBCStorage <GlyphSlotEntry> sGlyphSlotStore;
+
extern DBCStorage <GtBarberShopCostBaseEntry> sGtBarberShopCostBaseStore;
extern DBCStorage <GtCombatRatingsEntry> sGtCombatRatingsStore;
extern DBCStorage <GtChanceToMeleeCritBaseEntry> sGtChanceToMeleeCritBaseStore;
@@ -132,7 +146,9 @@ extern DBCStorage <VehicleSeatEntry> sVehicleSeatStore;
//extern DBCStorage <WorldMapAreaEntry> sWorldMapAreaStore; -- use Zone2MapCoordinates and Map2ZoneCoordinates
extern DBCStorage <WorldMapOverlayEntry> sWorldMapOverlayStore;
extern DBCStorage <WorldSafeLocsEntry> sWorldSafeLocsStore;
+
void LoadDBCStores(const std::string& dataPath);
+
// script support functions
TRINITY_DLL_SPEC DBCStorage <SoundEntriesEntry> const* GetSoundEntriesStore();
TRINITY_DLL_SPEC DBCStorage <SpellEntry> const* GetSpellStore();
diff --git a/src/game/DBCStructure.h b/src/game/DBCStructure.h
index 98f4dfd3744..7e459c61e8b 100644
--- a/src/game/DBCStructure.h
+++ b/src/game/DBCStructure.h
@@ -17,21 +17,27 @@
* 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_DBCSTRUCTURE_H
#define MANGOS_DBCSTRUCTURE_H
+
#include "DBCEnums.h"
#include "Platform/Define.h"
#include "Util.h"
+
#include <map>
#include <set>
#include <vector>
+
// Structures using to access raw DBC data and required packing to portability
+
// GCC have alternative #pragma pack(N) syntax and old gcc version not support pack(push,N), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
+
struct AchievementEntry
{
uint32 ID; // 0
@@ -52,6 +58,7 @@ struct AchievementEntry
uint32 count; // 60 - need this count of completed criterias (own or referenced achievement criterias)
uint32 refAchievement; // 61 - referenced achievement (counting of all completed criterias)
};
+
struct AchievementCategoryEntry
{
uint32 ID; // 0
@@ -60,6 +67,7 @@ struct AchievementCategoryEntry
//uint32 name_flags; // 18
//uint32 sortOrder; // 19
};
+
struct AchievementCriteriaEntry
{
uint32 ID; // 0
@@ -74,6 +82,7 @@ struct AchievementCriteriaEntry
uint32 creatureID; // 3
uint32 creatureCount; // 4
} kill_creature;
+
// ACHIEVEMENT_CRITERIA_TYPE_WIN_BG = 1
struct
{
@@ -84,89 +93,105 @@ struct AchievementCriteriaEntry
uint32 additionalRequirement2_type; // 7 additional requirement 2 type
uint32 additionalRequirement2_value; // 8 additional requirement 1 value
} win_bg;
+
// ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL = 5
struct
{
uint32 unused; // 3
uint32 level; // 4
} reach_level;
+
// ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL = 7
struct
{
uint32 skillID; // 3
uint32 skillLevel; // 4
} reach_skill_level;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_ACHIEVEMENT = 8
struct
{
uint32 linkedAchievement; // 3
} complete_achievement;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT = 9
struct
{
uint32 unused; // 3
uint32 totalQuestCount; // 4
} complete_quest_count;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST_DAILY = 10
struct
{
uint32 unused; // 3
uint32 numberOfDays; // 4
} complete_daily_quest_daily;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE = 11
struct
{
uint32 zoneID; // 3
uint32 questCount; // 4
} complete_quests_in_zone;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST = 14
struct
{
uint32 unused; // 3
uint32 questCount; // 4
} complete_daily_quest;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_BATTLEGROUND = 15
struct
{
uint32 mapID; // 3
} complete_battleground;
+
// ACHIEVEMENT_CRITERIA_TYPE_DEATH_AT_MAP = 16
struct
{
uint32 mapID; // 3
} death_at_map;
+
// ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON = 18
struct
{
uint32 manLimit; // 3
} death_in_dungeon;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_RAID = 19
struct
{
uint32 groupSize; // 3 can be 5, 10 or 25
} complete_raid;
+
// ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_CREATURE = 20
struct
{
uint32 creatureEntry; // 3
} killed_by_creature;
+
// ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING = 24
struct
{
uint32 unused; // 3
uint32 fallHeight; // 4
} fall_without_dying;
+
// ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM = 26
struct
{
uint32 type; // 3, see enum EnviromentalDamage
} death_from;
+
// ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST = 27
struct
{
uint32 questID; // 3
uint32 questCount; // 4
} complete_quest;
+
// ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET = 28
// ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2 = 69
struct
@@ -174,6 +199,7 @@ struct AchievementCriteriaEntry
uint32 spellID; // 3
uint32 spellCount; // 4
} be_spell_target;
+
// ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL = 29
// ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2 = 110
struct
@@ -181,33 +207,39 @@ struct AchievementCriteriaEntry
uint32 spellID; // 3
uint32 castCount; // 4
} cast_spell;
+
// ACHIEVEMENT_CRITERIA_TYPE_HONORABLE_KILL_AT_AREA = 31
struct
{
uint32 areaID; // 3 Reference to AreaTable.dbc
uint32 killCount; // 4
} honorable_kill_at_area;
+
// ACHIEVEMENT_CRITERIA_TYPE_WIN_ARENA = 32
struct
{
uint32 mapID; // 3 Reference to Map.dbc
} win_arena;
+
// ACHIEVEMENT_CRITERIA_TYPE_PLAY_ARENA = 33
struct
{
uint32 mapID; // 3 Reference to Map.dbc
} play_arena;
+
// ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL = 34
struct
{
uint32 spellID; // 3 Reference to Map.dbc
} learn_spell;
+
// ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM = 36
struct
{
uint32 itemID; // 3
uint32 itemCount; // 4
} own_item;
+
// ACHIEVEMENT_CRITERIA_TYPE_WIN_RATED_ARENA = 37
struct
{
@@ -215,77 +247,90 @@ struct AchievementCriteriaEntry
uint32 count; // 4
uint32 flag; // 5 4=in a row
} win_rated_arena;
+
// ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_TEAM_RATING = 38
struct
{
uint32 teamtype; // 3 {2,3,5}
} highest_team_rating;
+
// ACHIEVEMENT_CRITERIA_TYPE_REACH_TEAM_RATING = 39
struct
{
uint32 teamtype; // 3 {2,3,5}
uint32 teamrating; // 4
} reach_team_rating;
+
// ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL = 40
struct
{
uint32 skillID; // 3
uint32 skillLevel; // 4 apprentice=1, journeyman=2, expert=3, artisan=4, master=5, grand master=6
} learn_skill_level;
+
// ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM = 41
struct
{
uint32 itemID; // 3
uint32 itemCount; // 4
} use_item;
+
// ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM = 42
struct
{
uint32 itemID; // 3
uint32 itemCount; // 4
} loot_item;
+
// ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA = 43
struct
{
// TODO: This rank is _NOT_ the index from AreaTable.dbc
uint32 areaReference; // 3
} explore_area;
+
// ACHIEVEMENT_CRITERIA_TYPE_OWN_RANK = 44
struct
{
// TODO: This rank is _NOT_ the index from CharTitles.dbc
uint32 rank; // 3
} own_rank;
+
// ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT = 45
struct
{
uint32 unused; // 3
uint32 numberOfSlots; // 4
} buy_bank_slot;
+
// ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION = 46
struct
{
uint32 factionID; // 3
uint32 reputationAmount; // 4 Total reputation amount, so 42000 = exalted
} gain_reputation;
+
// ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION= 47
struct
{
uint32 unused; // 3
uint32 numberOfExaltedFactions; // 4
} gain_exalted_reputation;
+
// ACHIEVEMENT_CRITERIA_TYPE_VISIT_BARBER_SHOP = 48
struct
{
uint32 unused; // 3
uint32 numberOfVisits; // 4
} visit_barber;
+
// ACHIEVEMENT_CRITERIA_TYPE_EQUIP_EPIC_ITEM = 49
// TODO: where is the required itemlevel stored?
struct
{
uint32 itemSlot; // 3
} equip_epic_item;
+
// ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT = 50
struct
{
@@ -298,18 +343,21 @@ struct AchievementCriteriaEntry
uint32 rollValue; // 3
uint32 count; // 4
} roll_greed_on_loot;
+
// ACHIEVEMENT_CRITERIA_TYPE_HK_CLASS = 52
struct
{
uint32 classID; // 3
uint32 count; // 4
} hk_class;
+
// ACHIEVEMENT_CRITERIA_TYPE_HK_RACE = 53
struct
{
uint32 raceID; // 3
uint32 count; // 4
} hk_race;
+
// ACHIEVEMENT_CRITERIA_TYPE_DO_EMOTE = 54
// TODO: where is the information about the target stored?
struct
@@ -327,12 +375,14 @@ struct AchievementCriteriaEntry
uint32 flag; // 5 =3 for battleground healing
uint32 mapid; // 6
} healing_done;
+
// ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM = 57
struct
{
uint32 itemID; // 3
uint32 count; // 4
} equip_item;
+
// ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD= 62
struct
{
@@ -340,18 +390,21 @@ struct AchievementCriteriaEntry
uint32 goldInCopper; // 4
} quest_reward_money;
+
// ACHIEVEMENT_CRITERIA_TYPE_LOOT_MONEY = 67
struct
{
uint32 unused; // 3
uint32 goldInCopper; // 4
} loot_money;
+
// ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT = 68
struct
{
uint32 goEntry; // 3
uint32 useCount; // 4
} use_gameobject;
+
// ACHIEVEMENT_CRITERIA_TYPE_SPECIAL_PVP_KILL = 70
// TODO: are those special criteria stored in the dbc or do we have to add another sql table?
struct
@@ -359,62 +412,73 @@ struct AchievementCriteriaEntry
uint32 unused; // 3
uint32 killCount; // 4
} special_pvp_kill;
+
// ACHIEVEMENT_CRITERIA_TYPE_FISH_IN_GAMEOBJECT = 72
struct
{
uint32 goEntry; // 3
uint32 lootCount; // 4
} fish_in_gameobject;
+
// ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS = 75
struct
{
uint32 skillLine; // 3
uint32 spellCount; // 4
} learn_skillline_spell;
+
// ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL = 76
struct
{
uint32 unused; // 3
uint32 duelCount; // 4
} win_duel;
+
// ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_POWER = 96
struct
{
uint32 powerType; // 3 mana=0, 1=rage, 3=energy, 6=runic power
} highest_power;
+
// ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_STAT = 97
struct
{
uint32 statType; // 3 4=spirit, 3=int, 2=stamina, 1=agi, 0=strength
} highest_stat;
+
// ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_SPELLPOWER = 98
struct
{
uint32 spellSchool; // 3
} highest_spellpower;
+
// ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_RATING = 100
struct
{
uint32 ratingType; // 3
} highest_rating;
+
// ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE = 109
struct
{
uint32 lootType; // 3 3=fishing, 2=pickpocket, 4=disentchant
uint32 lootTypeCount; // 4
} loot_type;
+
// ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE = 112
struct
{
uint32 skillLine; // 3
uint32 spellCount; // 4
} learn_skill_line;
+
// ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL = 113
struct
{
uint32 unused; // 3
uint32 killCount; // 4
} honorable_kill;
+
struct
{
uint32 field3; // 3 main requirement
@@ -435,6 +499,7 @@ struct AchievementCriteriaEntry
uint32 timeLimit; // 29 time limit in seconds
//uint32 showOrder; // 30 show order
};
+
struct AreaTableEntry
{
uint32 ID; // 0
@@ -447,6 +512,7 @@ struct AreaTableEntry
char* area_name[16]; // 11-26
// 27, string flags, unused
uint32 team; // 28
+
// helpers
bool IsSanctuary() const
{
@@ -455,12 +521,14 @@ struct AreaTableEntry
return (flags & AREA_FLAG_SANCTUARY);
}
};
+
struct AreaGroupEntry
{
uint32 AreaGroupId; // 0
uint32 AreaId[6]; // 1-6
uint32 nextGroup; // 7 index of next group
};
+
struct AreaPOIEntry
{
uint32 id; //0
@@ -478,6 +546,7 @@ struct AreaPOIEntry
uint32 worldState; //52
//uint32 val2; //53
};
+
struct AreaTriggerEntry
{
uint32 id; // 0 m_ID
@@ -491,6 +560,7 @@ struct AreaTriggerEntry
float box_z; // 8 m_box_heigh
float box_orientation; // 9 m_box_yaw
};
+
struct AuctionHouseEntry
{
uint32 houseId; // 0 index
@@ -500,11 +570,13 @@ struct AuctionHouseEntry
//char* name[16]; // 4-19
// 20 string flag, unused
};
+
struct BankBagSlotPricesEntry
{
uint32 ID;
uint32 price;
};
+
struct BarberShopStyleEntry
{
uint32 Id; // 0
@@ -518,6 +590,7 @@ struct BarberShopStyleEntry
uint32 gender; // 38 0 -> male, 1 -> female
uint32 hair_id; // 39 real ID to hair/facial hair
};
+
struct BattlemasterListEntry
{
uint32 id; // 0
@@ -534,7 +607,9 @@ struct BattlemasterListEntry
// 33 unused
//uint32 unk; // 34 new 3.1
};
+
#define MAX_OUTFIT_ITEMS 24
+
struct CharStartOutfitEntry
{
//uint32 Id; // 0
@@ -546,6 +621,7 @@ struct CharStartOutfitEntry
//uint32 Unknown2; // 39
//uint32 Unknown3; // 40
};
+
struct CharTitlesEntry
{
uint32 ID; // 0, title ids, for example in Quest::GetCharTitleId()
@@ -556,6 +632,7 @@ struct CharTitlesEntry
// 35 string flag, unused
uint32 bit_index; // 36 used in PLAYER_CHOSEN_TITLE and 1<<index in PLAYER__FIELD_KNOWN_TITLES
};
+
struct ChatChannelsEntry
{
uint32 ChannelID; // 0
@@ -565,6 +642,7 @@ struct ChatChannelsEntry
//char* name[16]; // 20-35 unused
// 36 string flag, unused
};
+
struct ChrClassesEntry
{
uint32 ClassID; // 0
@@ -583,6 +661,7 @@ struct ChrClassesEntry
uint32 CinematicSequence; // 58 id from CinematicSequences.dbc
uint32 addon; // 59 (0 - original race, 1 - tbc addon, ...)
};
+
struct ChrRacesEntry
{
uint32 RaceID; // 0
@@ -604,6 +683,7 @@ struct ChrRacesEntry
// 65-67 unused
uint32 addon; // 68 (0 - original race, 1 - tbc addon, ...)
};
+
/* not used
struct CinematicCameraEntry
{
@@ -616,6 +696,7 @@ struct CinematicCameraEntry
float unk6; // 6 speed?
};
*/
+
struct CinematicSequencesEntry
{
uint32 Id; // 0 index
@@ -623,6 +704,7 @@ struct CinematicSequencesEntry
//uint32 cinematicCamera; // 2 id in CinematicCamera.dbc
// 3-9 always 0
};
+
struct CreatureDisplayInfoEntry
{
uint32 Displayid; // 0 m_ID
@@ -640,6 +722,7 @@ struct CreatureDisplayInfoEntry
// 14 m_creatureGeosetData
// 15 m_objectEffectPackageID
};
+
struct CreatureFamilyEntry
{
uint32 ID; // 0 m_ID
@@ -655,13 +738,16 @@ struct CreatureFamilyEntry
// 26 string flags
// 27 m_iconFile
};
+
#define MAX_CREATURE_SPELL_DATA_SLOT 4
+
struct CreatureSpellDataEntry
{
uint32 ID; // 0 m_ID
uint32 spellId[MAX_CREATURE_SPELL_DATA_SLOT]; // 1-4 m_spells[4]
//uint32 availability[MAX_CREATURE_SPELL_DATA_SLOT]; // 4-7 m_availability[4]
};
+
struct CreatureTypeEntry
{
uint32 ID; // 0 m_ID
@@ -669,6 +755,7 @@ struct CreatureTypeEntry
// 17 string flags
//uint32 no_expirience; // 18 no exp? critters, non-combat pets, gas cloud.
};
+
/* not used
struct CurrencyCategoryEntry
{
@@ -678,6 +765,7 @@ struct CurrencyCategoryEntry
// // 18 string flags
};
*/
+
struct CurrencyTypesEntry
{
//uint32 ID; // 0 not used
@@ -685,16 +773,19 @@ struct CurrencyTypesEntry
//uint32 Category; // 2 may be category
uint32 BitIndex; // 3 bit index in PLAYER_FIELD_KNOWN_CURRENCIES (1 << (index-1))
};
+
struct DurabilityCostsEntry
{
uint32 Itemlvl; // 0
uint32 multiplier[29]; // 1-29
};
+
struct DurabilityQualityEntry
{
uint32 Id; // 0
float quality_mod; // 1
};
+
struct EmotesEntry
{
uint32 Id; // 0
@@ -705,11 +796,13 @@ struct EmotesEntry
uint32 UnitStandState; // 5, uncomfirmed, may be enum UnitStandStateType
//uint32 SoundId; // 6, ref to soundEntries
};
+
struct EmotesTextEntry
{
uint32 Id;
uint32 textid;
};
+
struct FactionEntry
{
uint32 ID; // 0 m_ID
@@ -724,6 +817,7 @@ struct FactionEntry
//char* description[16]; // 36-51 m_description_lang
// 52 string flags
};
+
struct FactionTemplateEntry
{
uint32 ID; // 0 m_ID
@@ -735,6 +829,7 @@ struct FactionTemplateEntry
uint32 enemyFaction[4]; // 6 m_enemies[4]
uint32 friendFaction[4]; // 10 m_friend[4]
//------------------------------------------------------- end structure
+
// helpers
bool IsFriendlyTo(FactionTemplateEntry const& entry) const
{
@@ -776,6 +871,7 @@ struct FactionTemplateEntry
}
bool IsContestedGuardFaction() const { return (factionFlags & FACTION_TEMPLATE_FLAG_CONTESTED_GUARD)!=0; }
};
+
struct GameObjectDisplayInfoEntry
{
uint32 Displayid; // 0 m_ID
@@ -789,12 +885,14 @@ struct GameObjectDisplayInfoEntry
float maxZ;
//uint32 transport; //18
};
+
struct GemPropertiesEntry
{
uint32 ID;
uint32 spellitemenchantement;
uint32 color;
};
+
struct GlyphPropertiesEntry
{
uint32 Id;
@@ -802,54 +900,67 @@ struct GlyphPropertiesEntry
uint32 TypeFlags;
uint32 Unk1; // GlyphIconId (SpellIcon.dbc)
};
+
struct GlyphSlotEntry
{
uint32 Id;
uint32 TypeFlags;
uint32 Order;
};
+
// All Gt* DBC store data for 100 levels, some by 100 per class/race
#define GT_MAX_LEVEL 100
+
struct GtBarberShopCostBaseEntry
{
float cost;
};
+
struct GtCombatRatingsEntry
{
float ratio;
};
+
struct GtChanceToMeleeCritBaseEntry
{
float base;
};
+
struct GtChanceToMeleeCritEntry
{
float ratio;
};
+
struct GtChanceToSpellCritBaseEntry
{
float base;
};
+
struct GtChanceToSpellCritEntry
{
float ratio;
};
+
struct GtOCTRegenHPEntry
{
float ratio;
};
+
//struct GtOCTRegenMPEntry
//{
// float ratio;
//};
+
struct GtRegenHPPerSptEntry
{
float ratio;
};
+
struct GtRegenMPPerSptEntry
{
float ratio;
};
+
/* no used
struct HolidayDescriptionsEntry
{
@@ -858,6 +969,7 @@ struct HolidayDescriptionsEntry
// 17 name flags
};
*/
+
/* no used
struct HolidayNamesEntry
{
@@ -866,6 +978,7 @@ struct HolidayNamesEntry
// 17 name flags
};
*/
+
struct HolidaysEntry
{
uint32 ID; // 0, holiday id
@@ -882,6 +995,7 @@ struct HolidaysEntry
//uint32 unk53; // 53
//uint32 unk54; // 54
};
+
struct ItemEntry
{
uint32 ID; // 0
@@ -893,12 +1007,14 @@ struct ItemEntry
uint32 InventoryType; // 6
uint32 Sheath; // 7
};
+
struct ItemBagFamilyEntry
{
uint32 ID; // 0
//char* name[16] // 1-16 m_name_lang
// // 17 name flags
};
+
struct ItemDisplayInfoEntry
{
uint32 ID; // 0 m_ID
@@ -914,6 +1030,7 @@ struct ItemDisplayInfoEntry
// 10 m_itemVisual[8]
// 11 m_particleColorID
};
+
//struct ItemCondExtCostsEntry
//{
// uint32 ID;
@@ -921,6 +1038,7 @@ struct ItemDisplayInfoEntry
// uint32 itemextendedcostentry; // ItemPrototype::ExtendedCost
// uint32 arenaseason; // arena season number(1-4)
//};
+
struct ItemExtendedCostEntry
{
uint32 ID; // 0 extended-cost entry id
@@ -930,6 +1048,7 @@ struct ItemExtendedCostEntry
uint32 reqitemcount[5]; // 8-12 required count of 1st item
uint32 reqpersonalarenarating; // 13 required personal arena rating
};
+
struct ItemLimitCategoryEntry
{
uint32 ID; // 0 Id
@@ -938,6 +1057,7 @@ struct ItemLimitCategoryEntry
uint32 maxCount; // 18, max allowed equipped as item or in gem slot
//uint32 unk; // 19, 1 for gems only...
};
+
struct ItemRandomPropertiesEntry
{
uint32 ID; // 0 m_ID
@@ -946,6 +1066,7 @@ struct ItemRandomPropertiesEntry
//char* nameSuffix[16] // 7-22 m_name_lang
// 23 name flags
};
+
struct ItemRandomSuffixEntry
{
uint32 ID; // 0 m_ID
@@ -955,6 +1076,7 @@ struct ItemRandomSuffixEntry
uint32 enchant_id[5]; // 19-21 m_enchantment
uint32 prefix[5]; // 22-24 m_allocationPct
};
+
struct ItemSetEntry
{
//uint32 id // 0 m_ID
@@ -966,7 +1088,9 @@ struct ItemSetEntry
uint32 required_skill_id; // 51 m_requiredSkill
uint32 required_skill_value; // 52 m_requiredSkillRank
};
+
#define MAX_LOCK_CASE 8
+
struct LockEntry
{
uint32 ID; // 0 m_ID
@@ -975,6 +1099,7 @@ struct LockEntry
uint32 Skill[MAX_LOCK_CASE]; // 17-24 m_Skill
//uint32 Action[MAX_LOCK_CASE]; // 25-32 m_Action
};
+
struct MailTemplateEntry
{
uint32 ID; // 0
@@ -982,6 +1107,7 @@ struct MailTemplateEntry
// 17 name flags, unused
//char* content[16]; // 18-33
};
+
struct MapEntry
{
uint32 MapID; // 0
@@ -1012,8 +1138,10 @@ struct MapEntry
// 115 -1, 0 and 720
uint32 addon; // 116 (0-original maps,1-tbc addon)
// 117 some kind of time?
+
// Helpers
uint32 Expansion() const { return addon; }
+
bool IsDungeon() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID; }
bool IsNonRaidDungeon() const { return map_type == MAP_INSTANCE; }
bool Instanceable() const { return map_type == MAP_INSTANCE || map_type == MAP_RAID || map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; }
@@ -1023,6 +1151,7 @@ struct MapEntry
bool IsBattleGroundOrArena() const { return map_type == MAP_BATTLEGROUND || map_type == MAP_ARENA; }
bool SupportsHeroicMode() const { return resetTimeHeroic != 0; }
bool HasResetTime() const { return resetTimeHeroic || resetTimeRaid; }
+
bool IsMountAllowed() const
{
return !IsDungeon() ||
@@ -1031,23 +1160,27 @@ struct MapEntry
MapID==568 || MapID==580 || MapID==615 || // ZulAman, Sunwell Plateau, Obsidian Sanctrum
MapID==616; // Eye Of Eternity
}
+
bool IsContinent() const
{
return MapID == 0 || MapID == 1 || MapID == 530 || MapID == 571;
}
};
+
struct MovieEntry
{
uint32 Id; // 0 index
//char* filename; // 1
//uint32 unk2; // 2 always 100
};
+
struct QuestSortEntry
{
uint32 id; // 0 m_ID
//char* name[16]; // 1-16 m_SortName_lang
// 17 name flags
};
+
struct RandomPropertiesPointsEntry
{
//uint32 Id; // 0 hidden key
@@ -1056,6 +1189,7 @@ struct RandomPropertiesPointsEntry
uint32 RarePropertiesPoints[5]; // 7-11
uint32 UncommonPropertiesPoints[5]; // 12-16
};
+
struct ScalingStatDistributionEntry
{
uint32 Id;
@@ -1063,6 +1197,7 @@ struct ScalingStatDistributionEntry
uint32 Modifier[10];
uint32 MaxLevel;
};
+
struct ScalingStatValuesEntry
{
uint32 Id;
@@ -1072,6 +1207,7 @@ struct ScalingStatValuesEntry
uint32 dpsMod[6]; // DPS mod for level
uint32 spellBonus; // not sure.. TODO: need more info about
uint32 feralBonus; // Feral AP bonus
+
uint32 getssdMultiplier(uint32 mask) const
{
if (mask&0x001F)
@@ -1119,12 +1255,14 @@ struct ScalingStatValuesEntry
return 0;
}
};
+
//struct SkillLineCategoryEntry{
// uint32 id; // 0 m_ID
// char* name[16]; // 1-17 m_name_lang
// // 18 string flag
// uint32 displayOrder; // 19 m_sortIndex
//};
+
//struct SkillRaceClassInfoEntry{
// uint32 id; // 0 m_ID
// uint32 skillId; // 1 m_skillID
@@ -1135,11 +1273,13 @@ struct ScalingStatValuesEntry
// uint32 skillTierId; // 6 m_skillTierID
// uint32 skillCostID; // 7 m_skillCostIndex
//};
+
//struct SkillTiersEntry{
// uint32 id; // 0 m_ID
// uint32 skillValue[16]; // 1-17 m_cost
// uint32 maxSkillValue[16]; // 18-32 m_valueMax
//};
+
struct SkillLineEntry
{
uint32 id; // 0 m_ID
@@ -1154,6 +1294,7 @@ struct SkillLineEntry
// 54 string flags
uint32 canLink; // 55 m_canLink (prof. with recipes
};
+
struct SkillLineAbilityEntry
{
uint32 id; // 0 m_ID
@@ -1170,6 +1311,7 @@ struct SkillLineAbilityEntry
uint32 min_value; // 11 m_trivialSkillLineRankLow
//uint32 characterPoints[2]; // 12-13 m_characterPoints[2]
};
+
struct SoundEntriesEntry
{
uint32 Id; // 0 m_ID
@@ -1185,7 +1327,9 @@ struct SoundEntriesEntry
// 28 m_EAXDef
// 29 new in 3.1
};
+
#define MAX_SPELL_EFFECTS 3
+
struct SpellEntry
{
uint32 Id; // 0 m_ID
@@ -1293,16 +1437,20 @@ struct SpellEntry
uint32 runeCostID; // 229 m_runeCostID
//uint32 spellMissileID; // 230 m_spellMissileID not used
//uint32 PowerDisplayId; // 231 PowerDisplay.dbc, new in 3.1
+
// helpers
int32 CalculateSimpleValue(uint8 eff) const { return EffectBasePoints[eff]+int32(EffectBaseDice[eff]); }
+
private:
// prevent creating custom entries (copy data from original in fact)
SpellEntry(SpellEntry const&); // DON'T must have implementation
};
+
typedef std::set<uint32> SpellCategorySet;
typedef std::map<uint32,SpellCategorySet > SpellCategoryStore;
typedef std::set<uint32> PetFamilySpellsSet;
typedef std::map<uint32,PetFamilySpellsSet > PetFamilySpellsStore;
+
struct SpellCastTimesEntry
{
uint32 ID; // 0
@@ -1310,12 +1458,14 @@ struct SpellCastTimesEntry
//float CastTimePerLevel; // 2 unsure / per skill?
//int32 MinCastTime; // 3 unsure
};
+
struct SpellFocusObjectEntry
{
uint32 ID; // 0
//char* Name[16]; // 1-15 unused
// 16 string flags, unused
};
+
struct SpellRadiusEntry
{
uint32 ID;
@@ -1323,6 +1473,7 @@ struct SpellRadiusEntry
//uint32 Unk //always 0
float radiusFriend;
};
+
struct SpellRangeEntry
{
uint32 ID;
@@ -1336,15 +1487,19 @@ struct SpellRangeEntry
//char* Name2[16]; // 25-40 unused
// 41 string flags, unused
};
+
struct SpellRuneCostEntry
{
uint32 ID; // 0
uint32 RuneCost[3]; // 1-3 (0=blood, 1=frost, 2=unholy)
uint32 runePowerGain; // 4
+
bool NoRuneCost() const { return RuneCost[0] == 0 && RuneCost[1] == 0 && RuneCost[2] == 0; }
bool NoRunicPowerGain() const { return runePowerGain == 0; }
};
+
#define MAX_SHAPESHIFT_SPELLS 8
+
struct SpellShapeshiftEntry
{
uint32 ID; // 0
@@ -1361,11 +1516,13 @@ struct SpellShapeshiftEntry
//uint32 unk4; // 26 unused
uint32 stanceSpell[MAX_SHAPESHIFT_SPELLS]; // 27 - 34 unused
};
+
struct SpellDurationEntry
{
uint32 ID;
int32 Duration[3];
};
+
struct SpellItemEnchantmentEntry
{
uint32 ID; // 0 m_ID
@@ -1384,6 +1541,7 @@ struct SpellItemEnchantmentEntry
//uint32 requiredSkillValue; // 36 m_requiredSkillRank
// 37 new in 3.1
};
+
struct SpellItemEnchantmentConditionEntry
{
uint32 ID; // 0 m_ID
@@ -1394,11 +1552,13 @@ struct SpellItemEnchantmentConditionEntry
uint32 Value[5]; // 21-25 m_rt_operand[5]
//uint8 Logic[5] // 25-30 m_logic[5]
};
+
struct StableSlotPricesEntry
{
uint32 Slot;
uint32 Price;
};
+
struct SummonPropertiesEntry
{
uint32 Id; // 0
@@ -1408,8 +1568,10 @@ struct SummonPropertiesEntry
uint32 Slot; // 4, 0-6
uint32 Flags; // 5
};
+
#define MAX_TALENT_RANK 5
#define MAX_PET_TALENT_RANK 3 // use in calculations, expected <= MAX_TALENT_RANK
+
struct TalentEntry
{
uint32 TalentID; // 0
@@ -1426,6 +1588,7 @@ struct TalentEntry
//uint32 unk2; // 20, all 0
//uint64 allowForPet; // 21 its a 64 bit mask for pet 1<<m_categoryEnumID in CreatureFamily.dbc
};
+
struct TalentTabEntry
{
uint32 TalentTabID; // 0
@@ -1438,6 +1601,7 @@ struct TalentTabEntry
uint32 tabpage; // 22
//char* internalname; // 23
};
+
struct TaxiNodesEntry
{
uint32 ID; // 0 m_ID
@@ -1449,6 +1613,7 @@ struct TaxiNodesEntry
// 22 string flags
uint32 MountCreatureID[2]; // 23-24 m_MountCreatureID[2]
};
+
struct TaxiPathEntry
{
uint32 ID; // 0 m_ID
@@ -1456,6 +1621,7 @@ struct TaxiPathEntry
uint32 to; // 2 m_ToTaxiNode
uint32 price; // 3 m_Cost
};
+
struct TaxiPathNodeEntry
{
// 0 m_ID
@@ -1470,6 +1636,7 @@ struct TaxiPathNodeEntry
// 9 m_arrivalEventID
// 10 m_departureEventID
};
+
struct TotemCategoryEntry
{
uint32 ID; // 0
@@ -1478,6 +1645,7 @@ struct TotemCategoryEntry
uint32 categoryType; // 18 (one for specialization)
uint32 categoryMask; // 19 (compatibility mask for same type: different for totems, compatible from high to low for rods)
};
+
struct VehicleEntry
{
uint32 m_ID; // 0
@@ -1514,6 +1682,7 @@ struct VehicleEntry
// 38, new in 3.1
// 39, new in 3.1
};
+
struct VehicleSeatEntry
{
uint32 m_ID; // 0
@@ -1563,8 +1732,10 @@ struct VehicleSeatEntry
int32 m_uiSkin; // 44
uint32 m_flagsB; // 45
// 46-57 added in 3.1, floats mostly
+
bool IsUsable() const { return m_flags & 0x2000000; }
};
+
struct WorldMapAreaEntry
{
//uint32 ID; // 0
@@ -1578,7 +1749,9 @@ struct WorldMapAreaEntry
int32 virtual_map_id; // 8 -1 (map_id have correct map) other: virtual map where zone show (map_id - where zone in fact internally)
// int32 dungeonMap_id; // 9 pointer to DungeonMap.dbc (owerride x1,x2,y1,y2 coordinates)
};
+
#define MAX_WORLD_MAP_OVERLAY_AREA_IDX 4
+
struct WorldMapOverlayEntry
{
uint32 ID; // 0
@@ -1588,6 +1761,7 @@ struct WorldMapOverlayEntry
//char* internal_name // 8
// 9-16 some ints
};
+
struct WorldSafeLocsEntry
{
uint32 ID; // 0
@@ -1598,34 +1772,42 @@ struct WorldSafeLocsEntry
//char* name[16] // 5-20 name, unused
// 21 name flags, unused
};
+
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some platform
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
+
// Structures not used for casting to loaded DBC data and not required then packing
struct TalentSpellPos
{
TalentSpellPos() : talent_id(0), rank(0) {}
TalentSpellPos(uint16 _talent_id, uint8 _rank) : talent_id(_talent_id), rank(_rank) {}
+
uint16 talent_id;
uint8 rank;
};
+
typedef std::map<uint32,TalentSpellPos> TalentSpellPosMap;
+
struct TaxiPathBySourceAndDestination
{
TaxiPathBySourceAndDestination() : ID(0),price(0) {}
TaxiPathBySourceAndDestination(uint32 _id,uint32 _price) : ID(_id),price(_price) {}
+
uint32 ID;
uint32 price;
};
typedef std::map<uint32,TaxiPathBySourceAndDestination> TaxiPathSetForSource;
typedef std::map<uint32,TaxiPathSetForSource> TaxiPathSetBySource;
+
struct TaxiPathNode
{
TaxiPathNode() : mapid(0), x(0),y(0),z(0),actionFlag(0),delay(0) {}
TaxiPathNode(uint32 _mapid, float _x, float _y, float _z, uint32 _actionFlag, uint32 _delay) : mapid(_mapid), x(_x),y(_y),z(_z),actionFlag(_actionFlag),delay(_delay) {}
+
uint32 mapid;
float x;
float y;
@@ -1635,6 +1817,7 @@ struct TaxiPathNode
};
typedef std::vector<TaxiPathNode> TaxiPathNodeList;
typedef std::vector<TaxiPathNodeList> TaxiPathNodesByPath;
+
#define TaxiMaskSize 12
typedef uint32 TaxiMask[TaxiMaskSize];
#endif
diff --git a/src/game/DBCfmt.h b/src/game/DBCfmt.h
index 1152a3482c5..7487ac116a2 100644
--- a/src/game/DBCfmt.h
+++ b/src/game/DBCfmt.h
@@ -17,8 +17,10 @@
* 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_DBCSFRM_H
#define MANGOS_DBCSFRM_H
+
const char Achievementfmt[]="niixssssssssssssssssxxxxxxxxxxxxxxxxxxiixixxxxxxxxxxxxxxxxxxii";
const char AchievementCriteriafmt[]="niiiiiiiixxxxxxxxxxxxxxxxxiixix";
const char AreaTableEntryfmt[]="iiinixxxxxissssssssssssssssxixxxxxxx";
@@ -107,4 +109,5 @@ const char VehicleSeatEntryfmt[]="niiffffffffffiiiiiifffffffiiifffiiiiiiiffiiiii
const char WorldMapAreaEntryfmt[]="xinxffffix";
const char WorldMapOverlayEntryfmt[]="nxiiiixxxxxxxxxxx";
const char WorldSafeLocsEntryfmt[]="nifffxxxxxxxxxxxxxxxxx";
+
#endif
diff --git a/src/game/Debugcmds.cpp b/src/game/Debugcmds.cpp
index a74750ebce9..97f90e33af4 100644
--- a/src/game/Debugcmds.cpp
+++ b/src/game/Debugcmds.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -37,20 +38,26 @@
#include "GridNotifiersImpl.h"
#include "SpellMgr.h"
#include "ScriptCalls.h"
+
bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args)
{
if (!*args)
return false;
+
char* px = strtok((char*)args, " ");
if (!px)
return false;
+
uint8 failnum = (uint8)atoi(px);
if (failnum==0 && *px!='0')
return false;
+
char* p1 = strtok(NULL, " ");
uint8 failarg1 = p1 ? (uint8)atoi(p1) : 0;
+
char* p2 = strtok(NULL, " ");
uint8 failarg2 = p2 ? (uint8)atoi(p2) : 0;
+
WorldPacket data(SMSG_CAST_FAILED, 5);
data << uint8(0);
data << uint32(133);
@@ -59,13 +66,17 @@ bool ChatHandler::HandleDebugSendSpellFailCommand(const char* args)
data << uint32(failarg1);
if (p2)
data << uint32(failarg2);
+
m_session->SendPacket(&data);
+
return true;
}
+
bool ChatHandler::HandleDebugSendPoiCommand(const char* args)
{
if (!*args)
return false;
+
Player *pPlayer = m_session->GetPlayer();
Unit* target = getSelectedUnit();
if (!target)
@@ -73,40 +84,50 @@ bool ChatHandler::HandleDebugSendPoiCommand(const char* args)
SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
return true;
}
+
char* icon_text = strtok((char*)args, " ");
char* flags_text = strtok(NULL, " ");
if (!icon_text || !flags_text)
return false;
+
uint32 icon = atol(icon_text);
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::HandleDebugSendEquipErrorCommand(const char* args)
{
if (!*args)
return false;
+
uint8 msg = atoi(args);
m_session->GetPlayer()->SendEquipError(msg, 0, 0);
return true;
}
+
bool ChatHandler::HandleDebugSendSellErrorCommand(const char* args)
{
if (!*args)
return false;
+
uint8 msg = atoi(args);
m_session->GetPlayer()->SendSellError(msg, 0, 0, 0);
return true;
}
+
bool ChatHandler::HandleDebugSendBuyErrorCommand(const char* args)
{
if (!*args)
return false;
+
uint8 msg = atoi(args);
m_session->GetPlayer()->SendBuyError(msg, 0, 0, 0);
return true;
}
+
bool ChatHandler::HandleDebugSendOpcodeCommand(const char* /*args*/)
{
Unit *unit = getSelectedUnit();
@@ -116,18 +137,24 @@ bool ChatHandler::HandleDebugSendOpcodeCommand(const char* /*args*/)
else
player = (Player*)unit;
if(!unit) unit = player;
+
std::ifstream ifs("opcode.txt");
if (ifs.bad())
return false;
+
uint32 opcode;
ifs >> opcode;
+
WorldPacket data(opcode, 0);
+
while(!ifs.eof())
{
std::string type;
ifs >> type;
+
if(type == "")
break;
+
if(type == "uint8")
{
uint16 val1;
@@ -229,17 +256,21 @@ bool ChatHandler::HandleDebugSendOpcodeCommand(const char* /*args*/)
PSendSysMessage(LANG_COMMAND_OPCODESENT, data.GetOpcode(), unit->GetName());
return true;
}
+
bool ChatHandler::HandleDebugUpdateWorldStateCommand(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::HandleDebugPlayCinematicCommand(const char* args)
{
// USAGE: .debug play cinematic #cinematicid
@@ -250,16 +281,20 @@ bool ChatHandler::HandleDebugPlayCinematicCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 dwId = atoi((char*)args);
+
if (!sCinematicSequencesStore.LookupEntry(dwId))
{
PSendSysMessage(LANG_CINEMATIC_NOT_EXIST, dwId);
SetSentErrorMessage(true);
return false;
}
+
m_session->GetPlayer()->SendCinematicStart(dwId);
return true;
}
+
bool ChatHandler::HandleDebugPlayMovieCommand(const char* args)
{
// USAGE: .debug play movie #movieid
@@ -270,16 +305,20 @@ bool ChatHandler::HandleDebugPlayMovieCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 dwId = atoi((char*)args);
+
if (!sMovieStore.LookupEntry(dwId))
{
PSendSysMessage(LANG_MOVIE_NOT_EXIST, dwId);
SetSentErrorMessage(true);
return false;
}
+
m_session->GetPlayer()->SendMovieStart(dwId);
return true;
}
+
//Play sound
bool ChatHandler::HandleDebugPlaySoundCommand(const char* args)
{
@@ -291,13 +330,16 @@ bool ChatHandler::HandleDebugPlaySoundCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 dwSoundId = atoi((char*)args);
+
if (!sSoundEntriesStore.LookupEntry(dwSoundId))
{
PSendSysMessage(LANG_SOUND_NOT_EXIST, dwSoundId);
SetSentErrorMessage(true);
return false;
}
+
Unit* unit = getSelectedUnit();
if (!unit)
{
@@ -305,20 +347,25 @@ bool ChatHandler::HandleDebugPlaySoundCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if (m_session->GetPlayer()->GetSelection())
unit->PlayDistanceSound(dwSoundId,m_session->GetPlayer());
else
unit->PlayDirectSound(dwSoundId,m_session->GetPlayer());
+
PSendSysMessage(LANG_YOU_HEAR_SOUND, dwSoundId);
return true;
}
+
//Send notification in channel
bool ChatHandler::HandleDebugSendChannelNotifyCommand(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
@@ -327,11 +374,13 @@ bool ChatHandler::HandleDebugSendChannelNotifyCommand(const char* args)
m_session->SendPacket(&data);
return true;
}
+
//Send notification in chat
bool ChatHandler::HandleDebugSendChatMsgCommand(const char* args)
{
if (!*args)
return false;
+
const char *msg = "testtest";
uint8 type = atoi(args);
WorldPacket data;
@@ -339,31 +388,38 @@ bool ChatHandler::HandleDebugSendChatMsgCommand(const char* args)
m_session->SendPacket(&data);
return true;
}
+
bool ChatHandler::HandleDebugSendQuestPartyMsgCommand(const char* args)
{
uint32 msg = atol((char*)args);
m_session->GetPlayer()->SendPushToPartyResponse(m_session->GetPlayer(), msg);
return true;
}
+
bool ChatHandler::HandleDebugGetLootRecipientCommand(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::HandleDebugSendQuestInvalidMsgCommand(const char* args)
{
uint32 msg = atol((char*)args);
m_session->GetPlayer()->SendCanTakeQuestResponse(msg);
return true;
}
+
bool ChatHandler::HandleDebugGetItemStateCommand(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;
@@ -373,8 +429,10 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
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: ";
@@ -383,6 +441,7 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
{
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())
@@ -402,6 +461,7 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
}
}
}
+
if (list_queue)
{
std::vector<Item *> &updateQueue = player->GetItemUpdateQueue();
@@ -409,8 +469,10 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
{
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())
{
@@ -419,11 +481,13 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
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;
@@ -432,23 +496,28 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
{
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();
@@ -457,11 +526,13 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
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());
@@ -473,6 +544,7 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
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;
@@ -480,27 +552,32 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
{
Item* item2 = bag->GetItemByPos(j);
if (!item2) continue;
+
if (item2->GetSlot() != j)
{
PSendSysMessage("the item in bag %d slot %d, guid %d has an incorrect slot value: %d", bag->GetSlot(), j, item2->GetGUIDLow(), item2->GetSlot());
error = true; continue;
}
+
if (item2->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(), item2->GetSlot(), item2->GetGUIDLow(), GUID_LOPART(item2->GetOwnerGUID()), player->GetGUIDLow());
error = true; continue;
}
+
Bag *container = item2->GetContainer();
if (!container)
{
PSendSysMessage("the item in bag %d at slot %d with guid %d has no container!", bag->GetSlot(), item2->GetSlot(), item2->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(), item2->GetSlot(), item2->GetGUIDLow(), container->GetSlot(), container->GetGUIDLow());
error = true; continue;
}
+
if (item2->IsInUpdateQueue())
{
uint16 qp = item2->GetQueuePos();
@@ -509,11 +586,13 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
PSendSysMessage("item in bag: %d at slot: %d guid: %d has a queuepos (%d) larger than the update queue size! ", bag->GetSlot(), item2->GetSlot(), item2->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(), item2->GetSlot(), item2->GetGUIDLow(), qp);
error = true; continue;
}
+
if (updateQueue[qp] != item2)
{
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(), item2->GetSlot(), item2->GetGUIDLow(), qp, updateQueue[qp]->GetBagSlot(), updateQueue[qp]->GetSlot(), updateQueue[qp]->GetGUIDLow());
@@ -528,27 +607,33 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
}
}
}
+
for(size_t i = 0; i < updateQueue.size(); ++i)
{
Item *item = updateQueue[i];
if(!item) continue;
+
if (item->GetOwnerGUID() != player->GetGUID())
{
PSendSysMessage("queue(" SIZEFMTD "): 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(" SIZEFMTD "): 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(" SIZEFMTD "): 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(" SIZEFMTD "): 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());
@@ -558,23 +643,28 @@ bool ChatHandler::HandleDebugGetItemStateCommand(const char* args)
if (!error)
SendSysMessage("All OK!");
}
+
return true;
}
+
bool ChatHandler::HandleDebugBattlegroundCommand(const char * /*args*/)
{
sBattleGroundMgr.ToggleTesting();
return true;
}
+
bool ChatHandler::HandleDebugArenaCommand(const char * /*args*/)
{
sBattleGroundMgr.ToggleArenaTesting();
return true;
}
+
bool ChatHandler::HandleDebugThreatList(const char * /*args*/)
{
Creature* target = getSelectedCreature();
if(!target || target->isTotem() || target->isPet())
return false;
+
std::list<HostilReference*>& tlist = target->getThreatManager().getThreatList();
std::list<HostilReference*>::iterator itr;
uint32 cnt = 0;
@@ -590,6 +680,7 @@ bool ChatHandler::HandleDebugThreatList(const char * /*args*/)
SendSysMessage("End of threat list.");
return true;
}
+
bool ChatHandler::HandleDebugHostilRefList(const char * /*args*/)
{
Unit* target = getSelectedUnit();
@@ -610,35 +701,45 @@ bool ChatHandler::HandleDebugHostilRefList(const char * /*args*/)
SendSysMessage("End of hostil reference list.");
return true;
}
+
bool ChatHandler::HandleDebugSetVehicleId(const char *args)
{
Unit* target = getSelectedUnit();
if(!target || target->IsVehicle())
return false;
+
if(!args)
return false;
+
char* i = strtok((char*)args, " ");
if(!i)
return false;
+
uint32 id = (uint32)atoi(i);
//target->SetVehicleId(id);
target->SendUpdateObjectToAllExcept(NULL);
PSendSysMessage("Vehicle id set to %u", id);
return true;
}
+
bool ChatHandler::HandleDebugEnterVehicle(const char * args)
{
Unit* target = getSelectedUnit();
if(!target || !target->IsVehicle())
return false;
+
if(!args)
return false;
+
char* i = strtok((char*)args, " ");
if(!i)
return false;
+
char* j = strtok(NULL, " ");
+
uint32 entry = (uint32)atoi(i);
int8 seatId = j ? (int8)atoi(j) : -1;
+
if(!entry)
m_session->GetPlayer()->EnterVehicle(target, seatId);
else
@@ -651,39 +752,57 @@ bool ChatHandler::HandleDebugEnterVehicle(const char * args)
return false;
passenger->EnterVehicle(target, seatId);
}
+
PSendSysMessage("Unit %u entered vehicle %d", entry, (int32)seatId);
return true;
}
+
bool ChatHandler::HandleDebugSpawnVehicle(const char* args)
{
if (!*args)
return false;
+
char* e = strtok((char*)args, " ");
char* i = strtok(NULL, " ");
+
if (!e)
return false;
+
uint32 entry = (uint32)atoi(e);
+
float x, y, z, o = m_session->GetPlayer()->GetOrientation();
m_session->GetPlayer()->GetClosePoint(x, y, z, m_session->GetPlayer()->GetObjectSize());
+
if(!i)
return m_session->GetPlayer()->SummonCreature(entry, x, y, z, o);
+
uint32 id = (uint32)atoi(i);
+
CreatureInfo const *ci = objmgr.GetCreatureTemplate(entry);
+
if (!ci)
return false;
+
VehicleEntry const *ve = sVehicleStore.LookupEntry(id);
+
if (!ve)
return false;
+
Creature *v = new Creature;
+
Map *map = m_session->GetPlayer()->GetMap();
+
if(!v->Create(objmgr.GenerateLowGuid(HIGHGUID_VEHICLE), map, m_session->GetPlayer()->GetPhaseMask(), entry, id, m_session->GetPlayer()->GetTeam(), x, y, z, o))
{
delete v;
return false;
}
+
map->Add((Creature*)v);
+
return true;
}
+
bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/)
{
const char* stuffingString = "This is a dummy string to push the packet's size beyond 128000 bytes. ";
@@ -693,54 +812,74 @@ bool ChatHandler::HandleDebugSendLargePacketCommand(const char* /*args*/)
SendSysMessage(ss.str().c_str());
return true;
}
+
bool ChatHandler::HandleDebugSendSetPhaseShiftCommand(const char* args)
{
if (!*args)
return false;
+
uint32 PhaseShift = atoi(args);
m_session->SendSetPhaseShift(PhaseShift);
return true;
}
+
bool ChatHandler::HandleDebugSetItemFlagCommand(const char* args)
{
if (!*args)
return false;
+
char* e = strtok((char*)args, " ");
char* f = strtok(NULL, " ");
+
if (!e || !f)
return false;
+
uint32 guid = (uint32)atoi(e);
uint32 flag = (uint32)atoi(f);
+
Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM));
+
if (!i)
return false;
+
i->SetUInt32Value(ITEM_FIELD_FLAGS, flag);
+
return true;
}
+
bool ChatHandler::HandleDebugItemExpireCommand(const char* args)
{
if (!*args)
return false;
+
char* e = strtok((char*)args, " ");
if (!e)
return false;
+
uint32 guid = (uint32)atoi(e);
+
Item *i = m_session->GetPlayer()->GetItemByGuid(MAKE_NEW_GUID(guid, 0, HIGHGUID_ITEM));
+
if (!i)
return false;
+
m_session->GetPlayer()->DestroyItem( i->GetBagSlot(),i->GetSlot(), true);
Script->ItemExpire(m_session->GetPlayer(),i->GetProto());
+
return true;
}
+
//show animation
bool ChatHandler::HandleDebugAnimCommand(const char* args)
{
if (!*args)
return false;
+
uint32 anim_id = atoi((char*)args);
m_session->GetPlayer()->HandleEmoteCommand(anim_id);
return true;
}
+
bool ChatHandler::HandleDebugSetAuraStateCommand(const char* args)
{
if (!*args)
@@ -749,6 +888,7 @@ bool ChatHandler::HandleDebugSetAuraStateCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
Unit* unit = getSelectedUnit();
if (!unit)
{
@@ -756,6 +896,7 @@ bool ChatHandler::HandleDebugSetAuraStateCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
int32 state = atoi((char*)args);
if (!state)
{
@@ -764,18 +905,23 @@ bool ChatHandler::HandleDebugSetAuraStateCommand(const char* args)
unit->ModifyAuraState(AuraState(i),false);
return true;
}
+
unit->ModifyAuraState(AuraState(abs(state)),state > 0);
return true;
}
+
bool ChatHandler::HandleDebugSetValueCommand(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;
+
WorldObject* target = getSelectedObject();
if(!target)
{
@@ -783,7 +929,9 @@ bool ChatHandler::HandleDebugSetValueCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint64 guid = target->GetGUID();
+
uint32 Opcode = (uint32)atoi(px);
if(Opcode >= target->GetValuesCount())
{
@@ -809,16 +957,21 @@ bool ChatHandler::HandleDebugSetValueCommand(const char* args)
target->SetFloatValue( Opcode , fValue );
PSendSysMessage(LANG_SET_FLOAT_FIELD, GUID_LOPART(guid), Opcode,fValue);
}
+
return true;
}
+
bool ChatHandler::HandleDebugGetValueCommand(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)
{
@@ -826,7 +979,9 @@ bool ChatHandler::HandleDebugGetValueCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint64 guid = target->GetGUID();
+
uint32 Opcode = (uint32)atoi(px);
if(Opcode >= target->GetValuesCount())
{
@@ -838,6 +993,7 @@ bool ChatHandler::HandleDebugGetValueCommand(const char* args)
bool isint32 = true;
if(pz)
isint32 = (bool)atoi(pz);
+
if(isint32)
{
iValue = target->GetUInt32Value( Opcode );
@@ -850,37 +1006,52 @@ bool ChatHandler::HandleDebugGetValueCommand(const char* args)
sLog.outDebug(GetTrinityString(LANG_GET_FLOAT), GUID_LOPART(guid), Opcode, fValue);
PSendSysMessage(LANG_GET_FLOAT_FIELD, GUID_LOPART(guid), Opcode, fValue);
}
+
return true;
}
+
bool ChatHandler::HandleDebugMod32ValueCommand(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(GetTrinityString(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::HandleDebugUpdateCommand(const char* args)
{
if(!*args)
return false;
+
uint32 updateIndex;
uint32 value;
+
char* pUpdateIndex = strtok((char*)args, " ");
+
Unit* chr = getSelectedUnit();
if (chr == NULL)
{
@@ -888,6 +1059,7 @@ bool ChatHandler::HandleDebugUpdateCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(!pUpdateIndex)
{
return true;
@@ -902,15 +1074,21 @@ bool ChatHandler::HandleDebugUpdateCommand(const char* args)
{
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;
}
diff --git a/src/game/DestinationHolder.cpp b/src/game/DestinationHolder.cpp
index 997f250abca..f2158e3b5be 100644
--- a/src/game/DestinationHolder.cpp
+++ b/src/game/DestinationHolder.cpp
@@ -17,5 +17,6 @@
* 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
index f70538bd768..e41ced5980a 100644
--- a/src/game/DestinationHolder.h
+++ b/src/game/DestinationHolder.h
@@ -17,13 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_DESTINATION_HOLDER_H
#define TRINITY_DESTINATION_HOLDER_H
+
#include "Platform/Define.h"
#include "Timer.h"
+
class WorldObject;
class Map;
+
#define TRAVELLER_UPDATE_INTERVAL 300
+
template<typename TRAVELLER>
class TRINITY_DLL_DECL DestinationHolder
{
@@ -33,9 +38,11 @@ class TRINITY_DLL_DECL DestinationHolder
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(); }
@@ -50,8 +57,10 @@ class TRINITY_DLL_DECL DestinationHolder
void GetLocationNow(const Map * map, 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 GetDistance3dFromDestSq(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
index db14ae50f34..226a90d9279 100644
--- a/src/game/DestinationHolderImp.h
+++ b/src/game/DestinationHolderImp.h
@@ -17,11 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_DESTINATIONHOLDERIMP_H
#define TRINITY_DESTINATIONHOLDERIMP_H
+
#include "MapManager.h"
#include "DestinationHolder.h"
+
#include <cmath>
+
template<typename TRAVELLER>
void
DestinationHolder<TRAVELLER>::_findOffSetPoint(float x1, float y1, float x2, float y2, float offset, float &x, float &y)
@@ -57,6 +61,7 @@ DestinationHolder<TRAVELLER>::_findOffSetPoint(float x1, float y1, float x2, flo
}
}
}
+
template<typename TRAVELLER>
uint32
DestinationHolder<TRAVELLER>::SetDestination(TRAVELLER &traveller, float dest_x, float dest_y, float dest_z, bool sendMove)
@@ -65,34 +70,42 @@ DestinationHolder<TRAVELLER>::SetDestination(TRAVELLER &traveller, float dest_x,
i_destX = dest_x;
i_destY = dest_y;
i_destZ = dest_z;
+
return StartTravel(traveller, sendMove);
}
+
template<typename TRAVELLER>
uint32
DestinationHolder<TRAVELLER>::StartTravel(TRAVELLER &traveller, bool sendMove)
{
if(!i_destSet) return 0;
+
i_fromX = traveller.GetPositionX();
i_fromY = traveller.GetPositionY();
i_fromZ = traveller.GetPositionZ();
+
i_totalTravelTime = traveller.GetTotalTrevelTimeTo(i_destX,i_destY,i_destZ);
i_timeElapsed = 0;
if(sendMove)
traveller.MoveTo(i_destX, i_destY, i_destZ, i_totalTravelTime);
return i_totalTravelTime;
}
+
template<typename TRAVELLER>
bool
DestinationHolder<TRAVELLER>::UpdateTraveller(TRAVELLER &traveller, uint32 diff, bool micro_movement)
{
i_timeElapsed += diff;
+
// Update every TRAVELLER_UPDATE_INTERVAL
i_tracker.Update(diff);
if(!i_tracker.Passed())
return false;
else
ResetUpdate();
+
if(!i_destSet) return true;
+
float x, y, z;
if (!micro_movement)
GetLocationNowNoMicroMovement(x, y, z);
@@ -100,29 +113,36 @@ DestinationHolder<TRAVELLER>::UpdateTraveller(TRAVELLER &traveller, uint32 diff,
{
if (!traveller.GetTraveller().hasUnitState(UNIT_STAT_MOVING | UNIT_STAT_IN_FLIGHT))
return true;
+
if (traveller.GetTraveller().hasUnitState(UNIT_STAT_IN_FLIGHT))
GetLocationNow(traveller.GetTraveller().GetBaseMap() ,x, y, z, true); // Should reposition Object with right Coord, so I can bypass some Grid Relocation
else
GetLocationNow(traveller.GetTraveller().GetBaseMap(), x, y, z, false);
+
// 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;
}
+
if (traveller.GetTraveller().GetPositionX() != x || traveller.GetTraveller().GetPositionY() != y || traveller.GetTraveller().GetPositionZ() != z)
{
float ori = traveller.GetTraveller().GetAngle(x, y);
traveller.Relocation(x, y, z, ori);
}
+
return true;
}
+
template<typename TRAVELLER>
void
DestinationHolder<TRAVELLER>::GetLocationNow(const Map * map, float &x, float &y, float &z, bool is3D) const
@@ -157,6 +177,7 @@ DestinationHolder<TRAVELLER>::GetLocationNow(const Map * map, float &x, float &y
}
}
}
+
template<typename TRAVELLER>
float
DestinationHolder<TRAVELLER>::GetDistance3dFromDestSq(const WorldObject &obj) const
@@ -165,12 +186,14 @@ DestinationHolder<TRAVELLER>::GetDistance3dFromDestSq(const WorldObject &obj) co
obj.GetPosition(x,y,z);
return (i_destX-x)*(i_destX-x)+(i_destY-y)*(i_destY-y)+(i_destZ-z)*(i_destZ-z);
}
+
template<typename TRAVELLER>
float
DestinationHolder<TRAVELLER>::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<typename TRAVELLER>
void
DestinationHolder<TRAVELLER>::GetLocationNowNoMicroMovement(float &x, float &y, float &z) const
@@ -189,5 +212,6 @@ DestinationHolder<TRAVELLER>::GetLocationNowNoMicroMovement(float &x, float &y,
z = i_fromZ + ((i_destZ - i_fromZ) * percent_passed);
}
}
+
#endif
diff --git a/src/game/DuelHandler.cpp b/src/game/DuelHandler.cpp
index 64ec8f6352b..c35b9dbdc07 100644
--- a/src/game/DuelHandler.cpp
+++ b/src/game/DuelHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,49 +25,63 @@
#include "Opcodes.h"
#include "UpdateData.h"
#include "Player.h"
+
void WorldSession::HandleDuelAcceptedOpcode(WorldPacket& recvPacket)
{
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)
{
//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(), SPELL_ID_DUEL_BEG, 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
index fcc3922a987..30147732b08 100644
--- a/src/game/DynamicObject.cpp
+++ b/src/game/DynamicObject.cpp
@@ -17,6 +17,7 @@
* 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 "UpdateMask.h"
#include "Opcodes.h"
@@ -26,13 +27,17 @@
#include "GridNotifiers.h"
#include "CellImpl.h"
#include "GridNotifiersImpl.h"
+
DynamicObject::DynamicObject() : WorldObject()
{
m_objectType |= TYPEMASK_DYNAMICOBJECT;
m_objectTypeId = TYPEID_DYNAMICOBJECT;
+
m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION);
+
m_valuesCount = DYNAMICOBJECT_END;
}
+
void DynamicObject::AddToWorld()
{
///- Register the dynamicObject for guid lookup
@@ -42,6 +47,7 @@ void DynamicObject::AddToWorld()
WorldObject::AddToWorld();
}
}
+
void DynamicObject::RemoveFromWorld()
{
///- Remove the dynamicObject from the accessor
@@ -63,6 +69,7 @@ void DynamicObject::RemoveFromWorld()
ObjectAccessor::Instance().RemoveObject(this);
}
}
+
bool DynamicObject::Create(uint32 guidlow, Unit *caster, uint32 spellId, uint32 effMask, const Position &pos, int32 duration, float radius, bool active)
{
SetMap(caster->GetMap());
@@ -72,7 +79,9 @@ bool DynamicObject::Create(uint32 guidlow, Unit *caster, uint32 spellId, uint32
sLog.outError("DynamicObject (spell %u eff %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",spellId,effMask,GetPositionX(),GetPositionY());
return false;
}
+
WorldObject::_Create(guidlow, HIGHGUID_DYNAMICOBJECT, caster->GetPhaseMask());
+
SetEntry(spellId);
SetFloatValue( OBJECT_FIELD_SCALE_X, 1 );
SetUInt64Value( DYNAMICOBJECT_CASTER, caster->GetGUID() );
@@ -83,19 +92,23 @@ bool DynamicObject::Create(uint32 guidlow, Unit *caster, uint32 spellId, uint32
SetFloatValue( DYNAMICOBJECT_POS_Y, pos.m_positionY );
SetFloatValue( DYNAMICOBJECT_POS_Z, pos.m_positionZ );
SetUInt32Value( DYNAMICOBJECT_CASTTIME, getMSTime() ); // new 2.4.0
+
m_aliveDuration = duration;
m_radius = radius;
m_effMask = effMask;
m_spellId = spellId;
m_updateTimer = 0;
+
m_isWorldObject = active;
return true;
}
+
Unit* DynamicObject::GetCaster() const
{
// can be not found in some cases
return ObjectAccessor::GetUnit(*this, GetCasterGUID());
}
+
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
@@ -105,11 +118,14 @@ void DynamicObject::Update(uint32 p_time)
Delete();
return;
}
+
bool deleteThis = false;
+
if(m_aliveDuration > int32(p_time))
m_aliveDuration -= p_time;
else
deleteThis = true;
+
/*
// have radius and work as persistent effect
if(m_radius)
@@ -119,14 +135,18 @@ void DynamicObject::Update(uint32 p_time)
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::DynamicObjectUpdater notifier(*this, caster);
+
TypeContainerVisitor<MaNGOS::DynamicObjectUpdater, WorldTypeMapContainer > world_object_notifier(notifier);
TypeContainerVisitor<MaNGOS::DynamicObjectUpdater, GridTypeMapContainer > grid_object_notifier(notifier);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, world_object_notifier, *GetMap(), *this, m_radius);
cell_lock->Visit(cell_lock, grid_object_notifier, *GetMap(), *this, m_radius);
}
*/
+
if (m_effMask)
{
if (m_updateTimer < p_time)
@@ -136,18 +156,21 @@ void DynamicObject::Update(uint32 p_time)
m_updateTimer = 500; // is this official-like?
} else m_updateTimer -= p_time;
}
+
if (deleteThis)
{
caster->RemoveDynObjectWithGUID(GetGUID());
Delete();
}
}
+
void DynamicObject::Delete()
{
SendObjectDeSpawnAnim(GetGUID());
RemoveFromWorld();
AddObjectToRemoveList();
}
+
void DynamicObject::Delay(int32 delaytime)
{
m_aliveDuration -= delaytime;
@@ -155,6 +178,7 @@ void DynamicObject::Delay(int32 delaytime)
if (*iunit)
(*iunit)->DelayAura(m_spellId, GetCaster()->GetGUID(), delaytime);
}
+
bool DynamicObject::isVisibleForInState(Player const* u, bool inVisibleList) const
{
return IsInWorld() && u->IsInWorld()
diff --git a/src/game/DynamicObject.h b/src/game/DynamicObject.h
index 0fd5e50eb50..13747891964 100644
--- a/src/game/DynamicObject.h
+++ b/src/game/DynamicObject.h
@@ -17,18 +17,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_DYNAMICOBJECT_H
#define TRINITYCORE_DYNAMICOBJECT_H
+
#include "Object.h"
+
class Unit;
struct SpellEntry;
+
class DynamicObject : public WorldObject
{
public:
typedef std::set<Unit*> AffectedSet;
explicit DynamicObject();
+
void AddToWorld();
void RemoveFromWorld();
+
bool Create(uint32 guidlow, Unit *caster, uint32 spellId, uint32 effMask, const Position &pos, int32 duration, float radius, bool active);
void Update(uint32 p_time);
void Delete();
@@ -45,11 +51,13 @@ class DynamicObject : public WorldObject
void RemoveAffected(Unit *unit) { m_affected.erase(unit); }
void Delay(int32 delaytime);
bool isVisibleForInState(Player const* u, bool inVisibleList) const;
+
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 YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); }
+
GridReference<DynamicObject> &GetGridRef() { return m_gridRef; }
protected:
uint32 m_spellId;
diff --git a/src/game/FleeingMovementGenerator.cpp b/src/game/FleeingMovementGenerator.cpp
index 35cfcca52d0..eef98b705cc 100644
--- a/src/game/FleeingMovementGenerator.cpp
+++ b/src/game/FleeingMovementGenerator.cpp
@@ -17,53 +17,66 @@
* 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 "CreatureAI.h"
#include "MapManager.h"
#include "FleeingMovementGenerator.h"
#include "DestinationHolderImp.h"
#include "ObjectAccessor.h"
+
#define MIN_QUIET_DISTANCE 28.0f
#define MAX_QUIET_DISTANCE 43.0f
+
template<class T>
void
FleeingMovementGenerator<T>::_setTargetLocation(T &owner)
{
if( !&owner )
return;
+
if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
return;
+
if(!_setMoveData(owner))
return;
+
float x, y, z;
if(!_getPoint(owner, x, y, z))
return;
+
owner.addUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING);
Traveller<T> traveller(owner);
i_destinationHolder.SetDestination(traveller, x, y, z);
}
+
template<>
bool FleeingMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const
{
if(i_destinationHolder.HasArrived())
return false;
+
i_destinationHolder.GetDestination(x, y, z);
return true;
}
+
template<>
bool FleeingMovementGenerator<Player>::GetDestination(float &x, float &y, float &z) const
{
return false;
}
+
template<class T>
bool
FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
{
if(!&owner)
return false;
+
x = owner.GetPositionX();
y = owner.GetPositionY();
z = owner.GetPositionZ();
+
float temp_x, temp_y, angle;
const Map * _map = owner.GetBaseMap();
//primitive path-finding
@@ -71,7 +84,9 @@ FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
{
if(i_only_forward && i > 2)
break;
+
float distance = 5.0f;
+
switch(i)
{
case 0:
@@ -149,6 +164,7 @@ FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
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;
@@ -156,11 +172,15 @@ FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
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);
@@ -179,11 +199,13 @@ FleeingMovementGenerator<T>::_getPoint(T &owner, float &x, float &y, float &z)
i_nextCheckTime.Reset( urand(500,1000) );
return false;
}
+
template<class T>
bool
FleeingMovementGenerator<T>::_setMoveData(T &owner)
{
float cur_dist_xyz = owner.GetDistance(i_caster_x, i_caster_y, i_caster_z);
+
if(i_to_distance_from_caster > 0.0f)
{
if((i_last_distance_from_caster > i_to_distance_from_caster && cur_dist_xyz < i_to_distance_from_caster) ||
@@ -208,9 +230,12 @@ FleeingMovementGenerator<T>::_setMoveData(T &owner)
return true;
}
}
+
float cur_dist;
float angle_to_caster;
+
Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID);
+
if(fright)
{
cur_dist = fright->GetDistance(&owner);
@@ -232,10 +257,13 @@ FleeingMovementGenerator<T>::_setMoveData(T &owner)
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;
@@ -257,24 +285,30 @@ FleeingMovementGenerator<T>::_setMoveData(T &owner)
angle = rand_norm()*M_PI;
i_to_distance_from_caster = MIN_QUIET_DISTANCE + 2.5f + rand_norm()*(MAX_QUIET_DISTANCE - MIN_QUIET_DISTANCE - 2.5f);
}
+
int8 sign = rand_norm() > 0.5f ? 1 : -1;
i_cur_angle = sign*angle + angle_to_caster;
+
// current distance
i_last_distance_from_caster = cur_dist;
+
return true;
}
+
template<class T>
void
FleeingMovementGenerator<T>::Initialize(T &owner)
{
if(!&owner)
return;
+
_Init(owner);
owner.CastStop();
owner.addUnitState(UNIT_STAT_FLEEING | UNIT_STAT_ROAMING);
owner.SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
owner.SetUInt64Value(UNIT_FIELD_TARGET, 0);
owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
if(Unit * fright = ObjectAccessor::GetUnit(owner, i_frightGUID))
{
i_caster_x = fright->GetPositionX();
@@ -287,21 +321,25 @@ FleeingMovementGenerator<T>::Initialize(T &owner)
i_caster_y = owner.GetPositionY();
i_caster_z = owner.GetPositionZ();
}
+
i_only_forward = true;
i_cur_angle = 0.0f;
i_last_distance_from_caster = 0.0f;
i_to_distance_from_caster = 0.0f;
_setTargetLocation(owner);
}
+
template<>
void
FleeingMovementGenerator<Creature>::_Init(Creature &owner)
{
if(!&owner)
return;
+
is_water_ok = owner.canSwim();
is_land_ok = owner.canWalk();
}
+
template<>
void
FleeingMovementGenerator<Player>::_Init(Player &)
@@ -309,6 +347,7 @@ FleeingMovementGenerator<Player>::_Init(Player &)
is_water_ok = true;
is_land_ok = true;
}
+
template<class T>
void
FleeingMovementGenerator<T>::Finalize(T &owner)
@@ -318,12 +357,14 @@ FleeingMovementGenerator<T>::Finalize(T &owner)
if(owner.GetTypeId() == TYPEID_UNIT && owner.getVictim())
owner.SetUInt64Value(UNIT_FIELD_TARGET, owner.getVictim()->GetGUID());
}
+
template<class T>
void
FleeingMovementGenerator<T>::Reset(T &owner)
{
Initialize(owner);
}
+
template<class T>
bool
FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
@@ -332,13 +373,17 @@ FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
return false;
if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
return true;
+
Traveller<T> traveller(owner);
+
i_nextCheckTime.Update(time_diff);
+
if( (owner.IsStopped() && !i_destinationHolder.HasArrived()) || !i_destinationHolder.HasDestination() )
{
_setTargetLocation(owner);
return true;
}
+
if (i_destinationHolder.UpdateTraveller(traveller, time_diff))
{
i_destinationHolder.ResetUpdate(50);
@@ -350,6 +395,7 @@ FleeingMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
}
return true;
}
+
template void FleeingMovementGenerator<Player>::Initialize(Player &);
template void FleeingMovementGenerator<Creature>::Initialize(Creature &);
template bool FleeingMovementGenerator<Player>::_setMoveData(Player &);
@@ -364,6 +410,7 @@ template void FleeingMovementGenerator<Player>::Reset(Player &);
template void FleeingMovementGenerator<Creature>::Reset(Creature &);
template bool FleeingMovementGenerator<Player>::Update(Player &, const uint32 &);
template bool FleeingMovementGenerator<Creature>::Update(Creature &, const uint32 &);
+
void TimedFleeingMovementGenerator::Finalize(Unit &owner)
{
owner.RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_FLEEING);
@@ -377,15 +424,19 @@ void TimedFleeingMovementGenerator::Finalize(Unit &owner)
}
}
}
+
bool TimedFleeingMovementGenerator::Update(Unit & owner, const uint32 & time_diff)
{
if( !owner.isAlive() )
return false;
+
if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED) )
return true;
+
i_totalFleeTime.Update(time_diff);
if (i_totalFleeTime.Passed())
return false;
+
// This calls grant-parent Update method hiden by FleeingMovementGenerator::Update(Creature &, const uint32 &) version
// This is done instead of casting Unit& to Creature& and call parent method, then we can use Unit directly
return MovementGeneratorMedium< Creature, FleeingMovementGenerator<Creature> >::Update(owner, time_diff);
diff --git a/src/game/FleeingMovementGenerator.h b/src/game/FleeingMovementGenerator.h
index 98a2058ad1d..0bc4bdb9757 100644
--- a/src/game/FleeingMovementGenerator.h
+++ b/src/game/FleeingMovementGenerator.h
@@ -17,31 +17,39 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_FLEEINGMOVEMENTGENERATOR_H
#define TRINITY_FLEEINGMOVEMENTGENERATOR_H
+
#include "MovementGenerator.h"
#include "DestinationHolder.h"
#include "Traveller.h"
+
template<class T>
class TRINITY_DLL_SPEC FleeingMovementGenerator
: public MovementGeneratorMedium< T, FleeingMovementGenerator<T> >
{
public:
FleeingMovementGenerator(uint64 fright) : i_frightGUID(fright), i_nextCheckTime(0) {}
+
void Initialize(T &);
void Finalize(T &);
void Reset(T &);
bool Update(T &, const uint32 &);
bool GetDestination(float &x, float &y, float &z) const;
+
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;
@@ -50,8 +58,10 @@ class TRINITY_DLL_SPEC FleeingMovementGenerator
float i_cur_angle;
uint64 i_frightGUID;
TimeTracker i_nextCheckTime;
+
DestinationHolder< Traveller<T> > i_destinationHolder;
};
+
class MANGOS_DLL_SPEC TimedFleeingMovementGenerator
: public FleeingMovementGenerator<Creature>
{
@@ -59,11 +69,14 @@ class MANGOS_DLL_SPEC TimedFleeingMovementGenerator
TimedFleeingMovementGenerator(uint64 fright, uint32 time) :
FleeingMovementGenerator<Creature>(fright),
i_totalFleeTime(time) {}
+
MovementGeneratorType GetMovementGeneratorType() { return TIMED_FLEEING_MOTION_TYPE; }
bool Update(Unit &, const uint32 &);
void Finalize(Unit &);
+
private:
TimeTracker i_totalFleeTime;
};
+
#endif
diff --git a/src/game/FollowerRefManager.h b/src/game/FollowerRefManager.h
index 4f830b5e200..093c85adcee 100644
--- a/src/game/FollowerRefManager.h
+++ b/src/game/FollowerRefManager.h
@@ -17,13 +17,18 @@
* 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<Unit, TargetedMovementGeneratorBase>
{
+
};
#endif
diff --git a/src/game/FollowerReference.cpp b/src/game/FollowerReference.cpp
index b81194e6daa..173f9881c54 100644
--- a/src/game/FollowerReference.cpp
+++ b/src/game/FollowerReference.cpp
@@ -17,17 +17,21 @@
* 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
index 0e42ac54ce7..3c0b3dc9e7c 100644
--- a/src/game/FollowerReference.h
+++ b/src/game/FollowerReference.h
@@ -17,11 +17,15 @@
* 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 TRINITY_DLL_SPEC FollowerReference : public Reference<Unit, TargetedMovementGeneratorBase>
{
protected:
diff --git a/src/game/Formulas.h b/src/game/Formulas.h
index 7a2efdffc4e..2ec175f95be 100644
--- a/src/game/Formulas.h
+++ b/src/game/Formulas.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_FORMULAS_H
#define TRINITY_FORMULAS_H
+
#include "World.h"
+
namespace Trinity
{
namespace Honor
@@ -32,6 +35,7 @@ namespace Trinity
namespace XP
{
enum XPColorChar { RED, ORANGE, YELLOW, GREEN, GRAY };
+
inline uint32 GetGrayLevel(uint32 pl_level)
{
if( pl_level <= 5 )
@@ -43,6 +47,7 @@ namespace Trinity
else
return pl_level - 9;
}
+
inline XPColorChar GetColorCode(uint32 pl_level, uint32 mob_level)
{
if( mob_level >= pl_level + 5 )
@@ -56,6 +61,7 @@ namespace Trinity
else
return GRAY;
}
+
inline uint32 GetZeroDifference(uint32 pl_level)
{
if( pl_level < 8 ) return 5;
@@ -71,6 +77,7 @@ namespace Trinity
if( pl_level < 60 ) return 16;
return 17;
}
+
inline uint32 BaseGain(uint32 pl_level, uint32 mob_level, ContentLevels content)
{
uint32 nBaseExp;
@@ -83,6 +90,7 @@ namespace Trinity
sLog.outError("BaseGain: Unsupported content level %u",content);
nBaseExp = 45; break;
}
+
if( mob_level >= pl_level )
{
uint32 nLevelDiff = mob_level - pl_level;
@@ -101,19 +109,24 @@ namespace Trinity
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(u->GetMapId(),u->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 float xp_in_group_rate(uint32 count, bool isRaid)
{
if(isRaid)
diff --git a/src/game/GameEventMgr.cpp b/src/game/GameEventMgr.cpp
index 9b70b253afc..5b7aa737bc0 100644
--- a/src/game/GameEventMgr.cpp
+++ b/src/game/GameEventMgr.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "GameEventMgr.h"
#include "World.h"
#include "ObjectMgr.h"
@@ -29,7 +30,9 @@
#include "GossipDef.h"
#include "Player.h"
#include "BattleGroundMgr.h"
+
INSTANTIATE_SINGLETON_1(GameEventMgr);
+
bool GameEventMgr::CheckOneGameEvent(uint16 entry) const
{
switch(mGameEvent[entry].state)
@@ -67,21 +70,27 @@ bool GameEventMgr::CheckOneGameEvent(uint16 entry) const
}
}
}
+
uint32 GameEventMgr::NextCheck(uint16 entry) const
{
time_t currenttime = time(NULL);
+
// for NEXTPHASE state world events, return the delay to start the next event, so the followup event will be checked correctly
if ((mGameEvent[entry].state == GAMEEVENT_WORLD_NEXTPHASE || mGameEvent[entry].state == GAMEEVENT_WORLD_FINISHED) && mGameEvent[entry].nextstart >= currenttime)
return (mGameEvent[entry].nextstart - currenttime);
+
// for CONDITIONS state world events, return the length of the wait period, so if the conditions are met, this check will be called again to set the timer as NEXTPHASE event
if (mGameEvent[entry].state == GAMEEVENT_WORLD_CONDITIONS)
return mGameEvent[entry].length ? mGameEvent[entry].length * 60 : max_ge_check_delay;
+
// 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)))
@@ -95,16 +104,21 @@ uint32 GameEventMgr::NextCheck(uint16 entry) const
else
return delay;
}
+
void GameEventMgr::StartInternalEvent(uint16 event_id)
{
if(event_id < 1 || event_id >= mGameEvent.size())
return;
+
if(!mGameEvent[event_id].isValid())
return;
+
if(m_ActiveEvents.find(event_id) != m_ActiveEvents.end())
return;
+
StartEvent(event_id);
}
+
bool GameEventMgr::StartEvent( uint16 event_id, bool overwrite )
{
if(mGameEvent[event_id].state == GAMEEVENT_NORMAL
@@ -125,10 +139,12 @@ bool GameEventMgr::StartEvent( uint16 event_id, bool overwrite )
if( mGameEvent[event_id].state == GAMEEVENT_WORLD_INACTIVE )
// set to conditions phase
mGameEvent[event_id].state = GAMEEVENT_WORLD_CONDITIONS;
+
// add to active events
AddActiveEvent(event_id);
// add spawns
ApplyNewEvent(event_id);
+
// check if can go to next state
bool conditions_met = CheckOneGameEventConditions(event_id);
// save to db
@@ -138,14 +154,18 @@ bool GameEventMgr::StartEvent( uint16 event_id, bool overwrite )
// or to scedule another update where the next event will be started
if(overwrite && conditions_met)
sWorld.ForceGameEventUpdate();
+
return conditions_met;
}
}
+
void GameEventMgr::StopEvent( uint16 event_id, bool overwrite )
{
bool serverwide_evt = mGameEvent[event_id].state != GAMEEVENT_NORMAL && mGameEvent[event_id].state != GAMEEVENT_INTERNAL;
+
RemoveActiveEvent(event_id);
UnApplyEvent(event_id);
+
if(overwrite && !serverwide_evt)
{
mGameEvent[event_id].start = time(NULL) - mGameEvent[event_id].length * MINUTE;
@@ -170,6 +190,7 @@ void GameEventMgr::StopEvent( uint16 event_id, bool overwrite )
}
}
}
+
void GameEventMgr::LoadFromDB()
{
{
@@ -180,11 +201,15 @@ void GameEventMgr::LoadFromDB()
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,holiday,description,world_event FROM game_event");
if( !result )
{
@@ -193,20 +218,25 @@ void GameEventMgr::LoadFromDB()
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);
@@ -215,13 +245,16 @@ void GameEventMgr::LoadFromDB()
pGameEvent.occurence = fields[3].GetUInt32();
pGameEvent.length = fields[4].GetUInt32();
pGameEvent.holiday_id = fields[5].GetUInt32();
+
pGameEvent.state = (GameEventState)(fields[7].GetUInt8());
pGameEvent.nextstart = 0;
+
if(pGameEvent.length==0 && pGameEvent.state == GAMEEVENT_NORMAL) // length>0 is validity check
{
sLog.outErrorDb("`game_event` game event id (%i) isn't a world event and has length = 0, thus it can't be used.",event_id);
continue;
}
+
if(pGameEvent.holiday_id)
{
if(!sHolidaysStore.LookupEntry(pGameEvent.holiday_id))
@@ -230,36 +263,47 @@ void GameEventMgr::LoadFromDB()
pGameEvent.holiday_id = 0;
}
}
+
pGameEvent.description = fields[6].GetCppString();
+
} while( result->NextRow() );
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u game events", count );
}
+
// load game event saves
// 0 1 2
result = CharacterDatabase.Query("SELECT event_id, state, UNIX_TIMESTAMP(next_start) FROM game_event_save");
+
count = 0;
if( !result )
{
barGoLink bar2(1);
bar2.step();
+
sLog.outString();
sLog.outString(">> Loaded %u game event saves in game events", count );
}
else
{
+
barGoLink bar2( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar2.step();
+
uint16 event_id = fields[0].GetUInt16();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_save` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
if(mGameEvent[event_id].state != GAMEEVENT_NORMAL && mGameEvent[event_id].state != GAMEEVENT_INTERNAL)
{
mGameEvent[event_id].state = (GameEventState)(fields[1].GetUInt8());
@@ -270,35 +314,44 @@ void GameEventMgr::LoadFromDB()
sLog.outErrorDb("game_event_save includes event save for non-worldevent id %u",event_id);
continue;
}
+
++count;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u game event saves in game events", count );
delete result;
}
+
// load game event links (prerequisites)
result = WorldDatabase.Query("SELECT event_id, prerequisite_event FROM game_event_prerequisite");
if( !result )
{
barGoLink bar2(1);
bar2.step();
+
sLog.outString();
sLog.outString(">> Loaded %u game event prerequisites in game events", count );
}
else
{
+
barGoLink bar2( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar2.step();
+
uint16 event_id = fields[0].GetUInt16();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_prerequisite` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
if(mGameEvent[event_id].state != GAMEEVENT_NORMAL && mGameEvent[event_id].state != GAMEEVENT_INTERNAL)
{
uint16 prerequisite_event = fields[1].GetUInt16();
@@ -314,110 +367,141 @@ void GameEventMgr::LoadFromDB()
sLog.outErrorDb("game_event_prerequisiste includes event entry for non-worldevent id %u",event_id);
continue;
}
+
++count;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u game event prerequisites in 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 bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded %u creatures in game events", count );
}
else
{
+
barGoLink bar( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar.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() );
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u creatures in game events", count );
}
+
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 bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded %u gameobjects in game events", count );
}
else
{
+
barGoLink bar( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar.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() );
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u gameobjects in game events", count );
}
+
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 bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded %u model/equipment changes in game events", count );
}
else
{
+
barGoLink bar( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar.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;
@@ -425,6 +509,7 @@ void GameEventMgr::LoadFromDB()
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))
@@ -433,176 +518,223 @@ void GameEventMgr::LoadFromDB()
continue;
}
}
+
equiplist.push_back(std::pair<uint32, ModelEquip>(guid, newModelEquipSet));
+
} while( result->NextRow() );
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u model/equipment changes in game events", count );
}
+
mGameEventCreatureQuests.resize(mGameEvent.size());
// 0 1 2
result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_creature_quest");
+
count = 0;
if( !result )
{
barGoLink bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded %u quests additions in game events", count );
}
else
{
+
barGoLink bar( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar.step();
uint32 id = fields[0].GetUInt32();
uint32 quest = fields[1].GetUInt32();
uint16 event_id = fields[2].GetUInt16();
+
if(event_id >= mGameEventCreatureQuests.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 = mGameEventCreatureQuests[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;
}
+
mGameEventGameObjectQuests.resize(mGameEvent.size());
// 0 1 2
result = WorldDatabase.Query("SELECT id, quest, event FROM game_event_gameobject_quest");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u go 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 >= mGameEventGameObjectQuests.size())
{
sLog.outErrorDb("`game_event_gameobject_quest` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
++count;
QuestRelList& questlist = mGameEventGameObjectQuests[event_id];
questlist.push_back(QuestRelation(id, quest));
+
} while( result->NextRow() );
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u quests additions in game events", count );
}
+
// Load quest to (event,condition) mapping
// 0 1 2 3
result = WorldDatabase.Query("SELECT quest, event_id, condition_id, num FROM game_event_quest_condition");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u quest event conditions in game events", count );
}
else
{
+
barGoLink bar3( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar3.step();
uint32 quest = fields[0].GetUInt32();
uint16 event_id = fields[1].GetUInt16();
uint32 condition = fields[2].GetUInt32();
float num = fields[3].GetFloat();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_quest_condition` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
++count;
mQuestToEventConditions[quest].event_id = event_id;
mQuestToEventConditions[quest].condition = condition;
mQuestToEventConditions[quest].num = num;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u quest event conditions in game events", count );
+
delete result;
}
+
// load conditions of the events
// 0 1 2 3 4
result = WorldDatabase.Query("SELECT event_id, condition_id, req_num, max_world_state_field, done_world_state_field FROM game_event_condition");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u conditions in game events", count );
}
else
{
+
barGoLink bar3( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar3.step();
uint16 event_id = fields[0].GetUInt16();
uint32 condition = fields[1].GetUInt32();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_condition` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
mGameEvent[event_id].conditions[condition].reqNum = fields[2].GetFloat();
mGameEvent[event_id].conditions[condition].done = 0;
mGameEvent[event_id].conditions[condition].max_world_state = fields[3].GetUInt32();
mGameEvent[event_id].conditions[condition].done_world_state = fields[4].GetUInt32();
+
++count;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u conditions in game events", count );
+
delete result;
}
+
// load condition saves
// 0 1 2
result = CharacterDatabase.Query("SELECT event_id, condition_id, done FROM game_event_condition_save");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u condition saves in game events", count );
}
else
{
+
barGoLink bar3( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar3.step();
uint16 event_id = fields[0].GetUInt16();
uint32 condition = fields[1].GetUInt32();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_condition_save` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
std::map<uint32, GameEventFinishCondition>::iterator itr = mGameEvent[event_id].conditions.find(condition);
if(itr != mGameEvent[event_id].conditions.end())
{
@@ -613,70 +745,90 @@ void GameEventMgr::LoadFromDB()
sLog.outErrorDb("game_event_condition_save contains not present condition evt id %u cond id %u",event_id, condition);
continue;
}
+
++count;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u condition saves in game events", count );
+
delete result;
}
+
mGameEventNPCFlags.resize(mGameEvent.size());
// load game event npcflag
// 0 1 2
result = WorldDatabase.Query("SELECT guid, event_id, npcflag FROM game_event_npcflag");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u npcflags 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();
uint32 npcflag = fields[2].GetUInt32();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_npcflag` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
mGameEventNPCFlags[event_id].push_back(GuidNPCFlagPair(guid,npcflag));
+
++count;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u npcflags in game events", count );
+
delete result;
}
+
mGameEventVendors.resize(mGameEvent.size());
// 0 1 2 3 4 5
result = WorldDatabase.Query("SELECT event, guid, item, maxcount, incrtime, ExtendedCost FROM game_event_npc_vendor");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u vendor additions in game events", count );
}
else
{
+
barGoLink bar3( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar3.step();
uint16 event_id = fields[0].GetUInt16();
+
if(event_id >= mGameEventVendors.size())
{
sLog.outErrorDb("`game_event_npc_vendor` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
NPCVendorList& vendors = mGameEventVendors[event_id];
NPCVendorEntry newEntry;
uint32 guid = fields[1].GetUInt32();
@@ -697,132 +849,171 @@ void GameEventMgr::LoadFromDB()
}
// get creature entry
newEntry.entry = 0;
+
if( CreatureData const* data = objmgr.GetCreatureData(guid) )
newEntry.entry = data->id;
+
// check validity with event's npcflag
if(!objmgr.IsVendorItemValid(newEntry.entry, newEntry.item, newEntry.maxcount, newEntry.incrtime, newEntry.ExtendedCost, NULL, NULL, event_npc_flag))
continue;
++count;
vendors.push_back(newEntry);
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u vendor additions in game events", count );
+
delete result;
}
+
// load game event npc gossip ids
// 0 1 2
result = WorldDatabase.Query("SELECT guid, event_id, textid FROM game_event_npc_gossip");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u npc gossip textids 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();
uint32 textid = fields[2].GetUInt32();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_npc_gossip` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
mNPCGossipIds[guid]=EventNPCGossipIdPair(event_id, textid);
+
++count;
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u npc gossip textids in game events", count );
+
delete result;
}
+
// set all flags to 0
mGameEventBattleGroundHolidays.resize(mGameEvent.size(),0);
// load game event battleground flags
// 0 1
result = WorldDatabase.Query("SELECT event, bgflag FROM game_event_battleground_holiday");
+
count = 0;
if( !result )
{
barGoLink bar3(1);
bar3.step();
+
sLog.outString();
sLog.outString(">> Loaded %u battleground holidays in game events", count );
}
else
{
+
barGoLink bar3( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar3.step();
+
uint16 event_id = fields[0].GetUInt16();
+
if(event_id >= mGameEvent.size())
{
sLog.outErrorDb("`game_event_battleground_holiday` game event id (%u) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
++count;
+
mGameEventBattleGroundHolidays[event_id] = fields[1].GetUInt32();
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u battleground holidays in game events", count );
+
delete result;
}
+
////////////////////////
// GameEventPool
////////////////////////
+
mGameEventPoolIds.resize(mGameEvent.size()*2-1);
// 1 2
result = WorldDatabase.Query("SELECT pool_template.entry, game_event_pool.event "
"FROM pool_template JOIN game_event_pool ON pool_template.entry = game_event_pool.pool_entry");
+
count = 0;
if( !result )
{
barGoLink bar2(1);
bar2.step();
+
sLog.outString();
sLog.outString(">> Loaded %u pools in game events", count );
}
else
{
+
barGoLink bar2( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar2.step();
+
uint32 entry = fields[0].GetUInt16();
int16 event_id = fields[1].GetInt16();
+
int32 internal_event_id = mGameEvent.size() + event_id - 1;
+
if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size())
{
sLog.outErrorDb("`game_event_pool` game event id (%i) is out of range compared to max event id in `game_event`",event_id);
continue;
}
+
if (!poolhandler.CheckPool(entry))
{
sLog.outErrorDb("Pool Id (%u) has all creatures or gameobjects with explicit chance sum <>100 and no equal chance defined. The pool system cannot pick one to spawn.", entry);
continue;
}
+
++count;
IdList& poollist = mGameEventPoolIds[internal_event_id];
poollist.push_back(entry);
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u pools in game events", count );
delete result;
}
}
+
uint32 GameEventMgr::GetNPCFlag(Creature * cr)
{
uint32 mask = 0;
uint32 guid = cr->GetDBTableGUIDLow();
+
for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
{
for(NPCFlagList::iterator itr = mGameEventNPCFlags[*e_itr].begin();
@@ -831,8 +1022,10 @@ uint32 GameEventMgr::GetNPCFlag(Creature * cr)
if(itr->first == guid)
mask |= itr->second;
}
+
return mask;
}
+
uint32 GameEventMgr::GetNpcTextId(uint32 guid)
{
GuidEventNpcGossipIdMap::iterator itr = mNPCGossipIds.find(guid);
@@ -841,6 +1034,7 @@ uint32 GameEventMgr::GetNpcTextId(uint32 guid)
return itr->second.second;
return 0;
}
+
uint32 GameEventMgr::Initialize() // return the next event delay in ms
{
m_ActiveEvents.clear();
@@ -849,6 +1043,7 @@ uint32 GameEventMgr::Initialize() // return the next e
isSystemInit = true;
return delay;
}
+
uint32 GameEventMgr::Update() // return the next event delay in ms
{
time_t currenttime = time(NULL);
@@ -879,6 +1074,7 @@ uint32 GameEventMgr::Update() // return the next e
else if (mGameEvent[itr].state == GAMEEVENT_WORLD_CONDITIONS && CheckOneGameEventConditions(itr))
// changed, save to DB the gameevent state, will be updated in next update cycle
SaveWorldEventStateToDB(itr);
+
//sLog.outDebug("GameEvent %u is active",itr->first);
// queue for activation
if (!IsActiveEvent(itr))
@@ -917,6 +1113,7 @@ uint32 GameEventMgr::Update() // return the next e
sLog.outDetail("Next game event check in %u seconds.", nextEventDelay + 1);
return (nextEventDelay + 1) * IN_MILISECONDS; // Add 1 second to be sure event has started/stopped at next call
}
+
void GameEventMgr::UnApplyEvent(uint16 event_id)
{
sLog.outString("GameEvent %u \"%s\" removed.", event_id, mGameEvent[event_id].description.c_str());
@@ -936,6 +1133,7 @@ void GameEventMgr::UnApplyEvent(uint16 event_id)
// update bg holiday
UpdateBattleGroundSettings();
}
+
void GameEventMgr::ApplyNewEvent(uint16 event_id)
{
switch(sWorld.getConfig(CONFIG_EVENT_ANNOUNCE))
@@ -946,7 +1144,9 @@ void GameEventMgr::ApplyNewEvent(uint16 event_id)
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
@@ -963,6 +1163,7 @@ void GameEventMgr::ApplyNewEvent(uint16 event_id)
// update bg holiday
UpdateBattleGroundSettings();
}
+
void GameEventMgr::UpdateEventNPCFlags(uint16 event_id)
{
// go through the creatures whose npcflags are changed in the event
@@ -988,6 +1189,7 @@ void GameEventMgr::UpdateEventNPCFlags(uint16 event_id)
}
}
}
+
void GameEventMgr::UpdateBattleGroundSettings()
{
uint32 mask = 0;
@@ -995,6 +1197,7 @@ void GameEventMgr::UpdateBattleGroundSettings()
mask |= mGameEventBattleGroundHolidays[*itr];
sBattleGroundMgr.SetHolidayWeekends(mask);
}
+
void GameEventMgr::UpdateEventNPCVendor(uint16 event_id, bool activate)
{
for(NPCVendorList::iterator itr = mGameEventVendors[event_id].begin(); itr != mGameEventVendors[event_id].end(); ++itr)
@@ -1005,14 +1208,17 @@ void GameEventMgr::UpdateEventNPCVendor(uint16 event_id, bool activate)
objmgr.RemoveVendorItem(itr->entry, itr->item, false);
}
}
+
void GameEventMgr::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("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventCreatureGuids element %i (size: " SIZEFMTD ")",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
@@ -1020,6 +1226,7 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
if (data)
{
objmgr.AddCreatureToGrid(*itr, data);
+
// Spawn if necessary (loaded grids only)
Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(data->mapid));
// We use spawn coords to spawn
@@ -1038,11 +1245,13 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
}
}
}
+
if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
{
sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventGameobjectGuids element %i (size: " SIZEFMTD ")",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
@@ -1070,24 +1279,29 @@ void GameEventMgr::GameEventSpawn(int16 event_id)
}
}
}
+
if(internal_event_id < 0 || internal_event_id >= mGameEventPoolIds.size())
{
sLog.outError("GameEventMgr::GameEventSpawn attempt access to out of range mGameEventPoolIds element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventPoolIds.size());
return;
}
+
for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin();itr != mGameEventPoolIds[internal_event_id].end();++itr)
{
poolhandler.SpawnPool(*itr);
}
}
+
void GameEventMgr::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("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventCreatureGuids element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventCreatureGuids.size());
return;
}
+
for (GuidList::iterator itr = mGameEventCreatureGuids[internal_event_id].begin();itr != mGameEventCreatureGuids[internal_event_id].end();++itr)
{
// check if it's needed by another event, if so, don't remove
@@ -1097,15 +1311,18 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
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->AddObjectToRemoveList();
}
}
+
if(internal_event_id < 0 || internal_event_id >= mGameEventGameobjectGuids.size())
{
sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventGameobjectGuids element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventGameobjectGuids.size());
return;
}
+
for (GuidList::iterator itr = mGameEventGameobjectGuids[internal_event_id].begin();itr != mGameEventGameobjectGuids[internal_event_id].end();++itr)
{
// check if it's needed by another event, if so, don't remove
@@ -1115,6 +1332,7 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
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();
}
@@ -1124,11 +1342,13 @@ void GameEventMgr::GameEventUnspawn(int16 event_id)
sLog.outError("GameEventMgr::GameEventUnspawn attempt access to out of range mGameEventPoolIds element %i (size: " SIZEFMTD ")",internal_event_id,mGameEventPoolIds.size());
return;
}
+
for (IdList::iterator itr = mGameEventPoolIds[internal_event_id].begin();itr != mGameEventPoolIds[internal_event_id].end();++itr)
{
poolhandler.DespawnPool(*itr);
}
}
+
void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
{
for(ModelEquipList::iterator itr = mGameEventModelEquip[event_id].begin();itr != mGameEventModelEquip[event_id].end();++itr)
@@ -1137,6 +1357,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
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)
@@ -1184,6 +1405,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(display_id);
if (minfo)
display_id = minfo->modelid;
+
if (data2->equipmentId == 0)
itr->second.equipement_id_prev = cinfo->equipmentId;
else if (data2->equipmentId != -1)
@@ -1206,6 +1428,7 @@ void GameEventMgr::ChangeEquipOrModel(int16 event_id, bool activate)
}
}
}
+
bool GameEventMgr::hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 event_id)
{
for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
@@ -1219,6 +1442,7 @@ bool GameEventMgr::hasCreatureQuestActiveEventExcept(uint32 quest_id, uint16 eve
}
return false;
}
+
bool GameEventMgr::hasGameObjectQuestActiveEventExcept(uint32 quest_id, uint16 event_id)
{
for(ActiveEvents::iterator e_itr = m_ActiveEvents.begin(); e_itr != m_ActiveEvents.end(); ++e_itr)
@@ -1264,6 +1488,7 @@ bool GameEventMgr::hasGameObjectActiveEventExcept(uint32 go_id, uint16 event_id)
}
return false;
}
+
void GameEventMgr::UpdateEventQuests(uint16 event_id, bool Activate)
{
QuestRelList::iterator itr;
@@ -1317,10 +1542,12 @@ void GameEventMgr::UpdateEventQuests(uint16 event_id, bool Activate)
}
}
}}
+
GameEventMgr::GameEventMgr()
{
isSystemInit = false;
}
+
void GameEventMgr::HandleQuestComplete(uint32 quest_id)
{
// translate the quest to event and condition
@@ -1331,6 +1558,7 @@ void GameEventMgr::HandleQuestComplete(uint32 quest_id)
uint16 event_id = itr->second.event_id;
uint32 condition = itr->second.condition;
float num = itr->second.num;
+
// the event is not active, so return, don't increase condition finishes
if(!IsActiveEvent(event_id))
return;
@@ -1365,6 +1593,7 @@ void GameEventMgr::HandleQuestComplete(uint32 quest_id)
}
}
}
+
bool GameEventMgr::CheckOneGameEventConditions(uint16 event_id)
{
for(std::map<uint32,GameEventFinishCondition>::iterator itr = mGameEvent[event_id].conditions.begin(); itr != mGameEvent[event_id].conditions.end(); ++itr)
@@ -1381,6 +1610,7 @@ bool GameEventMgr::CheckOneGameEventConditions(uint16 event_id)
}
return true;
}
+
void GameEventMgr::SaveWorldEventStateToDB(uint16 event_id)
{
CharacterDatabase.BeginTransaction();
@@ -1391,6 +1621,7 @@ void GameEventMgr::SaveWorldEventStateToDB(uint16 event_id)
CharacterDatabase.PExecute("INSERT INTO game_event_save (event_id, state, next_start) VALUES ('%u','%u','0000-00-00 00:00:00')",event_id,mGameEvent[event_id].state);
CharacterDatabase.CommitTransaction();
}
+
void GameEventMgr::HandleWorldEventGossip(Player *plr, Creature *c)
{
// this function is used to send world state update before sending gossip menu
@@ -1402,6 +1633,7 @@ void GameEventMgr::HandleWorldEventGossip(Player *plr, Creature *c)
// send world state updates to the player about the progress
SendWorldStateUpdate(plr, itr->second.first);
}
+
void GameEventMgr::SendWorldStateUpdate(Player * plr, uint16 event_id)
{
std::map<uint32,GameEventFinishCondition>::iterator itr;
@@ -1413,12 +1645,15 @@ void GameEventMgr::SendWorldStateUpdate(Player * plr, uint16 event_id)
plr->SendUpdateWorldState(itr->second.max_world_state, (uint32)(itr->second.reqNum));
}
}
+
TRINITY_DLL_SPEC bool IsHolidayActive( HolidayIds id )
{
GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap();
GameEventMgr::ActiveEvents const& ae = gameeventmgr.GetActiveEventList();
+
for(GameEventMgr::ActiveEvents::const_iterator itr = ae.begin(); itr != ae.end(); ++itr)
if(events[*itr].holiday_id==id)
return true;
+
return false;
}
diff --git a/src/game/GameEventMgr.h b/src/game/GameEventMgr.h
index fb3947b4daf..e5c72524004 100644
--- a/src/game/GameEventMgr.h
+++ b/src/game/GameEventMgr.h
@@ -17,13 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GAMEEVENT_MGR_H
#define TRINITY_GAMEEVENT_MGR_H
+
#include "Common.h"
#include "SharedDefines.h"
#include "Platform/Define.h"
#include "Policies/Singleton.h"
+
#define max_ge_check_delay 86400 // 1 day in seconds
+
enum GameEventState
{
GAMEEVENT_NORMAL = 0, // standard game events
@@ -33,6 +37,7 @@ enum GameEventState
GAMEEVENT_WORLD_FINISHED = 4, // next events are started, unapply this one
GAMEEVENT_INTERNAL = 5, // never handled in update
};
+
struct GameEventFinishCondition
{
float reqNum; // required number // use float, since some events use percent
@@ -40,12 +45,14 @@ struct GameEventFinishCondition
uint32 max_world_state; // max resource count world state update id
uint32 done_world_state; // done resource count world state update id
};
+
struct GameEventQuestToEventConditionNum
{
uint16 event_id;
uint32 condition;
float num;
};
+
struct GameEventData
{
GameEventData() : start(1),end(0),nextstart(0),occurence(0),length(0),state(GAMEEVENT_NORMAL) {}
@@ -59,8 +66,10 @@ struct GameEventData
std::map<uint32 /*condition id*/, GameEventFinishCondition> conditions; // conditions to finish
std::set<uint16 /*gameevent id*/> prerequisite_events; // events that must be completed before starting this event
std::string description;
+
bool isValid() const { return ((length > 0) || (state > GAMEEVENT_NORMAL)); }
};
+
struct ModelEquip
{
uint32 modelid;
@@ -68,6 +77,7 @@ struct ModelEquip
uint32 modelid_prev;
uint32 equipement_id_prev;
};
+
struct NPCVendorEntry
{
uint32 entry; // creature entry
@@ -76,8 +86,10 @@ struct NPCVendorEntry
uint32 incrtime; // time for restore items amount if maxcount != 0
uint32 ExtendedCost;
};
+
class Player;
class Creature;
+
class GameEventMgr
{
public:
@@ -157,7 +169,10 @@ class GameEventMgr
GameEventGuidMap mGameEventCreatureGuids;
GameEventGuidMap mGameEventGameobjectGuids;
};
+
#define gameeventmgr Trinity::Singleton<GameEventMgr>::Instance()
+
TRINITY_DLL_SPEC bool IsHolidayActive(HolidayIds id);
+
#endif
diff --git a/src/game/GameObject.cpp b/src/game/GameObject.cpp
index f4cd1fb8af4..d7315f9626b 100644
--- a/src/game/GameObject.cpp
+++ b/src/game/GameObject.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -38,11 +39,14 @@
#include "Util.h"
#include "OutdoorPvPMgr.h"
#include "BattleGroundAV.h"
+
GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue)
{
m_objectType |= TYPEMASK_GAMEOBJECT;
m_objectTypeId = TYPEID_GAMEOBJECT;
+
m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_POSITION | UPDATEFLAG_ROTATION);
+
m_valuesCount = GAMEOBJECT_END;
m_respawnTime = 0;
m_respawnDelayTime = 25;
@@ -53,14 +57,17 @@ GameObject::GameObject() : WorldObject(), m_goValue(new GameObjectValue)
m_cooldownTime = 0;
m_goInfo = NULL;
m_goData = NULL;
+
m_DBTableGuid = 0;
m_rotation = 0;
}
+
GameObject::~GameObject()
{
//if(m_uint32Values) // field array can be not exist if GameOBject not loaded
// CleanupsBeforeDelete();
}
+
void GameObject::CleanupsBeforeDelete()
{
if(m_uint32Values) // field array can be not exist if GameOBject not loaded
@@ -74,6 +81,7 @@ void GameObject::CleanupsBeforeDelete()
owner = ObjectAccessor::GetObjectInWorld(owner_guid, (Player*)NULL);
else*/
owner = ObjectAccessor::GetUnit(*this,owner_guid);
+
if(owner)
owner->RemoveGameObject(this,false);
else
@@ -83,12 +91,14 @@ void GameObject::CleanupsBeforeDelete()
ownerType = "player";
else if(IS_PET_GUID(owner_guid))
ownerType = "pet";
+
sLog.outError("Delete GameObject (GUID: %u Entry: %u SpellId %u LinkedGO %u) that lost references to owner (GUID %u Type '%s') GO list. Crash possible later.",
GetGUIDLow(), GetGOInfo()->id, m_spellId, GetGOInfo()->GetLinkedGameObjectEntry(), GUID_LOPART(owner_guid), ownerType);
}
}
}
}
+
void GameObject::AddToWorld()
{
///- Register the gameobject for guid lookup
@@ -96,10 +106,12 @@ void GameObject::AddToWorld()
{
if(m_zoneScript)
m_zoneScript->OnGameObjectCreate(this, true);
+
ObjectAccessor::Instance().AddObject(this);
WorldObject::AddToWorld();
}
}
+
void GameObject::RemoveFromWorld()
{
///- Remove the gameobject from the accessor
@@ -107,6 +119,7 @@ void GameObject::RemoveFromWorld()
{
if(m_zoneScript)
m_zoneScript->OnGameObjectCreate(this, false);
+
// Possible crash at access to deleted GO in Unit::m_gameobj
if(uint64 owner_guid = GetOwnerGUID())
{
@@ -119,53 +132,73 @@ void GameObject::RemoveFromWorld()
ObjectAccessor::Instance().RemoveObject(this);
}
}
+
bool GameObject::Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit)
{
ASSERT(map);
SetMap(map);
+
Relocate(x,y,z,ang);
if(!IsPositionValid())
{
sLog.outError("Gameobject (GUID: %u Entry: %u ) not created. Suggested coordinates isn't valid (X: %f Y: %f)",guidlow,name_id,x,y);
return false;
}
+
SetPhaseMask(phaseMask,false);
+
GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(name_id);
if (!goinfo)
{
sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist entry in `gameobject_template`. Map: %u (X: %f Y: %f Z: %f) ang: %f rotation0: %f rotation1: %f rotation2: %f rotation3: %f",guidlow, name_id, map->GetId(), x, y, z, ang, rotation0, rotation1, rotation2, rotation3);
return false;
}
+
Object::_Create(guidlow, goinfo->id, HIGHGUID_GAMEOBJECT);
+
m_goInfo = goinfo;
+
if (goinfo->type >= MAX_GAMEOBJECT_TYPE)
{
sLog.outErrorDb("Gameobject (GUID: %u Entry: %u) not created: it have not exist GO type '%u' in `gameobject_template`. It's will crash client if created.",guidlow,name_id,goinfo->type);
return false;
}
+
SetFloatValue(GAMEOBJECT_PARENTROTATION+0, rotation0);
SetFloatValue(GAMEOBJECT_PARENTROTATION+1, rotation1);
+
UpdateRotationFields(rotation2,rotation3); // GAMEOBJECT_FACING, GAMEOBJECT_ROTATION, GAMEOBJECT_PARENTROTATION+2/3
+
SetFloatValue(OBJECT_FIELD_SCALE_X, goinfo->size);
+
SetUInt32Value(GAMEOBJECT_FACTION, goinfo->faction);
SetUInt32Value(GAMEOBJECT_FLAGS, goinfo->flags);
+
SetEntry(goinfo->id);
+
SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
// GAMEOBJECT_BYTES_1, index at 0, 1, 2 and 3
SetGoState(go_state);
SetGoType(GameobjectTypes(goinfo->type));
+
SetGoArtKit(0); // unknown what this is
SetGoAnimProgress(animprogress);
+
SetByteValue(GAMEOBJECT_BYTES_1, 2, artKit);
+
switch(goinfo->type)
{
case GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING:
m_goValue->building.health = goinfo->building.intactNumHits + goinfo->building.damagedNumHits;
break;
}
+
SetZoneScript();
+
return true;
}
+
void GameObject::Update(uint32 /*p_time*/)
{
if (IS_MO_TRANSPORT(GetGUID()))
@@ -173,6 +206,7 @@ void GameObject::Update(uint32 /*p_time*/)
//((Transport*)this)->Update(p_time);
return;
}
+
switch (m_lootState)
{
case GO_NOT_READY:
@@ -199,13 +233,16 @@ void GameObject::Update(uint32 /*p_time*/)
{
SetGoState(GO_STATE_ACTIVE);
SetUInt32Value(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
+
UpdateData udata;
WorldPacket packet;
BuildValuesUpdateBlockForPlayer(&udata,((Player*)caster));
udata.BuildPacket(&packet);
((Player*)caster)->GetSession()->SendPacket(&packet);
+
SendCustomAnim();
}
+
m_lootState = GO_READY; // can be successfully open with some chance
}
return;
@@ -225,6 +262,7 @@ void GameObject::Update(uint32 /*p_time*/)
m_respawnTime = 0;
m_SkillupList.clear();
m_usetimes = 0;
+
switch (GetGoType())
{
case GAMEOBJECT_TYPE_FISHINGNODE: // can't fish now
@@ -233,6 +271,7 @@ void GameObject::Update(uint32 /*p_time*/)
if(caster && caster->GetTypeId()==TYPEID_PLAYER)
{
caster->FinishSpell(CURRENT_CHANNELED_SPELL);
+
WorldPacket data(SMSG_FISH_NOT_HOOKED,0);
((Player*)caster)->GetSession()->SendPacket(&data);
}
@@ -263,6 +302,7 @@ void GameObject::Update(uint32 /*p_time*/)
}
}
}
+
if(isSpawned())
{
// traps can have time and can not have
@@ -271,9 +311,11 @@ void GameObject::Update(uint32 /*p_time*/)
{
if(m_cooldownTime >= time(NULL))
return;
+
// traps
Unit* owner = GetOwner();
Unit* ok = NULL; // pointer to appropriate target if found any
+
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
@@ -286,12 +328,15 @@ void GameObject::Update(uint32 /*p_time*/)
{
if(m_respawnTime > 0)
break;
+
radius = goInfo->trap.cooldown; // battlegrounds gameobjects has data2 == 0 && data5 == 3
IsBattleGroundTrap = true;
+
if(!radius)
return;
}
}
+
// Note: this hack with search required until GO casting not implemented
// search unfriendly creature
if(owner) // hunter trap
@@ -311,17 +356,22 @@ void GameObject::Update(uint32 /*p_time*/)
VisitNearbyWorldObject(radius, searcher);
ok = player;
}
+
if (ok)
{
// some traps do not have spell but should be triggered
if(goInfo->trap.spellId)
CastSpell(ok, goInfo->trap.spellId);
+
m_cooldownTime = time(NULL) + 4; // 4 seconds
+
// count charges
//if(goInfo->trap.charges > 0)
// AddUse();
+
if(owner)
SetLootState(GO_JUST_DEACTIVATED);
+
if(IsBattleGroundTrap && ok->GetTypeId() == TYPEID_PLAYER)
{
//BattleGround gameobjects case
@@ -340,6 +390,7 @@ void GameObject::Update(uint32 /*p_time*/)
}
}
}
+
break;
}
case GO_ACTIVATED:
@@ -361,6 +412,7 @@ void GameObject::Update(uint32 /*p_time*/)
if (GetGoType() == GAMEOBJECT_TYPE_GOOBER)
{
uint32 spellId = GetGOInfo()->goober.spellId;
+
if(spellId)
{
std::set<uint32>::const_iterator it = m_unique_users.begin();
@@ -372,11 +424,13 @@ void GameObject::Update(uint32 /*p_time*/)
//if (owner) owner->CastSpell(owner, spellId, false, 0, 0, GetGUID());
if (owner) owner->CastSpell(owner, spellId, false);
}
+
m_unique_users.clear();
m_usetimes = 0;
}
//any return here in case battleground traps
}
+
if(GetOwnerGUID())
{
if(Unit* owner = GetOwner(false))
@@ -387,6 +441,7 @@ void GameObject::Update(uint32 /*p_time*/)
}
return;
}
+
//burning flags in some battlegrounds, if you find better condition, just add it
if (GetGOInfo()->IsDespawnAtAction() || GetGoAnimProgress() > 0)
{
@@ -394,59 +449,76 @@ void GameObject::Update(uint32 /*p_time*/)
//reset flags
SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
}
+
loot.clear();
SetLootState(GO_READY);
+
if(!m_respawnDelayTime)
return;
+
if(!m_spawnedByDefault)
{
m_respawnTime = 0;
ObjectAccessor::UpdateObjectVisibility(this);
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_IMMEDIATELY))
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())
GetMap()->Add(this);
}
+
void GameObject::AddUniqueUse(Player* player)
{
AddUse();
m_unique_users.insert(player->GetGUIDLow());
}
+
void GameObject::Delete()
{
SetLootState(GO_NOT_READY);
if (GetOwnerGUID())
if (Unit * owner = GetOwner(false))
owner->RemoveGameObject(this, false);
+
assert (!GetOwnerGUID());
SendObjectDeSpawnAnim(GetGUID());
+
SetGoState(GO_STATE_READY);
SetUInt32Value(GAMEOBJECT_FLAGS, GetGOInfo()->flags);
+
uint16 poolid = poolhandler.IsPartOfAPool(GetGUIDLow(), TYPEID_GAMEOBJECT);
if (poolid)
poolhandler.UpdatePool(poolid, GetGUIDLow(), TYPEID_GAMEOBJECT);
else
AddObjectToRemoveList();
}
+
void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner)
{
fishloot->clear();
+
uint32 zone, subzone;
GetZoneAndAreaId(zone,subzone);
+
// if subzone loot exist use it
if(LootTemplates_Fishing.HaveLootFor(subzone))
fishloot->FillLoot(subzone, LootTemplates_Fishing, loot_owner,true);
@@ -454,6 +526,7 @@ void GameObject::getFishLoot(Loot *fishloot, Player* loot_owner)
else
fishloot->FillLoot(zone, LootTemplates_Fishing, loot_owner,true);
}
+
void GameObject::SaveToDB()
{
// this should only be used when the gameobject has already been loaded
@@ -464,17 +537,22 @@ void GameObject::SaveToDB()
sLog.outError("GameObject::SaveToDB failed, cannot get gameobject data!");
return;
}
+
SaveToDB(GetMapId(), data->spawnMask, data->phaseMask);
}
+
void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
{
const GameObjectInfo *goI = GetGOInfo();
+
if (!goI)
return;
+
if (!m_DBTableGuid)
m_DBTableGuid = GetGUIDLow();
// update in loaded data (changing data only in this place)
GameObjectData& data = objmgr.NewGOData(m_DBTableGuid);
+
// data->guid = guid don't must be update at save
data.id = GetEntry();
data.mapid = mapid;
@@ -492,6 +570,7 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
data.go_state = GetGoState();
data.spawnMask = spawnMask;
data.artKit = GetGoArtKit();
+
// updated in DB
std::ostringstream ss;
ss << "INSERT INTO gameobject VALUES ( "
@@ -511,19 +590,23 @@ void GameObject::SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask)
<< m_respawnDelayTime << ", "
<< uint32(GetGoAnimProgress()) << ", "
<< uint32(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("Gameobject (GUID: %u) not found in table `gameobject`, can't load. ",guid);
return false;
}
+
uint32 entry = data->id;
//uint32 map_id = data->mapid; // already used before call
uint32 phaseMask = data->phaseMask;
@@ -531,20 +614,26 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
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;
GOState go_state = data->go_state;
uint32 artKit = data->artKit;
+
m_DBTableGuid = guid;
if (map->GetInstanceId() != 0) guid = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
if (!Create(guid,entry, map, phaseMask, x, y, z, ang, rotation0, rotation1, rotation2, rotation3, animprogress, go_state, artKit) )
return false;
+
if(data->spawntimesecs >= 0)
{
m_spawnedByDefault = true;
+
if(!GetGOInfo()->GetDespawnPossibility() && !GetGOInfo()->IsDespawnAtAction())
{
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_NODESPAWN);
@@ -555,6 +644,7 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
{
m_respawnDelayTime = data->spawntimesecs;
m_respawnTime = objmgr.GetGORespawnTime(m_DBTableGuid, map->GetInstanceId());
+
// ready to respawn
if(m_respawnTime && m_respawnTime <= time(NULL))
{
@@ -569,9 +659,12 @@ bool GameObject::LoadFromDB(uint32 guid, Map *map)
m_respawnDelayTime = -data->spawntimesecs;
m_respawnTime = 0;
}
+
m_goData = data;
+
return true;
}
+
void GameObject::DeleteFromDB()
{
objmgr.SaveGORespawnTime(m_DBTableGuid,GetInstanceId(),0);
@@ -579,10 +672,12 @@ void GameObject::DeleteFromDB()
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 object.GetMap()->GetGameObject(guid);
}
+
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
@@ -596,6 +691,7 @@ bool GameObject::hasQuest(uint32 quest_id) const
}
return false;
}
+
bool GameObject::hasInvolvedQuest(uint32 quest_id) const
{
QuestRelations const& qr = objmgr.mGOQuestInvolvedRelations;
@@ -606,6 +702,7 @@ bool GameObject::hasInvolvedQuest(uint32 quest_id) const
}
return false;
}
+
bool GameObject::IsTransport() const
{
// If something is marked as a transport, don't transmit an out of range packet for it.
@@ -613,31 +710,37 @@ bool GameObject::IsTransport() const
if(!gInfo) return false;
return gInfo->type == GAMEOBJECT_TYPE_TRANSPORT || gInfo->type == GAMEOBJECT_TYPE_MO_TRANSPORT;
}
+
Unit* GameObject::GetOwner(bool inWorld) const
{
if (inWorld)
return ObjectAccessor::GetUnit(*this, GetOwnerGUID());
return ObjectAccessor::GetUnitInOrOutOfWorld(*this, GetOwnerGUID());
}
+
void GameObject::SaveRespawnTime()
{
if(m_goData && m_goData->dbData && 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
if(GetGOInfo()->type == GAMEOBJECT_TYPE_TRAP && GetGOInfo()->trap.stealthed)
{
@@ -646,10 +749,12 @@ bool GameObject::isVisibleForInState(Player const* u, bool inVisibleList) const
return false;
}
}
+
// check distance
return IsWithinDistInMap(u->m_seer,World::GetMaxVisibleDistanceForObject() +
(inVisibleList ? World::GetVisibleObjectGreyDistance() : 0.0f), false);
}
+
bool GameObject::canDetectTrap(Player const* u, float distance) const
{
if(u->hasUnitState(UNIT_STAT_STUNNED))
@@ -660,14 +765,17 @@ bool GameObject::canDetectTrap(Player const* u, float distance) const
return false;
if(u->HasAuraType(SPELL_AURA_DETECT_STEALTH))
return true;
+
//Visible distance is modified by -Level Diff (every level diff = 0.25f in visible distance)
float visibleDistance = (int32(u->getLevel()) - int32(GetOwner()->getLevel()))* 0.25f;
//GetModifier for trap (miscvalue 1)
//35y for aura 2836
//WARNING: these values are guessed, may be not blizzlike
visibleDistance += u->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DETECT, 1)* 0.5f;
+
return distance < visibleDistance;
}
+
void GameObject::Respawn()
{
if(m_spawnedByDefault && m_respawnTime > 0)
@@ -676,10 +784,12 @@ void GameObject::Respawn()
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
@@ -706,16 +816,20 @@ bool GameObject::ActivateToQuest( Player *pTarget)const
default:
break;
}
+
return false;
}
+
void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
{
GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(trapEntry);
if(!trapInfo || trapInfo->type!=GAMEOBJECT_TYPE_TRAP)
return;
+
SpellEntry const* trapSpell = sSpellStore.LookupEntry(trapInfo->trap.spellId);
if(!trapSpell) // checked at load already
return;
+
float range;
SpellRangeEntry const * srentry = sSpellRangeStore.LookupEntry(trapSpell->rangeIndex);
//get owner to check hostility of GameObject
@@ -730,6 +844,7 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
//if no owner assume that object is hostile to target
range = GetSpellMaxRangeForHostile(srentry);
}
+
// search nearest linked GO
GameObject* trapGO = NULL;
{
@@ -737,48 +852,63 @@ void GameObject::TriggeringLinkedGameObject( uint32 trapEntry, Unit* target)
CellPair p(Trinity::ComputeCellPair(GetPositionX(), GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*target,trapEntry,range);
MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(this, trapGO,go_check);
+
TypeContainerVisitor<Trinity::GameObjectLastSearcher<Trinity::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *GetMap(), *target, range);
}
+
// found correct GO
// FIXME: when GO casting will be implemented trap must cast spell to target
if(trapGO)
target->CastSpell(target,trapSpell,true, 0, 0, GetGUID());
}
+
GameObject* GameObject::LookupFishingHoleAround(float range)
{
GameObject* ok = NULL;
+
CellPair p(Trinity::ComputeCellPair(GetPositionX(),GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
MaNGOS::NearestGameObjectFishingHole u_check(*this, range);
MaNGOS::GameObjectSearcher<MaNGOS::NearestGameObjectFishingHole> checker(this, ok, u_check);
+
CellLock<GridReadGuard> cell_lock(cell, p);
+
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::NearestGameObjectFishingHole>, GridTypeMapContainer > grid_object_checker(checker);
cell_lock->Visit(cell_lock, grid_object_checker, *GetMap(), *this, range);
+
return ok;
}
+
void GameObject::ResetDoorOrButton()
{
if (m_lootState == GO_READY || m_lootState == GO_JUST_DEACTIVATED)
return;
+
SwitchDoorOrButton(false);
SetLootState(GO_JUST_DEACTIVATED);
m_cooldownTime = 0;
}
+
void GameObject::UseDoorOrButton(uint32 time_to_restore, bool alternative /* = false */)
{
if(m_lootState != GO_READY)
return;
+
if(!time_to_restore)
time_to_restore = GetGOInfo()->GetAutoCloseTime();
+
SwitchDoorOrButton(true,alternative);
SetLootState(GO_ACTIVATED);
+
m_cooldownTime = time(NULL) + time_to_restore;
}
+
void GameObject::SetGoArtKit(uint8 kit)
{
SetByteValue(GAMEOBJECT_BYTES_1, 2, kit);
@@ -786,6 +916,7 @@ void GameObject::SetGoArtKit(uint8 kit)
if(data)
data->artKit = kit;
}
+
void GameObject::SetGoArtKit(uint8 artkit, GameObject *go, uint32 lowguid)
{
const GameObjectData *data = NULL;
@@ -796,40 +927,49 @@ void GameObject::SetGoArtKit(uint8 artkit, GameObject *go, uint32 lowguid)
}
else if(lowguid)
data = objmgr.GetGOData(lowguid);
+
if(data)
const_cast<GameObjectData*>(data)->artKit = artkit;
}
+
void GameObject::SwitchDoorOrButton(bool activate, bool alternative /* = false */)
{
if(activate)
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
else
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_IN_USE);
+
if(GetGoState() == GO_STATE_READY) //if closed -> open
SetGoState(alternative ? GO_STATE_ACTIVE_ALTERNATIVE : GO_STATE_ACTIVE);
else //if open -> close
SetGoState(GO_STATE_READY);
}
+
void GameObject::Use(Unit* user)
{
// by default spell caster is user
Unit* spellCaster = user;
uint32 spellId = 0;
bool triggered = false;
+
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
GetMap()->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;
@@ -840,16 +980,22 @@ void GameObject::Use(Unit* user)
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;
@@ -858,16 +1004,20 @@ void GameObject::Use(Unit* user)
{
// 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;
@@ -889,9 +1039,11 @@ void GameObject::Use(Unit* user)
case GAMEOBJECT_TYPE_GOOBER: //10
{
GameObjectInfo const* info = GetGOInfo();
+
if(user->GetTypeId()==TYPEID_PLAYER)
{
Player* player = (Player*)user;
+
// show page
if(info->goober.pageId)
{
@@ -899,13 +1051,17 @@ void GameObject::Use(Unit* user)
data << GetGUID();
player->GetSession()->SendPacket(&data);
}
+
// possible quest objective for active quests
player->CastedCreatureOrGO(info->id, GetGUID(), 0);
+
if (info->goober.eventId)
GetMap()->ScriptsStart(sEventScripts, info->goober.eventId, player, this);
}
+
// cast this spell later if provided
spellId = info->goober.spellId;
+
break;
}
case GAMEOBJECT_TYPE_CAMERA: //13
@@ -913,13 +1069,18 @@ void GameObject::Use(Unit* user)
GameObjectInfo const* info = GetGOInfo();
if(!info)
return;
+
if(user->GetTypeId()!=TYPEID_PLAYER)
return;
+
Player* player = (Player*)user;
+
if (info->camera.cinematicId)
player->SendCinematicStart(info->camera.cinematicId);
+
if (info->camera.eventID)
GetMap()->ScriptsStart(sEventScripts, info->camera.eventID, player, this);
+
return;
}
//fishing bobber
@@ -927,9 +1088,12 @@ void GameObject::Use(Unit* user)
{
if(user->GetTypeId()!=TYPEID_PLAYER)
return;
+
Player* player = (Player*)user;
+
if(player->GetGUID() != GetOwnerGUID())
return;
+
switch(getLootState())
{
case GO_READY: // ready for loot
@@ -937,18 +1101,24 @@ void GameObject::Use(Unit* user)
// 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 zone, subzone;
GetZoneAndAreaId(zone,subzone);
+
int32 zone_skill = objmgr.GetFishingBaseSkillLevel( subzone );
if(!zone_skill)
zone_skill = objmgr.GetFishingBaseSkillLevel( zone );
+
//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(chance >= roll)
{
player->UpdateFishingSkill();
@@ -959,6 +1129,7 @@ void GameObject::Use(Unit* user)
// prevent removing GO at spell cancel
player->RemoveGameObject(this,false);
SetOwnerGUID(player->GetGUID());
+
//TODO: find reasonable value for fishing hole search
GameObject* ok = LookupFishingHoleAround(20.0f + CONTACT_DISTANCE);
if (ok)
@@ -978,34 +1149,47 @@ void GameObject::Use(Unit* user)
default:
{
SetLootState(GO_JUST_DEACTIVATED);
+
WorldPacket data(SMSG_FISH_NOT_HOOKED, 0);
player->GetSession()->SendPacket(&data);
break;
}
}
+
player->FinishSpell(CURRENT_CHANNELED_SPELL);
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->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
return;
+
spellId = info->summoningRitual.spellId;
if(spellId==62330) // GO store not existed spell, replace by expected
{
@@ -1014,41 +1198,54 @@ void GameObject::Use(Unit* user)
spellId = 61993;
triggered = true;
}
+
// finish spell
player->FinishSpell(CURRENT_CHANNELED_SPELL);
+
// 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)
@@ -1056,17 +1253,22 @@ void GameObject::Use(Unit* user)
level = targetPlayer->getLevel();
if (level < info->meetingstone.minLevel || level > info->meetingstone.maxLevel)
return;
+
if(info->id==194097)
spellId = 61994; // Ritual of Summoning
else
spellId = 59782; // Summoning Stone Effect
+
break;
}
+
case GAMEOBJECT_TYPE_FLAGSTAND: // 24
{
if(user->GetTypeId()!=TYPEID_PLAYER)
return;
+
Player* player = (Player*)user;
+
if( player->CanUseBattleGroundObject() )
{
// in battleground check
@@ -1089,7 +1291,9 @@ void GameObject::Use(Unit* user)
{
if(user->GetTypeId()!=TYPEID_PLAYER)
return;
+
Player* player = (Player*)user;
+
if( player->CanUseBattleGroundObject() )
{
// in battleground check
@@ -1133,13 +1337,18 @@ void GameObject::Use(Unit* user)
GameObjectInfo const* info = GetGOInfo();
if(!info)
return;
+
if(user->GetTypeId()!=TYPEID_PLAYER)
return;
+
Player* player = (Player*)user;
+
// 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);
+
WorldPacket data(SMSG_ENABLE_BARBER_SHOP, 0);
player->GetSession()->SendPacket(&data);
+
player->SetStandState(UNIT_STAND_STATE_SIT_LOW_CHAIR+info->barberChair.chairheight);
return;
}
@@ -1147,8 +1356,10 @@ void GameObject::Use(Unit* user)
sLog.outDebug("Unknown Object Type %u", GetGoType());
break;
}
+
if(!spellId)
return;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellId );
if(!spellInfo)
{
@@ -1158,18 +1369,23 @@ void GameObject::Use(Unit* user)
sLog.outDebug("WORLD: %u non-dbc spell was handled by OutdoorPvP", spellId);
return;
}
+
Spell *spell = new Spell(spellCaster, spellInfo, triggered);
//Spell *spell = new Spell(spellCaster, spellInfo, triggered,GetGUID());
+
// spell target is user of GO
SpellCastTargets targets;
targets.setUnitTarget( user );
+
spell->prepare(&targets);
}
+
void GameObject::CastSpell(Unit* target, uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return;
+
bool self = false;
for(uint8 i = 0; i < 3; ++i)
{
@@ -1179,15 +1395,18 @@ void GameObject::CastSpell(Unit* target, uint32 spellId)
break;
}
}
+
if(self)
{
if(target)
target->CastSpell(target, spellInfo, true);
return;
}
+
//summon world trigger
Creature *trigger = SummonTrigger(GetPositionX(), GetPositionY(), GetPositionZ(), 0, 1);
if(!trigger) return;
+
trigger->SetVisibility(VISIBILITY_OFF); //should this be true?
if(Unit *owner = GetOwner())
{
@@ -1197,13 +1416,14 @@ void GameObject::CastSpell(Unit* target, uint32 spellId)
else
{
trigger->setFaction(14);
- // Set owner guid for target if no owner avalible - needed by trigger auras
+ // Set owner guid for target if no owner avalible - needed by trigger auras
// - trigger gets despawned and there's no caster avalible (see AuraEffect::TriggerSpell())
trigger->CastSpell(target, spellInfo, true, 0, 0, target ? target->GetGUID() : 0);
}
//trigger->setDeathState(JUST_DIED);
//trigger->RemoveCorpse();
}
+
void GameObject::SendCustomAnim()
{
WorldPacket data(SMSG_GAMEOBJECT_CUSTOM_ANIM,8+4);
@@ -1211,11 +1431,13 @@ void GameObject::SendCustomAnim()
data << (uint32)0;
SendMessageToSet(&data, true);
}
+
bool GameObject::IsInRange(float x, float y, float z, float radius) const
{
GameObjectDisplayInfoEntry const * info = sGameObjectDisplayInfoStore.LookupEntry(GetUInt32Value(GAMEOBJECT_DISPLAYID));
if(!info)
return IsWithinDist3d(x, y, z, radius);
+
float sinA = sin(GetOrientation());
float cosA = cos(GetOrientation());
float dx = x - GetPositionX();
@@ -1230,19 +1452,23 @@ bool GameObject::IsInRange(float x, float y, float z, float radius) const
&& dy < info->maxY + radius && dy > info->minY - radius
&& dz < info->maxZ + radius && dz > info->minZ - radius;
}
+
void GameObject::TakenDamage(uint32 damage)
{
if(!m_goValue->building.health)
return;
+
if(m_goValue->building.health > damage)
m_goValue->building.health -= damage;
else
m_goValue->building.health = 0;
+
if(HasFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED)) // from damaged to destroyed
{
if(!m_goValue->building.health)
{
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED);
+
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DESTROYED);
SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->building.destroyedDisplayId);
EventInform(m_goInfo->building.destroyedEvent);
@@ -1256,12 +1482,14 @@ void GameObject::TakenDamage(uint32 damage)
m_goValue->building.health = 0;
else if(!m_goValue->building.health)
m_goValue->building.health = 1;
+
SetFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED);
SetUInt32Value(GAMEOBJECT_DISPLAYID, m_goInfo->building.damagedDisplayId);
EventInform(m_goInfo->building.damagedEvent);
}
}
}
+
void GameObject::Rebuild()
{
RemoveFlag(GAMEOBJECT_FLAGS, GO_FLAG_DAMAGED + GO_FLAG_DESTROYED);
@@ -1269,11 +1497,13 @@ void GameObject::Rebuild()
m_goValue->building.health = m_goInfo->building.intactNumHits + m_goInfo->building.damagedNumHits;
EventInform(m_goInfo->building.rebuildingEvent);
}
+
void GameObject::EventInform(uint32 eventId)
{
if(eventId && m_zoneScript)
m_zoneScript->ProcessEvent(this, eventId);
}
+
// overwrite WorldObject function for proper name localization
const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const
{
@@ -1286,27 +1516,36 @@ const char* GameObject::GetNameForLocaleIdx(int32 loc_idx) const
return cl->Name[loc_idx].c_str();
}
}
+
return GetName();
}
+
void GameObject::UpdateRotationFields(float rotation2 /*=0.0f*/, float rotation3 /*=0.0f*/)
{
static double const atan_pow = atan(pow(2.0f, -20.0f));
+
double f_rot1 = sin(GetOrientation() / 2.0f);
double f_rot2 = cos(GetOrientation() / 2.0f);
+
int64 i_rot1 = int64(f_rot1 / atan_pow *(f_rot2 >= 0 ? 1.0f : -1.0f));
int64 rotation = (i_rot1 << 43 >> 43) & 0x00000000001FFFFF;
+
//float f_rot2 = sin(0.0f / 2.0f);
//int64 i_rot2 = f_rot2 / atan(pow(2.0f, -20.0f));
//rotation |= (((i_rot2 << 22) >> 32) >> 11) & 0x000003FFFFE00000;
+
//float f_rot3 = sin(0.0f / 2.0f);
//int64 i_rot3 = f_rot3 / atan(pow(2.0f, -21.0f));
//rotation |= (i_rot3 >> 42) & 0x7FFFFC0000000000;
+
m_rotation = rotation;
+
if(rotation2==0.0f && rotation3==0.0f)
{
rotation2 = f_rot1;
rotation3 = f_rot2;
}
+
SetFloatValue(GAMEOBJECT_PARENTROTATION+2, rotation2);
SetFloatValue(GAMEOBJECT_PARENTROTATION+3, rotation3);
}
diff --git a/src/game/GameObject.h b/src/game/GameObject.h
index 7455925a1d5..358eb0cbfb7 100644
--- a/src/game/GameObject.h
+++ b/src/game/GameObject.h
@@ -17,19 +17,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_GAMEOBJECT_H
#define TRINITYCORE_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
{
@@ -387,6 +391,7 @@ struct GameObjectInfo
uint32 startOpen; // 1
uint32 autoClose; // 2
} trapDoor;
+
// not use for specific field access (only for output with loop by all filed), also this determinate max union size
struct
{
@@ -394,6 +399,7 @@ struct GameObjectInfo
} raw;
};
uint32 ScriptId;
+
// helpers
bool IsDespawnAtAction() const
{
@@ -404,6 +410,7 @@ struct GameObjectInfo
default: return false;
}
}
+
uint32 GetLockId() const
{
switch(type)
@@ -422,6 +429,7 @@ struct GameObjectInfo
default: return 0;
}
}
+
bool GetDespawnPossibility() const // despawn at targeting of cast?
{
switch(type)
@@ -435,6 +443,7 @@ struct GameObjectInfo
default: return true;
}
}
+
uint32 GetCharges() const // despawn at uses amount
{
switch(type)
@@ -445,6 +454,7 @@ struct GameObjectInfo
default: return 0;
}
}
+
uint32 GetLinkedGameObjectEntry() const
{
switch(type)
@@ -455,6 +465,7 @@ struct GameObjectInfo
default: return 0;
}
}
+
uint32 GetAutoCloseTime() const
{
uint32 autoCloseTime = 0;
@@ -470,6 +481,7 @@ struct GameObjectInfo
}
return autoCloseTime / 0x10000;
}
+
uint32 GetLootId() const
{
switch(type)
@@ -480,7 +492,9 @@ struct GameObjectInfo
}
}
};
+
class OPvPCapturePoint;
+
union GameObjectValue
{
//29 GAMEOBJECT_TYPE_CAPTURE_POINT
@@ -494,17 +508,20 @@ union GameObjectValue
uint32 health;
}building;
};
+
// 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 GameObjectLocale
{
std::vector<std::string> Name;
std::vector<std::string> CastBarCaption;
};
+
// client side GO show states
enum GOState
{
@@ -512,7 +529,9 @@ enum GOState
GO_STATE_READY = 1, // show in world as ready (closed door close)
GO_STATE_ACTIVE_ALTERNATIVE = 2 // show in world as used in alt way and not reset (closed door open by cannon fire)
};
+
#define MAX_GO_STATE 3
+
// from `gameobject`
struct GameObjectData
{
@@ -535,6 +554,7 @@ struct GameObjectData
uint8 artKit;
bool dbData;
};
+
// For containers: [GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY -> ...
// For bobber: GO_NOT_READY ->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED-><deleted>
// For door(closed):[GO_NOT_READY]->GO_READY (close)->GO_ACTIVATED (open) ->GO_JUST_DEACTIVATED->GO_READY(close) -> ...
@@ -546,37 +566,49 @@ enum LootState
GO_ACTIVATED,
GO_JUST_DEACTIVATED
};
+
class Unit;
+
// 5 sec for bobber catch
#define FISHING_BOBBER_READY_TIME 5
+
class TRINITY_DLL_SPEC GameObject : public WorldObject
{
public:
explicit GameObject();
~GameObject();
+
void AddToWorld();
void RemoveFromWorld();
void CleanupsBeforeDelete();
+
bool Create(uint32 guidlow, uint32 name_id, Map *map, uint32 phaseMask, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 animprogress, GOState go_state, uint32 artKit = 0);
void Update(uint32 p_time);
static GameObject* GetGameObject(WorldObject& object, uint64 guid);
GameObjectInfo const* GetGOInfo() const { return m_goInfo; }
GameObjectData const* GetGOData() const { return m_goData; }
GameObjectValue * GetGOValue() const { return m_goValue; }
+
bool IsTransport() const;
+
uint32 GetDBTableGUIDLow() const { return m_DBTableGuid; }
+
void UpdateRotationFields(float rotation2 = 0.0f, float rotation3 = 0.0f);
+
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 YellToZone(int32 textId, uint32 language, uint64 TargetGuid) { MonsterYellToZone(textId,language,TargetGuid); }
+
// overwrite WorldObject function for proper name localization
const char* GetNameForLocaleIdx(int32 locale_idx) const;
+
void SaveToDB();
void SaveToDB(uint32 mapid, uint8 spawnMask, uint32 phaseMask);
bool LoadFromDB(uint32 guid, Map *map);
void DeleteFromDB();
+
void SetOwnerGUID(uint64 owner)
{
// Owner already found and different than expected owner - remove object from old owner
@@ -589,12 +621,14 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject
}
uint64 GetOwnerGUID() const { return GetUInt64Value(OBJECT_FIELD_CREATED_BY); }
Unit* GetOwner(bool inWorld = true) const;
+
void SetSpellId(uint32 id)
{
m_spawnedByDefault = false; // all summoned object is despawned after delay
m_spellId = id;
}
uint32 GetSpellId() const { return m_spellId;}
+
time_t GetRespawnTime() const { return m_respawnTime; }
time_t GetRespawnTimeEx() const
{
@@ -604,6 +638,7 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject
else
return now;
}
+
void SetRespawnTime(int32 respawn)
{
m_respawnTime = respawn > 0 ? time(NULL) + respawn : 0;
@@ -631,9 +666,12 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject
uint8 GetGoAnimProgress() const { return GetByteValue(GAMEOBJECT_BYTES_1, 3); }
void SetGoAnimProgress(uint8 animprogress) { SetByteValue(GAMEOBJECT_BYTES_1, 3, animprogress); }
static void SetGoArtKit(uint8 artkit, GameObject *go, uint32 lowguid = 0);
+
void Use(Unit* user);
+
LootState getLootState() const { return m_lootState; }
void SetLootState(LootState s) { m_lootState = s; }
+
void AddToSkillupList(uint32 PlayerGuidLow) { m_SkillupList.push_back(PlayerGuidLow); }
bool IsInSkillupList(uint32 PlayerGuidLow) const
{
@@ -642,12 +680,17 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject
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;
@@ -655,17 +698,24 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject
// 0 = use `gameobject`.`spawntimesecs`
void ResetDoorOrButton();
+
void TriggeringLinkedGameObject( uint32 trapEntry, Unit* target);
+
bool isVisibleForInState(Player const* u, bool inVisibleList) const;
bool canDetectTrap(Player const* u, float distance) const;
+
GameObject* LookupFishingHoleAround(float range);
+
GridReference<GameObject> &GetGridRef() { return m_gridRef; }
+
void CastSpell(Unit *target, uint32 spell);
void SendCustomAnim();
bool IsInRange(float x, float y, float z, float radius) const;
void TakenDamage(uint32 damage);
void Rebuild();
+
void EventInform(uint32 eventId);
+
uint64 GetRotation() const { return m_rotation; }
protected:
uint32 m_spellId;
@@ -676,15 +726,19 @@ class TRINITY_DLL_SPEC GameObject : public WorldObject
time_t m_cooldownTime; // used as internal reaction delay time store (not state change reaction).
// For traps this: spell casting cooldown, for doors/buttons: reset time.
std::list<uint32> m_SkillupList;
+
std::set<uint32> m_unique_users;
uint32 m_usetimes;
+
uint32 m_DBTableGuid; ///< For new or temporary gameobjects is 0 for saved it is lowguid
GameObjectInfo const* m_goInfo;
GameObjectData const* m_goData;
GameObjectValue * const m_goValue;
+
uint64 m_rotation;
private:
void SwitchDoorOrButton(bool activate, bool alternative = false);
+
GridReference<GameObject> m_gridRef;
};
#endif
diff --git a/src/game/GlobalEvents.cpp b/src/game/GlobalEvents.cpp
index 25fd4550d40..a544113f5c8 100644
--- a/src/game/GlobalEvents.cpp
+++ b/src/game/GlobalEvents.cpp
@@ -17,9 +17,11 @@
* 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 "Database/DatabaseImpl.h"
@@ -29,10 +31,12 @@
#include "GlobalEvents.h"
#include "ObjectDefines.h"
#include "Corpse.h"
+
static void CorpsesEraseCallBack(QueryResult *result, bool bones)
{
if(!result)
return;
+
do
{
Field *fields = result->Fetch();
@@ -41,8 +45,11 @@ static void CorpsesEraseCallBack(QueryResult *result, bool bones)
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)
{
@@ -56,12 +63,15 @@ static void CorpsesEraseCallBack(QueryResult *result, bool bones)
///- 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;
}
+
/// Handle periodic erase of corpses and bones
static void CorpsesErase(bool bones,uint32 delay)
{
@@ -69,6 +79,7 @@ static void CorpsesErase(bool bones,uint32 delay)
//No SQL injection (uint32 and enum)
CharacterDatabase.AsyncPQuery(&CorpsesEraseCallBack, bones, "SELECT guid,position_x,position_y,map,player FROM corpse WHERE UNIX_TIMESTAMP()-time > '%u' AND corpse_type %s '0'", delay, (bones ? "=" : "<>"));
}
+
/// not thread guarded variant for call from other thread
void CorpsesErase()
{
diff --git a/src/game/GlobalEvents.h b/src/game/GlobalEvents.h
index 26b1fc80ae1..aa99c47985e 100644
--- a/src/game/GlobalEvents.h
+++ b/src/game/GlobalEvents.h
@@ -17,11 +17,14 @@
* 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
index 4f22573d2be..cd633f6ab8b 100644
--- a/src/game/GossipDef.cpp
+++ b/src/game/GossipDef.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,18 +25,23 @@
#include "WorldPacket.h"
#include "WorldSession.h"
#include "Formulas.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, const std::string& Message, uint32 dtSender, uint32 dtAction, const 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;
@@ -43,63 +49,81 @@ void GossipMenu::AddMenuItem(uint8 Icon, const std::string& Message, uint32 dtSe
gItem.m_gAction = dtAction;
gItem.m_gBoxMessage = BoxMessage;
gItem.m_gBoxMoney = BoxMoney;
+
m_gItems.push_back(gItem);
}
+
void GossipMenu::AddMenuItem(uint8 Icon, const std::string& Message, bool Coded)
{
AddMenuItem( Icon, Message, 0, 0, "", 0, Coded);
}
+
void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, bool Coded)
{
AddMenuItem(Icon, std::string(Message ? Message : ""),Coded);
}
+
void GossipMenu::AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded)
{
AddMenuItem(Icon, std::string(Message ? Message : ""), dtSender, dtAction, std::string(BoxMessage ? BoxMessage : ""), BoxMoney, Coded);
}
+
uint32 GossipMenu::MenuItemSender( unsigned int ItemId )
{
if ( ItemId >= m_gItems.size() ) return 0;
+
return m_gItems[ ItemId ].m_gSender;
}
+
uint32 GossipMenu::MenuItemAction( unsigned int ItemId )
{
if ( ItemId >= m_gItems.size() ) return 0;
+
return m_gItems[ ItemId ].m_gAction;
}
+
bool GossipMenu::MenuItemCoded( unsigned int ItemId )
{
if ( ItemId >= m_gItems.size() ) return 0;
+
return m_gItems[ ItemId ].m_gCoded;
}
+
void GossipMenu::ClearMenu()
{
m_gItems.clear();
}
+
PlayerMenu::PlayerMenu( WorldSession *session ) : pSession(session)
{
}
+
PlayerMenu::~PlayerMenu()
{
ClearMenus();
}
+
void PlayerMenu::ClearMenus()
{
mGossipMenu.ClearMenu();
mQuestMenu.ClearMenu();
}
+
uint32 PlayerMenu::GossipOptionSender( unsigned int Selection )
{
return mGossipMenu.MenuItemSender( Selection );
}
+
uint32 PlayerMenu::GossipOptionAction( unsigned int Selection )
{
return mGossipMenu.MenuItemAction( Selection );
}
+
bool PlayerMenu::GossipOptionCoded( unsigned int Selection )
{
return mGossipMenu.MenuItemCoded( Selection );
}
+
void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
{
WorldPacket data( SMSG_GOSSIP_MESSAGE, (100) ); // guess size
@@ -107,6 +131,7 @@ void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
data << uint32(0); // new 2.4.0
data << uint32( TitleTextId );
data << uint32( mGossipMenu.MenuItemCount() ); // max count 0x10
+
for (uint32 iI = 0; iI < mGossipMenu.MenuItemCount(); ++iI )
{
GossipMenuItem const& gItem = mGossipMenu.GetItem(iI);
@@ -117,16 +142,20 @@ void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
data << gItem.m_gMessage; // text for gossip item
data << gItem.m_gBoxMessage; // accept text (related to money) pop up box, 2.0.3
}
+
data << uint32( mQuestMenu.MenuItemCount() ); // max count 0x20
+
for (uint32 iI = 0; iI < mQuestMenu.MenuItemCount(); ++iI )
{
QuestMenuItem const& qItem = mQuestMenu.GetItem(iI);
uint32 questID = qItem.m_qId;
Quest const* pQuest = objmgr.GetQuestTemplate(questID);
+
data << uint32(questID);
data << uint32(qItem.m_qIcon);
data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest));
std::string Title = pQuest->GetTitle();
+
int loc_idx = pSession->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -139,15 +168,19 @@ void PlayerMenu::SendGossipMenu( uint32 TitleTextId, uint64 npcGUID )
}
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" );
}
+
// Outdated
void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, char const * locName )
{
@@ -157,9 +190,11 @@ void PlayerMenu::SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flag
data << uint32(Icon);
data << uint32(Data);
data << locName;
+
pSession->SendPacket( &data );
//sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI");
}
+
void PlayerMenu::SendPointOfInterest( uint32 poi_id )
{
PointOfInterest const* poi = objmgr.GetPointOfInterest(poi_id);
@@ -168,7 +203,9 @@ void PlayerMenu::SendPointOfInterest( uint32 poi_id )
sLog.outErrorDb("Requested send not existed POI (Id: %u), ignore.",poi_id);
return;
}
+
std::string icon_name = poi->icon_name;
+
int loc_idx = pSession->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -179,6 +216,7 @@ void PlayerMenu::SendPointOfInterest( uint32 poi_id )
icon_name = pl->IconName[loc_idx];
}
}
+
WorldPacket data( SMSG_GOSSIP_POI, (4+4+4+4+4+10) ); // guess size
data << uint32(poi->flags);
data << float(poi->x);
@@ -186,14 +224,18 @@ void PlayerMenu::SendPointOfInterest( uint32 poi_id )
data << uint32(poi->icon);
data << uint32(poi->data);
data << icon_name;
+
pSession->SendPacket( &data );
//sLog.outDebug("WORLD: Sent SMSG_GOSSIP_POI");
}
+
void PlayerMenu::SendTalking( uint32 textID )
{
GossipText const* 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)
@@ -236,15 +278,19 @@ void PlayerMenu::SendTalking( uint32 textID )
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;
+
for(int j = 0; j < 3; ++j)
{
data << pGossip->Options[i].Emotes[j]._Delay;
@@ -253,8 +299,10 @@ void PlayerMenu::SendTalking( uint32 textID )
}
}
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
@@ -272,30 +320,41 @@ void PlayerMenu::SendTalking( char const * title, char const * text )
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::const_iterator i = m_qItems.begin(); i != m_qItems.end(); ++i)
@@ -307,10 +366,12 @@ bool QuestMenu::HasItem( uint32 questid )
}
return false;
}
+
void QuestMenu::ClearMenu()
{
m_qItems.clear();
}
+
void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Title, uint64 npcGUID )
{
WorldPacket data( SMSG_QUESTGIVER_QUEST_LIST, 100 ); // guess size
@@ -319,12 +380,16 @@ void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Titl
data << uint32(eEmote._Delay ); // player emote
data << uint32(eEmote._Emote ); // NPC emote
data << uint8 ( mQuestMenu.MenuItemCount() );
+
for (uint32 iI = 0; iI < mQuestMenu.MenuItemCount(); ++iI )
{
QuestMenuItem const& qmi = mQuestMenu.GetItem(iI);
+
uint32 questID = qmi.m_qId;
Quest const *pQuest = objmgr.GetQuestTemplate(questID);
+
std::string title = pQuest ? pQuest->GetTitle() : "";
+
int loc_idx = pSession->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -334,6 +399,7 @@ void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Titl
title=ql->Title[loc_idx];
}
}
+
data << uint32(questID);
data << uint32(qmi.m_qIcon);
data << uint32(pSession->GetPlayer()->GetQuestLevel(pQuest));
@@ -342,21 +408,26 @@ void PlayerMenu::SendQuestGiverQuestList( QEmote eEmote, const std::string& Titl
pSession->SendPacket( &data );
sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_LIST NPC Guid=%u", GUID_LOPART(npcGUID));
}
+
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)
{
@@ -373,6 +444,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID
EndText=ql->EndText[loc_idx];
}
}
+
data << uint64(npcGUID);
data << uint64(0); // wotlk, something todo with quest sharing?
data << uint32(pQuest->GetQuestId());
@@ -383,6 +455,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID
data << uint32(pQuest->GetSuggestedPlayers());
data << uint8(0); // new wotlk
data << uint8(0); // new 3.1
+
if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
{
data << uint32(0); // Rewarded chosen items hidden
@@ -392,6 +465,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID
else
{
ItemPrototype const* IProto;
+
data << uint32(pQuest->GetRewChoiceItemsCount());
for (uint32 i=0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
{
@@ -404,6 +478,7 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID
else
data << uint32( 0x00 );
}
+
data << uint32(pQuest->GetRewItemsCount());
for (uint32 i=0; i < QUEST_REWARDS_COUNT; ++i)
{
@@ -416,14 +491,17 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID
else
data << uint32(0);
}
+
data << uint32(pQuest->GetRewOrReqMoney());
}
+
// rewarded honor points. Multiply with 10 to satisfy client
data << uint32(10*Trinity::Honor::hk_honor_at_level(pSession->GetPlayer()->getLevel(), pQuest->GetRewHonorableKills()));
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(pQuest->GetBonusTalents()); // bonus talents
+
data << uint32(QUEST_EMOTE_COUNT);
for (uint32 i=0; i < QUEST_EMOTE_COUNT; ++i)
{
@@ -431,8 +509,10 @@ void PlayerMenu::SendQuestGiverQuestDetails( Quest const *pQuest, uint64 npcGUID
data << uint32(pQuest->DetailsEmoteDelay[i]); // DetailsEmoteDelay (in ms)
}
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;
@@ -443,6 +523,7 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
EndText = pQuest->GetEndText();
for (int i=0;i<QUEST_OBJECTIVES_COUNT;++i)
ObjectiveText[i]=pQuest->ObjectiveText[i];
+
int loc_idx = pSession->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -457,30 +538,40 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
Objectives=ql->Objectives[loc_idx];
if (ql->EndText.size() > loc_idx && !ql->EndText[loc_idx].empty())
EndText=ql->EndText[loc_idx];
+
for (int i=0;i<QUEST_OBJECTIVES_COUNT;++i)
if (ql->ObjectiveText[i].size() > loc_idx && !ql->ObjectiveText[i][loc_idx].empty())
ObjectiveText[i]=ql->ObjectiveText[i][loc_idx];
}
}
+
WorldPacket data( SMSG_QUEST_QUERY_RESPONSE, 100 ); // guess size
+
data << uint32(pQuest->GetQuestId());
data << uint32(pQuest->GetQuestMethod()); // Accepted values: 0, 1 or 2. 0==IsAutoComplete() (skip objectives/details)
data << uint32(pQuest->GetQuestLevel()); // may be 0, static data, in other cases must be used dynamic level: Player::GetQuestLevel
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
+
// rewarded honor points
data << uint32(Trinity::Honor::hk_honor_at_level(pSession->GetPlayer()->getLevel(), pQuest->GetRewHonorableKills()));
data << uint32(pQuest->GetSrcItemId());
@@ -488,7 +579,9 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
data << uint32(pQuest->GetCharTitleId()); // CharTitleId, new 2.4.0, player gets this title (id from CharTitles)
data << uint32(pQuest->GetPlayersSlain()); // players slain
data << uint32(pQuest->GetBonusTalents()); // bonus talents
+
int iI;
+
if (pQuest->HasFlag(QUEST_FLAGS_HIDDEN_REWARDS))
{
for (iI = 0; iI < QUEST_REWARDS_COUNT; ++iI)
@@ -509,14 +602,17 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
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)
@@ -531,22 +627,28 @@ void PlayerMenu::SendQuestQueryResponse( Quest const *pQuest )
data << uint32(pQuest->ReqCreatureOrGOCount[iI]);
data << uint32(pQuest->ReqSourceId[iI]);
}
+
for (iI = 0; iI < QUEST_OBJECTIVES_COUNT; ++iI)
{
data << uint32(pQuest->ReqItemId[iI]);
data << uint32(pQuest->ReqItemCount[iI]);
}
+
data << uint32(0); // TODO: 5 item objective
data << uint32(0);
+
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 EnableNext )
{
std::string Title = pQuest->GetTitle();
std::string OfferRewardText = pQuest->GetOfferRewardText();
+
int loc_idx = pSession->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -559,13 +661,17 @@ void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID,
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( EnableNext );
data << uint32(0); // unk
+
uint32 EmoteCount = 0;
for (uint32 i = 0; i < QUEST_EMOTE_COUNT; ++i)
{
@@ -573,36 +679,45 @@ void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID,
break;
++EmoteCount;
}
+
data << EmoteCount; // Emote Count
for (uint32 i = 0; i < EmoteCount; ++i)
{
data << uint32(pQuest->OfferRewardEmoteDelay[i]); // Delay Emote
data << uint32(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());
+
// rewarded honor points. Multiply with 10 to satisfy client
data << uint32(10*Trinity::Honor::hk_honor_at_level(pSession->GetPlayer()->getLevel(), pQuest->GetRewHonorableKills()));
data << uint32(0x08); // unused by client?
@@ -613,12 +728,15 @@ void PlayerMenu::SendQuestGiverOfferReward( Quest const* pQuest, uint64 npcGUID,
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
+
std::string Title = pQuest->GetTitle();
std::string RequestItemsText = pQuest->GetRequestItemsText();
+
int loc_idx = pSession->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -631,29 +749,37 @@ void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID
RequestItemsText=ql->RequestItemsText[loc_idx];
}
}
+
if (!pQuest->GetReqItemsCount() && Completable)
{
SendQuestGiverOfferReward(pQuest, npcGUID, true);
return;
}
+
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)
@@ -662,18 +788,22 @@ void PlayerMenu::SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID
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);
data << uint32(0x08);
data << uint32(0x10);
+
pSession->SendPacket( &data );
sLog.outDebug( "WORLD: Sent SMSG_QUESTGIVER_REQUEST_ITEMS NPCGuid=%u, questid=%u", GUID_LOPART(npcGUID), pQuest->GetQuestId() );
}
diff --git a/src/game/GossipDef.h b/src/game/GossipDef.h
index b70348f1715..88c15f334a1 100644
--- a/src/game/GossipDef.h
+++ b/src/game/GossipDef.h
@@ -17,14 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_GOSSIP_H
#define TRINITYCORE_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 icons. Many more exist, list not complete.
enum Poi_Icon
{
@@ -70,6 +75,7 @@ enum Poi_Icon
ICON_POI_RWHORSE = 39, // Red and White Horse
ICON_POI_REDHORSE = 40 // Red Horse
};
+
struct GossipMenuItem
{
uint8 m_gIcon;
@@ -80,94 +86,123 @@ struct GossipMenuItem
std::string m_gBoxMessage;
uint32 m_gBoxMoney;
};
+
typedef std::vector<GossipMenuItem> GossipMenuItemList;
+
struct QuestMenuItem
{
uint32 m_qId;
uint8 m_qIcon;
};
+
typedef std::vector<QuestMenuItem> QuestMenuItemList;
+
class TRINITY_DLL_SPEC GossipMenu
{
public:
GossipMenu();
~GossipMenu();
+
void AddMenuItem(uint8 Icon, const std::string& Message, bool Coded = false);
void AddMenuItem(uint8 Icon, const std::string& Message, uint32 dtSender, uint32 dtAction, const std::string& BoxMessage, uint32 BoxMoney, bool Coded = false);
+
// for using from scripts, don't must be inlined
void AddMenuItem(uint8 Icon, char const* Message, bool Coded = false);
void AddMenuItem(uint8 Icon, char const* Message, uint32 dtSender, uint32 dtAction, char const* BoxMessage, uint32 BoxMoney, bool Coded = false);
+
unsigned int MenuItemCount() const
{
return m_gItems.size();
}
+
bool Empty() const
{
return m_gItems.empty();
}
+
GossipMenuItem const& GetItem( unsigned int Id )
{
return m_gItems[ Id ];
}
+
uint32 MenuItemSender( unsigned int ItemId );
uint32 MenuItemAction( unsigned int ItemId );
bool MenuItemCoded( unsigned int ItemId );
+
void ClearMenu();
+
protected:
GossipMenuItemList m_gItems;
};
+
class QuestMenu
{
public:
QuestMenu();
~QuestMenu();
+
void AddMenuItem( uint32 QuestId, uint8 Icon);
void ClearMenu();
+
uint8 MenuItemCount() const
{
return m_qItems.size();
}
+
bool Empty() const
{
return m_qItems.empty();
}
+
bool HasItem( uint32 questid );
+
QuestMenuItem const& GetItem( uint16 Id )
{
return m_qItems[ Id ];
}
+
protected:
QuestMenuItemList m_qItems;
};
+
class TRINITY_DLL_SPEC PlayerMenu
{
private:
GossipMenu mGossipMenu;
QuestMenu mQuestMenu;
WorldSession* pSession;
+
public:
PlayerMenu( WorldSession *Session );
~PlayerMenu();
+
GossipMenu& GetGossipMenu() { return mGossipMenu; }
QuestMenu& GetQuestMenu() { return mQuestMenu; }
+
bool Empty() const { return mGossipMenu.Empty() && mQuestMenu.Empty(); }
+
void ClearMenus();
uint32 GossipOptionSender( unsigned int Selection );
uint32 GossipOptionAction( unsigned int Selection );
bool GossipOptionCoded( unsigned int Selection );
+
void SendGossipMenu( uint32 TitleTextId, uint64 npcGUID );
void CloseGossip();
void SendPointOfInterest( float X, float Y, uint32 Icon, uint32 Flags, uint32 Data, const char * locName );
void SendPointOfInterest( uint32 poi_id );
void SendTalking( uint32 textID );
void SendTalking( char const * title, char const * text );
+
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
void SendQuestGiverStatus( uint8 questStatus, uint64 npcGUID );
+
void SendQuestGiverQuestList( QEmote eEmote, const 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 EnableNext );
void SendQuestGiverRequestItems( Quest const *pQuest, uint64 npcGUID, bool Completable, bool CloseOnCancel );
};
diff --git a/src/game/GridDefines.h b/src/game/GridDefines.h
index 403270fa1fd..c51b507fcbd 100644
--- a/src/game/GridDefines.h
+++ b/src/game/GridDefines.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GRIDDEFINES_H
#define TRINITY_GRIDDEFINES_H
+
#include "Common.h"
#include "GameSystem/NGrid.h"
#include <cmath>
+
// Forward class definitions
class Corpse;
class Creature;
@@ -29,32 +32,47 @@ class DynamicObject;
class GameObject;
class Pet;
class Player;
+
#define MAX_NUMBER_OF_CELLS 8
+
#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*IN_MILISECONDS)
#define MIN_MAP_UPDATE_DELAY 50
+
#define SIZE_OF_GRID_CELL (SIZE_OF_GRIDS/MAX_NUMBER_OF_CELLS)
+
#define CENTER_GRID_CELL_ID (MAX_NUMBER_OF_CELLS*MAX_NUMBER_OF_GRIDS/2)
#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 128
+
#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_4(Player, Creature/*pets*/, Corpse/*resurrectable*/, DynamicObject/*farsight target*/) AllWorldObjectTypes;
typedef TYPELIST_4(GameObject, Creature/*except pets*/, DynamicObject, Corpse/*Bones*/) AllGridObjectTypes;
+
typedef GridRefManager<Corpse> CorpseMapType;
typedef GridRefManager<Creature> CreatureMapType;
typedef GridRefManager<DynamicObject> DynamicObjectMapType;
typedef GridRefManager<GameObject> GameObjectMapType;
typedef GridRefManager<Player> PlayerMapType;
+
typedef Grid<Player, AllWorldObjectTypes,AllGridObjectTypes> GridType;
typedef NGrid<MAX_NUMBER_OF_CELLS, Player, AllWorldObjectTypes, AllGridObjectTypes> NGridType;
+
typedef TypeMapContainer<AllGridObjectTypes> GridTypeMapContainer;
typedef TypeMapContainer<AllWorldObjectTypes> WorldTypeMapContainer;
+
template<const unsigned int LIMIT>
struct TRINITY_DLL_DECL CoordPair
{
@@ -68,6 +86,7 @@ struct TRINITY_DLL_DECL CoordPair
y_coord = obj.y_coord;
return *this;
}
+
void operator<<(const uint32 val)
{
if( x_coord > val )
@@ -75,6 +94,7 @@ struct TRINITY_DLL_DECL CoordPair
else
x_coord = 0;
}
+
void operator>>(const uint32 val)
{
if( x_coord+val < LIMIT )
@@ -82,6 +102,7 @@ struct TRINITY_DLL_DECL CoordPair
else
x_coord = LIMIT - 1;
}
+
void operator-=(const uint32 val)
{
if( y_coord > val )
@@ -89,6 +110,7 @@ struct TRINITY_DLL_DECL CoordPair
else
y_coord = 0;
}
+
void operator+=(const uint32 val)
{
if( y_coord+val < LIMIT )
@@ -96,11 +118,14 @@ struct TRINITY_DLL_DECL CoordPair
else
y_coord = LIMIT - 1;
}
+
uint32 x_coord;
uint32 y_coord;
};
+
typedef CoordPair<MAX_NUMBER_OF_GRIDS> GridPair;
typedef CoordPair<TOTAL_NUMBER_OF_CELLS_PER_MAP> CellPair;
+
namespace Trinity
{
template<class RET_TYPE, int CENTER_VAL>
@@ -109,28 +134,34 @@ namespace Trinity
// 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<GridPair, CENTER_GRID_ID>(x, y, CENTER_GRID_OFFSET, SIZE_OF_GRIDS);
}
+
inline CellPair ComputeCellPair(float x, float y)
{
return Compute<CellPair, CENTER_GRID_CELL_ID>(x, y, CENTER_GRID_CELL_OFFSET, SIZE_OF_GRID_CELL);
}
+
inline CellPair ComputeCellPair(float x, float y, float &x_off, float &y_off)
{
double x_offset = (double(x) - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL;
double y_offset = (double(y) - CENTER_GRID_CELL_OFFSET)/SIZE_OF_GRID_CELL;
+
int x_val = int(x_offset + CENTER_GRID_CELL_ID + 0.5);
int y_val = int(y_offset + CENTER_GRID_CELL_ID + 0.5);
x_off = (float(x_offset) - x_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL;
y_off = (float(y_offset) - y_val + CENTER_GRID_CELL_ID) * SIZE_OF_GRID_CELL;
return CellPair(x_val, y_val);
}
+
inline void NormalizeMapCoord(float &c)
{
if(c > MAP_HALFSIZE - 0.5)
@@ -138,18 +169,22 @@ namespace Trinity
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);
diff --git a/src/game/GridNotifiers.cpp b/src/game/GridNotifiers.cpp
index 9673db24732..11cbb741e45 100644
--- a/src/game/GridNotifiers.cpp
+++ b/src/game/GridNotifiers.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -25,7 +26,9 @@
#include "Map.h"
#include "Transports.h"
#include "ObjectAccessor.h"
+
using namespace Trinity;
+
void
VisibleChangesNotifier::Visit(PlayerMapType &m)
{
@@ -33,7 +36,9 @@ VisibleChangesNotifier::Visit(PlayerMapType &m)
{
if(iter->getSource() == &i_object)
continue;
+
iter->getSource()->UpdateVisibilityOf(&i_object);
+
if(!iter->getSource()->GetSharedVisionList().empty())
for(SharedVisionList::const_iterator i = iter->getSource()->GetSharedVisionList().begin();
i != iter->getSource()->GetSharedVisionList().end(); ++i)
@@ -41,6 +46,7 @@ VisibleChangesNotifier::Visit(PlayerMapType &m)
(*i)->UpdateVisibilityOf(&i_object);
}
}
+
void
VisibleChangesNotifier::Visit(CreatureMapType &m)
{
@@ -51,6 +57,7 @@ VisibleChangesNotifier::Visit(CreatureMapType &m)
if((*i)->m_seer == iter->getSource())
(*i)->UpdateVisibilityOf(&i_object);
}
+
void
VisibleChangesNotifier::Visit(DynamicObjectMapType &m)
{
@@ -60,6 +67,7 @@ VisibleChangesNotifier::Visit(DynamicObjectMapType &m)
if(caster->m_seer == iter->getSource())
caster->UpdateVisibilityOf(&i_object);
}
+
void
PlayerVisibilityNotifier::Notify()
{
@@ -77,16 +85,19 @@ PlayerVisibilityNotifier::Notify()
}
}
}
+
// 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 TRINITY_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
}
+
if( i_data.HasData() )
{
/*uint32 entry = 0, map;
@@ -99,30 +110,37 @@ PlayerVisibilityNotifier::Notify()
y = (*i_visibleNow.begin())->GetPositionY();
sLog.outError("notify %u %u %f %f", entry, map, x, y);
}*/
+
// 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<uint64> const& oor = i_data.GetOutOfRangeGUIDs();
for(std::set<uint64>::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
+
// send data at target visibility change (adding to client)
for(std::set<WorldObject*>::const_iterator vItr = i_visibleNow.begin(); vItr != i_visibleNow.end(); ++vItr)
// target aura duration for caster show only if target exist at caster client
if((*vItr)!=&i_player && (*vItr)->isType(TYPEMASK_UNIT))
i_player.SendInitialVisiblePackets((Unit*)(*vItr));
+
if(i_visibleNow.size() >= 30)
i_player.SetToNotify();
}
+
void
MessageDistDeliverer::Visit(PlayerMapType &m)
{
@@ -131,8 +149,10 @@ MessageDistDeliverer::Visit(PlayerMapType &m)
Player *target = iter->getSource();
if(!target->InSamePhase(i_phaseMask))
continue;
+
if(target->GetExactDistSq(i_source) > i_distSq)
continue;
+
// Send packet to all who are sharing the player's vision
if (!target->GetSharedVisionList().empty())
{
@@ -141,10 +161,12 @@ MessageDistDeliverer::Visit(PlayerMapType &m)
if((*i)->m_seer == target)
SendPacket(*i);
}
+
if(target->m_seer == target || target->GetVehicle())
SendPacket(target);
}
}
+
void
MessageDistDeliverer::Visit(CreatureMapType &m)
{
@@ -152,8 +174,10 @@ MessageDistDeliverer::Visit(CreatureMapType &m)
{
if(!iter->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(iter->getSource()->GetExactDistSq(i_source) > i_distSq)
continue;
+
// Send packet to all who are sharing the creature's vision
if (!iter->getSource()->GetSharedVisionList().empty())
{
@@ -164,6 +188,7 @@ MessageDistDeliverer::Visit(CreatureMapType &m)
}
}
}
+
void
MessageDistDeliverer::Visit(DynamicObjectMapType &m)
{
@@ -171,8 +196,10 @@ MessageDistDeliverer::Visit(DynamicObjectMapType &m)
{
if(!iter->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(iter->getSource()->GetExactDistSq(i_source) > i_distSq)
continue;
+
if (IS_PLAYER_GUID(iter->getSource()->GetCasterGUID()))
{
// Send packet back to the caster if the caster has vision of dynamic object
@@ -182,6 +209,7 @@ MessageDistDeliverer::Visit(DynamicObjectMapType &m)
}
}
}
+
/*
void
MessageDistDeliverer::VisitObject(Player* plr)
@@ -192,6 +220,7 @@ MessageDistDeliverer::VisitObject(Player* plr)
}
}
*/
+
template<class T> void
ObjectUpdater::Visit(GridRefManager<T> &m)
{
@@ -201,17 +230,23 @@ ObjectUpdater::Visit(GridRefManager<T> &m)
iter->getSource()->Update(i_timeDiff);
}
}
+
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;
}
+
template void ObjectUpdater::Visit<GameObject>(GameObjectMapType &);
template void ObjectUpdater::Visit<DynamicObject>(DynamicObjectMapType &);
diff --git a/src/game/GridNotifiers.h b/src/game/GridNotifiers.h
index 54e3e466c41..b76bbb27256 100644
--- a/src/game/GridNotifiers.h
+++ b/src/game/GridNotifiers.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GRIDNOTIFIERS_H
#define TRINITY_GRIDNOTIFIERS_H
+
#include "ObjectGridLoader.h"
#include "UpdateData.h"
#include <iostream>
+
#include "Corpse.h"
#include "Object.h"
#include "DynamicObject.h"
@@ -29,8 +32,10 @@
#include "Player.h"
#include "Unit.h"
#include "CreatureAI.h"
+
class Player;
//class Map;
+
namespace Trinity
{
struct TRINITY_DLL_DECL PlayerVisibilityNotifier
@@ -39,10 +44,14 @@ namespace Trinity
UpdateData i_data;
Player::ClientGUIDs i_clientGUIDs;
std::set<WorldObject*> i_visibleNow;
+
PlayerVisibilityNotifier(Player &player) : i_player(player),i_clientGUIDs(player.m_clientGUIDs) {}
+
template<class T> inline void Visit(GridRefManager<T> &);
+
void Notify(void);
};
+
struct TRINITY_DLL_DECL PlayerRelocationNotifier : public PlayerVisibilityNotifier
{
PlayerRelocationNotifier(Player &player) : PlayerVisibilityNotifier(player) {}
@@ -52,6 +61,7 @@ namespace Trinity
template<> inline void Visit(CreatureMapType &);
#endif
};
+
struct TRINITY_DLL_DECL CreatureRelocationNotifier
{
Creature &i_creature;
@@ -62,31 +72,37 @@ namespace Trinity
template<> inline void Visit(CreatureMapType &);
#endif
};
+
struct TRINITY_DLL_DECL VisibleChangesNotifier
{
WorldObject &i_object;
+
explicit VisibleChangesNotifier(WorldObject &object) : i_object(object) {}
template<class T> void Visit(GridRefManager<T> &) {}
void Visit(PlayerMapType &);
void Visit(CreatureMapType &);
void Visit(DynamicObjectMapType &);
};
+
struct TRINITY_DLL_DECL GridUpdater
{
GridType &i_grid;
uint32 i_timeDiff;
GridUpdater(GridType &grid, uint32 diff) : i_grid(grid), i_timeDiff(diff) {}
+
template<class T> void updateObjects(GridRefManager<T> &m)
{
for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
iter->getSource()->Update(i_timeDiff);
}
+
void Visit(PlayerMapType &m) { updateObjects<Player>(m); }
void Visit(CreatureMapType &m){ updateObjects<Creature>(m); }
void Visit(GameObjectMapType &m) { updateObjects<GameObject>(m); }
void Visit(DynamicObjectMapType &m) { updateObjects<DynamicObject>(m); }
void Visit(CorpseMapType &m) { updateObjects<Corpse>(m); }
};
+
struct TRINITY_DLL_DECL MessageDistDeliverer
{
WorldObject *i_source;
@@ -103,14 +119,17 @@ namespace Trinity
void Visit(CreatureMapType &m);
void Visit(DynamicObjectMapType &m);
template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
+
void SendPacket(Player* plr)
{
// never send packet to self
if(plr == i_source || team && plr->GetTeam() != team)
return;
+
plr->GetSession()->SendPacket(i_message);
}
};
+
struct TRINITY_DLL_DECL ObjectUpdater
{
uint32 i_timeDiff;
@@ -120,15 +139,18 @@ namespace Trinity
void Visit(CorpseMapType &) {}
void Visit(CreatureMapType &);
};
+
template<class T>
struct TRINITY_DLL_DECL ObjectAccessorNotifier
{
T *& i_object;
+
uint64 i_id;
ObjectAccessorNotifier(T * &obj, uint64 id) : i_object(obj), i_id(id)
{
i_object = NULL;
}
+
void Visit(GridRefManager<T> &m )
{
if( i_object == NULL )
@@ -141,8 +163,10 @@ namespace Trinity
}
}
}
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
struct TRINITY_DLL_DECL DynamicObjectUpdater
{
DynamicObject &i_dynobject;
@@ -154,58 +178,74 @@ namespace Trinity
if(owner)
i_check = owner;
}
+
template<class T> inline void Visit(GridRefManager<T> &) {}
#ifdef WIN32
template<> inline void Visit<Player>(PlayerMapType &);
template<> inline void Visit<Creature>(CreatureMapType &);
#endif
+
void VisitHelper(Unit* target);
};
+
// SEARCHERS & LIST SEARCHERS & WORKERS
+
// WorldObject searchers & workers
+
template<class Check>
struct TRINITY_DLL_DECL WorldObjectSearcher
{
uint32 i_phaseMask;
WorldObject* &i_object;
Check &i_check;
+
WorldObjectSearcher(WorldObject const* searcher, WorldObject* & result, Check& check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(GameObjectMapType &m);
void Visit(PlayerMapType &m);
void Visit(CreatureMapType &m);
void Visit(CorpseMapType &m);
void Visit(DynamicObjectMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Check>
struct TRINITY_DLL_DECL WorldObjectListSearcher
{
uint32 i_phaseMask;
std::list<WorldObject*> &i_objects;
Check& i_check;
+
WorldObjectListSearcher(WorldObject const* searcher, std::list<WorldObject*> &objects, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {}
+
void Visit(PlayerMapType &m);
void Visit(CreatureMapType &m);
void Visit(CorpseMapType &m);
void Visit(GameObjectMapType &m);
void Visit(DynamicObjectMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Do>
struct TRINITY_DLL_DECL WorldObjectWorker
{
uint32 i_phaseMask;
Do const& i_do;
+
WorldObjectWorker(WorldObject const* searcher, Do const& _do)
: i_phaseMask(searcher->GetPhaseMask()), i_do(_do) {}
+
void Visit(GameObjectMapType &m)
{
for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
if(itr->getSource()->InSamePhase(i_phaseMask))
i_do(itr->getSource());
}
+
void Visit(PlayerMapType &m)
{
for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
@@ -218,32 +258,41 @@ namespace Trinity
if(itr->getSource()->InSamePhase(i_phaseMask))
i_do(itr->getSource());
}
+
void Visit(CorpseMapType &m)
{
for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
if(itr->getSource()->InSamePhase(i_phaseMask))
i_do(itr->getSource());
}
+
void Visit(DynamicObjectMapType &m)
{
for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
if(itr->getSource()->InSamePhase(i_phaseMask))
i_do(itr->getSource());
}
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Gameobject searchers
+
template<class Check>
struct TRINITY_DLL_DECL GameObjectSearcher
{
uint32 i_phaseMask;
GameObject* &i_object;
Check &i_check;
+
GameObjectSearcher(WorldObject const* searcher, GameObject* & result, Check& check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(GameObjectMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Last accepted by Check GO if any (Check can change requirements at each call)
template<class Check>
struct TRINITY_DLL_DECL GameObjectLastSearcher
@@ -251,23 +300,32 @@ namespace Trinity
uint32 i_phaseMask;
GameObject* &i_object;
Check& i_check;
+
GameObjectLastSearcher(WorldObject const* searcher, GameObject* & result, Check& check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result), i_check(check) {}
+
void Visit(GameObjectMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Check>
struct TRINITY_DLL_DECL GameObjectListSearcher
{
uint32 i_phaseMask;
std::list<GameObject*> &i_objects;
Check& i_check;
+
GameObjectListSearcher(WorldObject const* searcher, std::list<GameObject*> &objects, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_objects(objects), i_check(check) {}
+
void Visit(GameObjectMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Unit searchers
+
// First accepted by Check Unit if any
template<class Check>
struct TRINITY_DLL_DECL UnitSearcher
@@ -275,12 +333,16 @@ namespace Trinity
uint32 i_phaseMask;
Unit* &i_object;
Check & i_check;
+
UnitSearcher(WorldObject const* searcher, Unit* & result, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(CreatureMapType &m);
void Visit(PlayerMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Last accepted by Check Unit if any (Check can change requirements at each call)
template<class Check>
struct TRINITY_DLL_DECL UnitLastSearcher
@@ -288,12 +350,16 @@ namespace Trinity
uint32 i_phaseMask;
Unit* &i_object;
Check & i_check;
+
UnitLastSearcher(WorldObject const* searcher, Unit* & result, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(CreatureMapType &m);
void Visit(PlayerMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// All accepted by Check units if any
template<class Check>
struct TRINITY_DLL_DECL UnitListSearcher
@@ -301,24 +367,33 @@ namespace Trinity
uint32 i_phaseMask;
std::list<Unit*> &i_objects;
Check& i_check;
+
UnitListSearcher(WorldObject const* searcher, std::list<Unit*> &objects, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {}
+
void Visit(PlayerMapType &m);
void Visit(CreatureMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Creature searchers
+
template<class Check>
struct TRINITY_DLL_DECL CreatureSearcher
{
uint32 i_phaseMask;
Creature* &i_object;
Check & i_check;
+
CreatureSearcher(WorldObject const* searcher, Creature* & result, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(CreatureMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Last accepted by Check Creature if any (Check can change requirements at each call)
template<class Check>
struct TRINITY_DLL_DECL CreatureLastSearcher
@@ -326,92 +401,122 @@ namespace Trinity
uint32 i_phaseMask;
Creature* &i_object;
Check & i_check;
+
CreatureLastSearcher(WorldObject const* searcher, Creature* & result, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(CreatureMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Check>
struct TRINITY_DLL_DECL CreatureListSearcher
{
uint32 i_phaseMask;
std::list<Creature*> &i_objects;
Check& i_check;
+
CreatureListSearcher(WorldObject const* searcher, std::list<Creature*> &objects, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {}
+
void Visit(CreatureMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Do>
struct MANGOS_DLL_DECL CreatureWorker
{
uint32 i_phaseMask;
Do& i_do;
+
CreatureWorker(WorldObject const* searcher, Do& _do)
: i_phaseMask(searcher->GetPhaseMask()), i_do(_do) {}
+
void Visit(CreatureMapType &m)
{
for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
if(itr->getSource()->InSamePhase(i_phaseMask))
i_do(itr->getSource());
}
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// Player searchers
+
template<class Check>
struct TRINITY_DLL_DECL PlayerSearcher
{
uint32 i_phaseMask;
Player* &i_object;
Check & i_check;
+
PlayerSearcher(WorldObject const* searcher, Player* & result, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_object(result),i_check(check) {}
+
void Visit(PlayerMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Check>
struct TRINITY_DLL_DECL PlayerListSearcher
{
uint32 i_phaseMask;
std::list<Player*> &i_objects;
Check& i_check;
+
PlayerListSearcher(WorldObject const* searcher, std::list<Player*> &objects, Check & check)
: i_phaseMask(searcher->GetPhaseMask()), i_objects(objects),i_check(check) {}
+
void Visit(PlayerMapType &m);
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Do>
struct TRINITY_DLL_DECL PlayerWorker
{
uint32 i_phaseMask;
Do& i_do;
+
PlayerWorker(WorldObject const* searcher, Do& _do)
: i_phaseMask(searcher->GetPhaseMask()), i_do(_do) {}
+
void Visit(PlayerMapType &m)
{
for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
if(itr->getSource()->InSamePhase(i_phaseMask))
i_do(itr->getSource());
}
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
template<class Do>
struct TRINITY_DLL_DECL PlayerDistWorker
{
WorldObject const* i_searcher;
float i_dist;
Do& i_do;
+
PlayerDistWorker(WorldObject const* searcher, float _dist, Do& _do)
: i_searcher(searcher), i_dist(_dist), i_do(_do) {}
+
void Visit(PlayerMapType &m)
{
for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
if (itr->getSource()->InSamePhase(i_searcher) && itr->getSource()->IsWithinDist(i_searcher,i_dist))
i_do(itr->getSource());
}
+
template<class NOT_INTERESTED> void Visit(GridRefManager<NOT_INTERESTED> &) {}
};
+
// CHECKS && DO classes
+
// WorldObject check classes
class RaiseDeadObjectCheck
{
@@ -424,6 +529,7 @@ namespace Trinity
( u->GetCreatureTypeMask() & (1 << (CREATURE_TYPE_HUMANOID-1)) )==0 ||
(u->GetDisplayId() != u->GetNativeDisplayId()))
return false;
+
return i_funit->IsWithinDistInMap(u, i_range);
}
template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED*) { return false; }
@@ -431,6 +537,7 @@ namespace Trinity
Unit* const i_funit;
float i_range;
};
+
class ExplodeCorpseObjectCheck
{
public:
@@ -440,6 +547,7 @@ namespace Trinity
if (u->getDeathState()!=CORPSE || u->isInFlight() ||
u->HasAuraType(SPELL_AURA_GHOST) || (u->GetDisplayId() != u->GetNativeDisplayId()))
return false;
+
return i_funit->IsWithinDistInMap(u, i_range);
}
bool operator()(Creature* u)
@@ -448,6 +556,7 @@ namespace Trinity
(u->GetDisplayId() != u->GetNativeDisplayId()) ||
(u->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL)!=0)
return false;
+
return i_funit->IsWithinDistInMap(u, i_range);
}
template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED*) { return false; }
@@ -455,6 +564,7 @@ namespace Trinity
Unit* const i_funit;
float i_range;
};
+
class CannibalizeObjectCheck
{
public:
@@ -463,6 +573,7 @@ namespace Trinity
{
if( i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() )
return false;
+
return i_funit->IsWithinDistInMap(u, i_range);
}
bool operator()(Corpse* u);
@@ -471,6 +582,7 @@ namespace Trinity
if (i_funit->IsFriendlyTo(u) || u->isAlive() || u->isInFlight() ||
(u->GetCreatureTypeMask() & CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD)==0)
return false;
+
return i_funit->IsWithinDistInMap(u, i_range);
}
template<class NOT_INTERESTED> bool operator()(NOT_INTERESTED*) { return false; }
@@ -478,7 +590,9 @@ namespace Trinity
Unit* const i_funit;
float i_range;
};
+
// WorldObject do classes
+
class RespawnDo
{
public:
@@ -488,7 +602,9 @@ namespace Trinity
void operator()(WorldObject*) const {}
void operator()(Corpse*) const {}
};
+
// GameObject checks
+
class GameObjectFocusCheck
{
public:
@@ -497,15 +613,19 @@ namespace Trinity
{
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
{
@@ -524,9 +644,11 @@ namespace Trinity
private:
WorldObject const& i_obj;
float i_range;
+
// prevent clone
NearestGameObjectFishingHole(NearestGameObjectFishingHole const&);
};
+
class NearestGameObjectCheck
{
public:
@@ -544,9 +666,11 @@ namespace Trinity
private:
WorldObject const& i_obj;
float i_range;
+
// prevent clone this object
NearestGameObjectCheck(NearestGameObjectCheck const&);
};
+
// Success at unit in range, range update for next check (this can be use with GameobjectLastSearcher to find nearest GO)
class NearestGameObjectEntryInObjectRangeCheck
{
@@ -566,9 +690,11 @@ namespace Trinity
WorldObject const& i_obj;
uint32 i_entry;
float i_range;
+
// prevent clone this object
NearestGameObjectEntryInObjectRangeCheck(NearestGameObjectEntryInObjectRangeCheck const&);
};
+
class GameObjectWithDbGUIDCheck
{
public:
@@ -581,7 +707,9 @@ namespace Trinity
WorldObject const& i_obj;
uint32 i_db_guid;
};
+
// Unit checks
+
class MostHPMissingInRange
{
public:
@@ -600,6 +728,7 @@ namespace Trinity
float i_range;
uint32 i_hp;
};
+
class FriendlyCCedInRange
{
public:
@@ -617,6 +746,7 @@ namespace Trinity
Unit const* i_obj;
float i_range;
};
+
class FriendlyMissingBuffInRange
{
public:
@@ -635,6 +765,7 @@ namespace Trinity
float i_range;
uint32 i_spell;
};
+
class AnyUnfriendlyUnitInObjectRangeCheck
{
public:
@@ -651,6 +782,7 @@ namespace Trinity
Unit const* i_funit;
float i_range;
};
+
class AnyUnfriendlyNoTotemUnitInObjectRangeCheck
{
public:
@@ -659,8 +791,10 @@ namespace Trinity
{
if(!u->isAlive())
return false;
+
if(u->GetTypeId()==TYPEID_UNIT && ((Creature*)u)->isTotem())
return false;
+
return i_obj->IsWithinDistInMap(u, i_range) && !i_funit->IsFriendlyTo(u);
}
private:
@@ -668,11 +802,13 @@ namespace Trinity
Unit const* i_funit;
float i_range;
};
+
class AnyUnfriendlyVisibleUnitInObjectRangeCheck
{
public:
AnyUnfriendlyVisibleUnitInObjectRangeCheck(WorldObject const* obj, Unit const* funit, float range)
: i_obj(obj), i_funit(funit), i_range(range) {}
+
bool operator()(Unit* u)
{
return u->isAlive()
@@ -685,6 +821,7 @@ namespace Trinity
Unit const* i_funit;
float i_range;
};
+
class CreatureWithDbGUIDCheck
{
public:
@@ -697,6 +834,7 @@ namespace Trinity
WorldObject const* i_obj;
uint32 i_lowguid;
};
+
class AnyFriendlyUnitInObjectRangeCheck
{
public:
@@ -713,6 +851,7 @@ namespace Trinity
Unit const* i_funit;
float i_range;
};
+
class AnyUnitInObjectRangeCheck
{
public:
@@ -721,12 +860,14 @@ namespace Trinity
{
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
{
@@ -740,15 +881,18 @@ namespace Trinity
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:
@@ -768,8 +912,10 @@ namespace Trinity
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:
@@ -778,6 +924,7 @@ namespace Trinity
Unit const* i_funit;
float i_range;
};
+
// do attack at call of help to friendly crearture
class CallOfHelpCreatureInRangeDo
{
@@ -789,14 +936,18 @@ namespace Trinity
{
if (u == i_funit)
return;
+
if (!u->CanAssistTo(i_funit, i_enemy, false))
return;
+
// too far
if (!u->IsWithinDistInMap(i_enemy, i_range))
return;
+
// only if see assisted creature's enemy
if (!u->IsWithinLOSInMap(i_enemy))
return;
+
if (u->AI())
u->AI()->AttackStart(i_enemy);
}
@@ -805,15 +956,19 @@ namespace Trinity
Unit* const i_enemy;
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 NearestHostileUnitInAttackDistanceCheck
{
public:
@@ -827,6 +982,7 @@ namespace Trinity
// TODO: addthreat for every enemy in range?
if(!m_creature->IsWithinDistInMap(u, m_range))
return false;
+
if(m_force)
{
if(!m_creature->canAttack(u))
@@ -837,6 +993,7 @@ namespace Trinity
if(!m_creature->canStartAttack(u, false))
return false;
}
+
m_range = m_creature->GetDistance(u);
return true;
}
@@ -847,6 +1004,7 @@ namespace Trinity
bool m_force;
NearestHostileUnitInAttackDistanceCheck(NearestHostileUnitInAttackDistanceCheck const&);
};
+
class AnyAssistCreatureInRangeCheck
{
public:
@@ -858,14 +1016,18 @@ namespace Trinity
{
if(u == i_funit)
return false;
+
if ( !u->CanAssistTo(i_funit, i_enemy) )
return false;
+
// too far
if( !i_funit->IsWithinDistInMap(u, i_range) )
return false;
+
// only if see assisted creature
if( !i_funit->IsWithinLOSInMap(u) )
return false;
+
return true;
}
private:
@@ -873,21 +1035,26 @@ namespace Trinity
Unit* const i_enemy;
float i_range;
};
+
class NearestAssistCreatureInCreatureRangeCheck
{
public:
NearestAssistCreatureInCreatureRangeCheck(Creature* obj, Unit* enemy, float range)
: i_obj(obj), i_enemy(enemy), i_range(range) {}
+
bool operator()(Creature* u)
{
if(u == i_obj)
return false;
if(!u->CanAssistTo(i_obj,i_enemy))
return false;
+
if(!i_obj->IsWithinDistInMap(u, i_range))
return false;
+
if(!i_obj->IsWithinLOSInMap(u))
return false;
+
i_range = i_obj->GetDistance(u); // use found unit range as new range limit for next check
return true;
}
@@ -896,15 +1063,18 @@ namespace Trinity
Creature* const i_obj;
Unit* const i_enemy;
float i_range;
+
// prevent clone this object
NearestAssistCreatureInCreatureRangeCheck(NearestAssistCreatureInCreatureRangeCheck const&);
};
+
// 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))
@@ -920,9 +1090,11 @@ namespace Trinity
uint32 i_entry;
bool i_alive;
float i_range;
+
// prevent clone this object
NearestCreatureEntryWithLiveStateInObjectRangeCheck(NearestCreatureEntryWithLiveStateInObjectRangeCheck const&);
};
+
class AnyPlayerInObjectRangeCheck
{
public:
@@ -931,12 +1103,14 @@ namespace Trinity
{
if(u->isAlive() && i_obj->IsWithinDistInMap(u, i_range))
return true;
+
return false;
}
private:
WorldObject const* i_obj;
float i_range;
};
+
class AllFriendlyCreaturesInGrid
{
public:
@@ -945,11 +1119,13 @@ namespace Trinity
{
if(u->isAlive() && u->GetVisibility() == VISIBILITY_ON && u->IsFriendlyTo(pUnit))
return true;
+
return false;
}
private:
Unit const* pUnit;
};
+
class AllGameObjectsWithEntryInRange
{
public:
@@ -958,6 +1134,7 @@ namespace Trinity
{
if (pGo->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(pGo,m_fRange,false))
return true;
+
return false;
}
private:
@@ -965,6 +1142,7 @@ namespace Trinity
uint32 m_uiEntry;
float m_fRange;
};
+
class AllCreaturesOfEntryInRange
{
public:
@@ -973,13 +1151,16 @@ namespace Trinity
{
if (pUnit->GetEntry() == m_uiEntry && m_pObject->IsWithinDist(pUnit,m_fRange,false))
return true;
+
return false;
}
+
private:
const WorldObject* m_pObject;
uint32 m_uiEntry;
float m_fRange;
};
+
class PlayerAtMinimumRangeAway
{
public:
@@ -989,12 +1170,15 @@ namespace Trinity
//No threat list check, must be done explicit if expected to be in combat with creature
if (!pPlayer->isGameMaster() && pPlayer->isAlive() && !pUnit->IsWithinDist(pPlayer,fRange,false))
return true;
+
return false;
}
+
private:
Unit const* pUnit;
float fRange;
};
+
class GameObjectInRangeCheck
{
public:
@@ -1006,23 +1190,28 @@ namespace Trinity
private:
float x, y, z, range;
};
+
// Player checks and do
+
// Prepare using Builder localized packets with caching and send to player
template<class Builder>
class LocalizedPacketDo
{
public:
explicit LocalizedPacketDo(Builder& builder) : i_builder(builder) {}
+
~LocalizedPacketDo()
{
for(size_t i = 0; i < i_data_cache.size(); ++i)
delete i_data_cache[i];
}
void operator()( Player* p );
+
private:
Builder& i_builder;
std::vector<WorldPacket*> i_data_cache; // 0 = default, i => i-1 locale index
};
+
// Prepare using Builder localized packets with caching and send to player
template<class Builder>
class LocalizedPacketListDo
@@ -1030,6 +1219,7 @@ namespace Trinity
public:
typedef std::vector<WorldPacket*> WorldPacketList;
explicit LocalizedPacketListDo(Builder& builder) : i_builder(builder) {}
+
~LocalizedPacketListDo()
{
for(size_t i = 0; i < i_data_cache.size(); ++i)
@@ -1037,11 +1227,13 @@ namespace Trinity
delete i_data_cache[i][j];
}
void operator()( Player* p );
+
private:
Builder& i_builder;
std::vector<WorldPacketList> i_data_cache;
// 0 = default, i => i-1 locale index
};
+
#ifndef WIN32
template<> inline void PlayerRelocationNotifier::Visit<Creature>(CreatureMapType &);
template<> inline void PlayerRelocationNotifier::Visit<Player>(PlayerMapType &);
diff --git a/src/game/GridNotifiersImpl.h b/src/game/GridNotifiersImpl.h
index a8e0116a7cf..d9e27cc8bcc 100644
--- a/src/game/GridNotifiersImpl.h
+++ b/src/game/GridNotifiersImpl.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GRIDNOTIFIERSIMPL_H
#define TRINITY_GRIDNOTIFIERSIMPL_H
+
#include "GridNotifiers.h"
#include "WorldPacket.h"
#include "Corpse.h"
@@ -26,6 +28,7 @@
#include "UpdateData.h"
#include "CreatureAI.h"
#include "SpellAuras.h"
+
inline void
Trinity::ObjectUpdater::Visit(CreatureMapType &m)
{
@@ -33,24 +36,29 @@ Trinity::ObjectUpdater::Visit(CreatureMapType &m)
if(iter->getSource()->IsInWorld() && !iter->getSource()->isSpiritService())
iter->getSource()->Update(i_timeDiff);
}
+
inline void PlayerCreatureRelocationWorker(Player* pl, Creature* c)
{
if(!pl->isAlive() || !c->isAlive() || pl->isInFlight())
return;
+
// Creature AI reaction
if(c->HasReactState(REACT_AGGRESSIVE) && !c->hasUnitState(UNIT_STAT_SIGHTLESS))
if(c->_IsWithinDist(pl, c->m_SightDistance, true) && c->IsAIEnabled)
c->AI()->MoveInLineOfSight(pl);
}
+
inline void CreatureCreatureRelocationWorker(Creature* c1, Creature* c2)
{
if(c1->HasReactState(REACT_AGGRESSIVE) && !c1->hasUnitState(UNIT_STAT_SIGHTLESS))
if(c1->_IsWithinDist(c2, c1->m_SightDistance, true) && c1->IsAIEnabled)
c1->AI()->MoveInLineOfSight(c2);
+
if(c2->HasReactState(REACT_AGGRESSIVE) && !c2->hasUnitState(UNIT_STAT_SIGHTLESS))
if(c1->_IsWithinDist(c2, c2->m_SightDistance, true) && c2->IsAIEnabled)
c2->AI()->MoveInLineOfSight(c1);
}
+
template<class T>
inline void
Trinity::PlayerVisibilityNotifier::Visit(GridRefManager<T> &m)
@@ -61,6 +69,7 @@ Trinity::PlayerVisibilityNotifier::Visit(GridRefManager<T> &m)
i_clientGUIDs.erase(iter->getSource()->GetGUID());
}
}
+
template<>
inline void
Trinity::PlayerRelocationNotifier::Visit(PlayerMapType &m)
@@ -68,19 +77,24 @@ Trinity::PlayerRelocationNotifier::Visit(PlayerMapType &m)
for(PlayerMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
i_clientGUIDs.erase(iter->getSource()->GetGUID());
+
if(iter->getSource()->m_Notified) //self is also skipped in this check
continue;
+
i_player.UpdateVisibilityOf(iter->getSource(),i_data,i_visibleNow);
iter->getSource()->UpdateVisibilityOf(&i_player);
+
//if (!i_player.GetSharedVisionList().empty())
// for (SharedVisionList::const_iterator it = i_player.GetSharedVisionList().begin(); it != i_player.GetSharedVisionList().end(); ++it)
// (*it)->UpdateVisibilityOf(iter->getSource());
+
// Cancel Trade
if(i_player.GetTrader()==iter->getSource())
if(!i_player.IsWithinDistInMap(iter->getSource(), 5)) // iteraction distance
i_player.GetSession()->SendCancelTrade(); // will clode both side trade windows
}
}
+
template<>
inline void
Trinity::PlayerRelocationNotifier::Visit(CreatureMapType &m)
@@ -88,12 +102,16 @@ Trinity::PlayerRelocationNotifier::Visit(CreatureMapType &m)
for(CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
i_clientGUIDs.erase(iter->getSource()->GetGUID());
+
if(iter->getSource()->m_Notified)
continue;
+
i_player.UpdateVisibilityOf(iter->getSource(),i_data,i_visibleNow);
+
PlayerCreatureRelocationWorker(&i_player, iter->getSource());
}
}
+
template<>
inline void
Trinity::CreatureRelocationNotifier::Visit(PlayerMapType &m)
@@ -102,53 +120,70 @@ Trinity::CreatureRelocationNotifier::Visit(PlayerMapType &m)
{
if(iter->getSource()->m_Notified)
continue;
+
iter->getSource()->UpdateVisibilityOf(&i_creature);
+
PlayerCreatureRelocationWorker(iter->getSource(), &i_creature);
}
}
+
template<>
inline void
Trinity::CreatureRelocationNotifier::Visit(CreatureMapType &m)
{
if(!i_creature.isAlive())
return;
+
for(CreatureMapType::iterator iter = m.begin(); iter != m.end(); ++iter)
{
if(iter->getSource()->m_Notified)
continue;
+
if(!iter->getSource()->isAlive())
continue;
+
CreatureCreatureRelocationWorker(iter->getSource(), &i_creature);
}
}
+
inline void Trinity::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_dynobject.IsAffecting(target))
return;
+
if(target->HasAura(i_dynobject.GetSpellId(), i_check->GetGUID()))
return;
+
uint32 eff_index = 0;
for(; eff_index < MAX_SPELL_EFFECTS; ++eff_index)
if(i_dynobject.HasEffect(eff_index))
break;
+
if(eff_index == MAX_SPELL_EFFECTS)
return;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(i_dynobject.GetSpellId());
if(spellInfo->EffectImplicitTargetB[eff_index] == TARGET_DEST_DYNOBJ_ALLY
|| spellInfo->EffectImplicitTargetB[eff_index] == TARGET_UNIT_AREA_ALLY_DST)
@@ -160,23 +195,28 @@ inline void Trinity::DynamicObjectUpdater::VisitHelper(Unit* target)
{
if (i_check->IsFriendlyTo( target ))
return;
+
i_check->CombatStart(target);
}
else
{
if (!i_check->IsHostileTo( target ))
return;
+
i_check->CombatStart(target);
}
+
// Check target immune to spell or aura
if (target->IsImmunedToSpell(spellInfo) || target->IsImmunedToSpellEffect(spellInfo, eff_index))
return;
+
// Apply PersistentAreaAura on target
Aura *aur = new Aura(spellInfo, i_dynobject.GetEffectMask(), target, &i_dynobject, i_check);
aur->SetAuraDuration(i_dynobject.GetDuration());
if(target->AddAura(aur, true))
i_dynobject.AddAffected(target);
}
+
template<>
inline void
Trinity::DynamicObjectUpdater::Visit(CreatureMapType &m)
@@ -184,6 +224,7 @@ Trinity::DynamicObjectUpdater::Visit(CreatureMapType &m)
for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
VisitHelper(itr->getSource());
}
+
template<>
inline void
Trinity::DynamicObjectUpdater::Visit(PlayerMapType &m)
@@ -191,18 +232,23 @@ Trinity::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<class Check>
void Trinity::WorldObjectSearcher<Check>::Visit(GameObjectMapType &m)
{
// already found
if(i_object)
return;
+
for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if (i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -210,16 +256,19 @@ void Trinity::WorldObjectSearcher<Check>::Visit(GameObjectMapType &m)
}
}
}
+
template<class Check>
void Trinity::WorldObjectSearcher<Check>::Visit(PlayerMapType &m)
{
// already found
if(i_object)
return;
+
for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -227,16 +276,19 @@ void Trinity::WorldObjectSearcher<Check>::Visit(PlayerMapType &m)
}
}
}
+
template<class Check>
void Trinity::WorldObjectSearcher<Check>::Visit(CreatureMapType &m)
{
// already found
if(i_object)
return;
+
for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -244,16 +296,19 @@ void Trinity::WorldObjectSearcher<Check>::Visit(CreatureMapType &m)
}
}
}
+
template<class Check>
void Trinity::WorldObjectSearcher<Check>::Visit(CorpseMapType &m)
{
// already found
if(i_object)
return;
+
for(CorpseMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -261,16 +316,19 @@ void Trinity::WorldObjectSearcher<Check>::Visit(CorpseMapType &m)
}
}
}
+
template<class Check>
void Trinity::WorldObjectSearcher<Check>::Visit(DynamicObjectMapType &m)
{
// already found
if(i_object)
return;
+
for(DynamicObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -278,6 +336,7 @@ void Trinity::WorldObjectSearcher<Check>::Visit(DynamicObjectMapType &m)
}
}
}
+
template<class Check>
void Trinity::WorldObjectListSearcher<Check>::Visit(PlayerMapType &m)
{
@@ -286,6 +345,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(PlayerMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::WorldObjectListSearcher<Check>::Visit(CreatureMapType &m)
{
@@ -294,6 +354,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(CreatureMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::WorldObjectListSearcher<Check>::Visit(CorpseMapType &m)
{
@@ -302,6 +363,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(CorpseMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::WorldObjectListSearcher<Check>::Visit(GameObjectMapType &m)
{
@@ -310,6 +372,7 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(GameObjectMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::WorldObjectListSearcher<Check>::Visit(DynamicObjectMapType &m)
{
@@ -318,17 +381,21 @@ void Trinity::WorldObjectListSearcher<Check>::Visit(DynamicObjectMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
// Gameobject searchers
+
template<class Check>
void Trinity::GameObjectSearcher<Check>::Visit(GameObjectMapType &m)
{
// already found
if(i_object)
return;
+
for(GameObjectMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -336,6 +403,7 @@ void Trinity::GameObjectSearcher<Check>::Visit(GameObjectMapType &m)
}
}
}
+
template<class Check>
void Trinity::GameObjectLastSearcher<Check>::Visit(GameObjectMapType &m)
{
@@ -343,10 +411,12 @@ void Trinity::GameObjectLastSearcher<Check>::Visit(GameObjectMapType &m)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
i_object = itr->getSource();
}
}
+
template<class Check>
void Trinity::GameObjectListSearcher<Check>::Visit(GameObjectMapType &m)
{
@@ -355,17 +425,21 @@ void Trinity::GameObjectListSearcher<Check>::Visit(GameObjectMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
// Unit searchers
+
template<class Check>
void Trinity::UnitSearcher<Check>::Visit(CreatureMapType &m)
{
// already found
if(i_object)
return;
+
for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -373,16 +447,19 @@ void Trinity::UnitSearcher<Check>::Visit(CreatureMapType &m)
}
}
}
+
template<class Check>
void Trinity::UnitSearcher<Check>::Visit(PlayerMapType &m)
{
// already found
if(i_object)
return;
+
for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -390,6 +467,7 @@ void Trinity::UnitSearcher<Check>::Visit(PlayerMapType &m)
}
}
}
+
template<class Check>
void Trinity::UnitLastSearcher<Check>::Visit(CreatureMapType &m)
{
@@ -397,10 +475,12 @@ void Trinity::UnitLastSearcher<Check>::Visit(CreatureMapType &m)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
i_object = itr->getSource();
}
}
+
template<class Check>
void Trinity::UnitLastSearcher<Check>::Visit(PlayerMapType &m)
{
@@ -408,10 +488,12 @@ void Trinity::UnitLastSearcher<Check>::Visit(PlayerMapType &m)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
i_object = itr->getSource();
}
}
+
template<class Check>
void Trinity::UnitListSearcher<Check>::Visit(PlayerMapType &m)
{
@@ -420,6 +502,7 @@ void Trinity::UnitListSearcher<Check>::Visit(PlayerMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::UnitListSearcher<Check>::Visit(CreatureMapType &m)
{
@@ -428,17 +511,21 @@ void Trinity::UnitListSearcher<Check>::Visit(CreatureMapType &m)
if(i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
// Creature searchers
+
template<class Check>
void Trinity::CreatureSearcher<Check>::Visit(CreatureMapType &m)
{
// already found
if(i_object)
return;
+
for(CreatureMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -446,6 +533,7 @@ void Trinity::CreatureSearcher<Check>::Visit(CreatureMapType &m)
}
}
}
+
template<class Check>
void Trinity::CreatureLastSearcher<Check>::Visit(CreatureMapType &m)
{
@@ -453,10 +541,12 @@ void Trinity::CreatureLastSearcher<Check>::Visit(CreatureMapType &m)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
i_object = itr->getSource();
}
}
+
template<class Check>
void Trinity::CreatureListSearcher<Check>::Visit(CreatureMapType &m)
{
@@ -465,6 +555,7 @@ void Trinity::CreatureListSearcher<Check>::Visit(CreatureMapType &m)
if( i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::PlayerListSearcher<Check>::Visit(PlayerMapType &m)
{
@@ -473,16 +564,19 @@ void Trinity::PlayerListSearcher<Check>::Visit(PlayerMapType &m)
if( i_check(itr->getSource()))
i_objects.push_back(itr->getSource());
}
+
template<class Check>
void Trinity::PlayerSearcher<Check>::Visit(PlayerMapType &m)
{
// already found
if(i_object)
return;
+
for(PlayerMapType::iterator itr=m.begin(); itr != m.end(); ++itr)
{
if(!itr->getSource()->InSamePhase(i_phaseMask))
continue;
+
if(i_check(itr->getSource()))
{
i_object = itr->getSource();
@@ -490,60 +584,78 @@ void Trinity::PlayerSearcher<Check>::Visit(PlayerMapType &m)
}
}
}
+
template<class Builder>
void MaNGOS::LocalizedPacketDo<Builder>::operator()( Player* p )
{
int32 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);
+
data = new WorldPacket(SMSG_MESSAGECHAT, 200);
+
i_builder(*data,loc_idx);
+
i_data_cache[cache_idx] = data;
}
else
data = i_data_cache[cache_idx];
+
p->SendDirectMessage(data);
}
+
template<class Builder>
void MaNGOS::LocalizedPacketListDo<Builder>::operator()( Player* p )
{
int32 loc_idx = p->GetSession()->GetSessionDbLocaleIndex();
uint32 cache_idx = loc_idx+1;
WorldPacketList* data_list;
+
// create if not cached yet
if(i_data_cache.size() < cache_idx+1 || i_data_cache[cache_idx].empty())
{
if(i_data_cache.size() < cache_idx+1)
i_data_cache.resize(cache_idx+1);
+
data_list = &i_data_cache[cache_idx];
+
i_builder(*data_list,loc_idx);
}
else
data_list = &i_data_cache[cache_idx];
+
for(size_t i = 0; i < data_list->size(); ++i)
p->SendDirectMessage((*data_list)[i]);
}
+
struct ObjectDistanceOrder : public std::binary_function<const WorldObject, const WorldObject, bool>
{
const Unit* m_pSource;
+
ObjectDistanceOrder(const Unit* pSource) : m_pSource(pSource) {};
+
bool operator()(const WorldObject* pLeft, const WorldObject* pRight) const
{
return m_pSource->GetDistanceOrder(pLeft, pRight);
}
};
+
struct ObjectDistanceOrderReversed : public std::binary_function<const WorldObject, const WorldObject, bool>
{
const Unit* m_pSource;
+
ObjectDistanceOrderReversed(const Unit* pSource) : m_pSource(pSource) {};
+
bool operator()(const WorldObject* pLeft, const WorldObject* pRight) const
{
return !m_pSource->GetDistanceOrder(pLeft, pRight);
}
};
+
#endif // MANGOS_GRIDNOTIFIERSIMPL_H
diff --git a/src/game/GridStates.cpp b/src/game/GridStates.cpp
index b8cb6d40ebb..36092fbdc29 100644
--- a/src/game/GridStates.cpp
+++ b/src/game/GridStates.cpp
@@ -17,14 +17,17 @@
* 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 "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
{
@@ -45,6 +48,7 @@ ActiveState::Update(Map &m, NGridType &grid, GridInfo & info, const uint32 &x, c
}
}
}
+
void
IdleState::Update(Map &m, NGridType &grid, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &) const
{
@@ -52,6 +56,7 @@ IdleState::Update(Map &m, NGridType &grid, GridInfo &, const uint32 &x, const ui
grid.SetGridState(GRID_STATE_REMOVAL);
sLog.outDebug("Grid[%u,%u] on map %u moved to REMOVAL 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
{
diff --git a/src/game/GridStates.h b/src/game/GridStates.h
index f44cd1cd1db..12d64ad58ca 100644
--- a/src/game/GridStates.h
+++ b/src/game/GridStates.h
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GRIDSTATES_H
#define TRINITY_GRIDSTATES_H
+
#include "Map.h"
#include "Object.h"
+
class TRINITY_DLL_DECL GridState
{
public:
@@ -41,24 +44,32 @@ class TRINITY_DLL_DECL GridState
#endif
virtual void Update(Map &, NGridType&, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const = 0;
};
+
class TRINITY_DLL_DECL InvalidState : public GridState
{
public:
+
void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
};
+
class TRINITY_DLL_DECL ActiveState : public GridState
{
public:
+
void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
};
+
class TRINITY_DLL_DECL IdleState : public GridState
{
public:
+
void Update(Map &, NGridType &, GridInfo &, const uint32 &x, const uint32 &y, const uint32 &t_diff) const;
};
+
class TRINITY_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
index 16e91e564d2..8a02afa25d2 100644
--- a/src/game/Group.cpp
+++ b/src/game/Group.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -32,6 +33,7 @@
#include "InstanceSaveMgr.h"
#include "MapInstanced.h"
#include "Util.h"
+
Group::Group()
{
m_leaderGuid = 0;
@@ -43,9 +45,11 @@ Group::Group()
m_looterGuid = 0;
m_lootThreshold = ITEM_QUALITY_UNCOMMON;
m_subGroupsCounts = NULL;
+
for (int i=0; i<TARGETICONCOUNT; ++i)
m_targetIcons[i] = 0;
}
+
Group::~Group()
{
if(m_bgGroup)
@@ -63,32 +67,41 @@ Group::~Group()
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 itr2 = m_boundInstances[i].begin(); itr2 != m_boundInstances[i].end(); ++itr2)
itr2->second.save->RemoveGroup(this);
+
// Sub group counters clean up
if (m_subGroupsCounts)
delete[] m_subGroupsCounts;
}
+
bool Group::Create(const uint64 &guid, const char * name)
{
m_leaderGuid = guid;
m_leaderName = name;
+
m_groupType = isBGGroup() ? GROUPTYPE_RAID : GROUPTYPE_NORMAL;
+
if (m_groupType == GROUPTYPE_RAID)
_initRaidSubGroupsCounter();
+
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));
@@ -98,15 +111,20 @@ bool Group::Create(const uint64 &guid, const char * name)
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)
{
@@ -116,30 +134,38 @@ bool Group::LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result, bool
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;
+
if (m_groupType == GROUPTYPE_RAID)
_initRaidSubGroupsCounter();
+
m_difficulty = (*result)[14].GetUInt8();
m_mainTank = (*result)[0].GetUInt64();
m_mainAssistant = (*result)[1].GetUInt64();
m_lootMethod = (LootMethod)(*result)[2].GetUInt8();
m_looterGuid = MAKE_NEW_GUID((*result)[3].GetUInt32(), 0, HIGHGUID_PLAYER);
m_lootThreshold = (ItemQualities)(*result)[4].GetUInt16();
+
for(int i=0; i<TARGETICONCOUNT; ++i)
m_targetIcons[i] = (*result)[5+i].GetUInt64();
if(!external) delete result;
+
if(loadMembers)
{
result = CharacterDatabase.PQuery("SELECT memberGuid, assistant, subgroup FROM group_member WHERE leaderGuid ='%u'", GUID_LOPART(leaderGuid));
if(!result)
return false;
+
do
{
LoadMemberFromDB((*result)[0].GetUInt32(), (*result)[2].GetUInt8(), (*result)[1].GetBool());
@@ -149,33 +175,44 @@ bool Group::LoadGroupFromDB(const uint64 &leaderGuid, QueryResult *result, bool
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);
+
SubGroupCounterIncrease(subgroup);
+
return true;
}
+
void Group::ConvertToRaid()
{
m_groupType = GROUPTYPE_RAID;
+
_initRaidSubGroupsCounter();
+
if(!isBGGroup())
CharacterDatabase.PExecute("UPDATE groups SET isRaid = 1 WHERE leaderGuid='%u'", GUID_LOPART(m_leaderGuid));
SendUpdate();
+
// update quest related GO states (quest activity dependent from raid membership)
for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
if(Player* player = objmgr.GetPlayer(citr->guid))
player->UpdateForQuestWorldObjects();
}
+
bool Group::AddInvite(Player *player)
{
if( !player || player->GetGroupInvite() )
@@ -185,31 +222,42 @@ bool Group::AddInvite(Player *player)
group = player->GetOriginalGroup();
if( group )
return false;
+
RemoveInvite(player);
+
m_invitees.insert(player);
+
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)
{
m_invitees.erase(player);
+
player->SetGroupInvite(NULL);
return GetMembersCount();
}
+
void Group::RemoveAllInvites()
{
for(InvitesList::iterator itr=m_invitees.begin(); itr!=m_invitees.end(); ++itr)
(*itr)->SetGroupInvite(NULL);
+
m_invitees.clear();
}
+
Player* Group::GetInvited(const uint64& guid) const
{
for(InvitesList::const_iterator itr = m_invitees.begin(); itr != m_invitees.end(); ++itr)
@@ -219,6 +267,7 @@ Player* Group::GetInvited(const uint64& guid) const
}
return NULL;
}
+
Player* Group::GetInvited(const std::string& name) const
{
for(InvitesList::const_iterator itr = m_invitees.begin(); itr != m_invitees.end(); ++itr)
@@ -228,11 +277,13 @@ Player* Group::GetInvited(const std::string& name) const
}
return NULL;
}
+
bool Group::AddMember(const uint64 &guid, const char* name)
{
if(!_addMember(guid, name))
return false;
SendUpdate();
+
Player *player = objmgr.GetPlayer(guid);
if(player)
{
@@ -241,6 +292,7 @@ bool Group::AddMember(const uint64 &guid, const char* name)
// 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);
@@ -249,30 +301,38 @@ bool Group::AddMember(const uint64 &guid, const char* name)
}
player->SetGroupUpdateFlag(GROUP_UPDATE_FULL);
UpdatePlayerOutOfRange(player);
+
// quest related GO state dependent from raid memebership
if(isRaidGroup())
player->UpdateForQuestWorldObjects();
}
+
return true;
}
+
uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method)
{
BroadcastGroupUpdate();
+
// 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);
+
if(Player *player = objmgr.GetPlayer( guid ))
{
// quest related GO state dependent from raid membership
if(isRaidGroup())
player->UpdateForQuestWorldObjects();
+
WorldPacket data;
+
if(method == 1)
{
data.Initialize( SMSG_GROUP_UNINVITE, 0 );
player->GetSession()->SendPacket( &data );
}
+
//we already removed player from group and in player->GetGroup() is his original group!
if( Group* group = player->GetGroup() )
{
@@ -284,40 +344,51 @@ uint32 Group::RemoveMember(const uint64 &guid, const uint8 &method)
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, true);
}
+
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, true);
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;
+
//we cannot call _removeMember because it would invalidate member iterator
//if we are removing player from battleground raid
if( isBGGroup() )
@@ -330,17 +401,21 @@ void Group::Disband(bool hideDestroy)
else
player->SetGroup(NULL);
}
+
// quest related GO state dependent from raid membership
if(isRaidGroup())
player->UpdateForQuestWorldObjects();
+
if(!player->GetSession())
continue;
+
WorldPacket data;
if(!hideDestroy)
{
data.Initialize(SMSG_GROUP_DESTROYED, 0);
player->GetSession()->SendPacket(&data);
}
+
//we already removed player from group and in player->GetGroup() is his original group, send update
if( Group* group = player->GetGroup() )
{
@@ -352,11 +427,14 @@ void Group::Disband(bool hideDestroy)
data << uint64(0) << uint64(0) << uint64(0);
player->GetSession()->SendPacket(&data);
}
+
_homebindIfInstance(player);
}
RollId.clear();
m_memberSlots.clear();
+
RemoveAllInvites();
+
if(!isBGGroup())
{
CharacterDatabase.BeginTransaction();
@@ -365,12 +443,15 @@ void Group::Disband(bool hideDestroy)
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));
@@ -380,15 +461,18 @@ void Group::SendLootStartRoll(uint32 CountDown, const Roll &r)
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(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
{
WorldPacket data(SMSG_LOOT_ROLL, (8+4+8+4+4+4+1+1));
@@ -401,15 +485,18 @@ void Group::SendLootRoll(const uint64& SourceGuid, const uint64& TargetGuid, uin
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(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r)
{
WorldPacket data(SMSG_LOOT_ROLL_WON, (8+4+4+4+4+8+1+1));
@@ -421,15 +508,18 @@ void Group::SendLootRollWon(const uint64& SourceGuid, const uint64& TargetGuid,
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));
@@ -438,15 +528,18 @@ void Group::SendLootAllPassed(uint32 NumberOfPlayers, const Roll &r)
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(const uint64& playerGUID, Loot *loot, Creature *creature)
{
std::vector<LootItem>::iterator i;
@@ -454,6 +547,7 @@ void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature)
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);
@@ -462,11 +556,13 @@ void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature)
//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())
{
@@ -482,37 +578,47 @@ void Group::GroupLoot(const uint64& playerGUID, Loot *loot, Creature *creature)
}
}
}
+
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(const uint64& playerGUID, Loot *loot, Creature *creature)
{
ItemPrototype const *item;
Player *player = objmgr.GetPlayer(playerGUID);
Group *group = player->GetGroup();
+
uint8 itemSlot = 0;
for(std::vector<LootItem>::iterator i=loot->items.begin(); i != loot->items.end(); ++i, ++itemSlot)
{
item = objmgr.GetItemPrototype(i->itemid);
+
//only roll for one-player items, not for ones everyone can get
if (item->Quality >= uint32(m_lootThreshold) && !i->freeforall)
{
uint64 newitemGUID = MAKE_NEW_GUID(objmgr.GenerateLowGuid(HIGHGUID_ITEM),0,HIGHGUID_ITEM);
Roll* r=new Roll(newitemGUID,*i);
+
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *playerToRoll = itr->getSource();
if(!playerToRoll || !playerToRoll->GetSession())
continue;
+
if (playerToRoll->CanUseItem(item) && i->AllowedForPlayer(playerToRoll) )
{
if (playerToRoll->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
@@ -522,12 +628,16 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea
}
}
}
+
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
@@ -539,27 +649,35 @@ void Group::NeedBeforeGreed(const uint64& playerGUID, Loot *loot, Creature *crea
i->is_underthreshold=1;
}
}
+
void Group::MasterLoot(const 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->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
{
data << looter->GetGUID();
++real_count;
}
}
+
data.put<uint8>(0,real_count);
+
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *looter = itr->getSource();
@@ -567,19 +685,23 @@ void Group::MasterLoot(const uint64& playerGUID, Loot* /*loot*/, Creature *creat
looter->GetSession()->SendPacket(&data);
}
}
+
void Group::CountRollVote(const uint64& playerGUID, const 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
@@ -609,6 +731,7 @@ void Group::CountRollVote(const uint64& playerGUID, const uint64& Guid, uint32 N
CountTheRoll(rollI, NumberOfPlayers);
}
}
+
//called when roll timer expires
void Group::EndRoll()
{
@@ -620,6 +743,7 @@ void Group::EndRoll()
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;
@@ -637,10 +761,12 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
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)
@@ -651,9 +777,11 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
}
SendLootRollWon(0, maxguid, maxresul, 1, *roll);
player = objmgr.GetPlayer(maxguid);
+
if(player && player->GetSession())
{
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_NEED_ON_LOOT, roll->itemid, maxresul);
+
ItemPosCountVec dest;
LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
@@ -679,11 +807,13 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
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)
@@ -694,9 +824,11 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
}
SendLootRollWon(0, maxguid, maxresul, 2, *roll);
player = objmgr.GetPlayer(maxguid);
+
if(player && player->GetSession())
{
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ROLL_GREED_ON_LOOT, roll->itemid, maxresul);
+
ItemPosCountVec dest;
LootItem *item = &(roll->getLoot()->items[roll->itemSlot]);
uint8 msg = player->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, roll->itemid, item->count );
@@ -724,22 +856,27 @@ void Group::CountTheRoll(Rolls::iterator rollI, uint32 NumberOfPlayers)
RollId.erase(rollI);
delete roll;
}
+
void Group::SetTargetIcon(uint8 id, uint64 guid)
{
if(id >= TARGETICONCOUNT)
return;
+
// clean other icons
if( guid != 0 )
for(int i=0; i<TARGETICONCOUNT; ++i)
if( m_targetIcons[i] == guid )
SetTargetIcon(i, 0);
+
m_targetIcons[id] = guid;
+
WorldPacket data(MSG_RAID_TARGET_UPDATE, (2+8));
data << (uint8)0;
data << id;
data << guid;
BroadcastPacket(&data, true);
}
+
void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_level, Player* & member_with_max_level, Player* & not_gray_member_with_max_level)
{
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
@@ -747,36 +884,46 @@ void Group::GetDataForXPAtKill(Unit const* victim, uint32& count,uint32& sum_lev
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;
+
uint32 gray_level = MaNGOS::XP::GetGrayLevel(member->getLevel());
if( victim->getLevel() > gray_level && (!not_gray_member_with_max_level
|| not_gray_member_with_max_level->getLevel() < member->getLevel()))
not_gray_member_with_max_level = member;
}
}
+
void Group::SendTargetIconList(WorldSession *session)
{
if(!session)
return;
+
WorldPacket data(MSG_RAID_TARGET_UPDATE, (1+TARGETICONCOUNT*9));
data << (uint8)1;
+
for(int i=0; i<TARGETICONCOUNT; ++i)
{
if(m_targetIcons[i] == 0)
continue;
+
data << (uint8)i;
data << m_targetIcons[i];
}
+
session->SendPacket(&data);
}
+
void Group::SendUpdate()
{
Player *player;
+
for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
{
player = objmgr.GetPlayer(citr->guid);
@@ -797,6 +944,7 @@ void Group::SendUpdate()
Player* member = objmgr.GetPlayer(citr2->guid);
uint8 onlineState = (member) ? MEMBER_STATUS_ONLINE : MEMBER_STATUS_OFFLINE;
onlineState = onlineState | ((isBGGroup()) ? MEMBER_STATUS_PVP : 0);
+
data << citr2->name;
data << (uint64)citr2->guid;
// online-state
@@ -804,6 +952,7 @@ void Group::SendUpdate()
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)
{
@@ -815,13 +964,16 @@ void Group::SendUpdate()
player->GetSession()->SendPacket( &data );
}
}
+
void Group::UpdatePlayerOutOfRange(Player* pPlayer)
{
if(!pPlayer || !pPlayer->IsInWorld())
return;
+
Player *player;
WorldPacket data;
pPlayer->GetSession()->BuildPartyMemberStatsChangedPacket(pPlayer, &data);
+
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
{
player = itr->getSource();
@@ -829,6 +981,7 @@ void Group::UpdatePlayerOutOfRange(Player* pPlayer)
player->GetSession()->SendPacket(&data);
}
}
+
void Group::BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int group, uint64 ignore)
{
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
@@ -836,10 +989,12 @@ void Group::BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int
Player *pl = itr->getSource();
if(!pl || (ignore != 0 && pl->GetGUID() == ignore) || (ignorePlayersInBGRaid && pl->GetGroup() != this) )
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())
@@ -850,6 +1005,7 @@ void Group::BroadcastReadyCheck(WorldPacket *packet)
pl->GetSession()->SendPacket(packet);
}
}
+
void Group::OfflineReadyCheck()
{
for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
@@ -864,6 +1020,7 @@ void Group::OfflineReadyCheck()
}
}
}
+
bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant)
{
// get first not-full group
@@ -883,22 +1040,29 @@ bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant)
if (!groupFound)
return false;
}
+
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);
+
SubGroupCounterIncrease(group);
+
if(player)
{
player->SetGroupInvite(NULL);
@@ -916,18 +1080,22 @@ bool Group::_addMember(const uint64 &guid, const char* name, bool isAssistant, u
if(bind && bind->save->GetInstanceId() == player->GetInstanceId())
player->m_InstanceValid = true;
}
+
if(!isRaidGroup()) // reset targetIcons for non-raid-groups
{
for(int i=0; i<TARGETICONCOUNT; ++i)
m_targetIcons[i] = 0;
}
+
if(!isBGGroup())
{
// insert into group table
CharacterDatabase.PExecute("INSERT INTO group_member(leaderGuid,memberGuid,assistant,subgroup) VALUES('%u','%u','%u','%u')", GUID_LOPART(m_leaderGuid), GUID_LOPART(member.guid), ((member.assistant==1)?1:0), member.group);
}
+
return true;
}
+
bool Group::_removeMember(const uint64 &guid)
{
Player *player = objmgr.GetPlayer(guid);
@@ -945,33 +1113,43 @@ bool Group::_removeMember(const uint64 &guid)
player->SetGroup(NULL);
}
}
+
_removeRolls(guid);
+
member_witerator slot = _getMemberWSlot(guid);
if (slot != m_memberSlots.end())
{
SubGroupCounterDecrease(slot->group);
+
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
@@ -980,6 +1158,7 @@ void Group::_setLeader(const uint64 &guid)
"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)
{
@@ -997,20 +1176,25 @@ void Group::_setLeader(const uint64 &guid)
}
}
}
+
// 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)
@@ -1019,72 +1203,91 @@ void Group::_removeRolls(const uint64 &guid)
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);
}
}
+
bool Group::_setMembersGroup(const uint64 &guid, const uint8 &group)
{
member_witerator slot = _getMemberWSlot(guid);
if(slot==m_memberSlots.end())
return false;
+
slot->group = group;
+
SubGroupCounterIncrease(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)
{
uint8 prevSubGroup;
prevSubGroup = GetMemberGroup(guid);
+
SubGroupCounterDecrease(prevSubGroup);
+
if(_setMembersGroup(guid, group))
SendUpdate();
}
@@ -1092,6 +1295,7 @@ void Group::ChangeMembersGroup(const uint64 &guid, const uint8 &group)
// This methods handles itself groupcounter decrease
ChangeMembersGroup(player, group);
}
+
// only for online members
void Group::ChangeMembersGroup(Player *player, const uint8 &group)
{
@@ -1109,9 +1313,11 @@ void Group::ChangeMembersGroup(Player *player, const uint8 &group)
player->GetOriginalGroupRef().setSubGroup(group);
}
SubGroupCounterDecrease(prevSubGroup);
+
SendUpdate();
}
}
+
void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
{
switch (GetLootMethod())
@@ -1124,6 +1330,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
// 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())
{
@@ -1136,6 +1343,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
}
++guid_itr;
}
+
// search next after current
if(guid_itr != m_memberSlots.end())
{
@@ -1146,6 +1354,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
if (pl->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
{
bool refresh = pl->GetLootGUID()==creature->GetGUID();
+
//if(refresh) // update loot for new looter
// pl->GetSession()->DoLootRelease(pl->GetLootGUID());
SetLooterGuid(pl->GetGUID());
@@ -1157,6 +1366,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
}
}
}
+
// search from start
for(member_citerator itr = m_memberSlots.begin(); itr != guid_itr; ++itr)
{
@@ -1165,6 +1375,7 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
if (pl->IsWithinDist(creature,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
{
bool refresh = pl->GetLootGUID()==creature->GetGUID();
+
//if(refresh) // update loot for new looter
// pl->GetSession()->DoLootRelease(pl->GetLootGUID());
SetLooterGuid(pl->GetGUID());
@@ -1175,9 +1386,11 @@ void Group::UpdateLooterGuid( Creature* creature, bool ifneed )
}
}
}
+
SetLooterGuid(0);
SendUpdate();
}
+
uint32 Group::CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot)
{
// check for min / max count
@@ -1186,14 +1399,17 @@ uint32 Group::CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId, BattleGround
return BG_JOIN_ERR_GROUP_NOT_ENOUGH;
if(memberscount > MaxPlayerCount)
return BG_JOIN_ERR_GROUP_TOO_MANY;
+
// get a player as reference, to compare other players' stats to (arena team id, queue id based on level, etc.)
Player * reference = GetFirstMember()->getSource();
// no reference found, can't join this way
if(!reference)
return BG_JOIN_ERR_OFFLINE_MEMBER;
+
BGQueueIdBasedOnLevel queue_id = reference->GetBattleGroundQueueIdFromLevel(bgTypeId);
uint32 arenaTeamId = reference->GetArenaTeamId(arenaSlot);
uint32 team = reference->GetTeam();
+
// check every member of the group to be able to join
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
{
@@ -1222,18 +1438,22 @@ uint32 Group::CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId, BattleGround
}
return BG_JOIN_ERR_OK;
}
+
//===================================================
//============== Roll ===============================
//===================================================
+
void Roll::targetObjectBuildLink()
{
// called from link()
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();
@@ -1243,6 +1463,7 @@ void Group::SetDifficulty(uint8 difficulty)
player->SendDungeonDifficulty(true);
}
}
+
bool Group::InCombatToInstance(uint32 instanceId)
{
for(GroupReference *itr = GetFirstMember(); itr != NULL; itr = itr->next())
@@ -1255,13 +1476,17 @@ bool Group::InCombatToInstance(uint32 instanceId)
}
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;
@@ -1271,6 +1496,7 @@ void Group::ResetInstances(uint8 method, Player* SendMsgTo)
++itr;
continue;
}
+
if(method == INSTANCE_RESET_ALL)
{
// the "reset all instances" method can only reset normal maps
@@ -1280,6 +1506,7 @@ void Group::ResetInstances(uint8 method, Player* SendMsgTo)
continue;
}
}
+
bool isEmpty = true;
// if the map is loaded, reset it
Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
@@ -1290,11 +1517,13 @@ void Group::ResetInstances(uint8 method, Player* SendMsgTo)
else
isEmpty = !map->HavePlayers();
}
+
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
@@ -1311,17 +1540,20 @@ void Group::ResetInstances(uint8 method, Player* SendMsgTo)
++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())
@@ -1335,11 +1567,13 @@ InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, boo
}
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());
@@ -1348,6 +1582,7 @@ InstanceGroupBind* Group::BindToInstance(InstanceSave *save, bool permanent, boo
else
return NULL;
}
+
void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
{
BoundInstancesMap::iterator itr = m_boundInstances[difficulty].find(mapid);
@@ -1358,6 +1593,7 @@ void Group::UnbindInstance(uint32 mapid, uint8 difficulty, bool unload)
m_boundInstances[difficulty].erase(itr);
}
}
+
void Group::_homebindIfInstance(Player *player)
{
if(player && !player->isGameMaster() && sMapStore.LookupEntry(player->GetMapId())->IsDungeon())
@@ -1370,12 +1606,14 @@ void Group::_homebindIfInstance(Player *player)
player->m_InstanceValid = false;
}
}
+
void Group::BroadcastGroupUpdate(void)
{
// FG: HACK: force flags update on group leave - for values update hack
// -- not very efficient but safe
for(member_citerator citr = m_memberSlots.begin(); citr != m_memberSlots.end(); ++citr)
{
+
Player *pp = objmgr.GetPlayer(citr->guid);
if(pp && pp->IsInWorld())
{
diff --git a/src/game/Group.h b/src/game/Group.h
index a77c106c550..d09ef616897 100644
--- a/src/game/Group.h
+++ b/src/game/Group.h
@@ -17,17 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_GROUP_H
#define TRINITYCORE_GROUP_H
+
#include "GroupReference.h"
#include "GroupRefManager.h"
#include "BattleGround.h"
#include "LootMgr.h"
+
#include <map>
#include <vector>
+
#define MAXGROUPSIZE 5
#define MAXRAIDSIZE 40
#define TARGETICONCOUNT 8
+
enum RollVote
{
PASS = 0,
@@ -36,6 +41,7 @@ enum RollVote
NOT_EMITED_YET = 3,
NOT_VALID = 4
};
+
enum GroupMemberOnlineStatus
{
MEMBER_STATUS_OFFLINE = 0x0000,
@@ -48,12 +54,15 @@ enum GroupMemberOnlineStatus
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
@@ -80,10 +89,13 @@ enum GroupUpdateFlags
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:
@@ -94,6 +106,7 @@ class Roll : public LootValidatorRef
void setLoot(Loot *pLoot) { link(pLoot, this); }
Loot *getLoot() { return getTarget(); }
void targetObjectBuildLink();
+
uint64 itemGUID;
uint32 itemid;
int32 itemRandomPropId;
@@ -106,6 +119,7 @@ class Roll : public LootValidatorRef
uint8 totalPass;
uint8 itemSlot;
};
+
struct InstanceGroupBind
{
InstanceSave *save;
@@ -114,6 +128,7 @@ struct InstanceGroupBind
PlayerInstanceBind for the same instance. */
InstanceGroupBind() : save(NULL), perm(false) {}
};
+
/** request member stats checken **/
/** todo: uninvite people that not accepted invite **/
class TRINITY_DLL_SPEC Group
@@ -128,14 +143,18 @@ class TRINITY_DLL_SPEC Group
};
typedef std::list<MemberSlot> MemberSlotList;
typedef MemberSlotList::const_iterator member_citerator;
+
typedef UNORDERED_MAP< uint32 /*mapId*/, InstanceGroupBind> BoundInstancesMap;
protected:
typedef MemberSlotList::iterator member_witerator;
typedef std::set<Player*> InvitesList;
+
typedef std::vector<Roll*> 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);
@@ -153,6 +172,7 @@ class TRINITY_DLL_SPEC Group
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; }
@@ -163,6 +183,7 @@ class TRINITY_DLL_SPEC Group
LootMethod GetLootMethod() const { return m_lootMethod; }
const uint64& GetLooterGuid() const { return m_looterGuid; }
ItemQualities GetLootThreshold() const { return m_lootThreshold; }
+
// member manipulation methods
bool IsMember(const uint64& guid) const { return _getMemberCSlot(guid) != m_memberSlots.end(); }
bool IsLeader(const uint64& guid) const { return (GetLeaderGUID() == guid); }
@@ -182,29 +203,37 @@ class TRINITY_DLL_SPEC Group
member_citerator mslot = _getMemberCSlot(guid);
if(mslot==m_memberSlots.end())
return false;
+
return mslot->assistant;
}
Player* GetInvited(const uint64& guid) const;
Player* GetInvited(const std::string& name) const;
+
bool SameSubGroup(uint64 guid1,const 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 HasFreeSlotSubGroup(uint8 subgroup) const
{
return (m_subGroupsCounts && m_subGroupsCounts[subgroup] < MAXGROUPSIZE);
}
+
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(); }
@@ -214,14 +243,19 @@ class TRINITY_DLL_SPEC Group
member_citerator mslot = _getMemberCSlot(guid);
if(mslot==m_memberSlots.end())
return (MAXRAIDSIZE/MAXGROUPSIZE+1);
+
return mslot->group;
}
+
// some additional raid methods
void ConvertToRaid();
+
void SetBattlegroundGroup(BattleGround *bg) { m_bgGroup = bg; }
uint32 CanJoinBattleGroundQueue(BattleGroundTypeId bgTypeId, BattleGroundQueueTypeId bgQueueTypeId, uint32 MinPlayerCount, uint32 MaxPlayerCount, bool isRated, uint32 arenaSlot);
+
void ChangeMembersGroup(const uint64 &guid, const uint8 &group);
void ChangeMembersGroup(Player *player, const uint8 &group);
+
void SetAssistant(uint64 guid, const bool &state)
{
if(!isRaidGroup())
@@ -233,6 +267,7 @@ class TRINITY_DLL_SPEC Group
{
if(!isRaidGroup())
return;
+
if(_setMainTank(guid))
SendUpdate();
}
@@ -240,15 +275,18 @@ class TRINITY_DLL_SPEC Group
{
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);
@@ -258,9 +296,11 @@ class TRINITY_DLL_SPEC Group
void BroadcastPacket(WorldPacket *packet, bool ignorePlayersInBGRaid, int group=-1, uint64 ignore=0);
void BroadcastReadyCheck(WorldPacket *packet);
void OfflineReadyCheck();
+
/*********************************************************/
/*** LOOT SYSTEM ***/
/*********************************************************/
+
void SendLootStartRoll(uint32 CountDown, const Roll &r);
void SendLootRoll(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r);
void SendLootRollWon(const uint64& SourceGuid, const uint64& TargetGuid, uint8 RollNumber, uint8 RollType, const Roll &r);
@@ -283,34 +323,45 @@ class TRINITY_DLL_SPEC Group
void CountTheRoll(Rolls::iterator roll, uint32 NumberOfPlayers);
void CountRollVote(const uint64& playerGUID, const 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]; }
+
// FG: evil hacks
void BroadcastGroupUpdate(void);
+
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);
+
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);
+
void _initRaidSubGroupsCounter()
{
// Sub group counters initialization
if (!m_subGroupsCounts)
m_subGroupsCounts = new uint8[MAXRAIDSIZE / MAXGROUPSIZE];
+
memset((void*)m_subGroupsCounts, 0, (MAXRAIDSIZE / MAXGROUPSIZE)*sizeof(uint8));
+
for (member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
++m_subGroupsCounts[itr->group];
}
+
member_citerator _getMemberCSlot(uint64 Guid) const
{
for(member_citerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
@@ -320,6 +371,7 @@ class TRINITY_DLL_SPEC Group
}
return m_memberSlots.end();
}
+
member_witerator _getMemberWSlot(uint64 Guid)
{
for(member_witerator itr = m_memberSlots.begin(); itr != m_memberSlots.end(); ++itr)
@@ -329,16 +381,19 @@ class TRINITY_DLL_SPEC Group
}
return m_memberSlots.end();
}
+
void SubGroupCounterIncrease(uint8 subgroup)
{
if (m_subGroupsCounts)
++m_subGroupsCounts[subgroup];
}
+
void SubGroupCounterDecrease(uint8 subgroup)
{
if (m_subGroupsCounts)
--m_subGroupsCounts[subgroup];
}
+
MemberSlotList m_memberSlots;
GroupRefManager m_memberMgr;
InvitesList m_invitees;
diff --git a/src/game/GroupHandler.cpp b/src/game/GroupHandler.cpp
index d2a347c8763..2746c6c7772 100644
--- a/src/game/GroupHandler.cpp
+++ b/src/game/GroupHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -30,7 +31,9 @@
#include "SocialMgr.h"
#include "Util.h"
#include "SpellAuras.h"
+
class Aura;
+
/* differeces from off:
-you can uninvite yourself - is is useful
-you can accept invitation even if leader went offline
@@ -41,32 +44,40 @@ class Aura;
-quest sharing has to be corrected
-FIX sending PartyMemberStats
*/
+
void WorldSession::SendPartyResult(PartyOperation operation, const 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;
+
// 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;
}
+
// restrict invite to GMs
if (!sWorld.getConfig(CONFIG_ALLOW_GM_GROUP) && !GetPlayer()->isGameMaster() && player->isGameMaster())
{
@@ -90,14 +101,17 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data )
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;
}
+
Group *group = GetPlayer()->GetGroup();
if( group && group->isBGGroup() )
group = GetPlayer()->GetOriginalGroup();
+
Group *group2 = player->GetGroup();
if( group2 && group2->isBGGroup() )
group2 = player->GetOriginalGroup();
@@ -107,6 +121,7 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data )
SendPartyResult(PARTY_OP_INVITE, membername, PARTY_RESULT_ALREADY_IN_GROUP);
return;
}
+
if(group)
{
// not have permissions for invite
@@ -122,6 +137,7 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data )
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
@@ -148,33 +164,42 @@ void WorldSession::HandleGroupInviteOpcode( WorldPacket & recv_data )
return;
}
}
+
// ok, we do it
WorldPacket data(SMSG_GROUP_INVITE, 10); // guess size
data << uint8(1); // ok
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());
+
// forming a new group, create it
if(!group->IsCreated())
{
@@ -183,156 +208,198 @@ void WorldSession::HandleGroupAcceptOpcode( WorldPacket & /*recv_data*/ )
group->Create(group->GetLeaderGUID(), group->GetLeaderName());
objmgr.AddGroup(group);
}
+
// everything's fine, do it, PLAYER'S GROUP IS SET IN ADDMEMBER!!!
if(!group->AddMember(GetPlayer()->GetGUID(), GetPlayer()->GetName()))
return;
+
group->BroadcastGroupUpdate();
}
+
void WorldSession::HandleGroupDeclineOpcode( WorldPacket & /*recv_data*/ )
{
Group *group = GetPlayer()->GetGroupInvite();
if (!group) return;
+
// remember leader if online
Player *leader = objmgr.GetPlayer(group->GetLeaderGUID());
+
// uninvite, group can be deleted
GetPlayer()->UninviteFromGroup();
+
if(!leader || !leader->GetSession())
return;
+
// report
WorldPacket data( SMSG_GROUP_DECLINE, 10 ); // guess size
data << GetPlayer()->GetName();
leader->GetSession()->SendPacket( &data );
}
+
void WorldSession::HandleGroupUninviteGuidOpcode(WorldPacket & recv_data)
{
uint64 guid;
recv_data >> guid;
+
//can't uninvite yourself
if(guid == GetPlayer()->GetGUID())
{
sLog.outError("WorldSession::HandleGroupUninviteGuidOpcode: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
return;
}
+
PartyResult res = GetPlayer()->CanUninviteFromGroup();
if(res != PARTY_RESULT_OK)
{
SendPartyResult(PARTY_OP_LEAVE, "", res);
return;
}
+
Group* grp = GetPlayer()->GetGroup();
if(!grp)
return;
+
if(grp->IsMember(guid))
{
Player::RemoveFromGroup(grp,guid);
return;
}
+
if(Player* plr = grp->GetInvited(guid))
{
plr->UninviteFromGroup();
return;
}
+
SendPartyResult(PARTY_OP_LEAVE, "", PARTY_RESULT_NOT_IN_YOUR_PARTY);
}
+
void WorldSession::HandleGroupUninviteOpcode(WorldPacket & recv_data)
{
std::string membername;
recv_data >> membername;
+
// player not found
if(!normalizePlayerName(membername))
return;
+
// can't uninvite yourself
if(GetPlayer()->GetName() == membername)
{
sLog.outError("WorldSession::HandleGroupUninviteOpcode: leader %s(%d) tried to uninvite himself from the group.", GetPlayer()->GetName(), GetPlayer()->GetGUIDLow());
return;
}
+
PartyResult res = GetPlayer()->CanUninviteFromGroup();
if(res != PARTY_RESULT_OK)
{
SendPartyResult(PARTY_OP_LEAVE, "", res);
return;
}
+
Group* grp = GetPlayer()->GetGroup();
if(!grp)
return;
+
if(uint64 guid = grp->GetMemberGUID(membername))
{
Player::RemoveFromGroup(grp,guid);
return;
}
+
if(Player* plr = grp->GetInvited(membername))
{
plr->UninviteFromGroup();
return;
}
+
SendPartyResult(PARTY_OP_LEAVE, membername, PARTY_RESULT_NOT_IN_YOUR_PARTY);
}
+
void WorldSession::HandleGroupSetLeaderOpcode( WorldPacket & recv_data )
{
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::HandleGroupDisbandOpcode( 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 )
{
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 )
{
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);
+
switch (Choise)
{
case 1:
@@ -343,16 +410,21 @@ void WorldSession::HandleLootRoll( WorldPacket &recv_data )
break;
}
}
+
void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data)
{
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();
@@ -360,18 +432,23 @@ void WorldSession::HandleMinimapPingOpcode(WorldPacket& recv_data)
data << y;
GetPlayer()->GetGroup()->BroadcastPacket(&data, true, -1, GetPlayer()->GetGUID());
}
+
void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data)
{
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;
@@ -382,15 +459,19 @@ void WorldSession::HandleRandomRollOpcode(WorldPacket& recv_data)
else
SendPacket(&data);
}
+
void WorldSession::HandleRaidTargetUpdateOpcode( WorldPacket & recv_data )
{
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
{
@@ -400,45 +481,57 @@ void WorldSession::HandleRaidTargetUpdateOpcode( WorldPacket & recv_data )
{
if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
return;
+
uint64 guid;
recv_data >> guid;
group->SetTargetIcon(x, guid);
}
}
+
void WorldSession::HandleGroupRaidConvertOpcode( 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 )
{
// we will get correct pointer for group here, so we don't have to check if group is BG raid
Group *group = GetPlayer()->GetGroup();
if(!group)
return;
+
std::string name;
uint8 groupNr;
recv_data >> name;
+
recv_data >> groupNr;
+
/** error handling **/
if(!group->IsLeader(GetPlayer()->GetGUID()) && !group->IsAssistant(GetPlayer()->GetGUID()))
return;
+
if (!group->HasFreeSlotSubGroup(groupNr))
return;
/********************/
+
Player *movedPlayer=objmgr.GetPlayer(name.c_str());
if(!movedPlayer)
return;
+
//Do not allow leader to change group of player in combat
if (movedPlayer->isInCombat())
{
@@ -446,31 +539,39 @@ void WorldSession::HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
// everything's fine, do it
group->ChangeMembersGroup(movedPlayer, groupNr);
}
+
void WorldSession::HandleGroupAssistantLeaderOpcode( WorldPacket & recv_data )
{
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::HandlePartyAssignmentOpcode( WorldPacket & recv_data )
{
sLog.outDebug("MSG_PARTY_ASSIGNMENT");
+
Group *group = GetPlayer()->GetGroup();
if(!group)
return;
+
uint8 flag1, flag2;
uint64 guid;
recv_data >> flag1 >> flag2;
@@ -479,37 +580,44 @@ void WorldSession::HandlePartyAssignmentOpcode( WorldPacket & recv_data )
// 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, false, -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();
@@ -517,31 +625,41 @@ void WorldSession::HandleRaidReadyCheckOpcode( WorldPacket & recv_data )
group->BroadcastReadyCheck(&data);
}
}
+
void WorldSession::HandleRaidReadyCheckFinishedOpcode( 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)
@@ -554,23 +672,32 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint16) MEMBER_STATUS_OFFLINE;
}
+
if (mask & GROUP_UPDATE_FLAG_CUR_HP)
*data << (uint32) player->GetHealth();
+
if (mask & GROUP_UPDATE_FLAG_MAX_HP)
*data << (uint32) 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)
{
const uint64& auramask = player->GetAuraUpdateMaskForRaid();
@@ -585,6 +712,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
}
}
}
+
Pet *pet = player->GetPet();
if (mask & GROUP_UPDATE_FLAG_PET_GUID)
{
@@ -593,6 +721,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint64) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_NAME)
{
if(pet)
@@ -600,6 +729,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint8) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_MODEL_ID)
{
if(pet)
@@ -607,6 +737,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint16) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_CUR_HP)
{
if(pet)
@@ -614,6 +745,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint32) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_MAX_HP)
{
if(pet)
@@ -621,6 +753,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint32) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_POWER_TYPE)
{
if(pet)
@@ -628,6 +761,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint8) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_CUR_POWER)
{
if(pet)
@@ -635,6 +769,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint16) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_MAX_POWER)
{
if(pet)
@@ -642,6 +777,7 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
else
*data << (uint16) 0;
}
+
if (mask & GROUP_UPDATE_FLAG_PET_AURAS)
{
if(pet)
@@ -662,12 +798,14 @@ void WorldSession::BuildPartyMemberStatsChangedPacket(Player *player, WorldPacke
*data << (uint64) 0;
}
}
+
/*this procedure handles clients CMSG_REQUEST_PARTY_MEMBER_STATS request*/
void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
{
sLog.outDebug("WORLD: Received CMSG_REQUEST_PARTY_MEMBER_STATS");
uint64 Guid;
recv_data >> Guid;
+
Player *player = objmgr.GetPlayer(Guid);
if(!player)
{
@@ -679,13 +817,17 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
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 << uint8(0); // only for SMSG_PARTY_MEMBER_STATS_FULL, probably arena/bg related
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
@@ -698,6 +840,7 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
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
@@ -711,6 +854,7 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
}
}
data.put<uint64>(maskPos,auramask); // GROUP_UPDATE_FLAG_AURAS
+
if(pet)
{
Powers petpowertype = pet->getPowerType();
@@ -722,6 +866,7 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
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
@@ -741,22 +886,28 @@ void WorldSession::HandleRequestPartyMemberStatsOpcode( WorldPacket &recv_data )
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::HandleOptOutOfLootOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received CMSG_OPT_OUT_OF_LOOT");
+
uint32 unkn;
recv_data >> unkn;
+
// ignore if player not loaded
if(!GetPlayer()) // needed because STATUS_AUTHED
{
@@ -764,6 +915,7 @@ void WorldSession::HandleOptOutOfLootOpcode( WorldPacket & recv_data )
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
index d1f24c3f847..ab3b6417611 100644
--- a/src/game/GroupRefManager.h
+++ b/src/game/GroupRefManager.h
@@ -17,12 +17,16 @@
* 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<Group, Player>
{
public:
diff --git a/src/game/GroupReference.cpp b/src/game/GroupReference.cpp
index 218bceee0f4..a09fc769dad 100644
--- a/src/game/GroupReference.cpp
+++ b/src/game/GroupReference.cpp
@@ -17,19 +17,23 @@
* 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()
diff --git a/src/game/GroupReference.h b/src/game/GroupReference.h
index e1e550ada73..da896cf02dd 100644
--- a/src/game/GroupReference.h
+++ b/src/game/GroupReference.h
@@ -17,11 +17,15 @@
* 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 TRINITY_DLL_SPEC GroupReference : public Reference<Group, Player>
{
protected:
diff --git a/src/game/GuardAI.cpp b/src/game/GuardAI.cpp
index 5454213ce65..a821939910a 100644
--- a/src/game/GuardAI.cpp
+++ b/src/game/GuardAI.cpp
@@ -17,26 +17,32 @@
* 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 "Player.h"
#include "ObjectAccessor.h"
#include "World.h"
#include "CreatureAIImpl.h"
+
int GuardAI::Permissible(const Creature *creature)
{
if( creature->isGuard())
return PERMIT_BASE_SPECIAL;
+
return PERMIT_BASE_NO;
}
+
GuardAI::GuardAI(Creature *c) : CreatureAI(c), i_victimGuid(0), i_state(STATE_NORMAL), i_tracker(TIME_INTERVAL_LOOK)
{
}
+
void GuardAI::MoveInLineOfSight(Unit *u)
{
// Ignore Z for flying creatures
if ( !m_creature->canFly() && m_creature->GetDistanceZ(u) > CREATURE_Z_ATTACK_RANGE )
return;
+
if( !m_creature->getVictim() && m_creature->canAttack(u) &&
( u->IsHostileToPlayers() || m_creature->IsHostileTo(u) /*|| u->getVictim() && m_creature->IsFriendlyTo(u->getVictim())*/ ) &&
u->isInAccessiblePlaceFor(m_creature))
@@ -50,19 +56,24 @@ void GuardAI::MoveInLineOfSight(Unit *u)
}
}
}
+
void GuardAI::EnterEvadeMode()
{
if( !m_creature->isAlive() )
{
DEBUG_LOG("Creature stopped attacking because he's dead [guid=%u]", m_creature->GetGUIDLow());
m_creature->GetMotionMaster()->MoveIdle();
+
i_state = STATE_NORMAL;
+
i_victimGuid = 0;
m_creature->CombatStop(true);
m_creature->DeleteThreatList();
return;
}
+
Unit* victim = ObjectAccessor::GetUnit(*m_creature, i_victimGuid );
+
if( !victim )
{
DEBUG_LOG("Creature stopped attacking because victim is non exist [guid=%u]", m_creature->GetGUIDLow());
@@ -83,21 +94,26 @@ void GuardAI::EnterEvadeMode()
{
DEBUG_LOG("Creature stopped attacking because victim outran him [guid=%u]", m_creature->GetGUIDLow());
}
+
m_creature->RemoveAllAuras();
m_creature->DeleteThreatList();
i_victimGuid = 0;
m_creature->CombatStop(true);
i_state = STATE_NORMAL;
+
// Remove TargetedMovementGenerator from MotionMaster stack list, and add HomeMovementGenerator instead
if( m_creature->GetMotionMaster()->GetCurrentMovementGeneratorType() == TARGETED_MOTION_TYPE )
m_creature->GetMotionMaster()->MoveTargetedHome();
}
+
void GuardAI::UpdateAI(const uint32 /*diff*/)
{
// update i_victimGuid if m_creature->getVictim() !=0 and changed
if(!UpdateVictim())
return;
+
i_victimGuid = m_creature->getVictim()->GetGUID();
+
if( m_creature->isAttackReady() )
{
if( m_creature->IsWithinMeleeRange(m_creature->getVictim()))
@@ -107,14 +123,17 @@ void GuardAI::UpdateAI(const uint32 /*diff*/)
}
}
}
+
bool GuardAI::IsVisible(Unit *pl) const
{
return m_creature->IsWithinDist(pl,sWorld.getConfig(CONFIG_SIGHT_GUARDER))
&& pl->isVisibleForOrDetect(m_creature,true);
}
+
void GuardAI::JustDied(Unit *killer)
{
if(Player* pkiller = killer->GetCharmerOrOwnerPlayerOrPlayerItself())
m_creature->SendZoneUnderAttackMessage(pkiller);
}
+
diff --git a/src/game/GuardAI.h b/src/game/GuardAI.h
index 3d6b07e998b..db1bfe0229d 100644
--- a/src/game/GuardAI.h
+++ b/src/game/GuardAI.h
@@ -17,11 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_GUARDAI_H
#define TRINITY_GUARDAI_H
+
#include "CreatureAI.h"
#include "Timer.h"
+
class Creature;
+
class TRINITY_DLL_DECL GuardAI : public CreatureAI
{
enum GuardState
@@ -29,14 +33,19 @@ class TRINITY_DLL_DECL GuardAI : public CreatureAI
STATE_NORMAL = 1,
STATE_LOOK_AT_VICTIM = 2
};
+
public:
+
explicit GuardAI(Creature *c);
+
void MoveInLineOfSight(Unit *);
void EnterEvadeMode();
void JustDied(Unit *);
bool IsVisible(Unit *) const;
+
void UpdateAI(const uint32);
static int Permissible(const Creature *);
+
private:
uint64 i_victimGuid;
GuardState i_state;
diff --git a/src/game/Guild.cpp b/src/game/Guild.cpp
index 2b7d49d7dbf..92faebccad5 100644
--- a/src/game/Guild.cpp
+++ b/src/game/Guild.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -30,6 +31,7 @@
#include "Language.h"
#include "World.h"
#include "Config/ConfigEnv.h"
+
Guild::Guild()
{
m_Id = 0;
@@ -41,29 +43,37 @@ Guild::Guild()
m_BorderStyle = 0;
m_BorderColor = 0;
m_BackgroundColor = 0;
+
m_CreatedYear = 0;
m_CreatedMonth = 0;
m_CreatedDay = 0;
+
m_EventLogLoaded = false;
m_GuildBankLoaded = false;
m_OnlineMembers = 0;
m_GuildBankMoney = 0;
m_PurchasedTabs = 0;
+
m_GuildEventLogNextGuid = 0;
m_GuildBankEventLogNextGuid_Money = 0;
for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; i++)
m_GuildBankEventLogNextGuid_Item[i] = 0;
}
+
Guild::~Guild()
{
+
}
+
bool Guild::Create(Player* leader, std::string gname)
{
if (objmgr.GetGuildByName(gname))
return false;
+
WorldSession* lSession = leader->GetSession();
if (!lSession)
return false;
+
m_LeaderGuid = leader->GetGUID();
m_Name = gname;
GINFO = "";
@@ -71,13 +81,17 @@ bool Guild::Create(Player* leader, std::string gname)
m_GuildBankMoney = 0;
m_PurchasedTabs = 0;
m_Id = objmgr.GenerateGuildId();
+
sLog.outDebug("GUILD: creating guild %s to leader: %u", gname.c_str(), GUID_LOPART(m_LeaderGuid));
+
// 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_member WHERE guildid='%u'", m_Id);
@@ -85,19 +99,24 @@ bool Guild::Create(Player* leader, std::string gname)
"VALUES('%u','%s','%u', '%s', '%s', UNIX_TIMESTAMP(NOW()),'%u','%u','%u','%u','%u','" UI64FMTD "')",
m_Id, gname.c_str(), GUID_LOPART(m_LeaderGuid), dbGINFO.c_str(), dbMOTD.c_str(), m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_BackgroundColor, m_GuildBankMoney);
CharacterDatabase.CommitTransaction();
+
CreateDefaultGuildRanks(lSession->GetSessionDbLocaleIndex());
+
return AddMember(m_LeaderGuid, (uint32)GR_GUILDMASTER);
}
+
void Guild::CreateDefaultGuildRanks(int locale_idx)
{
CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid='%u'", m_Id);
CharacterDatabase.PExecute("DELETE FROM guild_bank_right WHERE guildid = '%u'", m_Id);
+
CreateRank(objmgr.GetMangosString(LANG_GUILD_MASTER, locale_idx), GR_RIGHT_ALL);
CreateRank(objmgr.GetMangosString(LANG_GUILD_OFFICER, locale_idx), GR_RIGHT_ALL);
CreateRank(objmgr.GetMangosString(LANG_GUILD_VETERAN, locale_idx), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
CreateRank(objmgr.GetMangosString(LANG_GUILD_MEMBER, locale_idx), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
CreateRank(objmgr.GetMangosString(LANG_GUILD_INITIATE, locale_idx), GR_RIGHT_GCHATLISTEN | GR_RIGHT_GCHATSPEAK);
}
+
bool Guild::AddMember(uint64 plGuid, uint32 plRank)
{
Player* pl = objmgr.GetPlayer(plGuid);
@@ -111,11 +130,14 @@ 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 (pl)
{
newmember.Name = pl->GetName();
@@ -128,6 +150,7 @@ bool Guild::AddMember(uint64 plGuid, uint32 plRank)
QueryResult *result = CharacterDatabase.PQuery("SELECT name,zone,level,class FROM characters WHERE guid = '%u'", GUID_LOPART(plGuid));
if (!result)
return false; // player doesn't exist
+
Field *fields = result->Fetch();
newmember.Name = fields[0].GetCppString();
newmember.ZoneId = fields[1].GetUInt32();
@@ -141,6 +164,7 @@ bool Guild::AddMember(uint64 plGuid, uint32 plRank)
return false;
}
}
+
newmember.RankId = plRank;
newmember.OFFnote = (std::string)"";
newmember.Pnote = (std::string)"";
@@ -149,12 +173,15 @@ bool Guild::AddMember(uint64 plGuid, uint32 plRank)
for (uint8 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')",
m_Id, GUID_LOPART(plGuid), newmember.RankId, dbPnote.c_str(), dbOFFnote.c_str());
+
// If player not in game data in data field will be loaded from guild tables, no need to update it!!
if (pl)
{
@@ -164,24 +191,30 @@ bool Guild::AddMember(uint64 plGuid, uint32 plRank)
}
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(), m_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(), m_Id);
}
+
bool Guild::LoadGuildFromDB(uint32 GuildId)
{
//set m_Id in case guild data are broken in DB and Guild will be Disbanded (deleted from DB)
m_Id = GuildId;
+
QueryResult *result = CharacterDatabase.PQuery("SELECT COUNT(TabId) FROM guild_bank_tab WHERE guildid='%u'", GuildId);
if (result)
{
@@ -191,20 +224,28 @@ bool Guild::LoadGuildFromDB(uint32 GuildId)
m_PurchasedTabs = GUILD_BANK_MAX_TABS;
delete result;
}
+
if (!LoadRanksFromDB(GuildId))
return false;
+
if (!LoadMembersFromDB(GuildId))
return false;
+
LoadBankRightsFromDB(GuildId); // Must be after LoadRanksFromDB because it populates rank struct
+
// 0 1 2 3 4 5
result = CharacterDatabase.PQuery("SELECT name, leaderguid, EmblemStyle, EmblemColor, BorderStyle, BorderColor,"
// 6 7 8 9 10
"BackgroundColor, info, motd, createdate, BankMoney FROM guild WHERE guildid = '%u'", GuildId);
+
if (!result)
return false;
+
Field *fields = result->Fetch();
+
m_Name = fields[0].GetCppString();
m_LeaderGuid = MAKE_NEW_GUID(fields[1].GetUInt32(), 0, HIGHGUID_PLAYER);
+
m_EmblemStyle = fields[2].GetUInt32();
m_EmblemColor = fields[3].GetUInt32();
m_BorderStyle = fields[4].GetUInt32();
@@ -214,7 +255,9 @@ bool Guild::LoadGuildFromDB(uint32 GuildId)
MOTD = fields[8].GetCppString();
time_t time = fields[9].GetUInt64();
m_GuildBankMoney = fields[10].GetUInt64();
+
delete result;
+
if (time > 0)
{
tm local = *(localtime(&time)); // dereference and assign
@@ -222,6 +265,7 @@ bool Guild::LoadGuildFromDB(uint32 GuildId)
m_CreatedMonth = local.tm_mon + 1;
m_CreatedYear = local.tm_year + 1900;
}
+
// Repair the structure of guild
// If the guildmaster doesn't exist or isn't the member of guild
// attempt to promote another member
@@ -235,45 +279,58 @@ bool Guild::LoadGuildFromDB(uint32 GuildId)
}
else if (GM_rights != GR_GUILDMASTER)
SetLeader(m_LeaderGuid);
+
// Check config if multiple guildmasters are allowed
if (sConfig.GetBoolDefault("Guild.AllowMultipleGuildMaster", 0) == 0)
for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
if (itr->second.RankId == GR_GUILDMASTER && GUID_LOPART(m_LeaderGuid) != itr->first) // Allow only 1 guildmaster
ChangeRank(itr->first, GR_OFFICER); // set right of member to officer
+
sLog.outDebug("Guild %u Creation time Loaded day: %u, month: %u, year: %u", GuildId, m_CreatedDay, m_CreatedMonth, m_CreatedYear);
+
return true;
}
+
bool Guild::LoadRanksFromDB(uint32 GuildId)
{
Field *fields;
// 0 1 2 3
QueryResult *result = CharacterDatabase.PQuery("SELECT rid,rname,rights,BankMoneyPerDay FROM guild_rank WHERE guildid = '%u' ORDER BY rid ASC", GuildId);
+
if (!result)
{
sLog.outError("Guild %u has broken `guild_rank` data, creating new...",GuildId);
CreateDefaultGuildRanks(0);
return true;
}
+
bool broken_ranks = false;
+
//GUILD RANKS are sequence starting from 0 = GUILD_MASTER (ALL PRIVILEGES) to max 9 (lowest privileges)
//the lower rank id is considered higher rank - so promotion does rank-- and demotion does rank++
//between ranks in sequence cannot be gaps - so 0,1,2,4 cannot be
//min ranks count is 5 and max is 10.
+
do
{
fields = result->Fetch();
+
uint32 rankID = fields[0].GetUInt32();
std::string rankName = fields[1].GetCppString();
uint32 rankRights = fields[2].GetUInt32();
uint32 rankMoney = fields[3].GetUInt32();
+
if (rankID != m_Ranks.size()) // guild_rank.ids are sequence 0,1,2,3..
broken_ranks = true;
+
//first rank is guildmaster, prevent loss leader rights
if (m_Ranks.empty())
rankRights |= GR_RIGHT_ALL;
+
AddRank(rankName,rankRights,rankMoney);
}while (result->NextRow());
delete result;
+
if (m_Ranks.size() < GUILD_RANKS_MIN_COUNT) // if too few ranks, renew them
{
m_Ranks.clear();
@@ -296,8 +353,10 @@ bool Guild::LoadRanksFromDB(uint32 GuildId)
}
CharacterDatabase.CommitTransaction();
}
+
return true;
}
+
bool Guild::LoadMembersFromDB(uint32 GuildId)
{
// 0 1 2 3 4 5
@@ -309,8 +368,10 @@ bool Guild::LoadMembersFromDB(uint32 GuildId)
// 18 19 20 21 22
"characters.name, characters.level, characters.zone, characters.class, characters.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();
@@ -320,6 +381,7 @@ bool Guild::LoadMembersFromDB(uint32 GuildId)
//don't allow member to have not existing rank!
if (newmember.RankId >= m_Ranks.size())
newmember.RankId = GetLowestRank();
+
newmember.Pnote = fields[2].GetCppString();
newmember.OFFnote = fields[3].GetCppString();
newmember.BankResetTimeMoney = fields[4].GetUInt32();
@@ -329,11 +391,13 @@ bool Guild::LoadMembersFromDB(uint32 GuildId)
newmember.BankResetTimeTab[i] = fields[6+(2*i)].GetUInt32();
newmember.BankRemSlotsTab[i] = fields[7+(2*i)].GetUInt32();
}
+
newmember.Name = fields[18].GetCppString();
newmember.Level = fields[19].GetUInt8();
newmember.ZoneId = fields[20].GetUInt32();
newmember.Class = fields[21].GetUInt8();
newmember.LogoutTime = fields[22].GetUInt64();
+
//this code will remove unexisting character guids from guild
if (newmember.Level < 1 || newmember.Level > STRONG_MAX_LEVEL) // can be at broken `data` field
{
@@ -354,18 +418,24 @@ bool Guild::LoadMembersFromDB(uint32 GuildId)
CharacterDatabase.PExecute("DELETE FROM guild_member WHERE guid = '%u'", GUID_LOPART(guid));
continue;
}
+
members[GUID_LOPART(guid)] = newmember;
+
}while (result->NextRow());
delete result;
+
if (members.empty())
return false;
+
return true;
}
+
void Guild::SetMemberStats(uint64 guid)
{
MemberList::iterator itr = members.find(GUID_LOPART(guid));
if (itr == members.end() )
return;
+
Player *pl = ObjectAccessor::FindPlayer(guid);
if (!pl)
return;
@@ -374,12 +444,15 @@ void Guild::SetMemberStats(uint64 guid)
itr->second.Class = pl->getClass();
itr->second.ZoneId = pl->GetZoneId();
}
+
void Guild::SetLeader(uint64 guid)
{
m_LeaderGuid = guid;
ChangeRank(guid, GR_GUILDMASTER);
+
CharacterDatabase.PExecute("UPDATE guild SET leaderguid='%u' WHERE guildid='%u'", GUID_LOPART(guid), m_Id);
}
+
void Guild::DelMember(uint64 guid, bool isDisbanding)
{
//guild master can be deleted when loading guild and guid doesn't exist in characters table
@@ -396,6 +469,7 @@ void Guild::DelMember(uint64 guid, bool isDisbanding)
oldLeader = &(i->second);
continue;
}
+
if (!best || best->RankId > i->second.RankId)
{
best = &(i->second);
@@ -407,10 +481,13 @@ void Guild::DelMember(uint64 guid, bool isDisbanding)
Disband();
return;
}
+
SetLeader(newLeaderGUID);
+
// If player not online data in data field will be loaded from guild tabs no need to update it !!
if (Player *newLeader = objmgr.GetPlayer(newLeaderGUID))
newLeader->SetRank(GR_GUILDMASTER);
+
// when leader non-exist (at guild load with deleted leader only) not send broadcasts
if (oldLeader)
{
@@ -420,15 +497,19 @@ void Guild::DelMember(uint64 guid, bool isDisbanding)
data << oldLeader->Name;
data << best->Name;
BroadcastPacket(&data);
+
data.Initialize(SMSG_GUILD_EVENT, (1+1+(oldLeader->Name).size()+1));
data << (uint8)GE_LEFT;
data << (uint8)1;
data << oldLeader->Name;
BroadcastPacket(&data);
}
+
sLog.outDebug( "WORLD: Sent (SMSG_GUILD_EVENT)" );
}
+
members.erase(GUID_LOPART(guid));
+
Player *player = objmgr.GetPlayer(guid);
// If player not online data in data field will be loaded from guild tabs no need to update it !!
if (player)
@@ -436,29 +517,37 @@ void Guild::DelMember(uint64 guid, bool isDisbanding)
player->SetInGuild(0);
player->SetRank(0);
}
+
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 not online data in data field will be loaded from guild tabs no need to update it !!
if (player)
player->SetRank(newRank);
+
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));
@@ -469,20 +558,24 @@ void Guild::SetOFFNOTE(uint64 guid,std::string offnote)
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, const 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, const std::string& msg, uint32 language)
{
if (session && session->GetPlayer() && HasRankRight(session->GetPlayer()->GetRank(),GR_RIGHT_OFFCHATSPEAK))
@@ -491,12 +584,15 @@ void Guild::BroadcastToOfficers(WorldSession *session, const std::string& msg, u
{
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::const_iterator itr = members.begin(); itr != members.end(); ++itr)
@@ -506,6 +602,7 @@ void Guild::BroadcastPacket(WorldPacket *packet)
player->GetSession()->SendPacket(packet);
}
}
+
void Guild::BroadcastPacketToRank(WorldPacket *packet, uint32 rankId)
{
for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
@@ -518,14 +615,19 @@ void Guild::BroadcastPacketToRank(WorldPacket *packet, uint32 rankId)
}
}
}
+
void Guild::CreateRank(std::string name_,uint32 rights)
{
if (m_Ranks.size() >= GUILD_RANKS_MAX_COUNT)
return;
+
// ranks are sequence 0,1,2,... where 0 means guildmaster
uint32 new_rank_id = m_Ranks.size();
+
AddRank(name_, rights, 0);
+
//existing records in db should be deleted before calling this procedure and m_PurchasedTabs must be loaded already
+
for (uint32 i = 0; i < m_PurchasedTabs; ++i)
{
//create bank rights with 0
@@ -535,65 +637,84 @@ void Guild::CreateRank(std::string name_,uint32 rights)
CharacterDatabase.escape_string(name_);
CharacterDatabase.PExecute( "INSERT INTO guild_rank (guildid,rid,rname,rights) VALUES ('%u', '%u', '%s', '%u')", m_Id, new_rank_id, name_.c_str(), rights );
}
+
void Guild::AddRank(const std::string& name_,uint32 rights, uint32 money)
{
m_Ranks.push_back(RankInfo(name_,rights,money));
}
+
void Guild::DelRank()
{
// client won't allow to have less than GUILD_RANKS_MIN_COUNT ranks in guild
if (m_Ranks.size() <= GUILD_RANKS_MIN_COUNT)
return;
+
// delete lowest guild_rank
uint32 rank = GetLowestRank();
CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE rid>='%u' AND guildid='%u'", rank, m_Id);
+
m_Ranks.pop_back();
}
+
std::string Guild::GetRankName(uint32 rankId)
{
if (rankId >= m_Ranks.size())
return "<unknown>";
+
return m_Ranks[rankId].Name;
}
+
uint32 Guild::GetRankRights(uint32 rankId)
{
if (rankId >= m_Ranks.size())
return 0;
+
return m_Ranks[rankId].Rights;
}
+
void Guild::SetRankName(uint32 rankId, std::string name_)
{
if (rankId >= m_Ranks.size())
return;
+
m_Ranks[rankId].Name = name_;
+
// name now can be used for encoding to DB
CharacterDatabase.escape_string(name_);
CharacterDatabase.PExecute("UPDATE guild_rank SET rname='%s' WHERE rid='%u' AND guildid='%u'", name_.c_str(), rankId, m_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, m_Id);
}
+
int32 Guild::GetRank(uint32 LowGuid)
{
MemberList::const_iterator itr = members.find(LowGuid);
if (itr == members.end())
return -1;
+
return itr->second.RankId;
}
+
void Guild::Disband()
{
WorldPacket data(SMSG_GUILD_EVENT, 1);
data << (uint8)GE_DISBANDED;
BroadcastPacket(&data);
+
while (!members.empty())
{
MemberList::const_iterator itr = members.begin();
DelMember(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER), true);
}
+
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM guild WHERE guildid = '%u'", m_Id);
CharacterDatabase.PExecute("DELETE FROM guild_rank WHERE guildid = '%u'", m_Id);
@@ -606,6 +727,7 @@ void Guild::Disband()
CharacterDatabase.CommitTransaction();
objmgr.RemoveGuild(m_Id);
}
+
void Guild::Roster(WorldSession *session /*= NULL*/)
{
// we can only guess size
@@ -613,6 +735,7 @@ void Guild::Roster(WorldSession *session /*= NULL*/)
data << (uint32)members.size();
data << MOTD;
data << GINFO;
+
data << (uint32)m_Ranks.size();
for (RankList::const_iterator ritr = m_Ranks.begin(); ritr != m_Ranks.end(); ++ritr)
{
@@ -660,11 +783,14 @@ void Guild::Roster(WorldSession *session /*= NULL*/)
BroadcastPacket(&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 << m_Id;
data << m_Name;
+
for (size_t i = 0 ; i < 10; ++i) // show always 10 ranks
{
if (i < m_Ranks.size())
@@ -672,15 +798,18 @@ void Guild::Query(WorldSession *session)
else
data << (uint8)0; // null string
}
+
data << uint32(m_EmblemStyle);
data << uint32(m_EmblemColor);
data << uint32(m_BorderStyle);
data << uint32(m_BorderColor);
data << uint32(m_BackgroundColor);
data << uint32(0); // something new in WotLK
+
session->SendPacket( &data );
sLog.outDebug( "WORLD: Sent (SMSG_GUILD_QUERY_RESPONSE)" );
}
+
void Guild::SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle, uint32 borderColor, uint32 backgroundColor)
{
m_EmblemStyle = emblemStyle;
@@ -688,14 +817,18 @@ void Guild::SetEmblem(uint32 emblemStyle, uint32 emblemColor, uint32 borderStyle
m_BorderStyle = borderStyle;
m_BorderColor = borderColor;
m_BackgroundColor = backgroundColor;
+
CharacterDatabase.PExecute("UPDATE guild SET EmblemStyle=%u, EmblemColor=%u, BorderStyle=%u, BorderColor=%u, BackgroundColor=%u WHERE guildid = %u", m_EmblemStyle, m_EmblemColor, m_BorderStyle, m_BorderColor, m_BackgroundColor, m_Id);
}
+
void Guild::UpdateLogoutTime(uint64 guid)
{
MemberList::iterator itr = members.find(GUID_LOPART(guid));
if (itr == members.end())
return;
+
itr->second.LogoutTime = time(NULL);
+
if (m_OnlineMembers > 0)
--m_OnlineMembers;
else
@@ -704,6 +837,7 @@ void Guild::UpdateLogoutTime(uint64 guid)
UnloadGuildEventLog();
}
}
+
// *************************************************
// Guild Eventlog part
// *************************************************
@@ -713,6 +847,7 @@ 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
@@ -735,12 +870,14 @@ void Guild::DisplayGuildEventLog(WorldSession *session)
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;
+
// 0 1 2 3 4 5
QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp FROM guild_eventlog WHERE guildid=%u ORDER BY TimeStamp DESC,LogGuid DESC LIMIT %u", m_Id, GUILD_EVENTLOG_MAX_RECORDS);
if (!result)
@@ -763,23 +900,30 @@ void Guild::LoadGuildEventLogFromDB()
NewEvent.PlayerGuid2 = fields[3].GetUInt32();
NewEvent.NewRank = fields[4].GetUInt8();
NewEvent.TimeStamp = fields[5].GetUInt64();
+
// There can be a problem if more events have same TimeStamp the ORDER can be broken when fields[0].GetUInt32() == configCount, but
// events with same timestamp can appear when there is lag, and we naivly suppose that mangos isn't laggy
// but if problem appears, player will see set of guild events that have same timestamp in bad order
+
// Add entry to list
m_GuildEventLog.push_front(NewEvent);
+
} while (result->NextRow());
delete result;
+
m_EventLogLoaded = true;
}
+
// Unload guild eventlog
void Guild::UnloadGuildEventLog()
{
if (!m_EventLogLoaded)
return;
+
m_GuildEventLog.clear();
m_EventLogLoaded = false;
}
+
// Add entry to guild eventlog
void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid2, uint8 NewRank)
{
@@ -802,6 +946,7 @@ void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid
CharacterDatabase.PExecute("INSERT INTO guild_eventlog (guildid, LogGuid, EventType, PlayerGuid1, PlayerGuid2, NewRank, TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','" UI64FMTD "')",
m_Id, m_GuildEventLogNextGuid, uint32(NewEvent.EventType), NewEvent.PlayerGuid1, NewEvent.PlayerGuid2, uint32(NewEvent.NewRank), NewEvent.TimeStamp);
}
+
// *************************************************
// Guild Bank part
// *************************************************
@@ -809,105 +954,142 @@ void Guild::LogGuildEvent(uint8 EventType, uint32 PlayerGuid1, uint32 PlayerGuid
void Guild::DisplayGuildBankContent(WorldSession *session, uint8 TabId)
{
WorldPacket data(SMSG_GUILD_BANK_LIST, 1300);
+
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);
data << uint32(GetMemberSlotWithdrawRem(session->GetPlayer()->GetGUIDLow(), TabId)); // remaining slots for today
data << uint8(0); // Tell client this is a tab content packet
+
data << uint8(GUILD_BANK_MAX_SLOTS);
+
for (uint8 i=0; i<GUILD_BANK_MAX_SLOTS; ++i)
AppendDisplayGuildBankSlot(data, tab, i);
+
session->SendPacket(&data);
+
sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
}
+
void Guild::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::const_iterator itr = members.begin(); itr != members.end(); ++itr)
{
Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
if (!player)
continue;
+
if (!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
continue;
+
data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
+
player->GetSession()->SendPacket(&data);
}
+
sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
}
+
void Guild::DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots)
{
GuildBankTab const* tab = GetBankTab(TabId);
if (!tab)
return;
+
WorldPacket data(SMSG_GUILD_BANK_LIST,1200);
+
data << uint64(GetGuildBankMoney());
data << uint8(TabId);
// remaining slots for today
+
size_t rempos = data.wpos();
data << uint32(0); // will be filled later
data << uint8(0); // Tell client this is a tab content packet
+
data << uint8(slots.size()); // updates count
+
for (GuildItemPosCountVec::const_iterator itr = slots.begin(); itr != slots.end(); ++itr)
AppendDisplayGuildBankSlot(data, tab, itr->Slot);
+
for (MemberList::const_iterator itr = members.begin(); itr != members.end(); ++itr)
{
Player *player = ObjectAccessor::FindPlayer(MAKE_NEW_GUID(itr->first, 0, HIGHGUID_PLAYER));
if (!player)
continue;
+
if (!IsMemberHaveRights(itr->first,TabId,GUILD_BANK_RIGHT_VIEW_TAB))
continue;
+
data.put<uint32>(rempos,uint32(GetMemberSlotWithdrawRem(player->GetGUIDLow(), TabId)));
+
player->GetSession()->SendPacket(&data);
}
+
sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
}
+
Item* Guild::GetItem(uint8 TabId, uint8 SlotId)
{
if (TabId >= m_TabListMap.size() || SlotId >= GUILD_BANK_MAX_SLOTS)
return NULL;
return m_TabListMap[TabId]->Slots[SlotId];
}
+
// *************************************************
// Tab related
+
void Guild::DisplayGuildBankTabsInfo(WorldSession *session)
{
// Time to load bank if not already done
if (!m_GuildBankLoaded)
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 << uint32(0);
data << uint8(1); // Tell Client this is a TabInfo packet
data << uint8(m_PurchasedTabs); // here is the number of tabs
+
for (uint8 i = 0; i < m_PurchasedTabs; ++i)
{
data << m_TabListMap[i]->Name.c_str();
@@ -915,53 +1097,69 @@ void Guild::DisplayGuildBankTabsInfo(WorldSession *session)
}
data << uint8(0); // Do not send tab content
session->SendPacket(&data);
+
sLog.outDebug("WORLD: Sent (SMSG_GUILD_BANK_LIST)");
}
+
void Guild::CreateNewBankTab()
{
if (m_PurchasedTabs >= GUILD_BANK_MAX_TABS)
return;
+
++m_PurchasedTabs;
+
GuildBankTab* AnotherTab = new GuildBankTab;
memset(AnotherTab->Slots, 0, GUILD_BANK_MAX_SLOTS * sizeof(Item*));
m_TabListMap.resize(m_PurchasedTabs);
m_TabListMap[m_PurchasedTabs-1] = AnotherTab;
+
CharacterDatabase.BeginTransaction();
CharacterDatabase.PExecute("DELETE FROM guild_bank_tab WHERE guildid='%u' AND TabId='%u'", m_Id, uint32(m_PurchasedTabs-1));
CharacterDatabase.PExecute("INSERT INTO guild_bank_tab (guildid,TabId) VALUES ('%u','%u')", m_Id, uint32(m_PurchasedTabs-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(), m_Id, uint32(TabId));
}
+
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_GuildBankLoaded)
return;
+
m_GuildBankLoaded = 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", m_Id);
if (!result)
@@ -969,24 +1167,31 @@ void Guild::LoadGuildBankFromDB()
m_PurchasedTabs = 0;
return;
}
+
m_TabListMap.resize(m_PurchasedTabs);
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;
+
// data needs to be at first place for Item::LoadFromDB
// 0 1 2 3 4
result = CharacterDatabase.PQuery("SELECT data, TabId, SlotId, item_guid, item_entry FROM guild_bank_item JOIN item_instance ON item_guid = guid WHERE guildid='%u' ORDER BY TabId", m_Id);
if (!result)
return;
+
do
{
Field *fields = result->Fetch();
@@ -994,22 +1199,27 @@ void Guild::LoadGuildBankFromDB()
uint8 SlotId = fields[2].GetUInt8();
uint32 ItemGuid = fields[3].GetUInt32();
uint32 ItemEntry = fields[4].GetUInt32();
+
if (TabId >= m_PurchasedTabs || 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, result))
{
@@ -1018,11 +1228,14 @@ void Guild::LoadGuildBankFromDB()
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()
{
@@ -1041,11 +1254,14 @@ void Guild::UnloadGuildBank()
delete m_TabListMap[i];
}
m_TabListMap.clear();
+
UnloadGuildBankEventLog();
m_GuildBankLoaded = false;
}
+
// *************************************************
// Money deposit/withdraw related
+
void Guild::SendMoneyInfo(WorldSession *session, uint32 LowGuid)
{
WorldPacket data(MSG_GUILD_BANK_MONEY_WITHDRAWN, 4);
@@ -1053,12 +1269,16 @@ void Guild::SendMoneyInfo(WorldSession *session, uint32 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);
@@ -1070,20 +1290,26 @@ bool Guild::MemberMoneyWithdraw(uint32 amount, uint32 LowGuid)
}
return true;
}
+
void Guild::SetBankMoney(int64 money)
{
if (money < 0) // I don't know how this happens, it does!!
money = 0;
m_GuildBankMoney = money;
+
CharacterDatabase.PExecute("UPDATE guild SET BankMoney='" UI64FMTD "' WHERE guildid='%u'", money, m_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);
@@ -1095,24 +1321,31 @@ bool Guild::MemberItemWithdraw(uint8 TabId, uint32 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)
{
@@ -1123,13 +1356,16 @@ uint32 Guild::GetMemberSlotWithdrawRem(uint32 LowGuid, uint8 TabId)
}
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)
@@ -1141,68 +1377,86 @@ uint32 Guild::GetMemberMoneyWithdrawRem(uint32 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, m_Id);
CharacterDatabase.PExecute("UPDATE guild_member SET BankResetTimeMoney='0' WHERE guildid='%u' AND rank='%u'", m_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 >= m_PurchasedTabs)
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 (uint8 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'", m_Id, uint32(TabId), rankId);
CharacterDatabase.PExecute("INSERT INTO guild_bank_right (guildid,TabId,rid,gbright,SlotPerDay) VALUES "
"('%u','%u','%u','%u','%u')", m_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), m_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();
@@ -1210,16 +1464,22 @@ void Guild::LoadBankRightsFromDB(uint32 GuildId)
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()
{
// Money log is in TabId = GUILD_BANK_MONEY_LOGS_TAB
+
//uint32 configCount = sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT);
//cycle through all purchased guild bank item tabs
for (uint32 tabId = 0; tabId < m_PurchasedTabs; tabId++)
@@ -1228,10 +1488,12 @@ void Guild::LoadGuildBankEventLogFromDB()
QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' AND TabId='%u' ORDER BY TimeStamp DESC,LogGuid DESC LIMIT %u", m_Id, tabId, GUILD_BANK_MAX_LOGS);
if (!result)
continue;
+
bool isNextLogGuidSet = false;
do
{
Field *fields = result->Fetch();
+
GuildBankEventLogEntry NewEvent;
NewEvent.EventType = fields[1].GetUInt8();
NewEvent.PlayerGuid = fields[2].GetUInt32();
@@ -1239,6 +1501,7 @@ void Guild::LoadGuildBankEventLogFromDB()
NewEvent.ItemStackCount = fields[4].GetUInt8();
NewEvent.DestTabId = fields[5].GetUInt8();
NewEvent.TimeStamp = fields[6].GetUInt64();
+
//if newEvent is moneyEvent, move it to moneyEventTab in DB and report error
if (NewEvent.isMoneyEvent())
{
@@ -1251,6 +1514,7 @@ void Guild::LoadGuildBankEventLogFromDB()
//add event to list
//events are ordered from oldest (in beginning) to latest (in the end)
m_GuildBankEventLog_Item[tabId].push_front(NewEvent);
+
if (!isNextLogGuidSet)
{
m_GuildBankEventLogNextGuid_Item[tabId] = fields[0].GetUInt32();
@@ -1260,11 +1524,13 @@ void Guild::LoadGuildBankEventLogFromDB()
} while (result->NextRow());
delete result;
}
+
//special handle for guild bank money log
// 0 1 2 3 4 5 6
QueryResult *result = CharacterDatabase.PQuery("SELECT LogGuid, EventType, PlayerGuid, ItemOrMoney, ItemStackCount, DestTabId, TimeStamp FROM guild_bank_eventlog WHERE guildid='%u' AND TabId='%u' ORDER BY TimeStamp DESC,LogGuid DESC LIMIT %u", m_Id, GUILD_BANK_MONEY_LOGS_TAB, GUILD_BANK_MAX_LOGS);
if (!result)
return;
+
bool isNextMoneyLogGuidSet = false;
do
{
@@ -1276,12 +1542,14 @@ void Guild::LoadGuildBankEventLogFromDB()
isNextMoneyLogGuidSet = true;
}
GuildBankEventLogEntry NewEvent;
+
NewEvent.EventType = fields[1].GetUInt8();
NewEvent.PlayerGuid = fields[2].GetUInt32();
NewEvent.ItemOrMoney = fields[3].GetUInt32();
NewEvent.ItemStackCount = fields[4].GetUInt8();
NewEvent.DestTabId = fields[5].GetUInt8();
NewEvent.TimeStamp = fields[6].GetUInt64();
+
//if newEvent is not moneyEvent, then report error
if (!NewEvent.isMoneyEvent())
sLog.outError("GuildBankEventLog ERROR: MoneyEvent LogGuid %u for Guild %u is not MoneyEvent - ignoring...", fields[0].GetUInt32(), m_Id);
@@ -1289,19 +1557,24 @@ void Guild::LoadGuildBankEventLogFromDB()
//add event to list
//events are ordered from oldest (in beginning) to latest (in the end)
m_GuildBankEventLog_Money.push_front(NewEvent);
+
} while (result->NextRow());
delete result;
}
+
void Guild::UnloadGuildBankEventLog()
{
m_GuildBankEventLog_Money.clear();
+
for (uint8 i = 0; i < GUILD_BANK_MAX_TABS; ++i)
m_GuildBankEventLog_Item[i].clear();
}
+
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
@@ -1363,6 +1636,7 @@ void Guild::DisplayGuildBankLogs(WorldSession *session, uint8 TabId)
}
sLog.outDebug("WORLD: Sent (MSG_GUILD_BANK_LOG_QUERY)");
}
+
void Guild::LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount, uint8 DestTabId)
{
//create Event
@@ -1373,6 +1647,7 @@ void Guild::LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uin
NewEvent.ItemStackCount = ItemStackCount;
NewEvent.DestTabId = DestTabId;
NewEvent.TimeStamp = uint32(time(NULL));
+
//add new event to the end of event list
uint32 currentTabId = TabId;
uint32 currentLogGuid = 0;
@@ -1383,6 +1658,7 @@ void Guild::LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uin
currentTabId = GUILD_BANK_MONEY_LOGS_TAB;
if (m_GuildBankEventLog_Money.size() >= GUILD_BANK_MAX_LOGS)
m_GuildBankEventLog_Money.pop_front();
+
m_GuildBankEventLog_Money.push_back(NewEvent);
}
else
@@ -1391,13 +1667,17 @@ void Guild::LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uin
currentLogGuid = m_GuildBankEventLogNextGuid_Item[TabId];
if (m_GuildBankEventLog_Item[TabId].size() >= GUILD_BANK_MAX_LOGS)
m_GuildBankEventLog_Item[TabId].pop_front();
+
m_GuildBankEventLog_Item[TabId].push_back(NewEvent);
}
+
//save event to database
CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE guildid='%u' AND LogGuid='%u' AND TabId='%u'", m_Id, currentLogGuid, currentTabId);
+
CharacterDatabase.PExecute("INSERT INTO guild_bank_eventlog (guildid,LogGuid,TabId,EventType,PlayerGuid,ItemOrMoney,ItemStackCount,DestTabId,TimeStamp) VALUES ('%u','%u','%u','%u','%u','%u','%u','%u','" UI64FMTD "')",
m_Id, currentLogGuid, currentTabId, uint32(NewEvent.EventType), NewEvent.PlayerGuid, NewEvent.ItemOrMoney, uint32(NewEvent.ItemStackCount), uint32(NewEvent.DestTabId), NewEvent.TimeStamp);
}
+
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);
@@ -1405,10 +1685,12 @@ bool Guild::AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot
"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)
@@ -1416,6 +1698,7 @@ void Guild::AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *t
data << (uint32) pItem->GetItemRandomPropertyId(); // random item property id +8
if (pItem->GetItemRandomPropertyId())
data << (uint32) pItem->GetItemSuffixFactor(); // SuffixFactor +4
+
data << uint32(pItem->GetCount()); // +12 // ITEM_FIELD_STACK_COUNT
data << uint32(0); // +16 // Unknown value
data << uint8(abs(pItem->GetSpellCharges())); // spell charges
@@ -1429,46 +1712,61 @@ void Guild::AppendDisplayGuildBankSlot( WorldPacket& data, GuildBankTab const *t
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
@@ -1476,28 +1774,35 @@ Item* Guild::_StoreItem( uint8 tab, uint8 slot, Item *pItem, uint32 count, bool
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)
{
@@ -1510,21 +1815,27 @@ uint8 Guild::_CanStoreItem_InSpecificSlot( uint8 tab, uint8 slot, GuildItemPosCo
// check item type
if (pItem2->GetEntry() != pSrcItem->GetEntry())
return EQUIP_ERR_ITEM_CANT_STACK;
+
// check free space
if (pItem2->GetCount() >= pSrcItem->GetMaxStackCount())
return EQUIP_ERR_ITEM_CANT_STACK;
+
need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
}
+
if (need_space > count)
need_space = count;
+
GuildItemPosCount newPosition = GuildItemPosCount(slot,need_space);
if (!newPosition.isContainedIn(dest))
{
dest.push_back(newPosition);
count -= need_space;
}
+
return EQUIP_ERR_OK;
}
+
uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32& count, bool merge, Item* pSrcItem, uint8 skip_slot ) const
{
for(uint32 j = 0; j < GUILD_BANK_MAX_SLOTS; j++)
@@ -1532,13 +1843,17 @@ uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32&
// 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())
@@ -1546,11 +1861,13 @@ uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32&
uint32 need_space = pSrcItem->GetMaxStackCount() - pItem2->GetCount();
if (need_space > count)
need_space = count;
+
GuildItemPosCount newPosition = GuildItemPosCount(j,need_space);
if (!newPosition.isContainedIn(dest))
{
dest.push_back(newPosition);
count -= need_space;
+
if (count == 0)
return EQUIP_ERR_OK;
}
@@ -1561,11 +1878,13 @@ uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32&
uint32 need_space = pSrcItem->GetMaxStackCount();
if (need_space > count)
need_space = count;
+
GuildItemPosCount newPosition = GuildItemPosCount(j,need_space);
if (!newPosition.isContainedIn(dest))
{
dest.push_back(newPosition);
count -= need_space;
+
if (count == 0)
return EQUIP_ERR_OK;
}
@@ -1573,40 +1892,52 @@ uint8 Guild::_CanStoreItem_InTab( uint8 tab, GuildItemPosCountVec &dest, uint32&
}
return EQUIP_ERR_OK;
}
+
uint8 Guild::CanStoreItem( uint8 tab, uint8 slot, GuildItemPosCountVec &dest, uint32 count, Item *pItem, bool swap ) const
{
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)
@@ -1615,53 +1946,69 @@ void Guild::SetGuildBankTabText(uint8 TabId, std::string text)
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(), m_Id, uint32(TabId));
+
// announce
SendGuildBankTabText(NULL,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;
+
if (session)
session->SendPacket(&data);
else
BroadcastPacket(&data);
}
+
void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankTabDst, uint8 BankTabSlotDst, uint32 SplitedAmount )
{
// empty operation
if (BankTab == BankTabDst && BankTabSlot == BankTabSlotDst)
return;
+
Item *pItemSrc = 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 = GetItem(BankTabDst, BankTabSlotDst);
+
if (BankTab != BankTabDst)
{
// check dest pos rights (if different tabs)
if (!IsMemberHaveRights(pl->GetGUIDLow(), BankTabDst, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
return;
+
// check source pos rights (if different tabs)
uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
if (remRight <= 0)
return;
}
+
if (SplitedAmount)
{ // Bank -> Bank item split (in empty or non empty slot
GuildItemPosCountVec dest;
@@ -1671,14 +2018,17 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT
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();
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);
@@ -1694,6 +2044,7 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT
{
CharacterDatabase.BeginTransaction();
LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst);
+
RemoveItem(BankTab, BankTabSlot);
StoreItem(BankTabDst, gDest, pItemSrc);
CharacterDatabase.CommitTransaction();
@@ -1707,6 +2058,7 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT
pl->SendEquipError(msg, pItemSrc, NULL);
return;
}
+
GuildItemPosCountVec gSrc;
msg = CanStoreItem(BankTab,BankTabSlot,gSrc,pItemDst->GetCount(),pItemDst,true);
if (msg != EQUIP_ERR_OK)
@@ -1714,19 +2066,23 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT
pl->SendEquipError(msg, pItemDst, NULL);
return;
}
+
if (BankTab != BankTabDst)
{
// check source pos rights (item swapped to src)
if (!IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
return;
+
// check dest pos rights (item swapped to src)
uint32 remRightDst = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTabDst);
if (remRightDst <= 0)
return;
}
+
CharacterDatabase.BeginTransaction();
LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTab, pl->GetGUIDLow(), pItemSrc->GetEntry(), pItemSrc->GetCount(), BankTabDst);
LogBankEvent(GUILD_BANK_LOG_MOVE_ITEM, BankTabDst, pl->GetGUIDLow(), pItemDst->GetEntry(), pItemDst->GetCount(), BankTab);
+
RemoveItem(BankTab, BankTabSlot);
RemoveItem(BankTabDst, BankTabSlotDst);
StoreItem(BankTab, gSrc, pItemDst);
@@ -1739,16 +2095,20 @@ void Guild::SwapItems(Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankT
DisplayGuildBankContentUpdate(BankTabDst,BankTabSlotDst);
}
+
void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 PlayerBag, uint8 PlayerSlot, uint32 SplitedAmount)
{
Item *pItemBank = GetItem(BankTab, BankTabSlot);
Item *pItemChar = pl->GetItemByPos(PlayerBag, PlayerSlot);
+
if (!pItemBank) // Problem to get bank item
return;
+
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);
@@ -1757,6 +2117,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
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)
@@ -1765,6 +2126,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
delete pNewItem;
return;
}
+
// check source pos rights (item moved to inventory)
uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
if (remRight <= 0)
@@ -1772,13 +2134,16 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
delete pNewItem;
return;
}
+
CharacterDatabase.BeginTransaction();
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();
+
MemberItemWithdraw(BankTab, pl->GetGUIDLow());
CharacterDatabase.CommitTransaction();
}
@@ -1792,11 +2157,14 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
if (remRight <= 0)
return;
+
CharacterDatabase.BeginTransaction();
LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount());
+
RemoveItem(BankTab, BankTabSlot);
pl->MoveItemToInventory(dest,pItemBank,true);
pl->SaveInventoryAndGoldToDB();
+
MemberItemWithdraw(BankTab, pl->GetGUIDLow());
CharacterDatabase.CommitTransaction();
}
@@ -1805,6 +2173,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
// check source pos rights (item swapped to bank)
if (!IsMemberHaveRights(pl->GetGUIDLow(), BankTab, GUILD_BANK_RIGHT_DEPOSIT_ITEM))
return;
+
if (pItemChar)
{
if (!pItemChar->CanBeTraded())
@@ -1813,6 +2182,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
return;
}
}
+
ItemPosCountVec iDest;
msg = pl->CanStoreItem(PlayerBag, PlayerSlot, iDest, pItemBank, true);
if (msg != EQUIP_ERR_OK)
@@ -1820,6 +2190,7 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
pl->SendEquipError(msg, pItemBank, NULL);
return;
}
+
GuildItemPosCountVec gDest;
if (pItemChar)
{
@@ -1830,10 +2201,12 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
return;
}
}
+
// check source pos rights (item moved to inventory)
uint32 remRight = GetMemberSlotWithdrawRem(pl->GetGUIDLow(), BankTab);
if (remRight <= 0)
return;
+
if (pItemChar)
{
// logging item move to bank
@@ -1845,20 +2218,24 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
m_Id);
}
}
+
CharacterDatabase.BeginTransaction();
LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount());
if (pItemChar)
LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount());
+
RemoveItem(BankTab, BankTabSlot);
if (pItemChar)
{
pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true);
pItemChar->DeleteFromInventoryDB();
}
+
if (pItemChar)
StoreItem(BankTab, gDest, pItemChar);
pl->MoveItemToInventory(iDest,pItemBank,true);
pl->SaveInventoryAndGoldToDB();
+
MemberItemWithdraw(BankTab, pl->GetGUIDLow());
CharacterDatabase.CommitTransaction();
}
@@ -1866,24 +2243,30 @@ void Guild::MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, u
DisplayGuildBankContentUpdate(BankTab,BankTabSlot);
}
+
void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot, uint8 BankTab, uint8 BankTabSlot, uint32 SplitedAmount )
{
Item *pItemBank = GetItem(BankTab, BankTabSlot);
Item *pItemChar = pl->GetItemByPos(PlayerBag, PlayerSlot);
+
if (!pItemChar) // Problem to get item from player
return;
+
if (!pItemChar->CanBeTraded())
{
pl->SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pItemChar, NULL );
return;
}
+
// check source pos rights (item moved to bank)
if (!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;
@@ -1893,12 +2276,14 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
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 (pl->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
@@ -1906,14 +2291,17 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
pl->GetName(),pl->GetSession()->GetAccountId(),
pItemChar->GetProto()->Name1,pItemChar->GetEntry(),SplitedAmount,m_Id);
}
+
CharacterDatabase.BeginTransaction();
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();
StoreItem(BankTab, dest, pNewItem);
CharacterDatabase.CommitTransaction();
+
DisplayGuildBankContentUpdate(BankTab,dest);
}
else // Char -> Bank swap with empty or non-empty (move)
@@ -1930,13 +2318,17 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(),
m_Id);
}
+
CharacterDatabase.BeginTransaction();
LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount());
+
pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true);
pItemChar->DeleteFromInventoryDB();
+
StoreItem(BankTab,dest,pItemChar);
pl->SaveInventoryAndGoldToDB();
CharacterDatabase.CommitTransaction();
+
DisplayGuildBankContentUpdate(BankTab,dest);
}
else // Char <-> Bank swap items (posible NULL bank item)
@@ -1951,6 +2343,7 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
return;
}
}
+
GuildItemPosCountVec gDest;
msg = CanStoreItem(BankTab,BankTabSlot,gDest,pItemChar->GetCount(),pItemChar,true);
if (msg != EQUIP_ERR_OK)
@@ -1958,6 +2351,7 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
pl->SendEquipError(msg, pItemChar, NULL);
return;
}
+
if (pItemBank)
{
// check bank pos rights (item swapped with inventory)
@@ -1965,6 +2359,7 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
if (remRight <= 0)
return;
}
+
// logging item move to bank
if (pl->GetSession()->GetSecurity() > SEC_PLAYER && sWorld.getConfig(CONFIG_GM_LOG_TRADE))
{
@@ -1973,14 +2368,17 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
pItemChar->GetProto()->Name1,pItemChar->GetEntry(),pItemChar->GetCount(),
m_Id);
}
+
CharacterDatabase.BeginTransaction();
if (pItemBank)
LogBankEvent(GUILD_BANK_LOG_WITHDRAW_ITEM, BankTab, pl->GetGUIDLow(), pItemBank->GetEntry(), pItemBank->GetCount());
LogBankEvent(GUILD_BANK_LOG_DEPOSIT_ITEM, BankTab, pl->GetGUIDLow(), pItemChar->GetEntry(), pItemChar->GetCount());
+
pl->MoveItemFromInventory(PlayerBag, PlayerSlot, true);
pItemChar->DeleteFromInventoryDB();
if (pItemBank)
RemoveItem(BankTab, BankTabSlot);
+
StoreItem(BankTab,gDest,pItemChar);
if (pItemBank)
pl->MoveItemToInventory(iDest,pItemBank,true);
@@ -1988,14 +2386,17 @@ void Guild::MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot,
if (pItemBank)
MemberItemWithdraw(BankTab, pl->GetGUIDLow());
CharacterDatabase.CommitTransaction();
+
DisplayGuildBankContentUpdate(BankTab,gDest);
}
}
}
+
bool GuildItemPosCount::isContainedIn(GuildItemPosCountVec const &vec) const
{
for (GuildItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end(); ++itr)
if (itr->Slot == this->Slot)
return true;
+
return false;
}
diff --git a/src/game/Guild.h b/src/game/Guild.h
index 94edb4465ac..8df8d01d264 100644
--- a/src/game/Guild.h
+++ b/src/game/Guild.h
@@ -17,14 +17,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_GUILD_H
#define TRINITYCORE_GUILD_H
+
#define WITHDRAW_MONEY_UNLIMITED 0xFFFFFFFF
#define WITHDRAW_SLOT_UNLIMITED 0xFFFFFFFF
+
#include "Item.h"
+
class Item;
+
#define GUILD_RANKS_MIN_COUNT 5
#define GUILD_RANKS_MAX_COUNT 10
+
enum GuildDefaultRanks
{
//these ranks can be modified, but they cannot be deleted
@@ -36,6 +42,7 @@ enum GuildDefaultRanks
//When promoting member server does: rank--;!
//When demoting member server does: rank++;!
};
+
enum GuildRankRights
{
GR_RIGHT_EMPTY = 0x00000040,
@@ -58,6 +65,7 @@ enum GuildRankRights
GR_RIGHT_CREATE_GUILD_EVENT = 0x00100000, // wotlk
GR_RIGHT_ALL = 0x001DF1FF
};
+
enum Typecommand
{
GUILD_CREATE_S = 0x00,
@@ -68,6 +76,7 @@ enum Typecommand
GUILD_BANK_S = 0x15,
GUILD_UNK3 = 0x16
};
+
enum CommandErrors
{
GUILD_PLAYER_NO_MORE_IN_GUILD = 0x00,
@@ -95,6 +104,7 @@ enum CommandErrors
GUILD_BANK_TAB_IS_FULL = 0x1B,
GUILD_BANK_ITEM_NOT_FOUND = 0x1C
};
+
enum GuildEvents
{
GE_PROMOTION = 0x00,
@@ -117,12 +127,14 @@ enum GuildEvents
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,
@@ -131,14 +143,17 @@ enum PetitionSigns
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 GuildBankEventLogTypes
{
GUILD_BANK_LOG_DEPOSIT_ITEM = 1,
@@ -151,6 +166,7 @@ enum GuildBankEventLogTypes
GUILD_BANK_LOG_UNK1 = 8,
GUILD_BANK_LOG_UNK2 = 9,
};
+
enum GuildEventLogTypes
{
GUILD_EVENT_LOG_INVITE_PLAYER = 1,
@@ -160,6 +176,7 @@ enum GuildEventLogTypes
GUILD_EVENT_LOG_UNINVITE_PLAYER = 5,
GUILD_EVENT_LOG_LEAVE_GUILD = 6,
};
+
enum GuildEmblem
{
ERR_GUILDEMBLEM_SUCCESS = 0,
@@ -169,6 +186,7 @@ enum GuildEmblem
ERR_GUILDEMBLEM_NOTENOUGHMONEY = 4,
ERR_GUILDEMBLEM_INVALIDVENDOR = 5
};
+
inline uint32 GetGuildBankTabPrice(uint8 Index)
{
switch(Index)
@@ -184,6 +202,7 @@ inline uint32 GetGuildBankTabPrice(uint8 Index)
}
return 0;
}
+
struct GuildEventLogEntry
{
uint8 EventType;
@@ -192,6 +211,7 @@ struct GuildEventLogEntry
uint8 NewRank;
uint64 TimeStamp;
};
+
struct GuildBankEventLogEntry
{
uint8 EventType;
@@ -200,6 +220,7 @@ struct GuildBankEventLogEntry
uint8 ItemStackCount;
uint8 DestTabId;
uint64 TimeStamp;
+
const bool isMoneyEvent()
{
return EventType == GUILD_BANK_LOG_DEPOSIT_MONEY ||
@@ -207,6 +228,7 @@ struct GuildBankEventLogEntry
EventType == GUILD_BANK_LOG_REPAIR_MONEY;
}
};
+
struct GuildBankTab
{
Item* Slots[GUILD_BANK_MAX_SLOTS];
@@ -214,14 +236,18 @@ struct GuildBankTab
std::string Icon;
std::string Text;
};
+
struct GuildItemPosCount
{
GuildItemPosCount(uint8 _slot, uint32 _count) : Slot(_slot), Count(_count) {}
+
bool isContainedIn(std::vector<GuildItemPosCount> const& vec) const;
+
uint8 Slot;
uint32 Count;
};
typedef std::vector<GuildItemPosCount> GuildItemPosCountVec;
+
struct MemberSlot
{
uint64 LogoutTime;
@@ -237,6 +263,7 @@ struct MemberSlot
uint32 BankResetTimeTab[GUILD_BANK_MAX_TABS];
uint32 BankRemSlotsTab[GUILD_BANK_MAX_TABS];
};
+
struct RankInfo
{
RankInfo(const std::string& _name, uint32 _rights, uint32 _money) : Name(_name), Rights(_rights), BankMoneyPerDay(_money)
@@ -247,57 +274,71 @@ struct RankInfo
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(Player* leader, std::string gname);
void CreateDefaultGuildRanks(int locale_idx);
void Disband();
+
typedef std::map<uint32, MemberSlot> MemberList;
typedef std::vector<RankInfo> RankList;
+
uint32 GetId(){ return m_Id; }
const uint64& GetLeader(){ return m_LeaderGuid; }
std::string const& GetName() const { return m_Name; }
std::string const& GetMOTD() const { return MOTD; }
std::string const& GetGINFO() const { return GINFO; }
+
uint32 GetCreatedYear() const { return m_CreatedYear; }
uint32 GetCreatedMonth() const { return m_CreatedMonth; }
uint32 GetCreatedDay() const { return m_CreatedDay; }
+
uint32 GetEmblemStyle() const { return m_EmblemStyle; }
uint32 GetEmblemColor() const { return m_EmblemColor; }
uint32 GetBorderStyle() const { return m_BorderStyle; }
uint32 GetBorderColor() const { return m_BorderColor; }
uint32 GetBackgroundColor() const { return m_BackgroundColor; }
+
void SetLeader(uint64 guid);
bool AddMember(uint64 plGuid, uint32 plRank);
void ChangeRank(uint64 guid, uint32 newRank);
void DelMember(uint64 guid, bool isDisbanding = false);
//lowest rank is the count of ranks - 1 (the highest rank_id in table)
uint32 GetLowestRank() const { return m_Ranks.size() - 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);
+
void SetMemberStats(uint64 guid);
+
void BroadcastToGuild(WorldSession *session, const std::string& msg, uint32 language = LANG_UNIVERSAL);
void BroadcastToOfficers(WorldSession *session, const std::string& msg, uint32 language = LANG_UNIVERSAL);
void BroadcastPacketToRank(WorldPacket *packet, uint32 rankId);
void BroadcastPacket(WorldPacket *packet);
+
template<class Do>
void BroadcastWorker(Do& _do, Player* except = NULL)
{
@@ -306,11 +347,13 @@ class Guild
if(player != except)
_do(player);
}
+
void CreateRank(std::string name,uint32 rights);
void DelRank();
std::string GetRankName(uint32 rankId);
uint32 GetRankRights(uint32 rankId);
uint32 GetRanksSize() const { return m_Ranks.size(); }
+
void SetRankName(uint32 rankId, std::string name);
void SetRankRights(uint32 rankId, uint32 rights);
bool HasRankRight(uint32 rankId, uint32 right)
@@ -334,20 +377,25 @@ class Guild
}
return NULL;
}
+
void Roster(WorldSession *session = NULL); // NULL = broadcast
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);
+
// ** Guild bank **
// Content & item deposit/withdraw
void DisplayGuildBankContent(WorldSession *session, uint8 TabId);
+
void SwapItems( Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 BankTabDst, uint8 BankTabSlotDst, uint32 SplitedAmount);
void MoveFromBankToChar( Player * pl, uint8 BankTab, uint8 BankTabSlot, uint8 PlayerBag, uint8 PlayerSlot, uint32 SplitedAmount);
void MoveFromCharToBank( Player * pl, uint8 PlayerBag, uint8 PlayerSlot, uint8 BankTab, uint8 BankTabSlot, uint32 SplitedAmount);
+
// Tabs
void DisplayGuildBankTabsInfo(WorldSession *session);
void CreateNewBankTab();
@@ -384,8 +432,10 @@ class Guild
void DisplayGuildBankLogs(WorldSession *session, uint8 TabId);
void LogBankEvent(uint8 EventType, uint8 TabId, uint32 PlayerGuidLow, uint32 ItemOrMoney, uint8 ItemStackCount=0, uint8 DestTabId=0);
bool AddGBankItemToDB(uint32 GuildId, uint32 BankTab , uint32 BankTabSlot , uint32 GUIDLow, uint32 Entry );
+
protected:
void AddRank(const std::string& name,uint32 rights,uint32 money);
+
uint32 m_Id;
std::string m_Name;
uint64 m_LeaderGuid;
@@ -394,29 +444,37 @@ class Guild
uint32 m_CreatedYear;
uint32 m_CreatedMonth;
uint32 m_CreatedDay;
+
uint32 m_EmblemStyle;
uint32 m_EmblemColor;
uint32 m_BorderStyle;
uint32 m_BorderColor;
uint32 m_BackgroundColor;
+
RankList m_Ranks;
+
MemberList members;
+
typedef std::vector<GuildBankTab*> TabListMap;
TabListMap m_TabListMap;
+
/** These are actually ordered lists. The first element is the oldest entry.*/
typedef std::list<GuildEventLogEntry> GuildEventLog;
typedef std::list<GuildBankEventLogEntry> GuildBankEventLog;
GuildEventLog m_GuildEventLog;
GuildBankEventLog m_GuildBankEventLog_Money;
GuildBankEventLog m_GuildBankEventLog_Item[GUILD_BANK_MAX_TABS];
+
uint32 m_GuildEventLogNextGuid;
uint32 m_GuildBankEventLogNextGuid_Money;
uint32 m_GuildBankEventLogNextGuid_Item[GUILD_BANK_MAX_TABS];
+
bool m_GuildBankLoaded;
bool m_EventLogLoaded;
uint32 m_OnlineMembers;
uint64 m_GuildBankMoney;
uint8 m_PurchasedTabs;
+
private:
// used only from high level Swap/Move functions
Item* GetItem(uint8 TabId, uint8 SlotId);
@@ -425,6 +483,7 @@ class Guild
void RemoveItem(uint8 tab, uint8 slot );
void DisplayGuildBankContentUpdate(uint8 TabId, int32 slot1, int32 slot2 = -1);
void DisplayGuildBankContentUpdate(uint8 TabId, GuildItemPosCountVec const& slots);
+
// 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;
diff --git a/src/game/GuildHandler.cpp b/src/game/GuildHandler.cpp
index cc9e514c823..c11ad11766b 100644
--- a/src/game/GuildHandler.cpp
+++ b/src/game/GuildHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -27,108 +28,142 @@
#include "Guild.h"
#include "GossipDef.h"
#include "SocialMgr.h"
+
void WorldSession::HandleGuildQueryOpcode(WorldPacket& recvPacket)
{
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)
{
std::string gname;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_CREATE");
+
recvPacket >> gname;
+
if(GetPlayer()->GetGuildId())
return;
+
Guild *guild = new Guild;
if(!guild->Create(GetPlayer(),gname))
{
delete guild;
return;
}
+
objmgr.AddGuild(guild);
}
+
void WorldSession::HandleGuildInviteOpcode(WorldPacket& recvPacket)
{
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)
{
std::string plName;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_REMOVE");
+
recvPacket >> plName;
+
if(!normalizePlayerName(plName))
return;
+
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_REMOVE))
{
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
uint64 plGuid;
MemberSlot* slot = guild->GetMemberSlot(plName, plGuid);
if(!slot)
@@ -136,20 +171,24 @@ void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket)
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
return;
}
+
if(slot->RankId == GR_GUILDMASTER)
{
SendGuildCommandResult(GUILD_QUIT_S, "", GUILD_LEADER_LEAVE);
return;
}
+
//do not allow to kick player with same or higher rights
if(GetPlayer()->GetRank() >= slot->RankId)
{
SendGuildCommandResult(GUILD_QUIT_S, plName, GUILD_RANK_TOO_HIGH_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
@@ -157,44 +196,56 @@ void WorldSession::HandleGuildRemoveOpcode(WorldPacket& recvPacket)
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; // strings count
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();
@@ -202,23 +253,32 @@ void WorldSession::HandleGuildInfoOpcode(WorldPacket& /*recvPacket*/)
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)
{
std::string plName;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_PROMOTE");
+
recvPacket >> plName;
+
if(!normalizePlayerName(plName))
return;
+
Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
if(!guild)
{
@@ -230,18 +290,22 @@ void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
uint64 plGuid;
MemberSlot* slot = guild->GetMemberSlot(plName, plGuid);
+
if(!slot)
{
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
return;
}
+
if(plGuid == GetPlayer()->GetGUID())
{
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_NAME_INVALID);
return;
}
+
//allow to promote only to lower rank than member's rank
//guildmaster's rank = 0
//GetPlayer()->GetRank() + 1 is highest rank that current player can promote to
@@ -250,7 +314,9 @@ void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_RANK_TOO_HIGH_S);
return;
}
+
uint32 newRankId = slot->RankId - 1; //when promoting player, rank is decreased
+
WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size
data << (uint8)GE_PROMOTION;
data << (uint8)3; // strings count
@@ -258,56 +324,72 @@ void WorldSession::HandleGuildPromoteOpcode(WorldPacket& recvPacket)
data << plName;
data << guild->GetRankName(newRankId);
guild->BroadcastPacket(&data);
+
guild->ChangeRank(plGuid, newRankId);
// Put record into guildlog
guild->LogGuildEvent(GUILD_EVENT_LOG_PROMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), newRankId);
}
+
void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket)
{
std::string plName;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_DEMOTE");
+
recvPacket >> plName;
+
if(!normalizePlayerName(plName))
return;
+
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_DEMOTE))
{
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
uint64 plGuid;
MemberSlot* slot = guild->GetMemberSlot(plName, plGuid);
+
if (!slot)
{
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
return;
}
+
if(plGuid == GetPlayer()->GetGUID())
{
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_NAME_INVALID);
return;
}
+
//do not allow to demote same or higher rank
if(GetPlayer()->GetRank() >= slot->RankId)
{
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_RANK_TOO_HIGH_S);
return;
}
+
//do not allow to demote lowest rank
if(slot->RankId >= guild->GetLowestRank())
{
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_ALREADY_LOWEST_RANK_S);
return;
}
+
uint32 newRankId = slot->RankId + 1; //when demoting player, rank is increased
+
guild->ChangeRank(plGuid, newRankId);
// Put record into guildlog
guild->LogGuildEvent(GUILD_EVENT_LOG_DEMOTE_PLAYER, GetPlayer()->GetGUIDLow(), GUID_LOPART(plGuid), newRankId);
+
WorldPacket data(SMSG_GUILD_EVENT, (2+30)); // guess size
data << (uint8)GE_DEMOTION;
data << (uint8)3; // strings count
@@ -316,11 +398,14 @@ void WorldSession::HandleGuildDemoteOpcode(WorldPacket& recvPacket)
data << guild->GetRankName(slot->RankId);
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)
{
@@ -332,28 +417,37 @@ void WorldSession::HandleGuildLeaveOpcode(WorldPacket& /*recvPacket*/)
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; // strings count
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)
{
@@ -365,51 +459,68 @@ void WorldSession::HandleGuildDisbandOpcode(WorldPacket& /*recvPacket*/)
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
guild->Disband();
+
//sLog.outDebug("WORLD: Guild Sucefully Disbanded");
}
+
void WorldSession::HandleGuildLeaderOpcode(WorldPacket& recvPacket)
{
std::string name;
Player *oldLeader = GetPlayer();
Guild *guild;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_LEADER");
+
recvPacket >> name;
+
if(!normalizePlayerName(name))
return;
+
guild = objmgr.GetGuildById(oldLeader->GetGuildId());
+
if (!guild)
{
SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
return;
}
+
if( oldLeader->GetGUID() != guild->GetLeader())
{
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
uint64 newLeaderGUID;
MemberSlot* slot = guild->GetMemberSlot(name, newLeaderGUID);
+
if (!slot)
{
SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_IN_GUILD_S);
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; // strings count
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)
{
@@ -421,54 +532,73 @@ void WorldSession::HandleGuildMOTDOpcode(WorldPacket& recvPacket)
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; // strings count
data << MOTD;
guild->BroadcastPacket(&data);
+
//sLog.outDebug("WORLD: Sent (SMSG_GUILD_EVENT)");
}
+
void WorldSession::HandleGuildSetPublicNoteOpcode(WorldPacket& recvPacket)
{
std::string name,PNOTE;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_SET_PUBLIC_NOTE");
+
recvPacket >> name;
+
if(!normalizePlayerName(name))
return;
+
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_EPNOTE))
{
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
uint64 plGuid;
MemberSlot* slot = guild->GetMemberSlot(name, plGuid);
+
if (!slot)
{
SendGuildCommandResult(GUILD_INVITE_S, name, GUILD_PLAYER_NOT_IN_GUILD_S);
return;
}
+
recvPacket >> PNOTE;
guild->SetPNOTE(plGuid, PNOTE);
+
guild->Roster(this);
}
+
void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
{
std::string plName, OFFNOTE;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_SET_OFFICER_NOTE");
+
recvPacket >> plName;
+
if(!normalizePlayerName(plName))
return;
+
Guild* guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
if (!guild)
{
@@ -480,25 +610,33 @@ void WorldSession::HandleGuildSetOfficerNoteOpcode(WorldPacket& recvPacket)
SendGuildCommandResult(GUILD_INVITE_S, "", GUILD_PERMISSIONS);
return;
}
+
uint64 plGuid;
MemberSlot* slot = guild->GetMemberSlot(plName, plGuid);
+
if (!slot)
{
SendGuildCommandResult(GUILD_INVITE_S, plName, GUILD_PLAYER_NOT_IN_GUILD_S);
return;
}
+
recvPacket >> OFFNOTE;
guild->SetOFFNOTE(plGuid, OFFNOTE);
+
guild->Roster(this);
}
+
void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
{
//recvPacket.hexlike();
+
Guild *guild;
std::string rankname;
uint32 rankId;
uint32 rights, MoneyPerDay;
+
//sLog.outDebug("WORLD: Received CMSG_GUILD_RANK");
+
guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
if(!guild)
{
@@ -506,76 +644,100 @@ void WorldSession::HandleGuildRankOpcode(WorldPacket& recvPacket)
SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_PLAYER_NOT_IN_GUILD);
return;
}
+
else if(GetPlayer()->GetGUID() != guild->GetLeader())
{
recvPacket.rpos(recvPacket.wpos()); // set to end to avoid warnings spam
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)
{
uint32 BankRights;
uint32 BankSlotPerDay;
+
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(); // broadcast for tab rights update
}
+
void WorldSession::HandleGuildAddRankOpcode(WorldPacket& recvPacket)
{
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->GetRanksSize() >= GUILD_RANKS_MAX_COUNT) // 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(); // broadcast for tab rights update
}
+
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(); // broadcast for tab rights update
}
+
void WorldSession::SendGuildCommandResult(uint32 typecmd, const std::string& str,uint32 cmdresult)
{
WorldPacket data(SMSG_GUILD_COMMAND_RESULT, (8+str.size()+1));
@@ -583,36 +745,48 @@ void WorldSession::SendGuildCommandResult(uint32 typecmd, const std::string& str
data << str;
data << cmdresult;
SendPacket(&data);
+
//sLog.outDebug("WORLD: Sent (SMSG_GUILD_COMMAND_RESULT)");
}
+
void WorldSession::HandleGuildChangeInfoTextOpcode(WorldPacket& recvPacket)
{
//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::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket)
{
//sLog.outDebug("WORLD: Received MSG_SAVE_GUILD_EMBLEM");
+
uint64 vendorGuid;
+
uint32 EmblemStyle;
uint32 EmblemColor;
uint32 BorderStyle;
uint32 BorderColor;
uint32 BackgroundColor;
+
recvPacket >> vendorGuid;
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(vendorGuid,UNIT_NPC_FLAG_TABARDDESIGNER);
if (!pCreature)
{
@@ -621,14 +795,17 @@ void WorldSession::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket)
sLog.outDebug("WORLD: HandleSaveGuildEmblemOpcode - 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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
recvPacket >> EmblemStyle;
recvPacket >> EmblemColor;
recvPacket >> BorderStyle;
recvPacket >> BorderColor;
recvPacket >> BackgroundColor;
+
Guild *guild = objmgr.GetGuildById(GetPlayer()->GetGuildId());
if(!guild)
{
@@ -636,60 +813,79 @@ void WorldSession::HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket)
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::HandleGuildEventLogQueryOpcode(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::HandleGuildBankMoneyWithdrawn( 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::HandleGuildPermissions( 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
@@ -705,6 +901,7 @@ void WorldSession::HandleGuildPermissions( WorldPacket& /* recv_data */ )
SendPacket(&data);
sLog.outDebug("WORLD: Sent (MSG_GUILD_PERMISSIONS)");
}
+
/* Called when clicking on Guild bank gameobject */
void WorldSession::HandleGuildBankerActivate( WorldPacket & recv_data )
{
@@ -712,8 +909,10 @@ void WorldSession::HandleGuildBankerActivate( WorldPacket & recv_data )
uint64 GoGuid;
uint8 unk;
recv_data >> GoGuid >> unk;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return;
+
if (uint32 GuildId = GetPlayer()->GetGuildId())
{
if(Guild *pGuild = objmgr.GetGuildById(GuildId))
@@ -722,8 +921,10 @@ void WorldSession::HandleGuildBankerActivate( WorldPacket & recv_data )
return;
}
}
+
SendGuildCommandResult(GUILD_BANK_S, "", GUILD_PLAYER_NOT_IN_GUILD);
}
+
/* Called when opening guild bank tab only (first one) */
void WorldSession::HandleGuildBankQueryTab( WorldPacket & recv_data )
{
@@ -731,94 +932,127 @@ void WorldSession::HandleGuildBankQueryTab( WorldPacket & recv_data )
uint64 GoGuid;
uint8 TabId,unk1;
recv_data >> GoGuid >> TabId >> unk1;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
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::HandleGuildBankDepositMoney( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_DEPOSIT_MONEY)");
uint64 GoGuid;
uint32 money;
recv_data >> GoGuid >> money;
+
if (!money)
return;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
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(_player->GetSession()->GetAccountId(),"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);
}
+
void WorldSession::HandleGuildBankWithdrawMoney( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_WITHDRAW_MONEY)");
uint64 GoGuid;
uint32 money;
recv_data >> GoGuid >> money;
+
if (!money)
return;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return;
+
uint32 GuildId = GetPlayer()->GetGuildId();
if (GuildId == 0)
return;
+
Guild *pGuild = objmgr.GetGuildById(GuildId);
if(!pGuild)
return;
+
if (pGuild->GetGuildBankMoney()<money) // not enough money in bank
return;
+
if (!pGuild->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);
}
+
void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_SWAP_ITEMS)");
//recv_data.hexlike();
+
uint64 GoGuid;
uint8 BankToBank;
+
uint8 BankTab, BankTabSlot, AutoStore;
uint8 PlayerSlot = NULL_SLOT;
uint8 PlayerBag = NULL_BAG;
@@ -827,6 +1061,7 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
uint32 ItemEntry, unk1;
uint32 AutoStoreCount = 0;
uint32 SplitedAmount = 0;
+
recv_data >> GoGuid >> BankToBank;
if (BankToBank)
{
@@ -838,6 +1073,7 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
recv_data >> ItemEntry;
recv_data >> unk2; // always 0
recv_data >> SplitedAmount;
+
if (BankTabSlotDst >= GUILD_BANK_MAX_SLOTS || (BankTabDst == BankTab && BankTabSlotDst == BankTabSlot))
{
recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet
@@ -863,68 +1099,87 @@ void WorldSession::HandleGuildBankSwapItems( WorldPacket & recv_data )
recv_data >> ToChar;
recv_data >> SplitedAmount;
}
+
if (BankTabSlot >= GUILD_BANK_MAX_SLOTS && BankTabSlot != 0xFF)
{
recv_data.rpos(recv_data.wpos()); // prevent additional spam at rejected packet
return;
}
}
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return;
+
uint32 GuildId = GetPlayer()->GetGuildId();
if (GuildId == 0)
return;
+
Guild *pGuild = objmgr.GetGuildById(GuildId);
if(!pGuild)
return;
+
Player *pl = GetPlayer();
+
// Bank <-> Bank
if (BankToBank)
{
pGuild->SwapItems(pl, BankTab, BankTabSlot, BankTabDst, BankTabSlotDst, SplitedAmount);
return;
}
+
// Player <-> Bank
+
// allow work with inventory only
if(!Player::IsInventoryPos(PlayerBag,PlayerSlot) && !(PlayerBag == NULL_BAG && PlayerSlot == NULL_SLOT) )
{
_player->SendEquipError( EQUIP_ERR_NONE, NULL, NULL );
return;
}
+
// BankToChar swap or char to bank remaining
if (ToChar) // Bank -> Char cases
pGuild->MoveFromBankToChar(pl, BankTab, BankTabSlot, PlayerBag, PlayerSlot, SplitedAmount);
else // Char -> Bank cases
pGuild->MoveFromCharToBank(pl, PlayerBag, PlayerSlot, BankTab, BankTabSlot, SplitedAmount);
}
+
void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_BUY_TAB)");
//recv_data.hexlike();
uint64 GoGuid;
uint8 TabId;
+
recv_data >> GoGuid;
recv_data >> TabId;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
return;
+
uint32 GuildId = GetPlayer()->GetGuildId();
if (GuildId==0)
return;
+
Guild *pGuild = objmgr.GetGuildById(GuildId);
if(!pGuild)
return;
+
uint32 TabCost = GetGuildBankTabPrice(TabId) * GOLD;
if (!TabCost)
return;
+
if (pGuild->GetPurchasedTabs() >= GUILD_BANK_MAX_TABS)
return;
+
if (TabId != pGuild->GetPurchasedTabs()) // m_PurchasedTabs = 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));
@@ -933,6 +1188,7 @@ void WorldSession::HandleGuildBankBuyTab( WorldPacket & recv_data )
pGuild->Roster(); // broadcast for tab rights update
pGuild->DisplayGuildBankTabsInfo(this);
}
+
void WorldSession::HandleGuildBankUpdateTab( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received (CMSG_GUILD_BANK_UPDATE_TAB)");
@@ -941,67 +1197,89 @@ void WorldSession::HandleGuildBankUpdateTab( WorldPacket & recv_data )
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 (!GetPlayer()->GetGameObjectIfCanInteractWith(GoGuid, GAMEOBJECT_TYPE_GUILD_BANK))
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::HandleGuildBankLogQuery( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Received (MSG_GUILD_BANK_LOG_QUERY)");
+
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::HandleQueryGuildBankTabText(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Received MSG_QUERY_GUILD_BANK_TEXT");
+
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::HandleSetGuildBankTabText(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Received CMSG_SET_GUILD_BANK_TEXT");
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);
diff --git a/src/game/HomeMovementGenerator.cpp b/src/game/HomeMovementGenerator.cpp
index 435d1be1ab2..e2fc9b1e0ec 100644
--- a/src/game/HomeMovementGenerator.cpp
+++ b/src/game/HomeMovementGenerator.cpp
@@ -17,12 +17,14 @@
* 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 "CreatureAI.h"
#include "Traveller.h"
#include "DestinationHolderImp.h"
#include "WorldPacket.h"
+
void
HomeMovementGenerator<Creature>::Initialize(Creature & owner)
{
@@ -32,32 +34,41 @@ HomeMovementGenerator<Creature>::Initialize(Creature & owner)
owner.addUnitState(UNIT_STAT_EVADE);
_setTargetLocation(owner);
}
+
void
HomeMovementGenerator<Creature>::Reset(Creature &)
{
}
+
void
HomeMovementGenerator<Creature>::_setTargetLocation(Creature & owner)
{
if( !&owner )
return;
+
if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) )
return;
+
float x, y, z;
owner.GetHomePosition(x, y, z, ori);
+
CreatureTraveller traveller(owner);
+
uint32 travel_time = i_destinationHolder.SetDestination(traveller, x, y, z);
modifyTravelTime(travel_time);
owner.clearUnitState(UNIT_STAT_ALL_STATE);
}
+
bool
HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff)
{
CreatureTraveller traveller( owner);
i_destinationHolder.UpdateTraveller(traveller, time_diff);
+
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)
{
@@ -67,11 +78,14 @@ HomeMovementGenerator<Creature>::Update(Creature &owner, const uint32& time_diff
owner.BuildHeartBeatMsg(&packet);
owner.SendMessageToSet(&packet, false);
}
+
owner.clearUnitState(UNIT_STAT_EVADE);
owner.AI()->JustReachedHome();
return false;
}
+
i_travel_timer -= time_diff;
+
return true;
}
diff --git a/src/game/HomeMovementGenerator.h b/src/game/HomeMovementGenerator.h
index 76be5747733..964b5c3e801 100644
--- a/src/game/HomeMovementGenerator.h
+++ b/src/game/HomeMovementGenerator.h
@@ -17,31 +17,41 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_HOMEMOVEMENTGENERATOR_H
#define TRINITY_HOMEMOVEMENTGENERATOR_H
+
#include "MovementGenerator.h"
#include "DestinationHolder.h"
#include "Traveller.h"
+
class Creature;
+
template < class T >
class TRINITY_DLL_SPEC HomeMovementGenerator;
+
template <>
class TRINITY_DLL_SPEC HomeMovementGenerator<Creature>
: public MovementGeneratorMedium< Creature, HomeMovementGenerator<Creature> >
{
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<Creature> > i_destinationHolder;
+
float ori;
uint32 i_travel_timer;
};
diff --git a/src/game/HostilRefManager.cpp b/src/game/HostilRefManager.cpp
index 567ec634cd4..4a51d54248d 100644
--- a/src/game/HostilRefManager.cpp
+++ b/src/game/HostilRefManager.cpp
@@ -17,22 +17,27 @@
* 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 "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)
@@ -45,10 +50,13 @@ void HostilRefManager::threatAssist(Unit *pVictim, float pThreat, SpellEntry con
ref = ref->next();
}
}
+
//=================================================
+
void HostilRefManager::addThreatPercent(int32 pValue)
{
HostilReference* ref;
+
ref = getFirst();
while(ref != NULL)
{
@@ -56,11 +64,14 @@ void HostilRefManager::addThreatPercent(int32 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)
{
@@ -68,8 +79,10 @@ void HostilRefManager::setOnlineOfflineState(bool pIsOnline)
ref = ref->next();
}
}
+
//=================================================
// The online / offline status is calculated and set
+
void HostilRefManager::updateThreatTables()
{
HostilReference* ref = getFirst();
@@ -79,9 +92,11 @@ void HostilRefManager::updateThreatTables()
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();
@@ -93,8 +108,10 @@ void HostilRefManager::deleteReferences()
ref = nextRef;
}
}
+
//=================================================
// delete one reference, defined by Unit
+
void HostilRefManager::deleteReference(Unit *pCreature)
{
HostilReference* ref = getFirst();
@@ -110,8 +127,10 @@ void HostilRefManager::deleteReference(Unit *pCreature)
ref = nextRef;
}
}
+
//=================================================
// set state for one reference, defined by Unit
+
void HostilRefManager::setOnlineOfflineState(Unit *pCreature,bool pIsOnline)
{
HostilReference* ref = getFirst();
@@ -126,5 +145,6 @@ void HostilRefManager::setOnlineOfflineState(Unit *pCreature,bool pIsOnline)
ref = nextRef;
}
}
+
//=================================================
diff --git a/src/game/HostilRefManager.h b/src/game/HostilRefManager.h
index 2964cad24bf..316509e3908 100644
--- a/src/game/HostilRefManager.h
+++ b/src/game/HostilRefManager.h
@@ -17,15 +17,20 @@
* 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<Unit, ThreatManager>
{
private:
@@ -33,20 +38,29 @@ class HostilRefManager : public RefManager<Unit, ThreatManager>
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<Unit, ThreatManager>::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);
};
diff --git a/src/game/IdleMovementGenerator.cpp b/src/game/IdleMovementGenerator.cpp
index 770020205c4..7c68e3c09f5 100644
--- a/src/game/IdleMovementGenerator.cpp
+++ b/src/game/IdleMovementGenerator.cpp
@@ -17,10 +17,13 @@
* 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 "CreatureAI.h"
#include "Creature.h"
+
IdleMovementGenerator si_idleMovement;
+
// StopMoving is needed to make unit stop if its last movement generator expires
// But it should not be sent otherwise there are many redundent packets
void IdleMovementGenerator::Initialize(Unit &owner)
@@ -28,21 +31,27 @@ void IdleMovementGenerator::Initialize(Unit &owner)
if(owner.hasUnitState(UNIT_STAT_MOVE))
owner.StopMoving();
}
+
void
IdleMovementGenerator::Reset(Unit& owner)
{
if(owner.hasUnitState(UNIT_STAT_MOVE))
owner.StopMoving();
}
+
void RotateMovementGenerator::Initialize(Unit& owner)
{
if(owner.hasUnitState(UNIT_STAT_MOVE))
owner.StopMoving();
+
if(owner.getVictim())
owner.SetInFront(owner.getVictim());
+
owner.addUnitState(UNIT_STAT_ROTATING);
- owner.AttackStop();
+
+ owner.AttackStop();
}
+
bool RotateMovementGenerator::Update(Unit& owner, const uint32& diff)
{
float angle = owner.GetOrientation();
@@ -58,36 +67,44 @@ bool RotateMovementGenerator::Update(Unit& owner, const uint32& diff)
}
owner.SetOrientation(angle);
owner.SendMovementFlagUpdate(); // this is a hack. we do not have anything correct to send in the beginning
+
if(m_duration > diff)
m_duration -= diff;
else
return false;
+
return true;
}
+
void RotateMovementGenerator::Finalize(Unit &unit)
{
unit.clearUnitState(UNIT_STAT_ROTATING);
if(unit.GetTypeId() == TYPEID_UNIT)
((Creature*)&unit)->AI()->MovementInform(ROTATE_MOTION_TYPE, 0);
}
+
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;
}
+
void
AssistanceDistractMovementGenerator::Finalize(Unit &unit)
{
diff --git a/src/game/IdleMovementGenerator.h b/src/game/IdleMovementGenerator.h
index 3ba04a08e39..7251e8b011f 100644
--- a/src/game/IdleMovementGenerator.h
+++ b/src/game/IdleMovementGenerator.h
@@ -17,51 +17,65 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_IDLEMOVEMENTGENERATOR_H
#define TRINITY_IDLEMOVEMENTGENERATOR_H
+
#include "MovementGenerator.h"
+
class TRINITY_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 TRINITY_DLL_SPEC RotateMovementGenerator : public MovementGenerator
{
public:
explicit RotateMovementGenerator(uint32 time, RotateDirection direction) : m_duration(time), m_maxDuration(time), m_direction(direction) {}
+
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 ROTATE_MOTION_TYPE; }
+
private:
uint32 m_duration, m_maxDuration;
RotateDirection m_direction;
};
+
class TRINITY_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;
};
+
class MANGOS_DLL_SPEC AssistanceDistractMovementGenerator : public DistractMovementGenerator
{
public:
AssistanceDistractMovementGenerator(uint32 timer) :
DistractMovementGenerator(timer) {}
+
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_DISTRACT_MOTION_TYPE; }
void Finalize(Unit& unit);
};
+
#endif
diff --git a/src/game/InstanceData.cpp b/src/game/InstanceData.cpp
index 00738a72a94..1398038d724 100644
--- a/src/game/InstanceData.cpp
+++ b/src/game/InstanceData.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,6 +25,7 @@
#include "GameObject.h"
#include "Creature.h"
#include "CreatureAI.h"
+
void InstanceData::SaveToDB()
{
std::string data = GetSaveData();
@@ -32,6 +34,7 @@ void InstanceData::SaveToDB()
CharacterDatabase.escape_string(data);
CharacterDatabase.PExecute("UPDATE instance SET data = '%s' WHERE id = '%d'", data.c_str(), instance->GetInstanceId());
}
+
void InstanceData::HandleGameObject(uint64 GUID, bool open, GameObject *go)
{
if(!go)
@@ -41,33 +44,40 @@ void InstanceData::HandleGameObject(uint64 GUID, bool open, GameObject *go)
else
debug_log("TSCR: InstanceData: HandleGameObject failed");
}
+
bool InstanceData::IsEncounterInProgress() const
{
for(std::vector<BossInfo>::const_iterator itr = bosses.begin(); itr != bosses.end(); ++itr)
if(itr->state == IN_PROGRESS)
return true;
+
return false;
}
+
void InstanceData::LoadMinionData(const MinionData *data)
{
while(data->entry)
{
if(data->bossId < bosses.size())
minions.insert(std::make_pair(data->entry, MinionInfo(&bosses[data->bossId])));
+
++data;
}
sLog.outDebug("InstanceData::LoadMinionData: %u minions loaded.", doors.size());
}
+
void InstanceData::LoadDoorData(const DoorData *data)
{
while(data->entry)
{
if(data->bossId < bosses.size())
doors.insert(std::make_pair(data->entry, DoorInfo(&bosses[data->bossId], data->type, BoundaryType(data->boundary))));
+
++data;
}
sLog.outDebug("InstanceData::LoadDoorData: %u doors loaded.", doors.size());
}
+
void InstanceData::UpdateMinionState(Creature *minion, EncounterState state)
{
switch(state)
@@ -86,12 +96,14 @@ void InstanceData::UpdateMinionState(Creature *minion, EncounterState state)
break;
}
}
+
void InstanceData::UpdateDoorState(GameObject *door)
{
DoorInfoMap::iterator lower = doors.lower_bound(door->GetEntry());
DoorInfoMap::iterator upper = doors.upper_bound(door->GetEntry());
if(lower == upper)
return;
+
bool open = true;
for(DoorInfoMap::iterator itr = lower; itr != upper; ++itr)
{
@@ -112,15 +124,18 @@ void InstanceData::UpdateDoorState(GameObject *door)
}
}
}
+
door->SetGoState(open ? GO_STATE_ACTIVE : GO_STATE_READY);
//sLog.outError("Door %u is %s.", door->GetEntry(), open ? "opened" : "closed");
}
+
void InstanceData::AddDoor(GameObject *door, bool add)
{
DoorInfoMap::iterator lower = doors.lower_bound(door->GetEntry());
DoorInfoMap::iterator upper = doors.upper_bound(door->GetEntry());
if(lower == upper)
return;
+
for(DoorInfoMap::iterator itr = lower; itr != upper; ++itr)
{
if(add)
@@ -152,19 +167,23 @@ void InstanceData::AddDoor(GameObject *door, bool add)
else
itr->second.bossInfo->door[itr->second.type].erase(door);
}
+
if(add)
UpdateDoorState(door);
}
+
void InstanceData::AddMinion(Creature *minion, bool add)
{
MinionInfoMap::iterator itr = minions.find(minion->GetEntry());
if(itr == minions.end())
return;
+
if(add)
itr->second.bossInfo->minion.insert(minion);
else
itr->second.bossInfo->minion.erase(minion);
}
+
bool InstanceData::SetBossState(uint32 id, EncounterState state)
{
if(id < bosses.size())
@@ -180,22 +199,28 @@ bool InstanceData::SetBossState(uint32 id, EncounterState state)
{
if(bossInfo->state == state)
return false;
+
if(state == DONE)
for(MinionSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i)
if((*i)->isWorldBoss() && (*i)->isAlive())
return false;
+
bossInfo->state = state;
SaveToDB();
}
+
for(uint32 type = 0; type < MAX_DOOR_TYPES; ++type)
for(DoorSet::iterator i = bossInfo->door[type].begin(); i != bossInfo->door[type].end(); ++i)
UpdateDoorState(*i);
+
for(MinionSet::iterator i = bossInfo->minion.begin(); i != bossInfo->minion.end(); ++i)
UpdateMinionState(*i, state);
+
return true;
}
return false;
}
+
std::string InstanceData::LoadBossState(const char * data)
{
if(!data) return NULL;
@@ -210,18 +235,22 @@ std::string InstanceData::LoadBossState(const char * data)
}
return loadStream.str();
}
+
std::string InstanceData::GetBossSaveData()
{
std::ostringstream saveStream;
for(std::vector<BossInfo>::iterator i = bosses.begin(); i != bosses.end(); ++i)
saveStream << (uint32)i->state << " ";
return saveStream.str();
-}
+}
+
void InstanceData::DoUseDoorOrButton(uint64 uiGuid, uint32 uiWithRestoreTime, bool bUseAlternativeState)
{
if (!uiGuid)
return;
+
GameObject* pGo = instance->GetGameObject(uiGuid);
+
if (pGo)
{
if (pGo->GetGoType() == GAMEOBJECT_TYPE_DOOR || pGo->GetGoType() == GAMEOBJECT_TYPE_BUTTON)
@@ -235,6 +264,7 @@ void InstanceData::DoUseDoorOrButton(uint64 uiGuid, uint32 uiWithRestoreTime, bo
error_log("SD2: Script call DoUseDoorOrButton, but gameobject entry %u is type %u.",pGo->GetEntry(),pGo->GetGoType());
}
}
+
void InstanceData::DoRespawnGameObject(uint64 uiGuid, uint32 uiTimeToDespawn)
{
if (GameObject* pGo = instance->GetGameObject(uiGuid))
@@ -243,14 +273,18 @@ void InstanceData::DoRespawnGameObject(uint64 uiGuid, uint32 uiTimeToDespawn)
if (pGo->GetGoType()==GAMEOBJECT_TYPE_FISHINGNODE || pGo->GetGoType()==GAMEOBJECT_TYPE_DOOR ||
pGo->GetGoType()==GAMEOBJECT_TYPE_BUTTON || pGo->GetGoType()==GAMEOBJECT_TYPE_TRAP)
return;
+
if (pGo->isSpawned())
return;
+
pGo->SetRespawnTime(uiTimeToDespawn);
}
}
+
void InstanceData::DoUpdateWorldState(uint32 uiStateId, uint32 uiStateData)
{
Map::PlayerList const& lPlayers = instance->GetPlayers();
+
if (!lPlayers.isEmpty())
{
for(Map::PlayerList::const_iterator itr = lPlayers.begin(); itr != lPlayers.end(); ++itr)
@@ -262,32 +296,38 @@ void InstanceData::DoUpdateWorldState(uint32 uiStateId, uint32 uiStateData)
else
debug_log("TSCR: DoUpdateWorldState attempt send data but no players in map.");
}
+
/* Not used anywhere yet, not sure if they're needed:
// Send Notify to all players in instance
void InstanceData::DoSendNotifyToInstance(const char *format, ...)
{
InstanceMap::PlayerList const &PlayerList = instance->GetPlayers();
InstanceMap::PlayerList::const_iterator i;
+
if (!PlayerList.isEmpty())
for (i = PlayerList.begin(); i != PlayerList.end(); ++i)
if ((*i).getSource() && (*i).getSource()->GetSession())
(*i).getSource()->GetSession()->SendNotification(format);
}
+
// Complete Achievement for all players in instance
void InstanceData::DoCompleteAchievement(uint32 achievement)
{
AchievementEntry const* AE = GetAchievementStore()->LookupEntry(achievement);
Map::PlayerList const &PlayerList = instance->GetPlayers();
+
if (!PlayerList.isEmpty())
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (i->getSource())
i->getSource()->CompletedAchievement(AE);
}
*/
+
// Remove Auras due to Spell on all players in instance
void InstanceData::DoRemoveAurasDueToSpellOnPlayers(uint32 spell)
{
Map::PlayerList const &PlayerList = instance->GetPlayers();
+
if (!PlayerList.isEmpty())
for (Map::PlayerList::const_iterator i = PlayerList.begin(); i != PlayerList.end(); ++i)
if (i->getSource() && i->getSource()->HasAura(spell)) i->getSource()->RemoveAurasDueToSpell(spell);
diff --git a/src/game/InstanceData.h b/src/game/InstanceData.h
index 29bae24436b..010a3d44d42 100644
--- a/src/game/InstanceData.h
+++ b/src/game/InstanceData.h
@@ -17,18 +17,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_INSTANCE_DATA_H
#define TRINITY_INSTANCE_DATA_H
+
#include "ZoneScript.h"
//#include "GameObject.h"
//#include "Map.h"
+
class Map;
class Unit;
class Player;
class GameObject;
class Creature;
+
typedef std::set<GameObject*> DoorSet;
typedef std::set<Creature*> MinionSet;
+
enum EncounterState
{
NOT_STARTED = 0,
@@ -38,12 +43,14 @@ enum EncounterState
SPECIAL = 4,
TO_BE_DECIDED = 5,
};
+
enum DoorType
{
DOOR_TYPE_ROOM = 0,
DOOR_TYPE_PASSAGE,
MAX_DOOR_TYPES,
};
+
enum BoundaryType
{
BOUNDARY_NONE = 0,
@@ -60,17 +67,21 @@ enum BoundaryType
BOUNDARY_MAX_Y = BOUNDARY_W,
BOUNDARY_MIN_Y = BOUNDARY_E,
};
+
typedef std::map<BoundaryType, float> BossBoundaryMap;
+
struct DoorData
{
uint32 entry, bossId;
DoorType type;
uint32 boundary;
};
+
struct MinionData
{
uint32 entry, bossId;
};
+
struct BossInfo
{
BossInfo() : state(TO_BE_DECIDED) {}
@@ -79,6 +90,7 @@ struct BossInfo
MinionSet minion;
BossBoundaryMap boundary;
};
+
struct DoorInfo
{
explicit DoorInfo(BossInfo *_bossInfo, DoorType _type, BoundaryType _boundary)
@@ -87,50 +99,70 @@ struct DoorInfo
DoorType type;
BoundaryType boundary;
};
+
struct MinionInfo
{
explicit MinionInfo(BossInfo *_bossInfo) : bossInfo(_bossInfo) {}
BossInfo *bossInfo;
};
+
typedef std::multimap<uint32 /*entry*/, DoorInfo> DoorInfoMap;
typedef std::map<uint32 /*entry*/, MinionInfo> MinionInfoMap;
+
class TRINITY_DLL_SPEC InstanceData : public ZoneScript
{
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) { LoadBossState(data); }
+
//When save is needed, this function generates the data
virtual std::string GetSaveData() { return GetBossSaveData(); }
+
void SaveToDB();
+
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;
+
//Called when a player successfully enters the instance.
virtual void OnPlayerEnter(Player *) {}
+
//Handle open / close objects
//use HandleGameObject(NULL,boolen,GO); in OnObjectCreate in instance scripts
//use HandleGameObject(GUID,boolen,NULL); in any other script
void HandleGameObject(uint64 GUID, bool open, GameObject *go = NULL);
+
//change active state of doors or buttons
void DoUseDoorOrButton(uint64 uiGuid, uint32 uiWithRestoreTime = 0, bool bUseAlternativeState = false);
+
//Respawns a GO having negative spawntimesecs in gameobject-table
void DoRespawnGameObject(uint64 uiGuid, uint32 uiTimeToDespawn = MINUTE);
+
//sends world state update to all players in instance
void DoUpdateWorldState(uint32 uiStateId, uint32 uiStateData);
+
/* Not used anywhere yet, not sure if they're needed:
// Send Notify to all players in instance
void DoSendNotifyToInstance(const char *format,...);
+
// Complete Achievement for all players in instance
void DoCompleteAchievement(uint32 achievement);
*/
+
// Remove Auras due to Spell on all players in instance
void DoRemoveAurasDueToSpellOnPlayers(uint32 spell);
+
virtual bool SetBossState(uint32 id, EncounterState state);
EncounterState GetBossState(uint32 id) const { return id < bosses.size() ? bosses[id].state : TO_BE_DECIDED; }
const BossBoundaryMap * GetBossBoundary(uint32 id) const { return id < bosses.size() ? &bosses[id].boundary : NULL; }
@@ -138,10 +170,13 @@ class TRINITY_DLL_SPEC InstanceData : public ZoneScript
void SetBossNumber(uint32 number) { bosses.resize(number); }
void LoadDoorData(const DoorData *data);
void LoadMinionData(const MinionData *data);
+
void AddDoor(GameObject *door, bool add);
void AddMinion(Creature *minion, bool add);
+
void UpdateDoorState(GameObject *door);
void UpdateMinionState(Creature *minion, EncounterState state);
+
std::string LoadBossState(const char * data);
std::string GetBossSaveData();
private:
diff --git a/src/game/InstanceSaveMgr.cpp b/src/game/InstanceSaveMgr.cpp
index ae66f2489ca..90714356fc1 100644
--- a/src/game/InstanceSaveMgr.cpp
+++ b/src/game/InstanceSaveMgr.cpp
@@ -18,8 +18,10 @@
* 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/SQLStorage.h"
+
#include "Player.h"
#include "GridNotifiers.h"
#include "Log.h"
@@ -40,10 +42,13 @@
#include "ProgressBar.h"
#include "Policies/Singleton.h"
#include "Policies/SingletonImp.h"
+
INSTANTIATE_SINGLETON_1( InstanceSaveManager );
+
InstanceSaveManager::InstanceSaveManager() : lock_instLists(false)
{
}
+
InstanceSaveManager::~InstanceSaveManager()
{
// it is undefined whether this or objectmgr will be unloaded first
@@ -67,6 +72,7 @@ InstanceSaveManager::~InstanceSaveManager()
delete save;
}
}
+
/*
- adding instance into manager
- called from InstanceMap::Add, _LoadBoundInstances, LoadGroups
@@ -75,12 +81,14 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instance
{
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
@@ -94,17 +102,22 @@ InstanceSave* InstanceSaveManager::AddInstanceSave(uint32 mapId, uint32 instance
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();
@@ -114,6 +127,7 @@ void InstanceSaveManager::DeleteInstanceFromDB(uint32 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 );
@@ -126,16 +140,19 @@ void InstanceSaveManager::RemoveInstanceSave(uint32 InstanceId)
m_instanceSaveById.erase(itr);
}
}
+
InstanceSave::InstanceSave(uint16 MapId, uint32 InstanceId, uint8 difficulty, time_t resetTime, bool canReset)
: m_resetTime(resetTime), m_instanceid(InstanceId), m_mapid(MapId),
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
*/
@@ -143,6 +160,7 @@ void InstanceSave::SaveToDB()
{
// save instance data too
std::string data;
+
Map *map = MapManager::Instance().FindMap(GetMapId(),m_instanceid);
if(map)
{
@@ -154,8 +172,10 @@ void InstanceSave::SaveToDB()
CharacterDatabase.escape_string(data);
}
}
+
CharacterDatabase.PExecute("INSERT INTO instance VALUES ('%u', '%u', '"UI64FMTD"', '%u', '%s')", m_instanceid, GetMapId(), (uint64)GetResetTimeForDB(), GetDifficulty(), data.c_str());
}
+
time_t InstanceSave::GetResetTimeForDB()
{
// only save the reset time for normal instances
@@ -165,19 +185,23 @@ time_t InstanceSave::GetResetTimeForDB()
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()
{
@@ -190,15 +214,18 @@ bool InstanceSave::UnloadIfEmpty()
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)
{
@@ -217,21 +244,28 @@ void InstanceSaveManager::_DelHelper(DatabaseType &db, const char *fields, const
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;
@@ -246,6 +280,7 @@ void InstanceSaveManager::CleanupInstances()
while (result->NextRow());
delete result;
}
+
// creature_respawn
result = WorldDatabase.Query("SELECT DISTINCT(instance) FROM creature_respawn WHERE instance <> 0");
if( result )
@@ -259,6 +294,7 @@ void InstanceSaveManager::CleanupInstances()
while (result->NextRow());
delete result;
}
+
// gameobject_respawn
result = WorldDatabase.Query("SELECT DISTINCT(instance) FROM gameobject_respawn WHERE instance <> 0");
if( result )
@@ -272,6 +308,7 @@ void InstanceSaveManager::CleanupInstances()
while (result->NextRow());
delete result;
}
+
// characters
result = CharacterDatabase.Query("SELECT DISTINCT(instance_id) FROM characters WHERE instance_id <> 0");
if( result )
@@ -285,6 +322,7 @@ void InstanceSaveManager::CleanupInstances()
while (result->NextRow());
delete result;
}
+
// corpse
result = CharacterDatabase.Query("SELECT DISTINCT(instance) FROM corpse WHERE instance <> 0");
if( result )
@@ -298,16 +336,20 @@ void InstanceSaveManager::CleanupInstances()
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
@@ -322,8 +364,10 @@ void InstanceSaveManager::PackInstances()
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)
@@ -339,17 +383,22 @@ void InstanceSaveManager::PackInstances()
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( ">> Instance numbers remapped, next instance id is %u", InstanceNumber );
sLog.outString();
}
+
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
@@ -369,6 +418,7 @@ void InstanceSaveManager::LoadResetTimes()
}
while (result->NextRow());
delete result;
+
// update reset time for normal instances with the max creature respawn time + X hours
result = WorldDatabase.Query("SELECT MAX(respawntime), instance FROM creature_respawn WHERE instance > 0 GROUP BY instance");
if( result )
@@ -388,11 +438,13 @@ void InstanceSaveManager::LoadResetTimes()
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);
@@ -409,18 +461,22 @@ void InstanceSaveManager::LoadResetTimes()
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 = '"UI64FMTD"' 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 < '"UI64FMTD"' AND instance.resettime > '0') OR (NOT instance_reset.resettime IS NULL AND instance_reset.resettime < '"UI64FMTD"')", (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++)
@@ -431,6 +487,7 @@ void InstanceSaveManager::LoadResetTimes()
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];
@@ -440,6 +497,7 @@ void InstanceSaveManager::LoadResetTimes()
t = today + period + diff;
CharacterDatabase.DirectPExecute("INSERT INTO instance_reset VALUES ('%u','"UI64FMTD"')", i, (uint64)t);
}
+
if(t < now)
{
// assume that expired instances have already been cleaned
@@ -448,7 +506,9 @@ void InstanceSaveManager::LoadResetTimes()
t += ((today - t) / period + 1) * period + diff;
CharacterDatabase.DirectPExecute("UPDATE instance_reset SET resettime = '"UI64FMTD"' 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};
@@ -457,6 +517,7 @@ void InstanceSaveManager::LoadResetTimes()
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_t, InstResetEvent>(time, event));
@@ -478,6 +539,7 @@ void InstanceSaveManager::ScheduleReset(bool add, time_t time, InstResetEvent ev
}
}
}
+
void InstanceSaveManager::Update()
{
time_t now = time(NULL), t;
@@ -506,6 +568,7 @@ void InstanceSaveManager::Update()
}
}
}
+
void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
{
// unbind all players bound to the instance
@@ -527,19 +590,23 @@ void InstanceSaveManager::_ResetSave(InstanceSaveHashMap::iterator &itr)
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().CreateBaseMap(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
@@ -548,6 +615,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLe
if(!map->Instanceable())
return;
uint64 now = (uint64)time(NULL);
+
if(!warn)
{
// this is called one minute before the reset time
@@ -557,6 +625,7 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLe
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();)
{
@@ -565,22 +634,26 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLe
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 = '"UI64FMTD"' WHERE mapid = '%d'", next_reset, mapid);
+
// schedule next reset.
m_resetTimeByMapId[mapid] = (time_t) next_reset;
ScheduleReset(true, (time_t) (next_reset-3600), InstResetEvent(1, mapid));
}
+
MapInstanced::InstancedMaps &instMaps = ((MapInstanced*)map)->GetInstancedMaps();
MapInstanced::InstancedMaps::iterator mitr;
for(mitr = instMaps.begin(); mitr != instMaps.end(); ++mitr)
@@ -590,8 +663,10 @@ void InstanceSaveManager::_ResetOrWarnAll(uint32 mapid, bool warn, uint32 timeLe
if(warn) ((InstanceMap*)map2)->SendResetWarnings(timeLeft);
else ((InstanceMap*)map2)->Reset(INSTANCE_RESET_GLOBAL);
}
+
// TODO: delete creature/gameobject respawn times even if the maps are not loaded
}
+
uint32 InstanceSaveManager::GetNumBoundPlayersTotal()
{
uint32 ret = 0;
@@ -599,6 +674,7 @@ uint32 InstanceSaveManager::GetNumBoundPlayersTotal()
ret += itr->second->GetPlayerCount();
return ret;
}
+
uint32 InstanceSaveManager::GetNumBoundGroupsTotal()
{
uint32 ret = 0;
diff --git a/src/game/InstanceSaveMgr.h b/src/game/InstanceSaveMgr.h
index 39e12f1e8b3..29972210f3d 100644
--- a/src/game/InstanceSaveMgr.h
+++ b/src/game/InstanceSaveMgr.h
@@ -18,8 +18,10 @@
* 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 "ace/Thread_Mutex.h"
@@ -27,10 +29,12 @@
#include <map>
#include "Utilities/UnorderedMap.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:
@@ -47,27 +51,34 @@ class InstanceSave
- 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); }
@@ -75,14 +86,17 @@ class InstanceSave
/* 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<Player*> PlayerListType;
typedef std::list<Group*> GroupListType;
private:
@@ -98,15 +112,18 @@ class InstanceSave
uint8 m_difficulty;
bool m_canReset;
};
+
class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSaveManager, MaNGOS::ClassLevelLockable<InstanceSaveManager, ACE_Thread_Mutex> >
{
friend class InstanceSave;
public:
InstanceSaveManager();
~InstanceSaveManager();
+
typedef std::map<uint32 /*InstanceId*/, InstanceSave*> InstanceSaveMap;
typedef UNORDERED_MAP<uint32 /*InstanceId*/, InstanceSave*> InstanceSaveHashMap;
typedef std::map<uint32 /*mapId*/, InstanceSaveMap> InstanceSaveMapMap;
+
/* resetTime is a global propery of each (raid/heroic) map
all instances of that map reset at the same time */
struct InstResetEvent
@@ -119,20 +136,27 @@ class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSav
};
typedef std::multimap<time_t /*resetTime*/, InstResetEvent> ResetTimeQueue;
typedef std::vector<time_t /*resetTime*/> 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);
@@ -146,6 +170,7 @@ class MANGOS_DLL_DECL InstanceSaveManager : public MaNGOS::Singleton<InstanceSav
ResetTimeVector m_resetTimeByMapId;
ResetTimeQueue m_resetTimeQueue;
};
+
#define sInstanceSaveManager Trinity::Singleton<InstanceSaveManager>::Instance()
#endif
diff --git a/src/game/Item.cpp b/src/game/Item.cpp
index ed91c888451..2d39b016623 100644
--- a/src/game/Item.cpp
+++ b/src/game/Item.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -25,19 +26,25 @@
#include "ItemEnchantmentMgr.h"
#include "SpellMgr.h"
#include "ScriptCalls.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)
@@ -46,21 +53,26 @@ void AddItemsSetItem(Player*player,Item *item)
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])
@@ -68,12 +80,15 @@ void AddItemsSetItem(Player*player,Item *item)
//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++)
{
@@ -85,6 +100,7 @@ void AddItemsSetItem(Player*player,Item *item)
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;
@@ -93,15 +109,19 @@ void AddItemsSetItem(Player*player,Item *item)
}
}
}
+
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++)
@@ -112,17 +132,22 @@ void RemoveItemsSetItem(Player*player,ItemPrototype const *proto)
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])
@@ -134,6 +159,7 @@ void RemoveItemsSetItem(Player*player,ItemPrototype const *proto)
}
}
}
+
if(!eff->item_count) //all items of a set were removed
{
assert(eff == player->ItemSetEff[setindex]);
@@ -141,10 +167,12 @@ void RemoveItemsSetItem(Player*player,ItemPrototype const *proto)
player->ItemSetEff[setindex] = NULL;
}
}
+
bool ItemCanGoIntoBag(ItemPrototype const *pProto, ItemPrototype const *pBagProto)
{
if(!pProto || !pBagProto)
return false;
+
switch(pBagProto->Class)
{
case ITEM_CLASS_CONTAINER:
@@ -204,11 +232,14 @@ bool ItemCanGoIntoBag(ItemPrototype const *pProto, ItemPrototype const *pBagProt
}
return false;
}
+
Item::Item( )
{
m_objectType |= TYPEMASK_ITEM;
m_objectTypeId = TYPEID_ITEM;
+
m_updateFlag = UPDATEFLAG_HIGHGUID;
+
m_valuesCount = ITEM_END;
m_slot = 0;
uState = ITEM_NEW;
@@ -217,39 +248,52 @@ Item::Item( )
m_lootGenerated = false;
mb_in_trade = false;
}
+
bool Item::Create( uint32 guidlow, uint32 itemid, Player const* owner)
{
Object::_Create( guidlow, 0, HIGHGUID_ITEM );
+
SetEntry(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(uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++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)
{
Script->ItemExpire(owner, GetProto());
- owner->DestroyItem(GetBagSlot(), GetSlot(), true);
+ owner->DestroyItem(GetBagSlot(), GetSlot(), true);
return;
}
+
SetUInt32Value(ITEM_FIELD_DURATION, GetUInt32Value(ITEM_FIELD_DURATION) - diff);
SetState(ITEM_CHANGED, owner); // save new time in database
}
+
void Item::SaveToDB()
{
uint32 guid = GetGUIDLow();
@@ -272,7 +316,9 @@ void Item::SaveToDB()
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;
@@ -291,30 +337,37 @@ void Item::SaveToDB()
}
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("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("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)
@@ -322,42 +375,51 @@ bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result)
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;
+
// update max durability (and durability) if need
if(proto->MaxDurability!= GetUInt32Value(ITEM_FIELD_MAXDURABILITY))
{
SetUInt32Value(ITEM_FIELD_MAXDURABILITY,proto->MaxDurability);
if(GetUInt32Value(ITEM_FIELD_DURABILITY) > proto->MaxDurability)
SetUInt32Value(ITEM_FIELD_DURABILITY,proto->MaxDurability);
+
need_save = true;
}
+
// 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;
@@ -365,26 +427,33 @@ bool Item::LoadFromDB(uint32 guid, uint64 owner_guid, QueryResult *result)
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(GetEntry());
}
+
Player* Item::GetOwner()const
{
return objmgr.GetPlayer(GetOwnerGUID());
}
+
uint32 Item::GetSkill()
{
const static uint32 item_weapon_skills[MAX_ITEM_SUBCLASS_WEAPON] =
@@ -395,11 +464,14 @@ uint32 Item::GetSkill()
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,0
};
+
ItemPrototype const* proto = GetProto();
+
switch (proto->Class)
{
case ITEM_CLASS_WEAPON:
@@ -407,18 +479,22 @@ uint32 Item::GetSkill()
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:
@@ -454,20 +530,25 @@ uint32 Item::GetSpell()
}
return 0;
}
+
int32 Item::GenerateItemRandomPropertyId(uint32 item_id)
{
ItemPrototype const *itemProto = sItemStorage.LookupEntry<ItemPrototype>(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)
{
@@ -478,6 +559,7 @@ int32 Item::GenerateItemRandomPropertyId(uint32 item_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
@@ -490,13 +572,16 @@ int32 Item::GenerateItemRandomPropertyId(uint32 item_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);
@@ -523,11 +608,13 @@ void Item::SetItemRandomProperties(int32 randomPropId)
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());
@@ -536,6 +623,7 @@ bool Item::UpdateItemSuffixFactor()
SetUInt32Value(ITEM_FIELD_PROPERTY_SEED,suffixFactor);
return true;
}
+
void Item::SetState(ItemUpdateState state, Player *forplayer)
{
if (uState == ITEM_NEW && state == ITEM_REMOVED)
@@ -545,6 +633,7 @@ void Item::SetState(ItemUpdateState state, Player *forplayer)
delete this;
return;
}
+
if (state != ITEM_UNCHANGED)
{
// new items must stay in new state until saved
@@ -559,9 +648,11 @@ void Item::SetState(ItemUpdateState state, Player *forplayer)
uState = ITEM_UNCHANGED;
}
}
+
void Item::AddToUpdateQueueOf(Player *player)
{
if (IsInUpdateQueue()) return;
+
if (!player)
{
player = GetOwner();
@@ -571,18 +662,23 @@ void Item::AddToUpdateQueueOf(Player *player)
return;
}
}
+
if (player->GetGUID() != GetOwnerGUID())
{
sLog.outDebug("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();
@@ -592,29 +688,37 @@ void Item::RemoveFromUpdateQueueOf(Player *player)
return;
}
}
+
if (player->GetGUID() != GetOwnerGUID())
{
sLog.outDebug("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(bool mail) const
{
if ((!mail || !IsBoundAccountWide()) && 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 )
@@ -622,10 +726,13 @@ bool Item::CanBeTraded(bool mail) const
if (owner->GetLootGUID()==GetGUID())
return false;
}
+
if (IsBoundByEnchant())
return false;
+
return true;
}
+
bool Item::IsBoundByEnchant() const
{
// Check all enchants for soulbound
@@ -634,17 +741,21 @@ bool Item::IsBoundByEnchant() const
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
{
// Special case - accept vellum for armor/weapon requirements
@@ -652,14 +763,17 @@ bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const
||( spellInfo->EquippedItemClass==ITEM_CLASS_WEAPON && proto->IsWeaponVellum()))
if (spellmgr.IsSkillTypeSpell(spellInfo->Id, SKILL_ENCHANTING)) // only for enchanting spells
return true;
+
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
{
// Special case - accept weapon type for main and offhand requirements
@@ -670,71 +784,90 @@ bool Item::IsFitToSpellRequirements(SpellEntry const* spellInfo) const
else if ((spellInfo->EquippedItemInventoryTypeMask & (1 << proto->InventoryType)) == 0)
return false; // inventory type not present in mask
}
+
return true;
}
+
bool Item::IsTargetValidForItemUse(Unit* pUnitTarget)
{
ItemRequiredTargetMapBounds bounds = objmgr.GetItemRequiredTargetMapBounds(GetProto()->ItemId);
+
if (bounds.first == bounds.second)
return true;
+
if (!pUnitTarget)
return false;
+
for(ItemRequiredTargetMap::const_iterator itr = bounds.first; itr != bounds.second; ++itr)
if(itr->second.IsFitToRequirements(pUnitTarget))
return true;
+
return false;
}
+
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((GetEnchantmentId(slot) == id) && (GetEnchantmentDuration(slot) == duration) && (GetEnchantmentCharges(slot) == charges))
return;
+
SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET,id);
SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration);
SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges);
SetState(ITEM_CHANGED);
}
+
void Item::SetEnchantmentDuration(EnchantmentSlot slot, uint32 duration)
{
if(GetEnchantmentDuration(slot) == duration)
return;
+
SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET,duration);
SetState(ITEM_CHANGED);
}
+
void Item::SetEnchantmentCharges(EnchantmentSlot slot, uint32 charges)
{
if(GetEnchantmentCharges(slot) == charges)
return;
+
SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_CHARGES_OFFSET,charges);
SetState(ITEM_CHANGED);
}
+
void Item::ClearEnchantment(EnchantmentSlot slot)
{
if(!GetEnchantmentId(slot))
return;
+
for(uint8 x = 0; x < 3; ++x)
SetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + 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+MAX_GEM_SOCKETS; ++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)
{
@@ -746,10 +879,12 @@ bool Item::GemsFitSockets() const
GemColor = gemProperty->color;
}
}
+
fits &= (GemColor & SocketColor) ? true : false;
}
return fits;
}
+
uint8 Item::GetGemCountWithID(uint32 GemID) const
{
uint8 count = 0;
@@ -758,14 +893,17 @@ uint8 Item::GetGemCountWithID(uint32 GemID) const
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;
}
+
uint8 Item::GetGemCountWithLimitCategory(uint32 limitCategory) const
{
uint8 count = 0;
@@ -774,22 +912,27 @@ uint8 Item::GetGemCountWithLimitCategory(uint32 limitCategory) const
uint32 enchant_id = GetEnchantmentId(EnchantmentSlot(enchant_slot));
if(!enchant_id)
continue;
+
SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if(!enchantEntry)
continue;
+
ItemPrototype const* gemProto = ObjectMgr::GetItemPrototype(enchantEntry->GemID);
if(!gemProto)
continue;
+
if(gemProto->ItemLimitCategory==limitCategory)
++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.
@@ -797,21 +940,26 @@ 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->GetMaxStackSize())
count = pProto->GetMaxStackSize();
+
assert(count !=0 && "pProto->Stackable==0 but checked at loading already");
+
Item *pItem = NewItemOrBag( pProto );
if( pItem->Create(objmgr.GenerateLowGuid(HIGHGUID_ITEM), item, player) )
{
@@ -825,11 +973,13 @@ Item* Item::CreateItem( uint32 item, uint32 count, Player const* player )
assert(false);
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 ) );
@@ -837,17 +987,21 @@ Item* Item::CloneItem( uint32 count, Player const* player ) const
newItem->SetItemRandomProperties(GetItemRandomPropertyId());
return newItem;
}
+
bool Item::IsBindedNotWith( Player const* player ) const
{
// not binded item
if(!IsSoulBound())
return false;
+
// own item
if(GetOwnerGUID()== player->GetGUID())
return false;
+
// not BOA item case
if(!IsBoundAccountWide())
return true;
+
// online
if(Player* owner = objmgr.GetPlayer(GetOwnerGUID()))
{
@@ -859,12 +1013,15 @@ bool Item::IsBindedNotWith( Player const* player ) const
return objmgr.GetPlayerAccountIdByGUID(GetOwnerGUID()) != player->GetSession()->GetAccountId();
}
}
+
bool ItemRequiredTarget::IsFitToRequirements( Unit* pUnitTarget ) const
{
if(pUnitTarget->GetTypeId() != TYPEID_UNIT)
return false;
+
if(pUnitTarget->GetEntry() != m_uiTargetEntry)
return false;
+
switch(m_uiType)
{
case ITEM_TARGET_TYPE_CREATURE:
diff --git a/src/game/Item.h b/src/game/Item.h
index 1c4e1c490f6..b70134fbb63 100644
--- a/src/game/Item.h
+++ b/src/game/Item.h
@@ -17,22 +17,27 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_ITEM_H
#define TRINITYCORE_ITEM_H
+
#include "Common.h"
#include "Object.h"
#include "LootMgr.h"
#include "ItemPrototype.h"
+
struct SpellEntry;
class Bag;
class QueryResult;
class Unit;
+
struct ItemSetEffect
{
uint32 setid;
uint32 item_count;
SpellEntry const *spells[8];
};
+
enum InventoryChangeFailure
{
EQUIP_ERR_OK = 0,
@@ -120,6 +125,7 @@ enum InventoryChangeFailure
// no output = 83,
// crash client = 84,
};
+
enum BuyFailure
{
BUY_ERR_CANT_FIND_ITEM = 0,
@@ -132,6 +138,7 @@ enum BuyFailure
BUY_ERR_RANK_REQUIRE = 11,
BUY_ERR_REPUTATION_REQUIRE = 12
};
+
enum SellFailure
{
SELL_ERR_CANT_FIND_ITEM = 1,
@@ -141,6 +148,7 @@ enum SellFailure
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
{
@@ -152,6 +160,7 @@ enum EnchantmentSlot
BONUS_ENCHANTMENT_SLOT = 5,
PRISMATIC_ENCHANTMENT_SLOT = 6, // added at apply special permanent enchantment
MAX_INSPECTED_ENCHANTMENT_SLOT = 7,
+
PROP_ENCHANTMENT_SLOT_0 = 7, // used with RandomSuffix
PROP_ENCHANTMENT_SLOT_1 = 8, // used with RandomSuffix
PROP_ENCHANTMENT_SLOT_2 = 9, // used with RandomSuffix and RandomProperty
@@ -159,15 +168,20 @@ enum EnchantmentSlot
PROP_ENCHANTMENT_SLOT_4 = 11, // used with RandomProperty
MAX_ENCHANTMENT_SLOT = 12
};
+
#define MAX_VISIBLE_ITEM_OFFSET 2 // 2 fields per visible item (entry+enchantment)
+
#define MAX_GEM_SOCKETS MAX_ITEM_PROTO_SOCKETS// (BONUS_ENCHANTMENT_SLOT-SOCK_ENCHANTMENT_SLOT) and item proto size, equal value expected
+
enum EnchantmentOffset
{
ENCHANTMENT_ID_OFFSET = 0,
ENCHANTMENT_DURATION_OFFSET = 1,
ENCHANTMENT_CHARGES_OFFSET = 2 // now here not only charges, but something new in wotlk
};
+
#define MAX_ENCHANTMENT_OFFSET 3
+
enum EnchantmentSlotMask
{
ENCHANTMENT_CAN_SOULBOUND = 0x01,
@@ -175,6 +189,7 @@ enum EnchantmentSlotMask
ENCHANTMENT_UNK2 = 0x04,
ENCHANTMENT_UNK3 = 0x08
};
+
enum ItemUpdateState
{
ITEM_UNCHANGED = 0,
@@ -182,32 +197,43 @@ enum ItemUpdateState
ITEM_NEW = 2,
ITEM_REMOVED = 3
};
+
enum ItemRequiredTargetType
{
ITEM_TARGET_TYPE_CREATURE = 1,
ITEM_TARGET_TYPE_DEAD = 2
};
+
#define MAX_ITEM_REQ_TARGET_TYPE 2
+
struct ItemRequiredTarget
{
ItemRequiredTarget(ItemRequiredTargetType uiType, uint32 uiTargetEntry) : m_uiType(uiType), m_uiTargetEntry(uiTargetEntry) {}
ItemRequiredTargetType m_uiType;
uint32 m_uiTargetEntry;
+
// helpers
bool IsFitToRequirements(Unit* pUnitTarget) const;
};
+
bool ItemCanGoIntoBag(ItemPrototype const *proto, ItemPrototype const *pBagProto);
+
class TRINITY_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 IsBoundAccountWide() const { return HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_BOA); }
@@ -217,30 +243,37 @@ class TRINITY_DLL_SPEC Item : public Object
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(bool mail = false) const;
void SetInTrade(bool b = true) { mb_in_trade = b; }
bool IsInTrade() const { return mb_in_trade; }
+
bool IsFitToSpellRequirements(SpellEntry const* spellInfo) const;
bool IsTargetValidForItemUse(Unit* pUnitTarget);
bool IsLimitedToAnotherMapOrZone( uint32 cur_mapId, uint32 cur_zoneId) const;
bool GemsFitSockets() const;
+
uint32 GetCount() const { return GetUInt32Value (ITEM_FIELD_STACK_COUNT); }
void SetCount(uint32 value) { SetUInt32Value (ITEM_FIELD_STACK_COUNT, value); }
uint32 GetMaxStackCount() const { return GetProto()->GetMaxStackSize(); }
uint8 GetGemCountWithID(uint32 GemID) const;
uint8 GetGemCountWithLimitCategory(uint32 limitCategory) 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); }
@@ -254,13 +287,17 @@ class TRINITY_DLL_SPEC Item : public Object
uint32 GetEnchantmentId(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_ID_OFFSET);}
uint32 GetEnchantmentDuration(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + slot*MAX_ENCHANTMENT_OFFSET + ENCHANTMENT_DURATION_OFFSET);}
uint32 GetEnchantmentCharges(EnchantmentSlot slot) const { return GetUInt32Value(ITEM_FIELD_ENCHANTMENT_1_1 + 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);
@@ -272,12 +309,14 @@ class TRINITY_DLL_SPEC Item : public Object
{
uState = state;
}
+
bool hasQuest(uint32 quest_id) const { return GetProto()->StartQuest == quest_id; }
bool hasInvolvedQuest(uint32 /*quest_id*/) const { return false; }
bool IsPotion() const { return GetProto()->IsPotion(); }
bool IsWeaponVellum() const { return GetProto()->IsWeaponVellum(); }
bool IsArmorVellum() const { return GetProto()->IsArmorVellum(); }
bool IsConjuredConsumable() const { return GetProto()->IsConjuredConsumable(); }
+
private:
uint8 m_slot;
Bag *m_container;
diff --git a/src/game/ItemEnchantmentMgr.cpp b/src/game/ItemEnchantmentMgr.cpp
index 0f62d1d8d52..b40a398a782 100644
--- a/src/game/ItemEnchantmentMgr.cpp
+++ b/src/game/ItemEnchantmentMgr.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <stdlib.h>
#include <functional>
#include "ItemEnchantmentMgr.h"
@@ -27,41 +28,56 @@
#include <list>
#include <vector>
#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<EnchStoreItem> EnchStoreList;
typedef UNORDERED_MAP<uint32, EnchStoreList> EnchantmentStore;
+
static EnchantmentStore RandomItemEnch;
+
void LoadRandomEnchantmentsTable()
{
RandomItemEnch.clear(); // for reload case
+
EnchantmentStore::const_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 );
}
@@ -71,42 +87,56 @@ void LoadRandomEnchantmentsTable()
sLog.outErrorDb( ">> Loaded 0 Item Enchantment definitions. DB table `item_enchantment_template` is empty.");
}
}
+
uint32 GetItemEnchantMod(uint32 entry)
{
if (!entry) return 0;
+
EnchantmentStore::const_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::const_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::const_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)
{
diff --git a/src/game/ItemEnchantmentMgr.h b/src/game/ItemEnchantmentMgr.h
index 65c66855bf4..ce627515aee 100644
--- a/src/game/ItemEnchantmentMgr.h
+++ b/src/game/ItemEnchantmentMgr.h
@@ -17,9 +17,12 @@
* 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);
diff --git a/src/game/ItemHandler.cpp b/src/game/ItemHandler.cpp
index 434d298b00c..129766b35f0 100644
--- a/src/game/ItemHandler.cpp
+++ b/src/game/ItemHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -27,106 +28,138 @@
#include "Item.h"
#include "UpdateData.h"
#include "ObjectAccessor.h"
+
void WorldSession::HandleSplitItemOpcode( WorldPacket & recv_data )
{
//sLog.outDebug("WORLD: CMSG_SPLIT_ITEM");
uint8 srcbag, srcslot, dstbag, dstslot;
uint32 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
+
if(!_player->IsValidPos(srcbag,srcslot))
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
+
if(!_player->IsValidPos(dstbag,dstslot))
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
+
_player->SplitItem( src, dst, count );
}
+
void WorldSession::HandleSwapInvItemOpcode( WorldPacket & recv_data )
{
//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;
+
if(!_player->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot))
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
+
if(!_player->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot))
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
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 )
{
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 )
{
//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;
+
if(!_player->IsValidPos(srcbag,srcslot))
{
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
+
if(!_player->IsValidPos(dstbag,dstslot))
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
+
_player->SwapItem( src, dst );
}
+
void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
{
//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 )
@@ -134,9 +167,11 @@ void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
_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
{
@@ -148,12 +183,14 @@ void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
{
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 = 0;
@@ -179,16 +216,20 @@ void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
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);
@@ -196,16 +237,21 @@ void WorldSession::HandleAutoEquipItemOpcode( WorldPacket & recv_data )
_player->BankItem(sSrc, pDstItem, true);
else if( _player->IsEquipmentPos( src ) )
_player->EquipItem(eSrc, pDstItem, true);
+
_player->AutoUnequipOffhandIfNeed();
}
}
+
void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
{
//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))
{
@@ -216,12 +262,14 @@ void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
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;
@@ -230,18 +278,22 @@ void WorldSession::HandleDestroyItemOpcode( WorldPacket & recv_data )
else
_player->DestroyItem( bag, slot, true );
}
+
// Only _static_ data send in this packet !!!
void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
{
//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 )
{
@@ -298,6 +350,7 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
data << pProto->Damage[i].DamageMax;
data << pProto->Damage[i].DamageType;
}
+
// resistances (7)
data << pProto->Armor;
data << pProto->HolyRes;
@@ -306,9 +359,11 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
data << pProto->FrostRes;
data << pProto->ShadowRes;
data << pProto->ArcaneRes;
+
data << pProto->Delay;
data << pProto->AmmoType;
data << pProto->RangedModRange;
+
for(int s = 0; s < MAX_ITEM_PROTO_SPELLS; ++s)
{
// send DBC data for cooldowns in same way as it used in Spell::SendSpellCooldown
@@ -317,9 +372,11 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
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);
@@ -383,16 +440,21 @@ void WorldSession::HandleItemQuerySingleOpcode( WorldPacket & recv_data )
SendPacket( &data );
}
}
+
void WorldSession::HandleReadItem( WorldPacket & recv_data )
{
//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 )
{
@@ -411,23 +473,31 @@ void WorldSession::HandleReadItem( WorldPacket & recv_data )
else
_player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
}
+
void WorldSession::HandlePageQuerySkippedOpcode( WorldPacket & recv_data )
{
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 )
{
sLog.outDebug( "WORLD: Received CMSG_SELL_ITEM" );
uint64 vendorguid, itemguid;
uint32 count;
+
recv_data >> vendorguid >> itemguid >> count;
+
if(!itemguid)
return;
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid,UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
@@ -435,9 +505,11 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
_player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, itemguid, 0);
return;
}
+
// remove fake death
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
Item *pItem = _player->GetItemByGuid( itemguid );
if( pItem )
{
@@ -447,18 +519,21 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
_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)
{
@@ -473,6 +548,7 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
return;
}
}
+
ItemPrototype const *pProto = pItem->GetProto();
if( pProto )
{
@@ -487,11 +563,13 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
_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 );
@@ -503,6 +581,7 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
pItem->RemoveFromUpdateQueueOf(_player);
_player->AddItemToBuyBackSlot( pItem );
}
+
_player->ModifyMoney( pProto->SellPrice * count );
}
else
@@ -513,12 +592,15 @@ void WorldSession::HandleSellItemOpcode( WorldPacket & recv_data )
_player->SendSellError( SELL_ERR_CANT_FIND_ITEM, pCreature, itemguid, 0);
return;
}
+
void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
{
sLog.outDebug( "WORLD: Received CMSG_BUYBACK_ITEM" );
uint64 vendorguid;
uint32 slot;
+
recv_data >> vendorguid >> slot;
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(vendorguid,UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
@@ -526,9 +608,11 @@ void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
_player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
return;
}
+
// remove fake death
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
Item *pItem = _player->GetItemFromBuyBackSlot( slot );
if( pItem )
{
@@ -538,6 +622,7 @@ void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
_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 )
@@ -554,14 +639,18 @@ void WorldSession::HandleBuybackItem(WorldPacket & recv_data)
else
_player->SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, 0, 0);
}
+
void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM_IN_SLOT" );
uint64 vendorguid, bagguid;
uint32 item, slot, count;
uint8 bagslot;
+
recv_data >> vendorguid >> item >> slot >> bagguid >> bagslot >> count;
+
uint8 bag = NULL_BAG; // init for case invalid bagGUID
+
// find bag slot by bag guid
if (bagguid == _player->GetGUID())
bag = INVENTORY_SLOT_BAG_0;
@@ -579,32 +668,44 @@ void WorldSession::HandleBuyItemInSlotOpcode( WorldPacket & recv_data )
}
}
}
+
// bag not found, cheating?
if (bag == NULL_BAG)
return;
+
GetPlayer()->BuyItemFromVendor(vendorguid,item,count,bag,bagslot);
}
+
void WorldSession::HandleBuyItemOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_BUY_ITEM" );
uint64 vendorguid;
uint32 item, slot, count;
uint8 unk1;
+
recv_data >> vendorguid >> item >> slot >> count >> unk1;
+
GetPlayer()->BuyItemFromVendor(vendorguid,item,count,NULL_BAG,NULL_SLOT);
}
+
void WorldSession::HandleListInventoryOpcode( WorldPacket & recv_data )
{
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 = GetPlayer()->GetNPCIfCanInteractWith(vendorguid,UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
@@ -612,23 +713,30 @@ void WorldSession::SendListInventory( uint64 vendorguid )
_player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
return;
}
+
// remove fake death
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
// Stop the npc if moving
pCreature->StopMoving();
+
VendorItemData const* vItems = pCreature->GetVendorItems();
if(!vItems)
{
_player->SendSellError( SELL_ERR_CANT_FIND_VENDOR, NULL, 0, 0);
return;
}
+
uint8 numitems = vItems->GetItemCount();
uint8 count = 0;
+
WorldPacket data( SMSG_LIST_INVENTORY, (8+1+numitems*8*4) );
data << uint64(vendorguid);
data << uint8(numitems);
+
float discountMod = _player->GetReputationPriceDiscount(pCreature);
+
for(int i = 0; i < numitems; ++i )
{
if(VendorItem const* crItem = vItems->GetItem(i))
@@ -637,9 +745,12 @@ void WorldSession::SendListInventory( uint64 vendorguid )
{
if((pProto->AllowableClass & _player->getClassMask()) == 0 && pProto->Bonding == BIND_WHEN_PICKED_UP && !_player->isGameMaster())
continue;
+
++count;
+
// reputation discount
uint32 price = uint32(floor(pProto->BuyPrice * discountMod));
+
data << uint32(count);
data << uint32(crItem->item);
data << uint32(pProto->DisplayInfoID);
@@ -651,26 +762,34 @@ void WorldSession::SendListInventory( uint64 vendorguid )
}
}
}
+
if ( count == 0 || data.size() != 8 + 1 + size_t(count) * 8 * 4 )
return;
+
data.put<uint8>(8, count);
SendPacket( &data );
}
+
void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
{
//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;
+
if(!_player->IsValidPos(dstbag,NULL_SLOT))
{
_player->SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return;
}
+
uint16 src = pItem->GetPos();
+
// check unequip potability for equipped items and bank bags
if(_player->IsEquipmentPos ( src ) || _player->IsBagPos ( src ))
{
@@ -681,6 +800,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
return;
}
}
+
ItemPosCountVec dest;
uint8 msg = _player->CanStoreItem( dstbag, NULL_SLOT, dest, pItem, false );
if( msg != EQUIP_ERR_OK )
@@ -688,6 +808,7 @@ void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
_player->SendEquipError( msg, pItem, NULL );
return;
}
+
// no-op: placed in same slot
if(dest.size()==1 && dest[0].pos==src)
{
@@ -695,14 +816,18 @@ void WorldSession::HandleAutoStoreBagItemOpcode( WorldPacket & recv_data )
_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");
+
uint64 guid;
recvPacket >> guid;
+
// cheating protection
/* not critical if "cheated", and check skip allow by slots in bank windows open by .bank command.
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_BANKER);
@@ -712,29 +837,42 @@ void WorldSession::HandleBuyBankSlotOpcode(WorldPacket& recvPacket)
return;
}
*/
+
uint32 slot = _player->GetBankBagSlotCount();
+
// 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->SetBankBagSlotCount(slot);
_player->ModifyMoney(-int32(price));
+
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BUY_BANK_SLOT);
}
+
void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
{
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 )
@@ -742,18 +880,23 @@ void WorldSession::HandleAutoBankItemOpcode(WorldPacket& recvPacket)
_player->SendEquipError( msg, pItem, NULL );
return;
}
+
_player->RemoveItem(srcbag, srcslot, true);
_player->BankItem( dest, pItem, true );
}
+
void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
{
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;
@@ -763,6 +906,7 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
_player->SendEquipError( msg, pItem, NULL );
return;
}
+
_player->RemoveItem(srcbag, srcslot, true);
_player->StoreItem( dest, pItem, true );
}
@@ -775,10 +919,12 @@ void WorldSession::HandleAutoStoreBankItemOpcode(WorldPacket& recvPacket)
_player->SendEquipError( msg, pItem, NULL );
return;
}
+
_player->RemoveItem(srcbag, srcslot, true);
_player->BankItem( dest, pItem, true );
}
}
+
void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
{
if(!GetPlayer()->isAlive())
@@ -786,14 +932,18 @@ void WorldSession::HandleSetAmmoOpcode(WorldPacket & recv_data)
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
@@ -804,6 +954,7 @@ void WorldSession::SendEnchantmentLog(uint64 Target, uint64 Caster,uint32 ItemID
data << uint8(0);
SendPacket(&data);
}
+
void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,uint32 slot,uint32 Duration)
{
// last check 2.0.10
@@ -814,17 +965,20 @@ void WorldSession::SendItemEnchantTimeUpdate(uint64 Playerguid, uint64 Itemguid,
data << uint64(Playerguid);
SendPacket(&data);
}
+
void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
{
uint32 itemid;
recv_data >> itemid;
recv_data.read_skip<uint64>(); // guid
+
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)
{
@@ -852,70 +1006,87 @@ void WorldSession::HandleItemNameQueryOpcode(WorldPacket & recv_data)
sLog.outError("WORLD: CMSG_ITEM_NAME_QUERY for item %u failed (unknown item, not listed in Item.dbc)", itemid);
}
}
+
void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
{
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->SetEntry(gift->GetEntry());
+
switch (item->GetEntry())
{
case 5042: item->SetEntry( 5043); break;
@@ -928,6 +1099,7 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
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
@@ -935,59 +1107,77 @@ void WorldSession::HandleWrapItemOpcode(WorldPacket& recv_data)
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");
+
uint64 item_guid;
uint64 gem_guids[MAX_GEM_SOCKETS];
+
recv_data >> item_guid;
if(!item_guid)
return;
+
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
recv_data >> gem_guids[i];
+
//cheat -> tried to socket same gem multiple times
if ((gem_guids[0] && (gem_guids[0] == gem_guids[1] || gem_guids[0] == gem_guids[2])) ||
(gem_guids[1] && (gem_guids[1] == gem_guids[2])))
return;
+
Item *itemTarget = _player->GetItemByGuid(item_guid);
if(!itemTarget) //missing item to socket
return;
+
ItemPrototype const* itemProto = itemTarget->GetProto();
if(!itemProto)
return;
+
//this slot is excepted when applying / removing meta gem bonus
uint8 slot = itemTarget->IsEquipped() ? itemTarget->GetSlot() : uint8(NULL_SLOT);
+
Item *Gems[MAX_GEM_SOCKETS];
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
Gems[i] = gem_guids[i] ? _player->GetItemByGuid(gem_guids[i]) : NULL;
+
GemPropertiesEntry const *GemProps[MAX_GEM_SOCKETS];
for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //get geminfo from dbc storage
GemProps[i] = (Gems[i]) ? sGemPropertiesStore.LookupEntry(Gems[i]->GetProto()->GemProperties) : NULL;
+
for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //check for hack maybe
{
if (!GemProps[i])
continue;
+
// tried to put gem in socket where no socket exists (take care about prismatic sockets)
if (!itemProto->Socket[i].Color)
{
// no prismatic socket
if(!itemTarget->GetEnchantmentId(PRISMATIC_ENCHANTMENT_SLOT))
return;
+
// not first not-colored (not normaly used) socket
if(i!=0 && !itemProto->Socket[i-1].Color && (i+1 >= MAX_GEM_SOCKETS || itemProto->Socket[i+1].Color))
return;
+
// ok, this is first not colored socket for item with prismatic socket
}
+
// tried to put normal gem in meta socket
if (itemProto->Socket[i].Color == SOCKET_COLOR_META && GemProps[i]->color != SOCKET_COLOR_META)
return;
+
// tried to put meta gem in normal socket
if (itemProto->Socket[i].Color != SOCKET_COLOR_META && GemProps[i]->color == SOCKET_COLOR_META)
return;
}
+
uint32 GemEnchants[MAX_GEM_SOCKETS];
uint32 OldEnchants[MAX_GEM_SOCKETS];
for(int i = 0; i < MAX_GEM_SOCKETS; ++i) //get new and old enchantments
@@ -995,13 +1185,16 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
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 < MAX_GEM_SOCKETS; ++i)
{
if(!Gems[i])
continue;
+
// continue check for case when attempt add 2 similar unique equipped gems in one item.
ItemPrototype const* iGemProto = Gems[i]->GetProto();
+
// unique item (for new and already placed bit removed enchantments
if (iGemProto->Flags & ITEM_FLAGS_UNIQUE_EQUIPPED)
{
@@ -1009,6 +1202,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
{
if(i==j) // skip self
continue;
+
if (Gems[j])
{
if (iGemProto->ItemId == Gems[j]->GetEntry())
@@ -1028,8 +1222,10 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
}
}
}
+
}
}
+
// unique limit type item
int32 limit_newcount = 0;
if (iGemProto->ItemLimitCategory)
@@ -1048,6 +1244,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
if (iGemProto->ItemLimitCategory == jProto->ItemLimitCategory)
--limit_newcount;
}
+
// new gem
if (iGemProto->ItemLimitCategory == Gems[j]->GetProto()->ItemLimitCategory)
++limit_newcount;
@@ -1061,6 +1258,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
++limit_newcount;
}
}
+
if(limit_newcount > 0 && uint32(limit_newcount) > limitEntry->maxCount)
{
_player->SendEquipError( EQUIP_ERR_ITEM_UNIQUE_EQUIPPABLE_SOCKETED, itemTarget, NULL );
@@ -1068,6 +1266,7 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
}
}
}
+
// for equipped item check all equipment for duplicate equipped gems
if(itemTarget->IsEquipped())
{
@@ -1078,12 +1277,16 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
}
}
}
+
bool 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+MAX_GEM_SOCKETS; ++enchant_slot)
_player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),false);
+
for(int i = 0; i < MAX_GEM_SOCKETS; ++i)
{
if(GemEnchants[i])
@@ -1093,8 +1296,10 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
_player->DestroyItem(guidItem->GetBagSlot(), guidItem->GetSlot(), true );
}
}
+
for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+MAX_GEM_SOCKETS; ++enchant_slot)
_player->ApplyEnchantment(itemTarget,EnchantmentSlot(enchant_slot),true);
+
bool SocketBonusToBeActivated = itemTarget->GemsFitSockets();//current socketbonus state
if(SocketBonusActivated ^ SocketBonusToBeActivated) //if there was a change...
{
@@ -1103,21 +1308,30 @@ void WorldSession::HandleSocketOpcode(WorldPacket& recv_data)
_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::HandleCancelTempEnchantmentOpcode(WorldPacket& recv_data)
{
sLog.outDebug("WORLD: CMSG_CANCEL_TEMP_ENCHANTMENT");
+
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
index 613d6aeaf5b..a6daf47cb48 100644
--- a/src/game/ItemPrototype.h
+++ b/src/game/ItemPrototype.h
@@ -17,9 +17,12 @@
* 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,
@@ -64,7 +67,9 @@ enum ItemModType
ITEM_MOD_ARMOR_PENETRATION_RATING = 44,
ITEM_MOD_SPELL_POWER = 45
};
+
#define MAX_ITEM_MOD 46
+
enum ItemSpelltriggerType
{
ITEM_SPELLTRIGGER_ON_USE = 0, // use after equip cooldown
@@ -80,7 +85,9 @@ enum ItemSpelltriggerType
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,
@@ -90,7 +97,9 @@ enum ItemBondingType
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
{
@@ -111,6 +120,7 @@ enum ITEM_FLAGS
ITEM_FLAGS_TRIGGERED_CAST = 0x10000000, // used by enchanting scrolls made with vellum
ITEM_FLAGS_MILLABLE = 0x20000000
};
+
enum BAG_FAMILY_MASK
{
BAG_FAMILY_MASK_NONE = 0x00000000,
@@ -130,6 +140,7 @@ enum BAG_FAMILY_MASK
BAG_FAMILY_MASK_CURRENCY_TOKENS = 0x00002000,
BAG_FAMILY_MASK_QUEST_ITEMS = 0x00004000
};
+
enum SocketColor
{
SOCKET_COLOR_META = 1,
@@ -137,7 +148,9 @@ enum SocketColor
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,
@@ -170,7 +183,9 @@ enum InventoryType
INVTYPE_QUIVER = 27,
INVTYPE_RELIC = 28
};
+
#define MAX_INVTYPE 29
+
enum ItemClass
{
ITEM_CLASS_CONSUMABLE = 0,
@@ -191,7 +206,9 @@ enum ItemClass
ITEM_CLASS_MISC = 15,
ITEM_CLASS_GLYPH = 16
};
+
#define MAX_ITEM_CLASS 17
+
enum ItemSubclassConsumable
{
ITEM_SUBCLASS_CONSUMABLE = 0,
@@ -204,7 +221,9 @@ enum ItemSubclassConsumable
ITEM_SUBCLASS_BANDAGE = 7,
ITEM_SUBCLASS_CONSUMABLE_OTHER = 8
};
+
#define MAX_ITEM_SUBCLASS_CONSUMABLE 9
+
enum ItemSubclassContainer
{
ITEM_SUBCLASS_CONTAINER = 0,
@@ -217,7 +236,9 @@ enum ItemSubclassContainer
ITEM_SUBCLASS_LEATHERWORKING_CONTAINER = 7,
ITEM_SUBCLASS_INSCRIPTION_CONTAINER = 8
};
+
#define MAX_ITEM_SUBCLASS_CONTAINER 9
+
enum ItemSubclassWeapon
{
ITEM_SUBCLASS_WEAPON_AXE = 0,
@@ -242,10 +263,13 @@ enum ItemSubclassWeapon
ITEM_SUBCLASS_WEAPON_WAND = 19,
ITEM_SUBCLASS_WEAPON_FISHING_POLE = 20
};
+
#define ITEM_SUBCLASS_MASK_WEAPON_RANGED (\
(1 << ITEM_SUBCLASS_WEAPON_BOW) | (1 << ITEM_SUBCLASS_WEAPON_GUN) |\
(1 << ITEM_SUBCLASS_WEAPON_CROSSBOW) | (1 << ITEM_SUBCLASS_WEAPON_THROWN))
+
#define MAX_ITEM_SUBCLASS_WEAPON 21
+
enum ItemSubclassGem
{
ITEM_SUBCLASS_GEM_RED = 0,
@@ -258,7 +282,9 @@ enum ItemSubclassGem
ITEM_SUBCLASS_GEM_SIMPLE = 7,
ITEM_SUBCLASS_GEM_PRISMATIC = 8
};
+
#define MAX_ITEM_SUBCLASS_GEM 9
+
enum ItemSubclassArmor
{
ITEM_SUBCLASS_ARMOR_MISC = 0,
@@ -273,12 +299,16 @@ enum ItemSubclassArmor
ITEM_SUBCLASS_ARMOR_TOTEM = 9,
ITEM_SUBCLASS_ARMOR_SIGIL = 10
};
+
#define MAX_ITEM_SUBCLASS_ARMOR 11
+
enum ItemSubclassReagent
{
ITEM_SUBCLASS_REAGENT = 0
};
+
#define MAX_ITEM_SUBCLASS_REAGENT 1
+
enum ItemSubclassProjectile
{
ITEM_SUBCLASS_WAND = 0, // ABS
@@ -287,7 +317,9 @@ enum ItemSubclassProjectile
ITEM_SUBCLASS_BULLET = 3,
ITEM_SUBCLASS_THROWN = 4 // ABS
};
+
#define MAX_ITEM_SUBCLASS_PROJECTILE 5
+
enum ItemSubclassTradeGoods
{
ITEM_SUBCLASS_TRADE_GOODS = 0,
@@ -307,12 +339,16 @@ enum ItemSubclassTradeGoods
ITEM_SUBCLASS_ARMOR_ENCHANTMENT = 14,
ITEM_SUBCLASS_WEAPON_ENCHANTMENT = 15
};
+
#define MAX_ITEM_SUBCLASS_TRADE_GOODS 16
+
enum ItemSubclassGeneric
{
ITEM_SUBCLASS_GENERIC = 0
};
+
#define MAX_ITEM_SUBCLASS_GENERIC 1
+
enum ItemSubclassRecipe
{
ITEM_SUBCLASS_BOOK = 0,
@@ -327,12 +363,16 @@ enum ItemSubclassRecipe
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
@@ -340,23 +380,31 @@ enum ItemSubclassQuiver
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,
@@ -366,7 +414,9 @@ enum ItemSubclassJunk
ITEM_SUBCLASS_JUNK_OTHER = 4,
ITEM_SUBCLASS_JUNK_MOUNT = 5
};
+
#define MAX_ITEM_SUBCLASS_JUNK 6
+
enum ItemSubclassGlyph
{
ITEM_SUBCLASS_GLYPH_WARRIOR = 1,
@@ -380,7 +430,9 @@ enum ItemSubclassGlyph
ITEM_SUBCLASS_GLYPH_WARLOCK = 9,
ITEM_SUBCLASS_GLYPH_DRUID = 11
};
+
#define MAX_ITEM_SUBCLASS_GLYPH 12
+
const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] =
{
MAX_ITEM_SUBCLASS_CONSUMABLE,
@@ -401,6 +453,7 @@ const uint32 MaxItemSubclassValues[MAX_ITEM_CLASS] =
MAX_ITEM_SUBCLASS_JUNK,
MAX_ITEM_SUBCLASS_GLYPH
};
+
inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemSubClass)
{
switch(ItemClass)
@@ -410,18 +463,21 @@ inline uint8 ItemSubClassToDurabilityMultiplierId(uint32 ItemClass, uint32 ItemS
}
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;
@@ -437,15 +493,18 @@ struct _Spell
uint32 SpellCategory; // id from SpellCategory.dbc
int32 SpellCategoryCooldown;
};
+
struct _Socket
{
uint32 Color;
uint32 Content;
};
+
#define MAX_ITEM_PROTO_DAMAGES 2 // changed in 3.1.0
#define MAX_ITEM_PROTO_SOCKETS 3
#define MAX_ITEM_PROTO_SPELLS 5
#define MAX_ITEM_PROTO_STATS 10
+
struct ItemPrototype
{
uint32 ItemId;
@@ -521,6 +580,7 @@ struct ItemPrototype
uint32 FoodType;
uint32 MinMoneyLoot;
uint32 MaxMoneyLoot;
+
// helpers
bool CanChangeEquipStateInCombat() const
{
@@ -531,18 +591,22 @@ struct ItemPrototype
case INVTYPE_HOLDABLE:
return true;
}
+
switch(Class)
{
case ITEM_CLASS_WEAPON:
case ITEM_CLASS_PROJECTILE:
return true;
}
+
return false;
}
+
uint32 GetMaxStackSize() const
{
return (Stackable == 2147483647 || Stackable <= 0) ? uint32(0x7FFFFFFF-1) : uint32(Stackable);
}
+
float getDPS() const
{
if (Delay == 0)
@@ -552,6 +616,7 @@ struct ItemPrototype
temp+=Damage[i].DamageMin + Damage[i].DamageMax;
return temp*500/Delay;
}
+
int32 getFeralBonus(int32 extraDPS = 0) const
{
// 0x02A5F3 - is mask for Melee weapon from ItemSubClassMask.dbc
@@ -564,16 +629,19 @@ struct ItemPrototype
}
return 0;
}
+
bool IsPotion() const { return Class==ITEM_CLASS_CONSUMABLE && SubClass==ITEM_SUBCLASS_POTION; }
bool IsWeaponVellum() const { return Class==ITEM_CLASS_TRADE_GOODS && SubClass==ITEM_SUBCLASS_WEAPON_ENCHANTMENT; }
bool IsArmorVellum() const { return Class==ITEM_CLASS_TRADE_GOODS && SubClass==ITEM_SUBCLASS_ARMOR_ENCHANTMENT; }
bool IsConjuredConsumable() const { return Class == ITEM_CLASS_CONSUMABLE && (Flags & ITEM_FLAGS_CONJURED); }
};
+
struct ItemLocale
{
std::vector<std::string> Name;
std::vector<std::string> 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()
diff --git a/src/game/LFGHandler.cpp b/src/game/LFGHandler.cpp
index a182e879f93..ae0baf1b15b 100644
--- a/src/game/LFGHandler.cpp
+++ b/src/game/LFGHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,28 +25,35 @@
#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<Player>::MapType const& players = ObjectAccessor::Instance().GetPlayers();
for(HashMapHolder<Player>::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 players not in world
if(!plr->IsInWorld())
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())
{
@@ -55,8 +63,10 @@ static void AttemptJoin(Player* _player)
delete group;
continue;
}
+
objmgr.AddGroup(group);
}
+
// stop at success join
if(plr->GetGroup()->AddMember(_player->GetGUID(), _player->GetName()))
{
@@ -72,28 +82,36 @@ static void AttemptJoin(Player* _player)
}
}
}
+
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<Player>::MapType const& players = ObjectAccessor::Instance().GetPlayers();
for(HashMapHolder<Player>::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;
+
if(!plr->IsInWorld())
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())
{
@@ -103,69 +121,90 @@ static void AttemptAddMore(Player* _player)
delete group;
return; // can'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::HandleLfgSetAutoJoinOpcode( WorldPacket & /*recv_data*/ )
{
sLog.outDebug("CMSG_LFG_SET_AUTOJOIN");
LookingForGroup_auto_join = true;
+
if(!_player) // needed because STATUS_AUTHED
return;
+
AttemptJoin(_player);
}
+
void WorldSession::HandleLfgClearAutoJoinOpcode( WorldPacket & /*recv_data*/ )
{
sLog.outDebug("CMSG_LFG_CLEAR_AUTOJOIN");
LookingForGroup_auto_join = false;
}
+
void WorldSession::HandleLfmSetAutoFillOpcode( WorldPacket & /*recv_data*/ )
{
sLog.outDebug("CMSG_LFM_SET_AUTOFILL");
LookingForGroup_auto_add = true;
+
if(!_player) // needed because STATUS_AUTHED
return;
+
AttemptAddMore(_player);
}
+
void WorldSession::HandleLfmClearAutoFillOpcode( WorldPacket & /*recv_data*/ )
{
sLog.outDebug("CMSG_LFM_CLEAR_AUTOFILL");
LookingForGroup_auto_add = false;
}
+
void WorldSession::HandleLfgClearOpcode( WorldPacket & /*recv_data */ )
{
// empty packet
sLog.outDebug("CMSG_CLEAR_LOOKING_FOR_GROUP");
+
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();
+
SendLfgUpdate(0, 0, 0);
}
+
void WorldSession::HandleLfmClearOpcode( WorldPacket & /*recv_data */)
{
// empty packet
sLog.outDebug("CMSG_CLEAR_LOOKING_FOR_MORE");
+
_player->m_lookingForGroup.more.Clear();
}
+
void WorldSession::HandleSetLfmOpcode( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_SET_LOOKING_FOR_MORE");
@@ -173,44 +212,60 @@ void WorldSession::HandleSetLfmOpcode( WorldPacket & recv_data )
uint32 temp, entry, type;
uint8 unk1;
uint8 unk2[3];
+
recv_data >> temp >> unk1 >> unk2[0] >> unk2[1] >> unk2[2];
+
entry = ( temp & 0x00FFFFFF);
type = ( (temp >> 24) & 0x000000FF);
+
_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::HandleSetLfgCommentOpcode( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_SET_LFG_COMMENT");
//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)
{
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);
SendLfgUpdate(0, 1, 0);
}
+
void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type)
{
uint32 number = 0;
+
WorldPacket data(MSG_LOOKING_FOR_GROUP);
data << uint32(type); // type
data << uint32(entry); // entry from LFGDungeons.dbc
+
data << uint8(0);
/*if(uint8)
{
@@ -220,6 +275,7 @@ void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type)
uint64; // player guid
}
}*/
+
data << uint32(0); // count2
data << uint32(0);
/*for(count2)
@@ -242,31 +298,42 @@ void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type)
}
}
}*/
+
size_t count3_pos = data.wpos();
data << uint32(0); // count3
data << uint32(0); // unk
+
//TODO: Guard Player map
HashMapHolder<Player>::MapType const& players = ObjectAccessor::Instance().GetPlayers();
for(HashMapHolder<Player>::MapType::const_iterator iter = players.begin(); iter != players.end(); ++iter)
{
Player *plr = iter->second;
+
if(!plr || plr->GetTeam() != _player->GetTeam())
continue;
+
if(!plr->IsInWorld())
continue;
+
if(!plr->m_lookingForGroup.HaveInSlot(entry, type))
continue;
+
++number;
+
data << uint64(plr->GetGUID()); // guid
+
uint32 flags = 0x1FF;
data << uint32(flags); // flags
+
if(flags & 0x1)
{
data << uint8(plr->getLevel());
data << uint8(plr->getClass());
data << uint8(plr->getRace());
+
for(int i = 0; i < 3; ++i)
data << uint8(0); // spent talents count in specific tab
+
data << uint32(0); // resistances1
data << uint32(0); // spd/heal
data << uint32(0); // spd/heal
@@ -288,20 +355,28 @@ void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type)
data << uint32(0); // combat_rating20
data << uint32(0); // unk
}
+
if(flags & 0x2)
data << plr->m_lookingForGroup.comment; // comment
+
if(flags & 0x4)
data << uint8(0); // unk
+
if(flags & 0x8)
data << uint64(0); // guid from count2 block, not player guid
+
if(flags & 0x10)
data << uint8(0); // unk
+
if(flags & 0x20)
data << uint8(plr->m_lookingForGroup.roles); // roles
+
if(flags & 0x40)
data << uint32(plr->GetZoneId()); // areaid
+
if(flags & 0x100)
data << uint8(0); // LFG/LFM flag?
+
if(flags & 0x80)
{
for(uint8 j = 0; j < MAX_LOOKING_FOR_GROUP_SLOT; ++j)
@@ -310,35 +385,48 @@ void WorldSession::SendLfgResult(uint32 type, uint32 entry, uint8 lfg_type)
}
}
}
+
data.put<uint32>(count3_pos, number); // fill count placeholder
+
SendPacket(&data);
}
+
void WorldSession::HandleSetLfgOpcode( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_SET_LOOKING_FOR_GROUP");
recv_data.hexlike();
uint32 slot, temp, entry, type;
uint8 roles, unk1;
+
recv_data >> slot >> temp >> roles >> unk1;
+
entry = ( temp & 0x00FFFFFF);
type = ( (temp >> 24) & 0x000000FF);
+
if(slot >= MAX_LOOKING_FOR_GROUP_SLOT)
return;
+
_player->m_lookingForGroup.slots[slot].Set(entry, type);
_player->m_lookingForGroup.roles = roles;
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);
SendLfgUpdate(0, 1, 0);
}
+
void WorldSession::HandleLfgSetRoles(WorldPacket &recv_data)
{
sLog.outDebug("CMSG_LFG_SET_ROLES");
+
uint8 roles;
recv_data >> roles;
+
_player->m_lookingForGroup.roles = roles;
}
+
void WorldSession::SendLfgUpdate(uint8 unk1, uint8 unk2, uint8 unk3)
{
WorldPacket data(SMSG_LFG_UPDATE, 3);
diff --git a/src/game/Language.h b/src/game/Language.h
index dd16debdaee..e38d58f109e 100644
--- a/src/game/Language.h
+++ b/src/game/Language.h
@@ -17,13 +17,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef __TRINITY_LANGUAGE_H
#define __TRINITY_LANGUAGE_H
+
enum TrinityStrings
{
// for chat commands
LANG_SELECT_CHAR_OR_CREATURE = 1,
LANG_SELECT_CREATURE = 2,
+
// level 0 chat
LANG_SYSTEMMESSAGE = 3,
LANG_EVENTMESSAGE = 4,
@@ -85,6 +88,7 @@ enum TrinityStrings
LANG_CONNECTED_PLAYERS = 60,
LANG_ACCOUNT_ADDON = 61,
// Room for more level 0 62-99 not used
+
// level 1 chat
LANG_GLOBAL_NOTIFY = 100,
LANG_MAP_POSITION = 101,
@@ -104,12 +108,14 @@ enum TrinityStrings
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,
@@ -123,6 +129,7 @@ enum TrinityStrings
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,
@@ -133,11 +140,13 @@ enum TrinityStrings
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,
@@ -146,7 +155,9 @@ enum TrinityStrings
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,
@@ -155,6 +166,7 @@ enum TrinityStrings
LANG_COMMAND_TELE_NOLOCATION = 166,
// 167 // not used
LANG_COMMAND_TELE_LOCATION = 168,
+
LANG_MAIL_SENT = 169,
LANG_SOUND_NOT_EXIST = 170,
LANG_CANT_TELEPORT_SELF = 171,
@@ -163,6 +175,7 @@ enum TrinityStrings
LANG_YOURS_RUNIC_POWER_CHANGED = 174,
LANG_LIQUID_STATUS = 175,
// Room for more level 1 176-199 not used
+
// level 2 chat
LANG_NO_SELECTION = 200,
LANG_OBJECT_GUID = 201,
@@ -170,20 +183,24 @@ enum TrinityStrings
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,
@@ -217,15 +234,19 @@ enum TrinityStrings
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,
@@ -253,15 +274,18 @@ enum TrinityStrings
LANG_COMMAND_WHISPEROFF = 286,
LANG_COMMAND_CREATGUIDNOTFOUND = 287,
// TICKET STRINGS NEED REWRITE // 288-296 FREE
+
// END
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,
@@ -307,6 +331,7 @@ enum TrinityStrings
LANG_COMMAND_GOTAXINODENOTFOUND = 347,
LANG_GAMEOBJECT_HAVE_INVALID_DATA = 348,
// Room for more level 2 349-399 not used
+
// level 3 chat
LANG_SCRIPTS_RELOADED = 400,
LANG_YOU_CHANGE_SECURITY = 401,
@@ -316,12 +341,16 @@ enum TrinityStrings
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_ACCOUNT_NOT_EXIST = 413,
+
LANG_BANINFO_NOCHARACTER = 414,
LANG_BANINFO_NOIP = 415,
LANG_BANINFO_NOACCOUNTBAN = 416,
@@ -332,11 +361,13 @@ enum TrinityStrings
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,
@@ -379,21 +410,28 @@ enum TrinityStrings
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,
@@ -402,6 +440,7 @@ enum TrinityStrings
LANG_FORGET_SPELL = 491,
LANG_REMOVEALL_COOLDOWN = 492,
LANG_REMOVE_COOLDOWN = 493,
+
LANG_ADDITEM = 494, //log
LANG_ADDITEMSET = 495, //log
LANG_REMOVEITEM = 496,
@@ -411,7 +450,9 @@ enum TrinityStrings
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,
@@ -419,6 +460,7 @@ enum TrinityStrings
LANG_ITEMLIST_SLOT = 508,
LANG_ITEMLIST_MAIL = 509,
LANG_ITEMLIST_AUCTION = 510,
+
LANG_WRONG_LINK_TYPE = 511,
LANG_ITEM_LIST_CHAT = 512,
LANG_QUEST_LIST_CHAT = 513,
@@ -430,10 +472,13 @@ enum TrinityStrings
LANG_TELE_LIST = 519,
LANG_SPELL_LIST = 520,
LANG_SKILL_LIST_CHAT = 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,
@@ -447,6 +492,7 @@ enum TrinityStrings
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,
@@ -456,13 +502,16 @@ enum TrinityStrings
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,
@@ -470,6 +519,7 @@ enum TrinityStrings
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,
@@ -485,21 +535,26 @@ enum TrinityStrings
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_CHAT = 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,
LANG_BANLIST_ACCOUNTS = 593,
LANG_BANLIST_ACCOUNTS_HEADER = 594,
@@ -509,13 +564,16 @@ enum TrinityStrings
LANG_GMLIST_HEADER = 598,
LANG_GMLIST_EMPTY = 599,
// End Level 3 list, continued at 1100
+
// Battleground
LANG_BG_A_WINS = 600,
LANG_BG_H_WINS = 601,
+
LANG_BG_WS_START_TWO_MINUTES = 753,
LANG_BG_WS_START_ONE_MINUTE = 602,
LANG_BG_WS_START_HALF_MINUTE = 603,
LANG_BG_WS_HAS_BEGUN = 604,
+
LANG_BG_WS_CAPTURED_HF = 605,
LANG_BG_WS_CAPTURED_AF = 606,
LANG_BG_WS_DROPPED_HF = 607,
@@ -527,10 +585,12 @@ enum TrinityStrings
LANG_BG_WS_F_PLACED = 613,
LANG_BG_WS_ALLIANCE_FLAG_RESPAWNED = 614,
LANG_BG_WS_HORDE_FLAG_RESPAWNED = 615,
+
LANG_BG_EY_START_TWO_MINUTES = 755,
LANG_BG_EY_START_ONE_MINUTE = 636,
LANG_BG_EY_START_HALF_MINUTE = 637,
LANG_BG_EY_HAS_BEGUN = 638,
+
LANG_BG_AB_ALLY = 650,
LANG_BG_AB_HORDE = 651,
LANG_BG_AB_NODE_STABLES = 652,
@@ -542,6 +602,7 @@ enum TrinityStrings
LANG_BG_AB_NODE_DEFENDED = 658,
LANG_BG_AB_NODE_ASSAULTED = 659,
LANG_BG_AB_NODE_CLAIMED = 660,
+
LANG_BG_AB_START_TWO_MINUTES = 754,
LANG_BG_AB_START_ONE_MINUTE = 661,
LANG_BG_AB_START_HALF_MINUTE = 662,
@@ -549,6 +610,7 @@ enum TrinityStrings
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,
@@ -570,17 +632,20 @@ enum TrinityStrings
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_HAS_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,
LANG_YOUR_ARENA_LEVEL_REQ_ERROR = 713,
@@ -590,6 +655,7 @@ enum TrinityStrings
LANG_BG_STARTED_ANNOUNCE_WORLD = 717,
LANG_ARENA_QUEUE_ANNOUNCE_WORLD_JOIN= 718,
LANG_ARENA_QUEUE_ANNOUNCE_WORLD_EXIT= 719,
+
LANG_BG_GROUP_TOO_LARGE = 720, // "Your group is too large for this battleground. Please regroup to join."
LANG_ARENA_GROUP_TOO_LARGE = 721, // "Your group is too large for this arena. Please regroup to join."
LANG_ARENA_YOUR_TEAM_ONLY = 722, // "Your group has members not in your arena team. Please regroup to join."
@@ -603,6 +669,7 @@ enum TrinityStrings
LANG_BG_GROUP_MEMBER_ALREADY_IN_QUEUE = 730, // "Someone in your party is already in this battleground queue. (S)he must leave it before joining as group."
LANG_BG_GROUP_MEMBER_DESERTER = 731, // "Someone in your party is Deserter. You can't join as group."
LANG_BG_GROUP_MEMBER_NO_FREE_QUEUE_SLOTS = 732, // "Someone in your party is already in three battleground queues. You cannot join as group."
+
LANG_CANNOT_TELE_TO_BG = 733, // "You cannot teleport to a battleground or arena map."
LANG_CANNOT_SUMMON_TO_BG = 734, // "You cannot summon players to a battleground or arena map."
LANG_CANNOT_GO_TO_BG_GM = 735, // "You must be in GM mode to teleport to a player in a battleground."
@@ -627,10 +694,12 @@ enum TrinityStrings
// LANG_BG_AB_START_TWO_MINUTES = 754, - defined above
// LANG_BG_EY_START_TWO_MINUTES = 755, - defined above
// Room for batleground/arena strings 756-799 not used
+
// Room for BG/ARENA 751-769 not used
LANG_ARENA_TESTING = 785,
LANG_AUTO_ANN = 786,
LANG_ANNOUNCE_COLOR = 787,
+
// in game strings
LANG_PET_INVALID_NAME = 800,
LANG_NOT_ENOUGH_GOLD = 801,
@@ -649,6 +718,7 @@ enum TrinityStrings
LANG_GUILD_MEMBER = 814,
LANG_GUILD_INITIATE = 815,
// Room for in-game strings 816-999 not used
+
// Level 4 (CLI only commands)
LANG_COMMAND_EXIT = 1000,
LANG_ACCOUNT_DELETED = 1001,
@@ -666,6 +736,7 @@ enum TrinityStrings
LANG_ACCOUNT_LIST_LINE = 1013,
LANG_ACCOUNT_LIST_EMPTY = 1014,
// Room for more level 4 1015-1099 not used
+
// Level 3 (continue)
LANG_ACCOUNT_SETADDON = 1100,
LANG_MOTD_NEW = 1101,
@@ -698,11 +769,14 @@ enum TrinityStrings
LANG_TAXINODE_ENTRY_LIST_CHAT = 1128,
LANG_TAXINODE_ENTRY_LIST_CONSOLE = 1129,
// Room for more level 3 1130-1199 not used
+
// Debug commands
LANG_CINEMATIC_NOT_EXIST = 1200,
LANG_MOVIE_NOT_EXIST = 1201,
// Room for more debug 1202-1299 not used
+
// FREE IDS 1300-9999
+
// AV
LANG_BG_AV_ALLY = 1300,
LANG_BG_AV_HORDE = 1301,
@@ -712,9 +786,11 @@ enum TrinityStrings
LANG_BG_AV_GRAVE_TAKEN = 1305,
LANG_BG_AV_GRAVE_DEFENDED = 1306,
LANG_BG_AV_GRAVE_ASSAULTED = 1307,
+
LANG_BG_AV_MINE_TAKEN = 1308,
LANG_BG_AV_MINE_NORTH = 1309,
LANG_BG_AV_MINE_SOUTH = 1310,
+
LANG_BG_AV_NODE_GRAVE_STORM_AID = 1311,
LANG_BG_AV_NODE_TOWER_DUN_S = 1312,
LANG_BG_AV_NODE_TOWER_DUN_N = 1313,
@@ -730,6 +806,7 @@ enum TrinityStrings
LANG_BG_AV_NODE_TOWER_FROST_E = 1323,
LANG_BG_AV_NODE_TOWER_FROST_W = 1324,
LANG_BG_AV_NODE_GRAVE_FROST_HUT = 1325,
+
LANG_BG_AV_START_ONE_MINUTE = 1326,
LANG_BG_AV_START_HALF_MINUTE = 1327,
LANG_BG_AV_HAS_BEGUN = 1328,
@@ -739,6 +816,7 @@ enum TrinityStrings
LANG_BG_AV_A_CAPTAIN_DEAD = 1332,
LANG_BG_AV_START_TWO_MINUTES = 1333,
// FREE IDS 1334-1999
+
// Ticket Strings 2000-2029
LANG_COMMAND_TICKETNEW = 2000,
LANG_COMMAND_TICKETUPDATED = 2001,
@@ -766,6 +844,7 @@ enum TrinityStrings
LANG_COMMAND_TICKETLISTCOMMENT = 2023,
LANG_COMMAND_TICKETLISTADDCOMMENT = 2024,
LANG_COMMAND_TICKETLISTAGECREATE = 2025,
+
// Trinity strings 5000-9999
LANG_COMMAND_FREEZE = 5000,
LANG_COMMAND_FREEZE_ERROR = 5001,
@@ -793,10 +872,13 @@ enum TrinityStrings
LANG_GM_BROADCAST = 6613,
LANG_GM_NOTIFY = 6614,
LANG_GM_ANNOUNCE_COLOR = 6615,
+
LANG_GM_SILENCE = 6616, // "Silence is ON for %s" - Spell 1852
+
LANG_WORLD_CLOSED = 7523,
LANG_WORLD_OPENED = 7524,
+
// Use for not-in-offcial-sources patches
// 10000-10999
// opvp hp
@@ -861,8 +943,10 @@ enum TrinityStrings
LANG_OPVP_EP_FLIGHT_CGT = 10053,
LANG_OPVP_ZM_GOSSIP_ALLIANCE = 10054,
LANG_OPVP_ZM_GOSSIP_HORDE = 10055,
+
// Use for custom patches 11000-11999
LANG_AUTO_BROADCAST = 11000,
+
// NOT RESERVED IDS 12000-1999999999
// `db_script_string` table index 2000000000-2000009999 (MIN_DB_SCRIPT_STRING_ID-MAX_DB_SCRIPT_STRING_ID)
// For other tables maybe 2000010000-2147483647 (max index)
diff --git a/src/game/Level0.cpp b/src/game/Level0.cpp
index 258440bd41a..890e8a9c3c9 100644
--- a/src/game/Level0.cpp
+++ b/src/game/Level0.cpp
@@ -17,6 +17,7 @@
* 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 "World.h"
@@ -29,6 +30,7 @@
#include "SystemConfig.h"
#include "revision.h"
#include "Util.h"
+
bool ChatHandler::HandleHelpCommand(const char* args)
{
char* cmd = strtok((char*)args, " ");
@@ -42,44 +44,53 @@ bool ChatHandler::HandleHelpCommand(const char* args)
if(!ShowHelpForCommand(getCommandTable(), cmd))
SendSysMessage(LANG_NO_HELP_CMD);
}
+
return true;
}
+
bool ChatHandler::HandleCommandsCommand(const char* args)
{
ShowHelpForCommand(getCommandTable(), "");
return true;
}
+
bool ChatHandler::HandleAccountCommand(const char* /*args*/)
{
AccountTypes gmlevel = m_session->GetSecurity();
PSendSysMessage(LANG_ACCOUNT_LEVEL, uint32(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;
}
+
if((chr->isDead()) || (chr->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST)))
{
// if player is dead and stuck, send ghost to graveyard
chr->RepopAtGraveyard();
return true;
}
+
// cast spell Stuck
chr->CastSpell(chr,7355,false);
return true;
}
+
bool ChatHandler::HandleServerInfoCommand(const char* /*args*/)
{
uint32 PlayersNum = sWorld.GetPlayerCount();
@@ -90,11 +101,13 @@ bool ChatHandler::HandleServerInfoCommand(const char* /*args*/)
uint32 maxQueuedClientsNum = sWorld.GetMaxQueuedSessionCount();
std::string uptime = secsToTimeString(sWorld.GetUptime());
uint32 updateTime = sWorld.GetUpdateTime();
+
PSendSysMessage(_FULLVERSION);
//if(m_session)
// full = _FULLVERSION(REVISION_DATE,REVISION_TIME,"|cffffffff|Hurl:" REVISION_ID "|h" REVISION_ID "|h|r");
//else
// full = _FULLVERSION(REVISION_DATE,REVISION_TIME,REVISION_ID);
+
//SendSysMessage(full);
//PSendSysMessage(LANG_USING_SCRIPT_LIB,sWorld.GetScriptsVersion());
//PSendSysMessage(LANG_USING_WORLD_DB,sWorld.GetDBVersion());
@@ -103,8 +116,10 @@ bool ChatHandler::HandleServerInfoCommand(const char* /*args*/)
PSendSysMessage(LANG_CONNECTED_USERS, activeClientsNum, maxActiveClientsNum, queuedClientsNum, maxQueuedClientsNum);
PSendSysMessage(LANG_UPTIME, uptime.c_str());
PSendSysMessage("Update time diff: %u.", updateTime);
+
return true;
}
+
bool ChatHandler::HandleDismountCommand(const char* /*args*/)
{
//If player is not mounted, so go out :)
@@ -114,19 +129,23 @@ bool ChatHandler::HandleDismountCommand(const char* /*args*/)
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()->RemoveAurasByType(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() > SEC_PLAYER)
{
@@ -134,15 +153,19 @@ bool ChatHandler::HandleSaveCommand(const char* /*args*/)
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*IN_MILISECONDS && player->GetSaveTimer() <= save_interval - 20*IN_MILISECONDS))
player->SaveToDB();
+
return true;
}
+
bool ChatHandler::HandleGMListIngameCommand(const char* /*args*/)
{
bool first = true;
+
HashMapHolder<Player>::MapType &m = HashMapHolder<Player>::GetContainer();
HashMapHolder<Player>::MapType::const_iterator itr = m.begin();
for(; itr != m.end(); ++itr)
@@ -156,38 +179,49 @@ bool ChatHandler::HandleGMListIngameCommand(const char* /*args*/)
SendSysMessage(LANG_GMS_ON_SRV);
first = false;
}
+
SendSysMessage(GetNameLink(itr->second).c_str());
}
}
+
if(first)
SendSysMessage(LANG_GMS_NOT_LOGGED);
+
return true;
}
+
bool ChatHandler::HandleAccountPasswordCommand(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 (strcmp(new_pass, new_pass_c) != 0)
{
SendSysMessage (LANG_NEW_PASSWORDS_NOT_MATCH);
SetSentErrorMessage (true);
return false;
}
+
if (!accmgr.CheckPassword (m_session->GetAccountId(), password_old))
{
SendSysMessage (LANG_COMMAND_WRONGOLDPASSWORD);
SetSentErrorMessage (true);
return false;
}
+
AccountOpResult result = accmgr.ChangePassword(m_session->GetAccountId(), password_new);
+
switch(result)
{
case AOR_OK:
@@ -203,22 +237,29 @@ bool ChatHandler::HandleAccountPasswordCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandleAccountAddonCommand(const char* args)
{
if(!*args)
return false;
+
char *szExp = strtok((char*)args," ");
+
uint32 account_id = m_session->GetAccountId();
+
int expansion=atoi(szExp); //get int anyway (0 if error)
if(expansion < 0 || expansion > sWorld.getConfig(CONFIG_EXPANSION))
return false;
+
// No SQL injection
loginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'", expansion, account_id);
PSendSysMessage(LANG_ACCOUNT_ADDON, expansion);
return true;
}
+
bool ChatHandler::HandleAccountLockCommand(const char* args)
{
if (!*args)
@@ -226,6 +267,7 @@ bool ChatHandler::HandleAccountLockCommand(const char* args)
SendSysMessage(LANG_USE_BOL);
return true;
}
+
std::string argstr = (char*)args;
if (argstr == "on")
{
@@ -233,15 +275,18 @@ bool ChatHandler::HandleAccountLockCommand(const char* args)
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;
}
+
/// Display the 'Message of the day' for the realm
bool ChatHandler::HandleServerMotdCommand(const char* /*args*/)
{
diff --git a/src/game/Level1.cpp b/src/game/Level1.cpp
index 1108e9e1240..160d65cd5cf 100644
--- a/src/game/Level1.cpp
+++ b/src/game/Level1.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -33,14 +34,17 @@
#include "CellImpl.h"
#include "InstanceSaveMgr.h"
#include "Util.h"
+
#ifdef _DEBUG_VMAPS
#include "VMapFactory.h"
#endif
+
//-----------------------Npc Commands-----------------------
bool ChatHandler::HandleNpcSayCommand(const char* args)
{
if(!*args)
return false;
+
Creature* pCreature = getSelectedCreature();
if(!pCreature)
{
@@ -48,7 +52,9 @@ bool ChatHandler::HandleNpcSayCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
pCreature->MonsterSay(args, LANG_UNIVERSAL, 0);
+
// make some emotes
char lastchar = args[strlen(args) - 1];
switch(lastchar)
@@ -57,12 +63,15 @@ bool ChatHandler::HandleNpcSayCommand(const char* args)
case '!': pCreature->HandleEmoteCommand(EMOTE_ONESHOT_EXCLAMATION); break;
default: pCreature->HandleEmoteCommand(EMOTE_ONESHOT_TALK); break;
}
+
return true;
}
+
bool ChatHandler::HandleNpcYellCommand(const char* args)
{
if(!*args)
return false;
+
Creature* pCreature = getSelectedCreature();
if(!pCreature)
{
@@ -70,103 +79,136 @@ bool ChatHandler::HandleNpcYellCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
pCreature->MonsterYell(args, LANG_UNIVERSAL, 0);
+
// make an emote
pCreature->HandleEmoteCommand(EMOTE_ONESHOT_SHOUT);
+
return true;
}
+
//show text emote by creature in chat
bool ChatHandler::HandleNpcTextEmoteCommand(const char* args)
{
if(!*args)
return false;
+
Creature* pCreature = getSelectedCreature();
+
if(!pCreature)
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
pCreature->MonsterTextEmote(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 = m_session->GetPlayer()->GetMap()->GetCreature(guid);
+
if(!pCreature || !receiver_str || !text)
{
return false;
}
+
uint64 receiver_guid= atol(receiver_str);
+
// check online security
if (HasLowerSecurity(objmgr.GetPlayer(receiver_guid), 0))
return false;
+
pCreature->MonsterWhisper(text,receiver_guid);
+
return true;
}
//----------------------------------------------------------
+
bool ChatHandler::HandleNameAnnounceCommand(const char* args)
{
WorldPacket data;
if(!*args)
return false;
+
sWorld.SendWorldText(LANG_ANNOUNCE_COLOR, m_session->GetPlayer()->GetName(), args);
return true;
}
+
bool ChatHandler::HandleGMNameAnnounceCommand(const char* args)
{
WorldPacket data;
if(!*args)
return false;
+
sWorld.SendGMText(LANG_GM_ANNOUNCE_COLOR, m_session->GetPlayer()->GetName(), args);
return true;
}
+
// global announce
bool ChatHandler::HandleAnnounceCommand(const char* args)
{
if(!*args)
return false;
+
sWorld.SendWorldText(LANG_SYSTEMMESSAGE,args);
return true;
}
+
// announce to logged in GMs
bool ChatHandler::HandleGMAnnounceCommand(const char* args)
{
if(!*args)
return false;
+
sWorld.SendGMText(LANG_GM_BROADCAST,args);
return true;
}
+
//notification player at the screen
bool ChatHandler::HandleNotifyCommand(const char* args)
{
if(!*args)
return false;
+
std::string str = GetTrinityString(LANG_GLOBAL_NOTIFY);
str += args;
+
WorldPacket data(SMSG_NOTIFICATION, (str.size()+1));
data << str;
sWorld.SendGlobalMessage(&data);
+
return true;
}
+
//notification GM at the screen
bool ChatHandler::HandleGMNotifyCommand(const char* args)
{
if(!*args)
return false;
+
std::string str = GetTrinityString(LANG_GM_NOTIFY);
str += args;
+
WorldPacket data(SMSG_NOTIFICATION, (str.size()+1));
data << str;
sWorld.SendGlobalGMMessage(&data);
+
return true;
}
+
//Enable\Dissable GM Mode
bool ChatHandler::HandleGMCommand(const char* args)
{
@@ -178,7 +220,9 @@ bool ChatHandler::HandleGMCommand(const char* args)
m_session->SendNotification(LANG_GM_OFF);
return true;
}
+
std::string argstr = (char*)args;
+
if (argstr == "on")
{
m_session->GetPlayer()->SetGameMaster(true);
@@ -189,6 +233,7 @@ bool ChatHandler::HandleGMCommand(const char* args)
#endif
return true;
}
+
if (argstr == "off")
{
m_session->GetPlayer()->SetGameMaster(false);
@@ -199,10 +244,12 @@ bool ChatHandler::HandleGMCommand(const char* args)
#endif
return true;
}
+
SendSysMessage(LANG_USE_BOL);
SetSentErrorMessage(true);
return false;
}
+
// Enables or disables hiding of the staff badge
bool ChatHandler::HandleGMChatCommand(const char* args)
{
@@ -214,23 +261,28 @@ bool ChatHandler::HandleGMChatCommand(const char* args)
m_session->SendNotification(LANG_GM_CHAT_OFF);
return true;
}
+
std::string argstr = (char*)args;
+
if (argstr == "on")
{
m_session->GetPlayer()->SetGMChat(true);
m_session->SendNotification(LANG_GM_CHAT_ON);
return true;
}
+
if (argstr == "off")
{
m_session->GetPlayer()->SetGMChat(false);
m_session->SendNotification(LANG_GM_CHAT_OFF);
return true;
}
+
SendSysMessage(LANG_USE_BOL);
SetSentErrorMessage(true);
return false;
}
+
std::string ChatHandler::PGetParseString(int32 entry, ...)
{
const char *format = GetTrinityString(entry);
@@ -241,6 +293,7 @@ std::string ChatHandler::PGetParseString(int32 entry, ...)
va_end(ap);
return (std::string)str;
}
+
bool ChatHandler::HandleGMTicketListCommand(const char* args)
{
SendSysMessage(LANG_COMMAND_TICKETSHOWLIST);
@@ -263,6 +316,7 @@ bool ChatHandler::HandleGMTicketListCommand(const char* args)
return true;
}
+
bool ChatHandler::HandleGMTicketListOnlineCommand(const char* args)
{
SendSysMessage(LANG_COMMAND_TICKETSHOWONLINELIST);
@@ -270,6 +324,7 @@ bool ChatHandler::HandleGMTicketListOnlineCommand(const char* args)
{
if((*itr)->closed != 0 || !objmgr.GetPlayer((*itr)->playerGuid))
continue;
+
std::string gmname;
std::stringstream ss;
ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid);
@@ -284,6 +339,7 @@ bool ChatHandler::HandleGMTicketListOnlineCommand(const char* args)
}
return true;
}
+
bool ChatHandler::HandleGMTicketListClosedCommand(const char* args)
{
SendSysMessage(LANG_COMMAND_TICKETSHOWCLOSEDLIST);
@@ -291,6 +347,7 @@ bool ChatHandler::HandleGMTicketListClosedCommand(const char* args)
{
if((*itr)->closed == 0)
continue;
+
std::string gmname;
std::stringstream ss;
ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, (*itr)->guid);
@@ -305,10 +362,12 @@ bool ChatHandler::HandleGMTicketListClosedCommand(const char* args)
}
return true;
}
+
bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args)
{
if(!*args)
return false;
+
uint64 tguid = atoi(args);
GM_Ticket *ticket = objmgr.GetGMTicket(tguid);
if(!ticket || ticket->closed != 0)
@@ -316,6 +375,7 @@ bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args)
SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
return true;
}
+
std::string gmname;
std::stringstream ss;
ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid);
@@ -334,24 +394,29 @@ bool ChatHandler::HandleGMTicketGetByIdCommand(const char* args)
SendSysMessage(ss.str().c_str());
return true;
}
+
bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args)
{
if(!*args)
return false;
+
std::string name = (char*)args;
normalizePlayerName(name);
+
Player *plr = objmgr.GetPlayer(name.c_str());
if(!plr)
{
SendSysMessage(LANG_NO_PLAYERS_FOUND);
return true;
}
+
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(plr->GetGUID());
if(!ticket)
{
SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
return true;
}
+
std::string gmname;
std::stringstream ss;
ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid);
@@ -370,10 +435,12 @@ bool ChatHandler::HandleGMTicketGetByNameCommand(const char* args)
SendSysMessage(ss.str().c_str());
return true;
}
+
bool ChatHandler::HandleGMTicketCloseByIdCommand(const char* args)
{
if(!*args)
return false;
+
uint64 tguid = atoi(args);
GM_Ticket *ticket = objmgr.GetGMTicket(tguid);
if(!ticket || ticket->closed != 0)
@@ -393,29 +460,38 @@ bool ChatHandler::HandleGMTicketCloseByIdCommand(const char* args)
SendGlobalGMSysMessage(ss.str().c_str());
Player *plr = objmgr.GetPlayer(ticket->playerGuid);
objmgr.RemoveGMTicket(ticket, m_session->GetPlayer()->GetGUID());
+
if(!plr || !plr->IsInWorld())
return true;
+
// send abandon ticket
WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4);
data << uint32(9);
plr->GetSession()->SendPacket( &data );
return true;
}
+
bool ChatHandler::HandleGMTicketAssignToCommand(const char* args)
{
if(!*args)
return false;
+
char* tguid = strtok((char*)args, " ");
uint64 ticketGuid = atoi(tguid);
char* targetgm = strtok( NULL, " ");
+
if(!targetgm)
return false;
+
std::string targm = targetgm;
+
if(!normalizePlayerName(targm))
return true;
+
Player *cplr = m_session->GetPlayer();
std::string gmname;
GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid);
+
if(!ticket || ticket->closed != 0)
{
SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
@@ -440,6 +516,7 @@ bool ChatHandler::HandleGMTicketAssignToCommand(const char* args)
PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->guid, gmname.c_str());
return true;
}
+
ticket->assignedToGM = tarGUID;
objmgr.AddOrUpdateGMTicket(*ticket);
std::stringstream ss;
@@ -449,13 +526,16 @@ bool ChatHandler::HandleGMTicketAssignToCommand(const char* args)
SendGlobalGMSysMessage(ss.str().c_str());
return true;
}
+
bool ChatHandler::HandleGMTicketUnAssignCommand(const char* args)
{
if(!*args)
return false;
+
uint64 ticketGuid = atoi(args);
Player *cplr = m_session->GetPlayer();
GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid);
+
if(!ticket|| ticket->closed != 0)
{
SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
@@ -466,6 +546,7 @@ bool ChatHandler::HandleGMTicketUnAssignCommand(const char* args)
PSendSysMessage(LANG_COMMAND_TICKETNOTASSIGNED, ticket->guid);
return true;
}
+
std::string gmname;
objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname);
Player *plr = objmgr.GetPlayer(ticket->assignedToGM);
@@ -474,6 +555,7 @@ bool ChatHandler::HandleGMTicketUnAssignCommand(const char* args)
SendSysMessage(LANG_COMMAND_TICKETUNASSIGNSECURITY);
return true;
}
+
std::stringstream ss;
ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid);
ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str());
@@ -484,17 +566,22 @@ bool ChatHandler::HandleGMTicketUnAssignCommand(const char* args)
objmgr.AddOrUpdateGMTicket(*ticket);
return true;
}
+
bool ChatHandler::HandleGMTicketCommentCommand(const char* args)
{
if(!*args)
return false;
+
char* tguid = strtok((char*)args, " ");
uint64 ticketGuid = atoi(tguid);
char* comment = strtok( NULL, "\n");
+
if(!comment)
return false;
+
Player *cplr = m_session->GetPlayer();
GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid);
+
if(!ticket || ticket->closed != 0)
{
PSendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
@@ -505,6 +592,7 @@ bool ChatHandler::HandleGMTicketCommentCommand(const char* args)
PSendSysMessage(LANG_COMMAND_TICKETALREADYASSIGNED, ticket->guid);
return true;
}
+
std::string gmname;
objmgr.GetPlayerNameByGUID(ticket->assignedToGM, gmname);
ticket->comment = comment;
@@ -520,12 +608,14 @@ bool ChatHandler::HandleGMTicketCommentCommand(const char* args)
SendGlobalGMSysMessage(ss.str().c_str());
return true;
}
+
bool ChatHandler::HandleGMTicketDeleteByIdCommand(const char* args)
{
if(!*args)
return false;
uint64 ticketGuid = atoi(args);
GM_Ticket *ticket = objmgr.GetGMTicket(ticketGuid);
+
if(!ticket)
{
SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
@@ -536,6 +626,7 @@ bool ChatHandler::HandleGMTicketDeleteByIdCommand(const char* args)
SendSysMessage(LANG_COMMAND_TICKETCLOSEFIRST);
return true;
}
+
std::stringstream ss;
ss << PGetParseString(LANG_COMMAND_TICKETLISTGUID, ticket->guid);
ss << PGetParseString(LANG_COMMAND_TICKETLISTNAME, ticket->name.c_str());
@@ -550,14 +641,17 @@ bool ChatHandler::HandleGMTicketDeleteByIdCommand(const char* args)
data << uint32(9);
plr->GetSession()->SendPacket( &data );
}
+
ticket = NULL;
return true;
}
+
bool ChatHandler::HandleGMTicketReloadCommand(const char*)
{
objmgr.LoadGMTickets();
return true;
}
+
//Enable\Dissable Invisible mode
bool ChatHandler::HandleGMVisibleCommand(const char* args)
{
@@ -566,24 +660,30 @@ bool ChatHandler::HandleGMVisibleCommand(const char* args)
PSendSysMessage(LANG_YOU_ARE, m_session->GetPlayer()->isGMVisible() ? GetTrinityString(LANG_VISIBLE) : GetTrinityString(LANG_INVISIBLE));
return true;
}
+
std::string argstr = (char*)args;
+
if (argstr == "on")
{
m_session->GetPlayer()->SetGMVisible(true);
m_session->SendNotification(LANG_INVISIBLE_VISIBLE);
return true;
}
+
if (argstr == "off")
{
m_session->SendNotification(LANG_INVISIBLE_INVISIBLE);
m_session->GetPlayer()->SetGMVisible(false);
return true;
}
+
SendSysMessage(LANG_USE_BOL);
SetSentErrorMessage(true);
return false;
}
+
+
bool ChatHandler::HandleGPSCommand(const char* args)
{
WorldObject *obj = NULL;
@@ -592,6 +692,7 @@ bool ChatHandler::HandleGPSCommand(const char* args)
uint64 guid = extractGuidFromLink((char*)args);
if(guid)
obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+
if(!obj)
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
@@ -602,6 +703,7 @@ bool ChatHandler::HandleGPSCommand(const char* args)
else
{
obj = getSelectedUnit();
+
if(!obj)
{
SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
@@ -611,22 +713,31 @@ bool ChatHandler::HandleGPSCommand(const char* args)
}
CellPair cell_val = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
Cell cell(cell_val);
+
uint32 zone_id, area_id;
obj->GetZoneAndAreaId(zone_id,area_id);
+
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 = obj->GetMap();
float ground_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), MAX_HEIGHT);
float floor_z = map->GetHeight(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ());
+
GridPair p = Trinity::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[GetSessionDbcLocale()] : "<unknown>" ),
zone_id, (zoneEntry ? zoneEntry->area_name[GetSessionDbcLocale()] : "<unknown>" ),
@@ -635,6 +746,7 @@ bool ChatHandler::HandleGPSCommand(const char* args)
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 ? GetNameLink().c_str() : GetMangosString(LANG_CONSOLE_COMMAND),
(obj->GetTypeId() == TYPEID_PLAYER ? "player" : "creature"), obj->GetName(),
@@ -647,6 +759,7 @@ bool ChatHandler::HandleGPSCommand(const char* args)
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 );
+
LiquidData liquid_status;
ZLiquidStatus res = map->getLiquidStatus(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), MAP_ALL_LIQUIDS, &liquid_status);
if (res)
@@ -655,6 +768,7 @@ bool ChatHandler::HandleGPSCommand(const char* args)
}
return true;
}
+
//Summon Player
bool ChatHandler::HandleNamegoCommand(const char* args)
{
@@ -663,6 +777,7 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
std::string target_name;
if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
Player* _player = m_session->GetPlayer();
if (target == _player || target_guid == _player->GetGUID())
{
@@ -670,19 +785,23 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if (target)
{
std::string nameLink = playerLink(target_name);
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
if (target->IsBeingTeleported())
{
PSendSysMessage(LANG_IS_TELEPORTED, nameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
Map* pMap = m_session->GetPlayer()->GetMap();
+
if (pMap->IsBattleGroundOrArena())
{
// only allow if gm mode is on
@@ -717,6 +836,7 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
//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() || !target->GetGroup() ||
(target->GetGroup()->GetLeaderGUID() != m_session->GetPlayer()->GetGUID()) ||
@@ -728,9 +848,11 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
return false;
}
}
+
PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),"");
if (needReportToTarget(target))
ChatHandler(target).PSendSysMessage(LANG_SUMMONED_BY, playerLink(_player->GetName()).c_str());
+
// stop flight if need
if (target->isInFlight())
{
@@ -740,6 +862,7 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
// save only in non-flight case
else
target->SaveRecallPosition();
+
// before GM
float x,y,z;
m_session->GetPlayer()->GetClosePoint(x,y,z,target->GetObjectSize());
@@ -751,8 +874,11 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
// check offline security
if (HasLowerSecurity(NULL, target_guid))
return false;
+
std::string nameLink = playerLink(target_name);
+
PSendSysMessage(LANG_SUMMONING, nameLink.c_str(),GetTrinityString(LANG_OFFLINE));
+
// in point where GM stay
Player::SavePositionInDB(m_session->GetPlayer()->GetMapId(),
m_session->GetPlayer()->GetPositionX(),
@@ -762,8 +888,10 @@ bool ChatHandler::HandleNamegoCommand(const char* args)
m_session->GetPlayer()->GetZoneId(),
target_guid);
}
+
return true;
}
+
//Teleport to Player
bool ChatHandler::HandleGonameCommand(const char* args)
{
@@ -772,6 +900,7 @@ bool ChatHandler::HandleGonameCommand(const char* args)
std::string target_name;
if (!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
Player* _player = m_session->GetPlayer();
if (target == _player || target_guid == _player->GetGUID())
{
@@ -780,12 +909,15 @@ bool ChatHandler::HandleGonameCommand(const char* args)
return false;
}
+
if (target)
{
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
std::string chrNameLink = playerLink(target_name);
+
Map* cMap = target->GetMap();
if (cMap->IsBattleGroundOrArena())
{
@@ -813,6 +945,7 @@ bool ChatHandler::HandleGonameCommand(const char* args)
else if(cMap->IsDungeon())
{
Map* pMap = _player->GetMap();
+
// 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
@@ -836,6 +969,7 @@ bool ChatHandler::HandleGonameCommand(const char* args)
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(target->GetMapId(), target->GetDifficulty());
@@ -849,11 +983,14 @@ bool ChatHandler::HandleGonameCommand(const char* args)
if (InstanceSave *save = sInstanceSaveManager.GetInstanceSave(target->GetInstanceId()))
_player->BindToInstance(save, !save->CanReset());
}
+
_player->SetDifficulty(target->GetDifficulty());
}
+
PSendSysMessage(LANG_APPEARING_AT, chrNameLink.c_str());
if (needReportToTarget(target))
ChatHandler(target).PSendSysMessage(LANG_APPEARING_TO, GetNameLink().c_str());
+
// stop flight if need
if (_player->isInFlight())
{
@@ -863,9 +1000,11 @@ bool ChatHandler::HandleGonameCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
// to point to see at target with same orientation
float x,y,z;
target->GetContactPoint(_player,x,y,z);
+
_player->TeleportTo(target->GetMapId(), x, y, z, _player->GetAngle(target), TELE_TO_GM_MODE);
_player->SetPhaseMask(target->GetPhaseMask(), true);
}
@@ -874,14 +1013,18 @@ bool ChatHandler::HandleGonameCommand(const char* args)
// check offline security
if (HasLowerSecurity(NULL, target_guid))
return false;
+
std::string nameLink = playerLink(target_name);
+
PSendSysMessage(LANG_APPEARING_AT, nameLink.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,target_guid))
return false;
+
// stop flight if need
if (_player->isInFlight())
{
@@ -891,41 +1034,52 @@ bool ChatHandler::HandleGonameCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
_player->TeleportTo(map, x, y, z,_player->GetOrientation());
}
+
return true;
}
+
// Teleport player to last position
bool ChatHandler::HandleRecallCommand(const char* args)
{
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
if (target->IsBeingTeleported())
{
PSendSysMessage(LANG_IS_TELEPORTED, GetNameLink(target).c_str());
SetSentErrorMessage(true);
return false;
}
+
// stop flight if need
if(target->isInFlight())
{
target->GetMotionMaster()->MovementExpired();
target->CleanupAfterTaxiFlight();
}
+
target->TeleportTo(target->m_recallMap, target->m_recallX, target->m_recallY, target->m_recallZ, target->m_recallO);
return true;
}
+
//Edit Player KnownTitles
bool ChatHandler::HandleModifyKnownTitlesCommand(const char* args)
{
if(!*args)
return false;
+
uint64 titles = 0;
+
sscanf((char*)args, UI64FMTD, &titles);
+
Player *chr = getSelectedPlayer();
if (!chr)
{
@@ -933,39 +1087,52 @@ bool ChatHandler::HandleModifyKnownTitlesCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
uint64 titles2 = titles;
+
for(uint32 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;
}
+
Unit *chr = getSelectedUnit();
if (chr == NULL)
{
@@ -973,37 +1140,47 @@ bool ChatHandler::HandleModifyHPCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (chr->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_HP, GetNameLink((Player*)chr).c_str(), hp, hpm);
if (chr->GetTypeId() == TYPEID_PLAYER && needReportToTarget((Player*)chr))
ChatHandler((Player*)chr).PSendSysMessage(LANG_YOURS_HP_CHANGED, GetNameLink().c_str(), 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)
{
@@ -1011,37 +1188,48 @@ bool ChatHandler::HandleModifyManaCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_MANA, GetNameLink(chr).c_str(), mana, manam);
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MANA_CHANGED, GetNameLink().c_str(), 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)
{
@@ -1049,38 +1237,50 @@ bool ChatHandler::HandleModifyEnergyCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_ENERGY, GetNameLink(chr).c_str(), energy/10, energym/10);
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_ENERGY_CHANGED, GetNameLink().c_str(), energy/10, energym/10);
+
chr->SetMaxPower(POWER_ENERGY,energym );
chr->SetPower(POWER_ENERGY, energy );
+
sLog.outDetail(GetTrinityString(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)
{
@@ -1088,29 +1288,37 @@ bool ChatHandler::HandleModifyRageCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_RAGE, GetNameLink(chr).c_str(), rage/10, ragem/10);
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_RAGE_CHANGED, GetNameLink().c_str(), rage/10, ragem/10);
+
chr->SetMaxPower(POWER_RAGE,ragem );
chr->SetPower(POWER_RAGE, rage );
+
return true;
}
+
// Edit Player Runic Power
bool ChatHandler::HandleModifyRunicPowerCommand(const char* args)
{
if(!*args)
return false;
+
int32 rune = atoi((char*)args)*10;
int32 runem = atoi((char*)args)*10;
+
if (rune <= 0 || runem <= 0 || runem < rune)
{
SendSysMessage(LANG_BAD_VALUE);
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1118,19 +1326,25 @@ bool ChatHandler::HandleModifyRunicPowerCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_YOU_CHANGE_RUNIC_POWER, GetNameLink(chr).c_str(), rune/10, runem/10);
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_RUNIC_POWER_CHANGED, GetNameLink().c_str(), rune/10, runem/10);
+
chr->SetMaxPower(POWER_RUNIC_POWER,runem );
chr->SetPower(POWER_RUNIC_POWER, rune );
+
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)
{
@@ -1138,6 +1352,7 @@ bool ChatHandler::HandleModifyFactionCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(!pfactionid)
{
if(chr)
@@ -1150,44 +1365,56 @@ bool ChatHandler::HandleModifyFactionCommand(const char* args)
}
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);
+
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)
{
@@ -1195,14 +1422,19 @@ bool ChatHandler::HandleModifySpellCommand(const char* args)
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);
@@ -1210,6 +1442,7 @@ bool ChatHandler::HandleModifySpellCommand(const char* args)
mark = 65535;
else
mark = atoi(pmark);
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1217,28 +1450,35 @@ bool ChatHandler::HandleModifySpellCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_SPELLFLATID, spellflatid, val, mark, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPELLFLATID_CHANGED, GetNameLink().c_str(), 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)
return false;
+
Unit* target = getSelectedUnit();
if(!target)
{
@@ -1246,6 +1486,7 @@ bool ChatHandler::HandleModifyTalentCommand (const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(target->GetTypeId()==TYPEID_PLAYER)
{
// check online security
@@ -1268,10 +1509,12 @@ bool ChatHandler::HandleModifyTalentCommand (const char* args)
return true;
}
}
+
SendSysMessage(LANG_NO_CHAR_SELECTED);
SetSentErrorMessage(true);
return false;
}
+
//Enable On\OFF all taxi paths
bool ChatHandler::HandleTaxiCheatCommand(const char* args)
{
@@ -1281,15 +1524,19 @@ bool ChatHandler::HandleTaxiCheatCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
std::string argstr = (char*)args;
+
Player *chr = getSelectedPlayer();
if (!chr)
{
chr=m_session->GetPlayer();
}
+
// check online security
else if (HasLowerSecurity(chr, 0))
return false;
+
if (argstr == "on")
{
chr->SetTaxiCheater(true);
@@ -1298,30 +1545,37 @@ bool ChatHandler::HandleTaxiCheatCommand(const char* args)
ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_ADDED, GetNameLink().c_str());
return true;
}
+
if (argstr == "off")
{
chr->SetTaxiCheater(false);
PSendSysMessage(LANG_YOU_REMOVE_TAXIS, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_TAXIS_REMOVED, GetNameLink().c_str());
+
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 > 50.0f || ASpeed < 0.1f)
{
SendSysMessage(LANG_BAD_VALUE);
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1329,19 +1583,24 @@ bool ChatHandler::HandleModifyASpeedCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
std::string chrNameLink = GetNameLink(chr);
+
if(chr->isInFlight())
{
PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_YOU_CHANGE_ASPEED, ASpeed, chrNameLink.c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_ASPEED_CHANGED, GetNameLink().c_str(), ASpeed);
+
chr->SetSpeed(MOVE_WALK, ASpeed,true);
chr->SetSpeed(MOVE_RUN, ASpeed,true);
chr->SetSpeed(MOVE_SWIM, ASpeed,true);
@@ -1349,18 +1608,22 @@ bool ChatHandler::HandleModifyASpeedCommand(const char* args)
chr->SetSpeed(MOVE_FLIGHT, 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 > 50.0f || Speed < 0.1f)
{
SendSysMessage(LANG_BAD_VALUE);
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1368,34 +1631,44 @@ bool ChatHandler::HandleModifySpeedCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
std::string chrNameLink = GetNameLink(chr);
+
if(chr->isInFlight())
{
PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_YOU_CHANGE_SPEED, Speed, chrNameLink.c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_SPEED_CHANGED, GetNameLink().c_str(), 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 > 50.0f || Swim < 0.1f)
{
SendSysMessage(LANG_BAD_VALUE);
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1403,34 +1676,44 @@ bool ChatHandler::HandleModifySwimCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
std::string chrNameLink = GetNameLink(chr);
+
if(chr->isInFlight())
{
PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_YOU_CHANGE_SWIM_SPEED, Swim, chrNameLink.c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_SWIM_SPEED_CHANGED, GetNameLink().c_str(), 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 > 50.0f || BSpeed < 0.1f)
{
SendSysMessage(LANG_BAD_VALUE);
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1438,34 +1721,44 @@ bool ChatHandler::HandleModifyBWalkCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
std::string chrNameLink = GetNameLink(chr);
+
if(chr->isInFlight())
{
PSendSysMessage(LANG_CHAR_IN_FLIGHT,chrNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_YOU_CHANGE_BACK_SPEED, BSpeed, chrNameLink.c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_BACK_SPEED_CHANGED, GetNameLink().c_str(), BSpeed);
+
chr->SetSpeed(MOVE_RUN_BACK,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 > 50.0f || FSpeed < 0.1f)
{
SendSysMessage(LANG_BAD_VALUE);
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1473,20 +1766,26 @@ bool ChatHandler::HandleModifyFlyCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_FLY_SPEED, FSpeed, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_FLY_SPEED_CHANGED, GetNameLink().c_str(), FSpeed);
+
chr->SetSpeed(MOVE_FLIGHT,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 > 10.0f || Scale <= 0.1f)
{
@@ -1494,6 +1793,7 @@ bool ChatHandler::HandleModifyScaleCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1501,23 +1801,30 @@ bool ChatHandler::HandleModifyScaleCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_CHANGE_SIZE, Scale, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_SIZE_CHANGED, GetNameLink().c_str(), 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)
{
@@ -1733,6 +2040,7 @@ bool ChatHandler::HandleModifyMountCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -1740,32 +2048,40 @@ bool ChatHandler::HandleModifyMountCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
PSendSysMessage(LANG_YOU_GIVE_MOUNT, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_MOUNT_GIVED, GetNameLink().c_str());
+
chr->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP);
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)
{
@@ -1773,26 +2089,33 @@ bool ChatHandler::HandleModifyMoneyCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(chr, 0))
return false;
+
int32 addmoney = atoi((char*)args);
+
uint32 moneyuser = chr->GetMoney();
+
if (addmoney < 0)
{
int32 newmoney = int32(moneyuser) + addmoney;
+
sLog.outDetail(GetTrinityString(LANG_CURRENT_MONEY), moneyuser, addmoney, newmoney);
if (newmoney <= 0 )
{
PSendSysMessage(LANG_YOU_TAKE_ALL_MONEY, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_ALL_MONEY_GONE, GetNameLink().c_str());
+
chr->SetMoney(0);
}
else
{
if (newmoney > MAX_MONEY_AMOUNT)
newmoney = MAX_MONEY_AMOUNT;
+
PSendSysMessage(LANG_YOU_TAKE_MONEY, abs(addmoney), GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_TAKEN, GetNameLink().c_str(), abs(addmoney));
@@ -1804,19 +2127,24 @@ bool ChatHandler::HandleModifyMoneyCommand(const char* args)
PSendSysMessage(LANG_YOU_GIVE_MONEY, addmoney, GetNameLink(chr).c_str());
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_MONEY_GIVEN, GetNameLink().c_str(), addmoney);
+
if (addmoney >=MAX_MONEY_AMOUNT)
chr->SetMoney(MAX_MONEY_AMOUNT);
else
chr->ModifyMoney( addmoney );
}
+
sLog.outDetail(GetTrinityString(LANG_NEW_MONEY), moneyuser, addmoney, chr->GetMoney() );
+
return true;
}
+
//Edit Unit field
bool ChatHandler::HandleModifyBitCommand(const char* args)
{
if( !*args )
return false;
+
Unit *unit = getSelectedUnit();
if (!unit)
{
@@ -1824,17 +2152,22 @@ bool ChatHandler::HandleModifyBitCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (unit->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player *)unit, 0))
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 < OBJECT_END || field >= unit->GetValuesCount())
{
SendSysMessage(LANG_BAD_VALUE);
@@ -1847,6 +2180,7 @@ bool ChatHandler::HandleModifyBitCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if ( unit->HasFlag( field, (1<<(bit-1)) ) )
{
unit->RemoveFlag( field, (1<<(bit-1)) );
@@ -1859,10 +2193,12 @@ bool ChatHandler::HandleModifyBitCommand(const char* args)
}
return true;
}
+
bool ChatHandler::HandleModifyHonorCommand (const char* args)
{
if (!*args)
return false;
+
Player *target = getSelectedPlayer();
if(!target)
{
@@ -1870,27 +2206,37 @@ bool ChatHandler::HandleModifyHonorCommand (const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
int32 amount = (uint32)atoi(args);
+
target->ModifyHonorPoints(amount);
+
PSendSysMessage(LANG_COMMAND_MODIFY_HONOR, GetNameLink(target).c_str(), target->GetHonorPoints());
+
return true;
}
+
bool ChatHandler::HandleTeleCommand(const char * args)
{
if(!*args)
return false;
+
Player* _player = m_session->GetPlayer();
+
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele const* tele = extractGameTeleFromLink((char*)args);
+
if (!tele)
{
SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
SetSentErrorMessage(true);
return false;
}
+
MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
if(!me || me->IsBattleGroundOrArena())
{
@@ -1898,6 +2244,7 @@ bool ChatHandler::HandleTeleCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
// stop flight if need
if(_player->isInFlight())
{
@@ -1907,20 +2254,27 @@ bool ChatHandler::HandleTeleCommand(const char * args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
_player->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
return true;
}
+
bool ChatHandler::HandleLookupAreaCommand(const char* args)
{
if (!*args)
return false;
+
std::string namepart = args;
std::wstring wnamepart;
+
if (!Utf8toWStr (namepart,wnamepart))
return false;
+
bool found = false;
+
// converting string that we try to find to lower case
wstrToLower (wnamepart);
+
// Search in AreaTable.dbc
for (uint32 areaflag = 0; areaflag < sAreaStore.GetNumRows (); ++areaflag)
{
@@ -1931,6 +2285,7 @@ bool ChatHandler::HandleLookupAreaCommand(const char* args)
std::string name = areaEntry->area_name[loc];
if (name.empty())
continue;
+
if (!Utf8FitTo (name, wnamepart))
{
loc = 0;
@@ -1938,13 +2293,16 @@ bool ChatHandler::HandleLookupAreaCommand(const char* args)
{
if (loc==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
@@ -1953,16 +2311,21 @@ bool ChatHandler::HandleLookupAreaCommand(const char* args)
ss << areaEntry->ID << " - |cffffffff|Harea:" << areaEntry->ID << "|h[" << name << " " << localeNames[loc]<< "]|h|r";
else
ss << areaEntry->ID << " - " << name << " " << localeNames[loc];
+
SendSysMessage (ss.str ().c_str());
+
if(!found)
found = true;
}
}
}
+
if (!found)
SendSysMessage (LANG_COMMAND_NOAREAFOUND);
+
return true;
}
+
//Find tele in game_tele order by name
bool ChatHandler::HandleLookupTeleCommand(const char * args)
{
@@ -1972,33 +2335,44 @@ bool ChatHandler::HandleLookupTeleCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
char const* str = strtok((char*)args, " ");
if(!str)
return false;
+
std::string namepart = str;
std::wstring wnamepart;
+
if(!Utf8toWStr(namepart,wnamepart))
return false;
+
// converting string that we try to find to lower case
wstrToLower( wnamepart );
+
std::ostringstream reply;
+
GameTeleMap const & teleMap = objmgr.GetGameTeleMap();
for(GameTeleMap::const_iterator itr = teleMap.begin(); itr != teleMap.end(); ++itr)
{
GameTele const* tele = &itr->second;
+
if(tele->wnameLow.find(wnamepart) == std::wstring::npos)
continue;
+
if (m_session)
reply << " |cffffffff|Htele:" << itr->first << "|h[" << tele->name << "]|h|r\n";
else
reply << " " << itr->first << " " << tele->name << "\n";
}
+
if(reply.str().empty())
SendSysMessage(LANG_COMMAND_TELE_NOLOCATION);
else
PSendSysMessage(LANG_COMMAND_TELE_LOCATION,reply.str().c_str());
+
return true;
}
+
//Enable\Dissable accept whispers (for GM)
bool ChatHandler::HandleWhispersCommand(const char* args)
{
@@ -2007,6 +2381,7 @@ bool ChatHandler::HandleWhispersCommand(const char* args)
PSendSysMessage(LANG_COMMAND_WHISPERACCEPTING, m_session->GetPlayer()->isAcceptWhispers() ? GetTrinityString(LANG_ON) : GetTrinityString(LANG_OFF));
return true;
}
+
std::string argstr = (char*)args;
// whisper on
if (argstr == "on")
@@ -2015,6 +2390,7 @@ bool ChatHandler::HandleWhispersCommand(const char* args)
SendSysMessage(LANG_COMMAND_WHISPERON);
return true;
}
+
// whisper off
if (argstr == "off")
{
@@ -2022,10 +2398,12 @@ bool ChatHandler::HandleWhispersCommand(const char* args)
SendSysMessage(LANG_COMMAND_WHISPEROFF);
return true;
}
+
SendSysMessage(LANG_USE_BOL);
SetSentErrorMessage(true);
return false;
}
+
//Save all players in the world
bool ChatHandler::HandleSaveAllCommand(const char* /*args*/)
{
@@ -2033,6 +2411,7 @@ bool ChatHandler::HandleSaveAllCommand(const char* /*args*/)
SendSysMessage(LANG_PLAYERS_SAVED);
return true;
}
+
//Send mail by command
bool ChatHandler::HandleSendMailCommand(const char* args)
{
@@ -2042,31 +2421,41 @@ bool ChatHandler::HandleSendMailCommand(const char* args)
std::string target_name;
if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
char* tail1 = strtok(NULL, "");
if(!tail1)
return false;
+
char* msgSubject = extractQuotedArg(tail1);
if (!msgSubject)
return false;
+
char* tail2 = strtok(NULL, "");
if(!tail2)
return false;
+
char* msgText = extractQuotedArg(tail2);
if (!msgText)
return false;
+
// msgSubject, msgText isn't NUL after prev. check
std::string subject = msgSubject;
std::string text = msgText;
+
// from console show not existed sender
uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0;
+
uint32 messagetype = MAIL_NORMAL;
uint32 stationery = MAIL_STATIONERY_GM;
uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
+
WorldSession::SendMailTo(target,messagetype, stationery, sender_guidlo, GUID_LOPART(target_guid), subject, itemTextId, NULL, 0, 0, MAIL_CHECK_MASK_NONE);
+
std::string nameLink = playerLink(target_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
return true;
}
+
// teleport player to given game_tele.entry
bool ChatHandler::HandleTeleNameCommand(const char * args)
{
@@ -2075,11 +2464,13 @@ bool ChatHandler::HandleTeleNameCommand(const char * args)
extractOptFirstArg((char*)args,&nameStr,&teleStr);
if(!teleStr)
return false;
+
Player* target;
uint64 target_guid;
std::string target_name;
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false;
+
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele const* tele = extractGameTeleFromLink(teleStr);
if(!tele)
@@ -2088,6 +2479,7 @@ bool ChatHandler::HandleTeleNameCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
/* MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
if(!me || me->IsBattleGroundOrArena())
{
@@ -2095,22 +2487,28 @@ bool ChatHandler::HandleTeleNameCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
Player *chr = objmgr.GetPlayer(name.c_str());*/
+
if (target)
{
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
std::string chrNameLink = playerLink(target_name);
+
if(target->IsBeingTeleported()==true)
{
PSendSysMessage(LANG_IS_TELEPORTED, chrNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_TELEPORTING_TO, chrNameLink.c_str(),"", tele->name.c_str());
if (needReportToTarget(target))
ChatHandler(target).PSendSysMessage(LANG_TELEPORTED_TO_BY, GetNameLink().c_str());
+
// stop flight if need
if(target->isInFlight())
{
@@ -2120,6 +2518,7 @@ bool ChatHandler::HandleTeleNameCommand(const char * args)
// save only in non-flight case
else
target->SaveRecallPosition();
+
target->TeleportTo(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation);
}
else
@@ -2127,18 +2526,23 @@ bool ChatHandler::HandleTeleNameCommand(const char * args)
// check offline security
if (HasLowerSecurity(NULL, target_guid))
return false;
+
std::string nameLink = playerLink(target_name);
+
PSendSysMessage(LANG_TELEPORTING_TO, nameLink.c_str(), GetMangosString(LANG_OFFLINE), tele->name.c_str());
Player::SavePositionInDB(tele->mapId,tele->position_x,tele->position_y,tele->position_z,tele->orientation,
MapManager::Instance().GetZoneId(tele->mapId,tele->position_x,tele->position_y,tele->position_z),target_guid);
}
+
return true;
}
+
//Teleport group to given game_tele.entry
bool ChatHandler::HandleTeleGroupCommand(const char * args)
{
if(!*args)
return false;
+
Player *player = getSelectedPlayer();
if (!player)
{
@@ -2146,9 +2550,11 @@ bool ChatHandler::HandleTeleGroupCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(player, 0))
return false;
+
// id, or string, or [name] Shift-click form |color|Htele:id|h[name]|h|r
GameTele const* tele = extractGameTeleFromLink((char*)args);
if(!tele)
@@ -2157,6 +2563,7 @@ bool ChatHandler::HandleTeleGroupCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
MapEntry const * me = sMapStore.LookupEntry(tele->mapId);
if(!me || me->IsBattleGroundOrArena())
{
@@ -2164,7 +2571,9 @@ bool ChatHandler::HandleTeleGroupCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
std::string nameLink = GetNameLink(player);
+
Group *grp = player->GetGroup();
if(!grp)
{
@@ -2172,23 +2581,30 @@ bool ChatHandler::HandleTeleGroupCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
for(GroupReference *itr = grp->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *pl = itr->getSource();
+
if(!pl || !pl->GetSession() )
continue;
+
// check online security
if (HasLowerSecurity(pl, 0))
return false;
+
std::string plNameLink = GetNameLink(pl);
+
if(pl->IsBeingTeleported())
{
PSendSysMessage(LANG_IS_TELEPORTED, plNameLink.c_str());
continue;
}
+
PSendSysMessage(LANG_TELEPORTING_TO, plNameLink.c_str(),"", tele->name.c_str());
if (needReportToTarget(pl))
ChatHandler(pl).PSendSysMessage(LANG_TELEPORTED_TO_BY, nameLink.c_str());
+
// stop flight if need
if(pl->isInFlight())
{
@@ -2198,29 +2614,38 @@ bool ChatHandler::HandleTeleGroupCommand(const char * args)
// save only in non-flight case
else
pl->SaveRecallPosition();
+
pl->TeleportTo(tele->mapId, tele->position_x, tele->position_y, tele->position_z, tele->orientation);
}
+
return true;
}
+
//Summon group of player
bool ChatHandler::HandleGroupgoCommand(const char* args)
{
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
Group *grp = target->GetGroup();
+
std::string nameLink = GetNameLink(target);
+
if(!grp)
{
PSendSysMessage(LANG_NOT_IN_GROUP,nameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
Map* gmMap = m_session->GetPlayer()->GetMap();
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()) ||
@@ -2231,24 +2656,31 @@ bool ChatHandler::HandleGroupgoCommand(const char* args)
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;
+
// check online security
if (HasLowerSecurity(pl, 0))
return false;
+
std::string plNameLink = GetNameLink(pl);
+
if(pl->IsBeingTeleported()==true)
{
PSendSysMessage(LANG_IS_TELEPORTED, plNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
if (to_instance)
{
Map* plMap = pl->GetMap();
+
if ( plMap->Instanceable() && plMap->GetInstanceId() != gmMap->GetInstanceId() )
{
// cannot summon from instance to instance
@@ -2257,9 +2689,11 @@ bool ChatHandler::HandleGroupgoCommand(const char* args)
return false;
}
}
+
PSendSysMessage(LANG_SUMMONING, plNameLink.c_str(),"");
if (needReportToTarget(pl))
ChatHandler(pl).PSendSysMessage(LANG_SUMMONED_BY, GetNameLink().c_str());
+
// stop flight if need
if(pl->isInFlight())
{
@@ -2269,24 +2703,31 @@ bool ChatHandler::HandleGroupgoCommand(const char* args)
// 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;
}
+
bool ChatHandler::HandleGoTaxinodeCommand(const char* args)
{
Player* _player = m_session->GetPlayer();
+
if (!*args)
return false;
+
char* cNodeId = extractKeyFromLink((char*)args,"Htaxinode");
if (!cNodeId)
return false;
+
int32 i_nodeId = atoi(cNodeId);
if (!i_nodeId)
return false;
+
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(i_nodeId);
if (!node)
{
@@ -2294,6 +2735,7 @@ bool ChatHandler::HandleGoTaxinodeCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if ((node->x == 0.0f && node->y == 0.0f && node->z == 0.0f) ||
!MapManager::IsValidMapCoord(node->map_id,node->x,node->y,node->z))
{
@@ -2301,6 +2743,7 @@ bool ChatHandler::HandleGoTaxinodeCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// stop flight if need
if (_player->isInFlight())
{
@@ -2310,32 +2753,40 @@ bool ChatHandler::HandleGoTaxinodeCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
_player->TeleportTo(node->map_id, node->x, node->y, node->z, _player->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())
{
@@ -2345,23 +2796,31 @@ bool ChatHandler::HandleGoXYCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
Map const *map = MapManager::Instance().CreateBaseMap(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);
@@ -2370,12 +2829,14 @@ bool ChatHandler::HandleGoXYZCommand(const char* args)
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())
{
@@ -2385,50 +2846,68 @@ bool ChatHandler::HandleGoXYZCommand(const char* args)
// 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);
+
// prevent accept wrong numeric args
if ((x==0.0f && *px!='0') || (y==0.0f && *py!='0'))
return false;
+
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().CreateBaseMap(zoneEntry->mapid);
+
if(map->Instanceable())
{
PSendSysMessage(LANG_INVALID_ZONE_MAP,areaEntry->ID,areaEntry->area_name[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())
{
@@ -2438,35 +2917,44 @@ bool ChatHandler::HandleGoZoneXYCommand(const char* args)
// 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())
{
@@ -2476,19 +2964,26 @@ bool ChatHandler::HandleGoGridCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
Map const *map = MapManager::Instance().CreateBaseMap(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::HandleModifyDrunkCommand(const char* args)
{
if(!*args) return false;
+
uint32 drunklevel = (uint32)atoi(args);
if(drunklevel > 100)
drunklevel = 100;
+
uint16 drunkMod = drunklevel * 0xFFFF / 100;
+
m_session->GetPlayer()->SetDrunkValue(drunkMod);
+
return true;
}
diff --git a/src/game/Level2.cpp b/src/game/Level2.cpp
index f390eea6a16..b6cf14402c6 100644
--- a/src/game/Level2.cpp
+++ b/src/game/Level2.cpp
@@ -17,6 +17,7 @@
* 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 "ObjectMgr.h"
@@ -39,8 +40,10 @@
#include <fstream>
#include <map>
#include "GlobalEvents.h"
+
#include "TargetedMovementGenerator.h" // for HandleNpcUnFollowCommand
#include "CreatureGroups.h"
+
//mute player for some times
bool ChatHandler::HandleMuteCommand(const char* args)
{
@@ -49,36 +52,50 @@ bool ChatHandler::HandleMuteCommand(const char* args)
extractOptFirstArg((char*)args,&nameStr,&delayStr);
if(!delayStr)
return false;
+
char *mutereason = strtok(NULL, "\r");
std::string mutereasonstr = "No reason";
if(mutereason != NULL)
mutereasonstr = mutereason;
+
Player* target;
uint64 target_guid;
std::string target_name;
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false;
+
uint32 account_id = target ? target->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(target_guid);
+
// find only player from same account if any
if(!target)
{
if(WorldSession* session = sWorld.FindSession(account_id))
target = session->GetPlayer();
}
+
uint32 notspeaktime = (uint32) atoi(delayStr);
+
// must have strong lesser security level
if(HasLowerSecurity (target,target_guid,true))
return false;
+
time_t mutetime = time(NULL) + notspeaktime*60;
+
if (target)
target->GetSession()->m_muteTime = mutetime;
+
loginDatabase.PExecute("UPDATE account SET mutetime = " UI64FMTD " WHERE id = '%u'",uint64(mutetime), account_id );
+
if(target)
ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_DISABLED, notspeaktime, mutereasonstr.c_str());
+
std::string nameLink = playerLink(target_name);
+
PSendSysMessage(LANG_YOU_DISABLE_CHAT, nameLink.c_str(), notspeaktime, mutereasonstr.c_str());
+
return true;
}
+
//unmute player
bool ChatHandler::HandleUnmuteCommand(const char* args)
{
@@ -87,16 +104,20 @@ bool ChatHandler::HandleUnmuteCommand(const char* args)
std::string target_name;
if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
uint32 account_id = target ? target->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(target_guid);
+
// find only player from same account if any
if(!target)
{
if(WorldSession* session = sWorld.FindSession(account_id))
target = session->GetPlayer();
}
+
// must have strong lesser security level
if(HasLowerSecurity (target,target_guid,true))
return false;
+
if (target)
{
if(target->CanSpeak())
@@ -105,37 +126,50 @@ bool ChatHandler::HandleUnmuteCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
target->GetSession()->m_muteTime = 0;
}
+
loginDatabase.PExecute("UPDATE account SET mutetime = '0' WHERE id = '%u'", account_id );
+
if(target)
ChatHandler(target).PSendSysMessage(LANG_YOUR_CHAT_ENABLED);
+
std::string nameLink = playerLink(target_name);
+
PSendSysMessage(LANG_YOU_ENABLE_CHAT, nameLink.c_str());
return true;
}
+
bool ChatHandler::HandleGoTicketCommand(const char * args)
{
if(!*args)
return false;
+
char *cstrticket_id = strtok((char*)args, " ");
+
if(!cstrticket_id)
return false;
+
uint64 ticket_id = atoi(cstrticket_id);
if(!ticket_id)
return false;
+
GM_Ticket *ticket = objmgr.GetGMTicket(ticket_id);
if(!ticket)
{
SendSysMessage(LANG_COMMAND_TICKETNOTEXIST);
return true;
}
+
float x, y, z;
int mapid;
+
x = ticket->pos_x;
y = ticket->pos_y;
z = ticket->pos_z;
mapid = ticket->map;
+
Player* _player = m_session->GetPlayer();
if(_player->isInFlight())
{
@@ -144,20 +178,27 @@ bool ChatHandler::HandleGoTicketCommand(const char * args)
}
else
_player->SaveRecallPosition();
+
_player->TeleportTo(mapid, x, y, z, 1, 0);
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)
{
@@ -165,12 +206,14 @@ bool ChatHandler::HandleGoTriggerCommand(const char* args)
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())
{
@@ -180,20 +223,27 @@ bool ChatHandler::HandleGoTriggerCommand(const char* args)
// 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)
{
@@ -201,12 +251,14 @@ bool ChatHandler::HandleGoGraveyardCommand(const char* args)
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())
{
@@ -216,9 +268,11 @@ bool ChatHandler::HandleGoGraveyardCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
_player->TeleportTo(gy->map_id, gy->x, gy->y, gy->z, _player->GetOrientation());
return true;
}
+
/** \brief Teleport the GM to the specified creature
*
* .gocreature <GUID> --> TP using creature.guid
@@ -235,15 +289,19 @@ 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,"");
@@ -252,16 +310,20 @@ bool ChatHandler::HandleGoCreatureCommand(const char* args)
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)
{
@@ -275,6 +337,7 @@ bool ChatHandler::HandleGoCreatureCommand(const char* args)
}
}
//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)
{
@@ -286,19 +349,23 @@ bool ChatHandler::HandleGoCreatureCommand(const char* args)
{
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())
{
@@ -308,24 +375,31 @@ bool ChatHandler::HandleGoCreatureCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
_player->TeleportTo(mapid, x, y, z, ort);
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))
{
@@ -341,12 +415,14 @@ bool ChatHandler::HandleGoObjectCommand(const char* args)
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())
{
@@ -356,9 +432,11 @@ bool ChatHandler::HandleGoObjectCommand(const char* args)
// save only in non-flight case
else
_player->SaveRecallPosition();
+
_player->TeleportTo(mapid, x, y, z, ort);
return true;
}
+
bool ChatHandler::HandleGameObjectTargetCommand(const char* args)
{
Player* pl = m_session->GetPlayer();
@@ -370,7 +448,9 @@ bool ChatHandler::HandleGameObjectTargetCommand(const char* args)
char* cId = extractKeyFromLink((char*)args,"Hgameobject_entry");
if(!cId)
return false;
+
uint32 id = atol(cId);
+
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);
@@ -389,6 +469,7 @@ bool ChatHandler::HandleGameObjectTargetCommand(const char* args)
std::ostringstream eventFilter;
eventFilter << " AND (event IS NULL ";
bool initString = true;
+
for (GameEventMgr::ActiveEvents::const_iterator itr = activeEventsList.begin(); itr != activeEventsList.end(); ++itr)
{
if (initString)
@@ -399,24 +480,29 @@ bool ChatHandler::HandleGameObjectTargetCommand(const char* args)
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 10",
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;
}
+
bool found = false;
float x, y, z, o;
uint32 lowguid, id;
uint16 mapid, pool_id;
+
do
{
Field *fields = result->Fetch();
@@ -431,31 +517,41 @@ bool ChatHandler::HandleGameObjectTargetCommand(const char* args)
if (!pool_id || (pool_id && poolhandler.IsSpawnedObject(pool_id, lowguid, TYPEID_GAMEOBJECT)))
found = true;
} while( result->NextRow() && (!found) );
+
delete result;
+
if (!found)
{
PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
return false;
}
+
GameObjectInfo const* goI = objmgr.GetGameObjectInfo(id);
+
if (!goI)
{
PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
return false;
}
+
GameObject* target = m_session->GetPlayer()->GetMap()->GetGameObject(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;
}
+
//delete object by selection or guid
bool ChatHandler::HandleGameObjectDeleteCommand(const char* args)
{
@@ -463,19 +559,24 @@ bool ChatHandler::HandleGameObjectDeleteCommand(const char* args)
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)
{
@@ -486,14 +587,19 @@ bool ChatHandler::HandleGameObjectDeleteCommand(const char* args)
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::HandleGameObjectTurnCommand(const char* args)
{
@@ -501,21 +607,27 @@ bool ChatHandler::HandleGameObjectTurnCommand(const char* args)
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);
@@ -525,15 +637,20 @@ bool ChatHandler::HandleGameObjectTurnCommand(const char* args)
Player *chr = m_session->GetPlayer();
o = chr->GetOrientation();
}
+
obj->Relocate(obj->GetPositionX(), obj->GetPositionY(), obj->GetPositionZ(), o);
obj->UpdateRotationFields();
obj->DestroyForNearbyPlayers();
ObjectAccessor::UpdateObjectVisibility(obj);
+
obj->SaveToDB();
obj->Refresh();
+
PSendSysMessage(LANG_COMMAND_TURNOBJMESSAGE, obj->GetGUIDLow(), obj->GetGOInfo()->name, obj->GetGUIDLow(), o);
+
return true;
}
+
//move selected object
bool ChatHandler::HandleGameObjectMoveCommand(const char* args)
{
@@ -541,22 +658,28 @@ bool ChatHandler::HandleGameObjectMoveCommand(const char* args)
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();
@@ -568,44 +691,57 @@ bool ChatHandler::HandleGameObjectMoveCommand(const char* args)
{
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;
}
+
obj->Relocate(x, y, z, obj->GetOrientation());
obj->DestroyForNearbyPlayers();
ObjectAccessor::UpdateObjectVisibility(obj);
}
+
obj->SaveToDB();
obj->Refresh();
+
PSendSysMessage(LANG_COMMAND_MOVEOBJMESSAGE, obj->GetGUIDLow(), obj->GetGOInfo()->name, obj->GetGUIDLow());
+
return true;
}
+
//spawn go
bool ChatHandler::HandleGameObjectAddCommand(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 id = atol(cId);
if(!id)
return false;
+
char* spawntimeSecs = strtok(NULL, " ");
+
const GameObjectInfo *gInfo = objmgr.GetGameObjectInfo(id);
+
if (!gInfo)
{
PSendSysMessage(LANG_GAMEOBJECT_NOT_EXIST,id);
SetSentErrorMessage(true);
return false;
}
+
if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
{
// report to DB errors log as in loading case
@@ -614,40 +750,51 @@ bool ChatHandler::HandleGameObjectAddCommand(const char* args)
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();
+
GameObject* pGameObj = new GameObject;
uint32 db_lowGUID = objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT);
+
if(!pGameObj->Create(db_lowGUID, gInfo->id, map, chr->GetPhaseMaskForSpawn(), x, y, z, o, 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
{
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()),chr->GetPhaseMaskForSpawn());
+
// 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), gInfo->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,gInfo->name,db_lowGUID,x,y,z);
return true;
}
+
//set pahsemask for selected object
bool ChatHandler::HandleGameObjectPhaseCommand(const char* args)
{
@@ -655,19 +802,24 @@ bool ChatHandler::HandleGameObjectPhaseCommand(const char* args)
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* phaseStr = strtok (NULL, " ");
uint32 phasemask = phaseStr? atoi(phaseStr) : 0;
if ( phasemask == 0 )
@@ -676,20 +828,24 @@ bool ChatHandler::HandleGameObjectPhaseCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
obj->SetPhaseMask(phasemask,true);
obj->SaveToDB();
return true;
}
+
bool ChatHandler::HandleGameObjectNearCommand(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
@@ -701,51 +857,68 @@ bool ChatHandler::HandleGameObjectNearCommand(const char* args)
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_CHAT, guid, guid, gInfo->name, x, y, z, mapid);
+
++count;
} while (result->NextRow());
+
delete result;
}
+
PSendSysMessage(LANG_COMMAND_NEAROBJMESSAGE,distance,count);
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::HandleModifyRepCommand(const char * args)
{
if (!*args) return false;
+
Player* target = NULL;
target = getSelectedPlayer();
+
if(!target)
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(target, 0))
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]))
{
@@ -754,6 +927,7 @@ bool ChatHandler::HandleModifyRepCommand(const char * args)
if(!Utf8toWStr(rankStr,wrankStr))
return false;
wstrToLower( wrankStr );
+
int r = 0;
amount = -42000;
for (; r < MAX_REPUTATION_RANK; ++r)
@@ -761,10 +935,13 @@ bool ChatHandler::HandleModifyRepCommand(const char * args)
std::string rank = GetTrinityString(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, " ");
@@ -790,24 +967,29 @@ bool ChatHandler::HandleModifyRepCommand(const char * args)
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[GetSessionDbcLocale()], factionId);
SetSentErrorMessage(true);
return false;
}
+
target->GetReputationMgr().SetReputation(factionEntry,amount);
PSendSysMessage(LANG_COMMAND_MODIFY_REP, factionEntry->name[GetSessionDbcLocale()], factionId,
GetNameLink(target).c_str(), target->GetReputationMgr().GetReputation(factionEntry));
return true;
}
+
//-----------------------Npc Commands-----------------------
//add spawn of creature
bool ChatHandler::HandleNpcAddCommand(const char* args)
@@ -817,36 +999,46 @@ bool ChatHandler::HandleNpcAddCommand(const char* args)
char* charID = extractKeyFromLink((char*)args,"Hcreature_entry");
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, chr->GetPhaseMaskForSpawn(), id, 0, (uint32)teamval, x, y, z, o))
{
delete pCreature;
return false;
}
+
pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
+
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;
}
+
//add item in vendorlist
bool ChatHandler::HandleNpcAddVendorItemCommand(const char* args)
{
if (!*args)
return false;
+
char* pitem = extractKeyFromLink((char*)args,"Hitem");
if (!pitem)
{
@@ -854,34 +1046,46 @@ bool ChatHandler::HandleNpcAddVendorItemCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 itemId = atol(pitem);
+
char* fmaxcount = strtok(NULL, " "); //add maxcount, default: 0
uint32 maxcount = 0;
if (fmaxcount)
maxcount = atol(fmaxcount);
+
char* fincrtime = strtok(NULL, " "); //add incrtime, default: 0
uint32 incrtime = 0;
if (fincrtime)
incrtime = atol(fincrtime);
+
char* fextendedcost = strtok(NULL, " "); //add ExtendedCost, default: 0
uint32 extendedcost = fextendedcost ? atol(fextendedcost) : 0;
+
Creature* vendor = getSelectedCreature();
+
uint32 vendor_entry = vendor ? vendor->GetEntry() : 0;
+
if(!objmgr.IsVendorItemValid(vendor_entry,itemId,maxcount,incrtime,extendedcost,m_session->GetPlayer()))
{
SetSentErrorMessage(true);
return false;
}
+
objmgr.AddVendorItem(vendor_entry,itemId,maxcount,incrtime,extendedcost);
+
ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
+
PSendSysMessage(LANG_ITEM_ADDED_TO_LIST,itemId,pProto->Name1,maxcount,incrtime,extendedcost);
return true;
}
+
//del item from vendor list
bool ChatHandler::HandleNpcDelVendorItemCommand(const char* args)
{
if (!*args)
return false;
+
Creature* vendor = getSelectedCreature();
if (!vendor || !vendor->isVendor())
{
@@ -889,6 +1093,7 @@ bool ChatHandler::HandleNpcDelVendorItemCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
char* pitem = extractKeyFromLink((char*)args,"Hitem");
if (!pitem)
{
@@ -897,29 +1102,38 @@ bool ChatHandler::HandleNpcDelVendorItemCommand(const char* args)
return false;
}
uint32 itemId = atol(pitem);
+
if(!objmgr.RemoveVendorItem(vendor->GetEntry(),itemId))
{
PSendSysMessage(LANG_ITEM_NOT_IN_LIST,itemId);
SetSentErrorMessage(true);
return false;
}
+
ItemPrototype const* pProto = objmgr.GetItemPrototype(itemId);
+
PSendSysMessage(LANG_ITEM_DELETED_FROM_LIST,itemId,pProto->Name1);
return true;
}
+
//add move for creature
bool ChatHandler::HandleNpcAddMoveCommand(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)
{
@@ -936,11 +1150,16 @@ bool ChatHandler::HandleNpcAddMoveCommand(const char* args)
// 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->GetWaypointPath())
@@ -954,14 +1173,18 @@ bool ChatHandler::HandleNpcAddMoveCommand(const char* args)
}
pCreature->SaveToDB();
}
+
SendSysMessage(LANG_WAYPOINT_ADDED);
+
return true;
}
+
//change level of creature or pet
bool ChatHandler::HandleNpcChangeLevelCommand(const char* args)
{
if (!*args)
return false;
+
uint8 lvl = (uint8) atoi((char*)args);
if ( lvl < 1 || lvl > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) + 3)
{
@@ -969,6 +1192,7 @@ bool ChatHandler::HandleNpcChangeLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
Creature* pCreature = getSelectedCreature();
if(!pCreature)
{
@@ -976,6 +1200,7 @@ bool ChatHandler::HandleNpcChangeLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(pCreature->isPet())
{
if(((Pet*)pCreature)->getPetType()==HUNTER_PET)
@@ -992,72 +1217,95 @@ bool ChatHandler::HandleNpcChangeLevelCommand(const char* args)
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;
}
+
bool ChatHandler::HandleNpcDeleteCommand(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 = m_session->GetPlayer()->GetMap()->GetCreature(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->AddObjectToRemoveList();
+
SendSysMessage(LANG_COMMAND_DELCREATMESSAGE);
+
return true;
}
+
//move selected creature
bool ChatHandler::HandleNpcMoveCommand(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;
+
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)
{
@@ -1068,7 +1316,9 @@ bool ChatHandler::HandleNpcMoveCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 map_id = data->mapid;
+
if(m_session->GetPlayer()->GetMapId()!=map_id)
{
PSendSysMessage(LANG_COMMAND_CREATUREATSAMEMAP, lowguid);
@@ -1085,10 +1335,12 @@ bool ChatHandler::HandleNpcMoveCommand(const char* args)
{
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()))
@@ -1106,10 +1358,12 @@ bool ChatHandler::HandleNpcMoveCommand(const char* args)
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;
}
+
/**HandleNpcSetMoveTypeCommand
* Set the movement type for an NPC.<br/>
* <br/>
@@ -1126,6 +1380,7 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(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)
@@ -1136,14 +1391,19 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args)
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
@@ -1168,6 +1428,7 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args)
}
}
}
+
if(!type_str) // case .setmovetype $move_type (with selected creature)
{
type_str = guid_str;
@@ -1179,10 +1440,12 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args)
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)
{
@@ -1199,10 +1462,14 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args)
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")
@@ -1211,14 +1478,17 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args)
move_type = WAYPOINT_MOTION_TYPE;
else
return false;
+
// update movement type
//if(doNotDelete == false)
// WaypointMgr.DeletePath(lowguid);
+
if(pCreature)
{
// update movement type
if(doNotDelete == false)
pCreature->LoadPath(0);
+
pCreature->SetDefaultMovementType(move_type);
pCreature->GetMotionMaster()->Initialize();
if(pCreature->isAlive()) // dead creature will reset movement generator at respawn
@@ -1236,24 +1506,32 @@ bool ChatHandler::HandleNpcSetMoveTypeCommand(const char* args)
{
PSendSysMessage(LANG_MOVE_TYPE_SET_NODEL,type_str);
}
+
return true;
}
+
//set model of creature
bool ChatHandler::HandleNpcSetModelCommand(const char* args)
{
if (!*args)
return false;
+
uint32 displayId = (uint32) atoi((char*)args);
+
Creature *pCreature = getSelectedCreature();
+
if(!pCreature || pCreature->isPet())
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
pCreature->SetDisplayId(displayId);
pCreature->SetNativeDisplayId(displayId);
+
pCreature->SaveToDB();
+
return true;
}
//set faction of creature
@@ -1261,30 +1539,39 @@ bool ChatHandler::HandleNpcFactionIdCommand(const char* args)
{
if (!*args)
return false;
+
uint32 factionId = (uint32) atoi((char*)args);
+
if (!sFactionTemplateStore.LookupEntry(factionId))
{
PSendSysMessage(LANG_WRONG_FACTION, factionId);
SetSentErrorMessage(true);
return false;
}
+
Creature* pCreature = getSelectedCreature();
+
if(!pCreature)
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
pCreature->setFaction(factionId);
+
// faction is set in creature_template - not inside creature
+
// update in memory
if(CreatureInfo const *cinfo = pCreature->GetCreatureInfo())
{
const_cast<CreatureInfo*>(cinfo)->faction_A = factionId;
const_cast<CreatureInfo*>(cinfo)->faction_H = factionId;
}
+
// and DB
WorldDatabase.PExecuteLog("UPDATE creature_template SET faction_A = '%u', faction_H = '%u' WHERE entry = '%u'", factionId, factionId, pCreature->GetEntry());
+
return true;
}
//set spawn dist of creature
@@ -1292,21 +1579,26 @@ bool ChatHandler::HandleNpcSpawnDistCommand(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();
@@ -1315,6 +1607,7 @@ bool ChatHandler::HandleNpcSpawnDistCommand(const char* args)
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;
@@ -1324,25 +1617,33 @@ bool ChatHandler::HandleNpcSpawnTimeCommand(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;
}
//npc follow handling
@@ -1350,14 +1651,17 @@ bool ChatHandler::HandleNpcFollowCommand(const char* /*args*/)
{
Player *player = m_session->GetPlayer();
Creature *creature = getSelectedCreature();
+
if(!creature)
{
PSendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
// Follow player - Using pet's default dist and angle
creature->GetMotionMaster()->MoveFollow(player, PET_FOLLOW_DIST, creature->GetFollowAngle());
+
PSendSysMessage(LANG_CREATURE_FOLLOW_YOU_NOW, creature->GetName());
return true;
}
@@ -1366,12 +1670,14 @@ bool ChatHandler::HandleNpcUnFollowCommand(const char* /*args*/)
{
Player *player = m_session->GetPlayer();
Creature *creature = getSelectedCreature();
+
if(!creature)
{
PSendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
if (/*creature->GetMotionMaster()->empty() ||*/
creature->GetMotionMaster()->GetCurrentMovementGeneratorType ()!=TARGETED_MOTION_TYPE)
{
@@ -1379,16 +1685,20 @@ bool ChatHandler::HandleNpcUnFollowCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
TargetedMovementGenerator<Creature> const* mgen
= static_cast<TargetedMovementGenerator<Creature> const*>((creature->GetMotionMaster()->top()));
+
if(mgen->GetTarget()!=player)
{
PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU);
SetSentErrorMessage(true);
return false;
}
+
// reset movement
creature->GetMotionMaster()->MovementExpired(true);
+
PSendSysMessage(LANG_CREATURE_NOT_FOLLOW_YOU_NOW, creature->GetName());
return true;
}
@@ -1402,20 +1712,25 @@ bool ChatHandler::HandleNpcTameCommand(const char* /*args*/)
SetSentErrorMessage (true);
return false;
}
+
Player *player = m_session->GetPlayer ();
+
if(player->GetPetGUID ())
{
SendSysMessage (LANG_YOU_ALREADY_HAVE_PET);
SetSentErrorMessage (true);
return false;
}
+
CreatureInfo const* cInfo = creatureTarget->GetCreatureInfo();
+
if (!cInfo->isTameable (player->CanTameExoticPets()))
{
PSendSysMessage (LANG_CREATURE_NON_TAMEABLE,cInfo->Entry);
SetSentErrorMessage (true);
return false;
}
+
// Everything looks OK, create new pet
Pet* pet = player->CreateTamedPetFrom (creatureTarget);
if (!pet)
@@ -1424,24 +1739,33 @@ bool ChatHandler::HandleNpcTameCommand(const char* /*args*/)
SetSentErrorMessage (true);
return false;
}
+
// place pet before player
float x,y,z;
player->GetClosePoint (x,y,z,creatureTarget->GetObjectSize (),CONTACT_DISTANCE);
pet->Relocate (x,y,z,M_PI-player->GetOrientation ());
+
// set pet to defensive mode by default (some classes can't control controlled pets in fact).
pet->SetReactState(REACT_DEFENSIVE);
+
// calculate proper level
uint32 level = (creatureTarget->getLevel() < (player->getLevel() - 5)) ? (player->getLevel() - 5) : creatureTarget->getLevel();
+
// prepare visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);
+
// add to world
pet->GetMap()->Add((Creature*)pet);
+
// visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
+
// caster have pet now
player->SetMinion(pet, true);
+
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
player->PetSpellInitialize();
+
return true;
}
//npc phasemask handling
@@ -1450,6 +1774,7 @@ bool ChatHandler::HandleNpcSetPhaseCommand(const char* args)
{
if (!*args)
return false;
+
uint32 phasemask = (uint32) atoi((char*)args);
if ( phasemask == 0 )
{
@@ -1457,6 +1782,7 @@ bool ChatHandler::HandleNpcSetPhaseCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
Creature* pCreature = getSelectedCreature();
if(!pCreature)
{
@@ -1464,9 +1790,12 @@ bool ChatHandler::HandleNpcSetPhaseCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
pCreature->SetPhaseMask(phasemask,true);
+
if(!pCreature->isPet())
pCreature->SaveToDB();
+
return true;
}
//npc deathstate handling
@@ -1474,6 +1803,7 @@ bool ChatHandler::HandleNpcSetDeathStateCommand(const char* args)
{
if (!*args)
return false;
+
Creature* pCreature = getSelectedCreature();
if(!pCreature || pCreature->isPet())
{
@@ -1481,6 +1811,7 @@ bool ChatHandler::HandleNpcSetDeathStateCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if (strncmp(args, "on", 3) == 0)
pCreature->SetDeadByDefault(true);
else if (strncmp(args, "off", 4) == 0)
@@ -1491,21 +1822,27 @@ bool ChatHandler::HandleNpcSetDeathStateCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
pCreature->SaveToDB();
pCreature->Respawn();
+
return true;
}
+
//TODO: NpcCommands that need to be fixed :
+
bool ChatHandler::HandleNpcNameCommand(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]!=' ')
@@ -1514,6 +1851,7 @@ bool ChatHandler::HandleNpcNameCommand(const char* /*args*/)
return false;
}
}
+
uint64 guid;
guid = m_session->GetPlayer()->GetSelection();
if (guid == 0)
@@ -1521,29 +1859,39 @@ bool ChatHandler::HandleNpcNameCommand(const char* /*args*/)
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::HandleNpcSubNameCommand(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]!=' ')
@@ -1559,43 +1907,58 @@ bool ChatHandler::HandleNpcSubNameCommand(const char* /*args*/)
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);
+
if(srcslot==dstslot)
return true;
+
if(!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,srcslot))
return false;
+
if(!m_session->GetPlayer()->IsValidPos(INVENTORY_SLOT_BAG_0,dstslot))
return false;
+
uint16 src = ((INVENTORY_SLOT_BAG_0 << 8) | srcslot);
uint16 dst = ((INVENTORY_SLOT_BAG_0 << 8) | dstslot);
+
m_session->GetPlayer()->SwapItem( src, dst );
+
return true;
}
+
//demorph player or unit
bool ChatHandler::HandleDeMorphCommand(const char* /*args*/)
{
@@ -1603,27 +1966,37 @@ bool ChatHandler::HandleDeMorphCommand(const char* /*args*/)
if(!target)
target = m_session->GetPlayer();
+
// check online security
else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0))
return false;
+
target->DeMorph();
+
return true;
}
+
//morph creature or player
bool ChatHandler::HandleModifyMorphCommand(const char* args)
{
if (!*args)
return false;
+
uint16 display_id = (uint16)atoi((char*)args);
+
Unit *target = getSelectedUnit();
if(!target)
target = m_session->GetPlayer();
+
// check online security
else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0))
return false;
+
target->SetDisplayId(display_id);
+
return true;
}
+
//kick player
bool ChatHandler::HandleKickPlayerCommand(const char *args)
{
@@ -1635,6 +2008,7 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args)
reason = kickReason;
if(m_session)
kicker = m_session->GetPlayer()->GetName();
+
if(!kickName)
{
Player* player = getSelectedPlayer();
@@ -1644,15 +2018,18 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
if(player==m_session->GetPlayer())
{
SendSysMessage(LANG_COMMAND_KICKSELF);
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(player, 0))
return false;
+
if(sWorld.getConfig(CONFIG_SHOW_KICK_IN_WORLD) == 1)
{
sWorld.SendWorldText(LANG_COMMAND_KICKMESSAGE, player->GetName(), kicker.c_str(), reason.c_str());
@@ -1661,6 +2038,7 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args)
{
PSendSysMessage(LANG_COMMAND_KICKMESSAGE, player->GetName(), kicker.c_str(), reason.c_str());
}
+
player->GetSession()->KickPlayer();
}
else
@@ -1672,12 +2050,14 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
if(m_session && name==m_session->GetPlayer()->GetName())
{
SendSysMessage(LANG_COMMAND_KICKSELF);
SetSentErrorMessage(true);
return false;
}
+
Player* player = objmgr.GetPlayer(kickName);
if(!player)
{
@@ -1685,13 +2065,16 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
if(HasLowerSecurity(player, 0))
{
SendSysMessage(LANG_YOURS_SECURITY_IS_LOW); //maybe replacement string for this later on
SetSentErrorMessage(true);
return false;
}
+
std::string nameLink = playerLink(name);
+
if(sWorld.KickPlayer(name))
{
if(sWorld.getConfig(CONFIG_SHOW_KICK_IN_WORLD) == 1)
@@ -1712,35 +2095,45 @@ bool ChatHandler::HandleKickPlayerCommand(const char *args)
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
if (m_session && target==m_session->GetPlayer())
{
SendSysMessage(LANG_COMMAND_KICKSELF);
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
// send before target pointer invalidate
PSendSysMessage(LANG_COMMAND_KICKMESSAGE,GetNameLink(target).c_str());
target->GetSession()->KickPlayer();
return true;
}
+
//set temporary phase mask for player
bool ChatHandler::HandleModifyPhaseCommand(const char* args)
{
if (!*args)
return false;
+
uint32 phasemask = (uint32)atoi((char*)args);
+
Unit *target = getSelectedUnit();
if(!target)
target = m_session->GetPlayer();
+
// check online security
else if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0))
return false;
+
target->SetPhaseMask(phasemask,true);
+
return true;
}
+
//show info of player
bool ChatHandler::HandlePInfoCommand(const char* args)
{
@@ -1749,6 +2142,7 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
std::string target_name;
if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
uint32 accId = 0;
uint32 money = 0;
uint32 total_player_time = 0;
@@ -1756,12 +2150,14 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
uint32 latency = 0;
uint8 race;
uint8 Class;
+
// get additional information from Player object
if(target)
{
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
accId = target->GetSession()->GetAccountId();
money = target->GetMoney();
total_player_time = target->GetTotalPlayedTime();
@@ -1776,10 +2172,12 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
// check offline security
if (HasLowerSecurity(NULL, target_guid))
return false;
+
// 0 1 2 3 4 5
QueryResult *result = CharacterDatabase.PQuery("SELECT totaltime, level, money, account, race, class FROM characters WHERE guid = '%u'", GUID_LOPART(target_guid));
if (!result)
return false;
+
Field *fields = result->Fetch();
total_player_time = fields[0].GetUInt32();
level = fields[1].GetUInt32();
@@ -1789,11 +2187,13 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
Class = fields[5].GetUInt8();
delete result;
}
+
std::string username = GetTrinityString(LANG_ERROR);
std::string email = GetTrinityString(LANG_ERROR);
std::string last_ip = GetTrinityString(LANG_ERROR);
uint32 security = 0;
std::string last_login = GetTrinityString(LANG_ERROR);
+
QueryResult* result = loginDatabase.PQuery("SELECT username,gmlevel,email,last_ip,last_login FROM account WHERE id = '%u'",accId);
if(result)
{
@@ -1801,8 +2201,10 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
username = fields[0].GetCppString();
security = fields[1].GetUInt32();
email = fields[2].GetCppString();
+
if(email.empty())
email = "-";
+
if(!m_session || m_session->GetSecurity() >= security)
{
last_ip = fields[3].GetCppString();
@@ -1813,10 +2215,14 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
last_ip = "-";
last_login = "-";
}
+
delete result;
}
+
std::string nameLink = playerLink(target_name);
+
PSendSysMessage(LANG_PINFO_ACCOUNT, (target?"":GetTrinityString(LANG_OFFLINE)), nameLink.c_str(), GUID_LOPART(target_guid), username.c_str(), accId, email.c_str(), security, last_ip.c_str(), last_login.c_str(), latency);
+
std::string race_s, Class_s;
switch(race)
{
@@ -1844,14 +2250,18 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
case CLASS_WARLOCK: Class_s = "Warlock"; break;
case CLASS_DRUID: Class_s = "Druid"; break;
}
+
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, race_s.c_str(), Class_s.c_str(), timeStr.c_str(), level, gold, silv, copp);
+
return true;
}
+
/////WAYPOINT COMMANDS
+
/**
* Add a waypoint to a creature.
*
@@ -1876,13 +2286,17 @@ bool ChatHandler::HandlePInfoCommand(const char* args)
bool ChatHandler::HandleWpAddCommand(const char* args)
{
sLog.outDebug("DEBUG: HandleWpAddCommand");
+
// optional
char* path_number = NULL;
uint32 pathid = 0;
+
if(*args)
path_number = strtok((char*)args, " ");
+
uint32 point = 0;
Creature* target = getSelectedCreature();
+
if (!path_number)
{
if(target)
@@ -1898,62 +2312,81 @@ bool ChatHandler::HandleWpAddCommand(const char* args)
}
else
pathid = atoi(path_number);
+
// path_id -> ID of the Path
// point -> number of the waypoint (if not 0)
+
if(!pathid)
{
sLog.outDebug("DEBUG: HandleWpAddCommand - Current creature haven't loaded path.");
PSendSysMessage("%s%s|r", "|cffff33ff", "Current creature haven't loaded path.");
return true;
}
+
sLog.outDebug("DEBUG: HandleWpAddCommand - point == 0");
+
QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM waypoint_data WHERE id = '%u'",pathid);
+
if( result )
{
point = (*result)[0].GetUInt32();
delete result;
}
+
Player* player = m_session->GetPlayer();
Map *map = player->GetMap();
+
WorldDatabase.PExecuteLog("INSERT INTO waypoint_data (id, point, position_x, position_y, position_z) VALUES ('%u','%u','%f', '%f', '%f')",
pathid, point+1, player->GetPositionX(), player->GetPositionY(), player->GetPositionZ());
+
PSendSysMessage("%s%s%u%s%u%s|r", "|cff00ff00", "PathID: |r|cff00ffff", pathid, "|r|cff00ff00: Waypoint |r|cff00ffff", point,"|r|cff00ff00 created. ");
return true;
} // HandleWpAddCommand
+
bool ChatHandler::HandleWpLoadPathCommand(const char *args)
{
if(!*args)
return false;
+
// optional
char* path_number = NULL;
+
if(*args)
path_number = strtok((char*)args, " ");
+
uint32 pathid = 0;
uint32 guidlow = 0;
Creature* target = getSelectedCreature();
+
// Did player provide a path_id?
if (!path_number)
sLog.outDebug("DEBUG: HandleWpLoadPathCommand - No path number provided");
+
if(!target)
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
if(target->GetEntry() == 1)
{
PSendSysMessage("%s%s|r", "|cffff33ff", "You want to load path to a waypoint? Aren't you?");
SetSentErrorMessage(true);
return false;
}
+
pathid = atoi(path_number);
+
if(!pathid)
{
PSendSysMessage("%s%s|r", "|cffff33ff", "No vallid path number provided.");
return true;
}
+
guidlow = target->GetDBTableGUIDLow();
QueryResult *result = WorldDatabase.PQuery( "SELECT guid FROM creature_addon WHERE guid = '%u'",guidlow);
+
if( result )
{
WorldDatabase.PExecute("UPDATE creature_addon SET path_id = '%u' WHERE guid = '%u'", pathid, guidlow);
@@ -1961,33 +2394,43 @@ bool ChatHandler::HandleWpLoadPathCommand(const char *args)
}
else
WorldDatabase.PExecute("INSERT INTO creature_addon(guid,path_id) VALUES ('%u','%u')", guidlow, pathid);
+
WorldDatabase.PExecute("UPDATE creature SET MovementType = '%u' WHERE guid = '%u'", WAYPOINT_MOTION_TYPE, guidlow);
+
target->LoadPath(pathid);
target->SetDefaultMovementType(WAYPOINT_MOTION_TYPE);
target->GetMotionMaster()->Initialize();
target->MonsterSay("Path loaded.",0,0);
+
return true;
}
+
bool ChatHandler::HandleReloadAllPaths(const char* args)
{
if(!*args)
return false;
+
uint32 id = atoi(args);
+
if(!id)
return false;
+
PSendSysMessage("%s%s|r|cff00ffff%u|r", "|cff00ff00", "Loading Path: ", id);
WaypointMgr.UpdatePath(id);
return true;
}
+
bool ChatHandler::HandleWpUnLoadPathCommand(const char *args)
{
uint32 guidlow = 0;
Creature* target = getSelectedCreature();
+
if(!target)
{
PSendSysMessage("%s%s|r", "|cff33ffff", "You must select target.");
return true;
}
+
if(target->GetCreatureAddon())
{
if(target->GetCreatureAddon()->path_id != 0)
@@ -2006,23 +2449,30 @@ bool ChatHandler::HandleWpUnLoadPathCommand(const char *args)
}
return true;
}
+
bool ChatHandler::HandleWpEventCommand(const char* args)
{
if(!*args)
return false;
+
char* show_str = strtok((char*)args, " ");
std::string show = show_str;
+
// Check
if( (show != "add") && (show != "mod") && (show != "del") && (show != "listid")) return false;
+
char* arg_id = strtok(NULL, " ");
uint32 id = 0;
+
if(show == "add")
{
if(arg_id)
id = atoi(arg_id);
+
if(id)
{
QueryResult *result = WorldDatabase.PQuery( "SELECT id FROM waypoint_scripts WHERE guid = %u", id);
+
if( !result )
{
WorldDatabase.PExecute("INSERT INTO waypoint_scripts(guid)VALUES(%u)", id);
@@ -2041,8 +2491,10 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
WorldDatabase.PExecute("INSERT INTO waypoint_scripts(guid)VALUES(%u)", id+1);
PSendSysMessage("%s%s%u|r", "|cff00ff00","Wp Event: New waypoint event added: |r|cff00ffff", id+1);
}
+
return true;
}
+
if(show == "listid")
{
if(!arg_id)
@@ -2050,17 +2502,23 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
PSendSysMessage("%s%s|r", "|cff33ffff","Wp Event: You must provide waypoint script id.");
return true;
}
+
id = atoi(arg_id);
+
uint32 a2, a3, a4, a5, a6;
float a8, a9, a10, a11;
char const* a7;
+
QueryResult *result = WorldDatabase.PQuery( "SELECT guid, delay, command, datalong, datalong2, dataint, x, y, z, o FROM waypoint_scripts WHERE id = %u", id);
+
if( !result )
{
PSendSysMessage("%s%s%u|r", "|cff33ffff", "Wp Event: No waypoint scripts found on id: ", id);
return true;
}
+
Field *fields;
+
do
{
fields = result->Fetch();
@@ -2074,15 +2532,20 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
a9 = fields[7].GetFloat();
a10 = fields[8].GetFloat();
a11 = fields[9].GetFloat();
+
PSendSysMessage("|cffff33ffid:|r|cff00ffff %u|r|cff00ff00, guid: |r|cff00ffff%u|r|cff00ff00, delay: |r|cff00ffff%u|r|cff00ff00, command: |r|cff00ffff%u|r|cff00ff00, datalong: |r|cff00ffff%u|r|cff00ff00, datalong2: |r|cff00ffff%u|r|cff00ff00, datatext: |r|cff00ffff%s|r|cff00ff00, posx: |r|cff00ffff%f|r|cff00ff00, posy: |r|cff00ffff%f|r|cff00ff00, posz: |r|cff00ffff%f|r|cff00ff00, orientation: |r|cff00ffff%f|r", id, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11);
}
while(result->NextRow());
+
delete result;
}
+
if(show == "del")
{
id = atoi(arg_id);
+
QueryResult *result = WorldDatabase.PQuery( "SELECT guid FROM waypoint_scripts WHERE guid = %u", id);
+
if( result )
{
WorldDatabase.PExecuteLog("DELETE FROM waypoint_scripts WHERE guid = %u", id);
@@ -2091,8 +2554,10 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
}
else
PSendSysMessage("|cffff33ffWp Event: ERROR: you have selected a non existing script: %u|r", id);
+
return true;
}
+
if(show == "mod")
{
if(!arg_id)
@@ -2100,19 +2565,25 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
SendSysMessage("|cffff33ffERROR: Waypoint script guid not present.|r");
return true;
}
+
id = atoi(arg_id);
+
if(!id)
{
SendSysMessage("|cffff33ffERROR: No vallid waypoint script id not present.|r");
return true;
}
+
char* arg_2 = strtok(NULL," ");
+
if(!arg_2)
{
SendSysMessage("|cffff33ffERROR: No argument present.|r");
return true;
}
+
std::string arg_string = arg_2;
+
if( (arg_string != "setid") && (arg_string != "delay") && (arg_string != "command")
&& (arg_string != "datalong") && (arg_string != "datalong2") && (arg_string != "dataint") && (arg_string != "posx")
&& (arg_string != "posy") && (arg_string != "posz") && (arg_string != "orientation"))
@@ -2120,15 +2591,19 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
SendSysMessage("|cffff33ffERROR: No valid argument present.|r");
return true;
}
+
char* arg_3;
std::string arg_str_2 = arg_2;
arg_3 = strtok(NULL," ");
+
if(!arg_3)
{
SendSysMessage("|cffff33ffERROR: No additional argument present.|r");
return true;
}
+
float coord;
+
if(arg_str_2 == "setid")
{
uint32 newid = atoi(arg_3);
@@ -2139,12 +2614,15 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
else
{
QueryResult *result = WorldDatabase.PQuery("SELECT id FROM waypoint_scripts WHERE guid='%u'",id);
+
if(!result)
{
SendSysMessage("|cffff33ffERROR: You have selected an non existing waypoint script guid.|r");
return true;
}
+
delete result;
+
if(arg_str_2 == "posx")
{
coord = atof(arg_3);
@@ -2196,17 +2674,21 @@ bool ChatHandler::HandleWpEventCommand(const char* args)
}
return true;
}
+
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!
@@ -2216,8 +2698,10 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
{
return false;
}
+
// Next arg is: <PATHID> <WPNUM> <ARGUMENT>
char* arg_str = NULL;
+
// Did user provide a GUID
// or did the user select a creature?
// -> variable lowguid is filled with the GUID of the NPC
@@ -2225,15 +2709,18 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
uint32 point = 0;
uint32 wpGuid = 0;
Creature* target = getSelectedCreature();
+
if(!target || target->GetEntry() != VISUAL_WAYPOINT)
{
SendSysMessage("|cffff33ffERROR: You must select a waypoint.|r");
return false;
}
+
sLog.outDebug("DEBUG: HandleWpModifyCommand - User did select an NPC");
// The visual waypoint
Creature* wpCreature = NULL;
wpGuid = target->GetGUIDLow();
+
// Did the user select a visual spawnpoint?
if(wpGuid)
wpCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
@@ -2249,9 +2736,11 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
{
QueryResult *result =
WorldDatabase.PQuery( "SELECT id, point FROM waypoint_data WHERE wpguid = %u", wpGuid);
+
if(!result)
{
sLog.outDebug("DEBUG: HandleWpModifyCommand - No waypoint found - used 'wpguid'");
+
PSendSysMessage(LANG_WAYPOINT_NOTFOUNDSEARCH, target->GetGUIDLow());
// Select waypoint number from database
// Since we compare float values, we have to deal with
@@ -2269,6 +2758,7 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
}
}
sLog.outDebug("DEBUG: HandleWpModifyCommand - After getting wpGuid");
+
do
{
Field *fields = result->Fetch();
@@ -2276,6 +2766,7 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
point = fields[1].GetUInt32();
}
while( result->NextRow() );
+
// Cleanup memory
sLog.outDebug("DEBUG: HandleWpModifyCommand - Cleanup memory");
delete result;
@@ -2283,18 +2774,23 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
// Text is enclosed in "<>", all other arguments not
arg_str = strtok((char*)NULL, " ");
}
+
sLog.outDebug("DEBUG: HandleWpModifyCommand - Parameters parsed - now execute the command");
+
// Check for argument
if(show != "del" && show != "move" && arg_str == NULL)
{
PSendSysMessage(LANG_WAYPOINT_ARGUMENTREQ, show_str);
return false;
}
+
if(show == "del" && target)
{
PSendSysMessage("|cff00ff00DEBUG: wp modify del, PathID: |r|cff00ffff%u|r", pathid);
+
// wpCreature
Creature* wpCreature = NULL;
+
if( wpGuid != 0 )
{
wpCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpGuid, VISUAL_WAYPOINT, HIGHGUID_UNIT));
@@ -2302,16 +2798,20 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
wpCreature->DeleteFromDB();
wpCreature->AddObjectToRemoveList();
}
+
WorldDatabase.PExecuteLog("DELETE FROM waypoint_data WHERE id='%u' AND point='%u'",
pathid, point);
WorldDatabase.PExecuteLog("UPDATE waypoint_data SET point=point-1 WHERE id='%u' AND point>'%u'",
pathid, point);
+
PSendSysMessage(LANG_WAYPOINT_REMOVED);
return true;
} // del
+
if(show == "move" && target)
{
PSendSysMessage("|cff00ff00DEBUG: wp move, PathID: |r|cff00ffff%u|r", pathid);
+
Player *chr = m_session->GetPlayer();
Map *map = chr->GetMap();
{
@@ -2334,20 +2834,25 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
delete wpCreature2;
return false;
}
+
wpCreature2->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
wpCreature2->LoadFromDB(wpCreature2->GetDBTableGUIDLow(), map);
map->Add(wpCreature2);
//MapManager::Instance().GetMap(npcCreature->GetMapId())->Add(wpCreature2);
}
+
WorldDatabase.PExecuteLog("UPDATE waypoint_data SET position_x = '%f',position_y = '%f',position_z = '%f' where id = '%u' AND point='%u'",
chr->GetPositionX(), chr->GetPositionY(), chr->GetPositionZ(), pathid, point );
+
PSendSysMessage(LANG_WAYPOINT_CHANGED);
}
return true;
} // move
+
const char *text = arg_str;
+
if( text == 0 )
{
// show_str check for present in list of correct values, no sql injection possible
@@ -2362,36 +2867,46 @@ bool ChatHandler::HandleWpModifyCommand(const char* args)
WorldDatabase.PExecuteLog("UPDATE waypoint_data SET %s='%s' WHERE id='%u' AND point='%u'",
show_str, text2.c_str(), pathid, point);
}
+
PSendSysMessage(LANG_WAYPOINT_CHANGED_NO, show_str);
return true;
}
+
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);
+
uint32 pathid = 0;
Creature* target = getSelectedCreature();
+
// Did player provide a PathID?
+
if (!guid_str)
{
sLog.outDebug("DEBUG: HandleWpShowCommand: !guid_str");
// No PathID provided
// -> Player must have selected a creature
+
if(!target)
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
pathid = target->GetWaypointPath();
}
else
@@ -2402,13 +2917,19 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
// -> Creature selection is ignored <-
if(target)
SendSysMessage(LANG_WAYPOINT_CREATSELECTED);
+
pathid = atoi((char*)guid_str);
}
+
sLog.outDebug("DEBUG: HandleWpShowCommand: danach");
+
std::string show = show_str;
uint32 Maxpoint;
+
sLog.outDebug("DEBUG: HandleWpShowCommand: PathID: %u", pathid);
+
//PSendSysMessage("wpshow - show: %s", show);
+
// Show info for the selected waypoint
if(show == "info")
{
@@ -2419,12 +2940,15 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
QueryResult *result = WorldDatabase.PQuery( "SELECT id, point, delay, move_flag, action, action_chance FROM waypoint_data WHERE wpguid = %u", target->GetGUIDLow());
+
if(!result)
{
SendSysMessage(LANG_WAYPOINT_NOTFOUNDDBPROBLEM);
return true;
}
+
SendSysMessage("|cff00ffffDEBUG: wp show info:|r");
do
{
@@ -2435,6 +2959,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
uint32 flag = fields[3].GetUInt32();
uint32 ev_id = fields[4].GetUInt32();
uint32 ev_chance = fields[5].GetUInt32();
+
PSendSysMessage("|cff00ff00Show info: for current point: |r|cff00ffff%u|r|cff00ff00, Path ID: |r|cff00ffff%u|r", point, pathid);
PSendSysMessage("|cff00ff00Show info: delay: |r|cff00ffff%u|r", delay);
PSendSysMessage("|cff00ff00Show info: Move flag: |r|cff00ffff%u|r", flag);
@@ -2442,22 +2967,28 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
PSendSysMessage("|cff00ff00Show info: Event chance: |r|cff00ffff%u|r", ev_chance);
}
while( result->NextRow() );
+
// Cleanup memory
delete result;
return true;
}
+
if(show == "on")
{
QueryResult *result = WorldDatabase.PQuery( "SELECT point, position_x,position_y,position_z FROM waypoint_data WHERE id = '%u'", pathid);
+
if(!result)
{
SendSysMessage("|cffff33ffPath no found.|r");
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage("|cff00ff00DEBUG: wp on, PathID: |cff00ffff%u|r", pathid);
+
// Delete all visuals for this NPC
QueryResult *result2 = WorldDatabase.PQuery( "SELECT wpguid FROM waypoint_data WHERE id = '%u' and wpguid <> 0", pathid);
+
if(result2)
{
bool hasError = false;
@@ -2466,6 +2997,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
Field *fields = result2->Fetch();
uint32 wpguid = fields[0].GetUInt32();
Creature* pCreature = m_session->GetPlayer()->GetMap()->GetCreature(MAKE_NEW_GUID(wpguid,VISUAL_WAYPOINT,HIGHGUID_UNIT));
+
if(!pCreature)
{
PSendSysMessage(LANG_WAYPOINT_NOTREMOVED, wpguid);
@@ -2478,9 +3010,12 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
pCreature->DeleteFromDB();
pCreature->AddObjectToRemoveList();
}
+
}
while( result2->NextRow() );
+
delete result2;
+
if( hasError )
{
PSendSysMessage(LANG_WAYPOINT_TOOFAR1);
@@ -2488,6 +3023,7 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
PSendSysMessage(LANG_WAYPOINT_TOOFAR3);
}
}
+
do
{
Field *fields = result->Fetch();
@@ -2495,10 +3031,13 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
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, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o))
{
@@ -2507,13 +3046,16 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
delete result;
return false;
}
+
sLog.outDebug("DEBUG: UPDATE waypoint_data SET wpguid = '%u");
// set "wpguid" column to the visual waypoint
WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '%u' WHERE id = '%u' and point = '%u'", wpCreature->GetGUIDLow(), pathid, point);
+
wpCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
// To call _LoadGoods(); _LoadQuests(); CreateTrainerSpells();
wpCreature->LoadFromDB(wpCreature->GetDBTableGUIDLow(),map);
map->Add(wpCreature);
+
if(target)
{
wpCreature->SetDisplayId(target->GetDisplayId());
@@ -2521,14 +3063,17 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
}
}
while( result->NextRow() );
+
SendSysMessage("|cff00ff00Showing the current creature's path.|r");
// Cleanup memory
delete result;
return true;
}
+
if(show == "first")
{
PSendSysMessage("|cff00ff00DEBUG: wp first, GUID: %u|r", pathid);
+
QueryResult *result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM waypoint_data WHERE point='1' AND id = '%u'",pathid);
if(!result)
{
@@ -2536,14 +3081,17 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
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, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o))
{
@@ -2552,29 +3100,36 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
delete result;
return false;
}
+
pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
map->Add(pCreature);
+
if(target)
{
pCreature->SetDisplayId(target->GetDisplayId());
pCreature->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5);
}
+
// Cleanup memory
delete result;
return true;
}
+
if(show == "last")
{
PSendSysMessage("|cff00ff00DEBUG: wp last, PathID: |r|cff00ffff%u|r", pathid);
+
QueryResult *result = WorldDatabase.PQuery( "SELECT MAX(point) FROM waypoint_data WHERE id = '%u'",pathid);
if( result )
{
Maxpoint = (*result)[0].GetUInt32();
+
delete result;
}
else
Maxpoint = 0;
+
result = WorldDatabase.PQuery( "SELECT position_x,position_y,position_z FROM waypoint_data WHERE point ='%u' AND id = '%u'",Maxpoint, pathid);
if(!result)
{
@@ -2587,9 +3142,11 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
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, chr->GetPhaseMaskForSpawn(), id, 0, 0, x, y, z, o))
{
@@ -2598,18 +3155,22 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
delete result;
return false;
}
+
pCreature->SaveToDB(map->GetId(), (1 << map->GetSpawnMode()), chr->GetPhaseMaskForSpawn());
pCreature->LoadFromDB(pCreature->GetDBTableGUIDLow(), map);
map->Add(pCreature);
+
if(target)
{
pCreature->SetDisplayId(target->GetDisplayId());
pCreature->SetFloatValue(OBJECT_FIELD_SCALE_X, 0.5);
}
+
// Cleanup memory
delete result;
return true;
}
+
if(show == "off")
{
QueryResult *result = WorldDatabase.PQuery("SELECT guid FROM creature WHERE id = '%u'", 1);
@@ -2642,21 +3203,26 @@ bool ChatHandler::HandleWpShowCommand(const char* args)
// set "wpguid" column to "empty" - no visual waypoint spawned
WorldDatabase.PExecuteLog("UPDATE waypoint_data SET wpguid = '0'");
//WorldDatabase.PExecuteLog("UPDATE creature_movement SET wpguid = '0' WHERE 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("|cffff33ffDEBUG: wpshow - no valid command found|r");
return true;
}
+
//////////// WAYPOINT COMMANDS //
+
//rename characters
bool ChatHandler::HandleCharacterRenameCommand(const char* args)
{
@@ -2665,11 +3231,13 @@ bool ChatHandler::HandleCharacterRenameCommand(const char* args)
std::string target_name;
if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
if(target)
{
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
PSendSysMessage(LANG_RENAME_PLAYER, GetNameLink(target).c_str());
target->SetAtLoginFlag(AT_LOGIN_RENAME);
}
@@ -2678,12 +3246,16 @@ bool ChatHandler::HandleCharacterRenameCommand(const char* args)
// check offline security
if (HasLowerSecurity(NULL, target_guid))
return false;
+
std::string oldNameLink = playerLink(target_name);
+
PSendSysMessage(LANG_RENAME_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(target_guid));
CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '1' WHERE guid = '%u'", GUID_LOPART(target_guid));
}
+
return true;
}
+
// customize characters
bool ChatHandler::HandleCharacterCustomizeCommand(const char* args)
{
@@ -2692,6 +3264,7 @@ bool ChatHandler::HandleCharacterCustomizeCommand(const char* args)
std::string target_name;
if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
if(target)
{
PSendSysMessage(LANG_CUSTOMIZE_PLAYER, GetNameLink(target).c_str());
@@ -2701,17 +3274,22 @@ bool ChatHandler::HandleCharacterCustomizeCommand(const char* args)
else
{
std::string oldNameLink = playerLink(target_name);
+
PSendSysMessage(LANG_CUSTOMIZE_PLAYER_GUID, oldNameLink.c_str(), GUID_LOPART(target_guid));
CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '8' WHERE guid = '%u'", GUID_LOPART(target_guid));
}
+
return true;
}
+
bool ChatHandler::HandleCharacterReputationCommand(const char* args)
{
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
LocaleConstant loc = GetSessionDbcLocale();
+
FactionStateList const& targetFSL = target->GetReputationMgr().GetStateList();
for(FactionStateList::const_iterator itr = targetFSL.begin(); itr != targetFSL.end(); ++itr)
{
@@ -2724,7 +3302,9 @@ bool ChatHandler::HandleCharacterReputationCommand(const char* args)
ss << itr->second.ID << " - |cffffffff|Hfaction:" << itr->second.ID << "|h[" << factionName << " " << localeNames[loc] << "]|h|r";
else
ss << itr->second.ID << " - " << factionName << " " << localeNames[loc];
+
ss << " " << rankName << " (" << target->GetReputationMgr().GetReputation(factionEntry) << ")";
+
if(itr->second.Flags & FACTION_FLAG_VISIBLE)
ss << GetMangosString(LANG_FACTION_VISIBLE);
if(itr->second.Flags & FACTION_FLAG_AT_WAR)
@@ -2737,23 +3317,29 @@ bool ChatHandler::HandleCharacterReputationCommand(const char* args)
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;
}
+
//change standstate
bool ChatHandler::HandleModifyStandStateCommand(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::HandleHonorAddCommand(const char* args)
{
if (!*args)
return false;
+
Player *target = getSelectedPlayer();
if(!target)
{
@@ -2761,13 +3347,16 @@ bool ChatHandler::HandleHonorAddCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
uint32 amount = (uint32)atoi(args);
target->RewardHonor(NULL, 1, amount);
return true;
}
+
bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/)
{
Unit *target = getSelectedUnit();
@@ -2777,12 +3366,15 @@ bool ChatHandler::HandleHonorAddKillCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (target->GetTypeId() == TYPEID_PLAYER && HasLowerSecurity((Player*)target, 0))
return false;
+
m_session->GetPlayer()->RewardHonor(target, 1);
return true;
}
+
bool ChatHandler::HandleHonorUpdateCommand(const char* /*args*/)
{
Player *target = getSelectedPlayer();
@@ -2792,82 +3384,111 @@ bool ChatHandler::HandleHonorUpdateCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(target, 0))
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);
+
bool found = false;
+
GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap();
GameEventMgr::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() ? GetTrinityString(LANG_ACTIVE) : "";
+
if(m_session)
PSendSysMessage(LANG_EVENT_ENTRY_LIST_CHAT,id,id,eventData.description.c_str(),active );
else
PSendSysMessage(LANG_EVENT_ENTRY_LIST_CONSOLE,id,eventData.description.c_str(),active );
+
if(!found)
found = true;
}
}
+
if (!found)
SendSysMessage(LANG_NOEVENTFOUND);
+
return true;
}
+
bool ChatHandler::HandleEventActiveListCommand(const char* args)
{
uint32 counter = 0;
+
GameEventMgr::GameEventDataMap const& events = gameeventmgr.GetEventMap();
GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
+
char const* active = GetTrinityString(LANG_ACTIVE);
+
for(GameEventMgr::ActiveEvents::const_iterator itr = activeEvents.begin(); itr != activeEvents.end(); ++itr )
{
uint32 event_id = *itr;
GameEventData const& eventData = events[event_id];
+
if(m_session)
PSendSysMessage(LANG_EVENT_ENTRY_LIST_CHAT,event_id,event_id,eventData.description.c_str(),active );
else
PSendSysMessage(LANG_EVENT_ENTRY_LIST_CONSOLE,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);
+
GameEventMgr::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())
{
@@ -2875,37 +3496,48 @@ bool ChatHandler::HandleEventInfoCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
bool active = activeEvents.find(event_id) != activeEvents.end();
char const* activeStr = active ? GetTrinityString(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);
+
GameEventMgr::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())
{
@@ -2913,6 +3545,7 @@ bool ChatHandler::HandleEventStartCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
GameEventMgr::ActiveEvents const& activeEvents = gameeventmgr.GetActiveEventList();
if(activeEvents.find(event_id) != activeEvents.end())
{
@@ -2920,25 +3553,32 @@ bool ChatHandler::HandleEventStartCommand(const char* args)
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);
+
GameEventMgr::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())
{
@@ -2946,104 +3586,134 @@ bool ChatHandler::HandleEventStopCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
GameEventMgr::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* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
target->CombatStop();
target->getHostilRefManager().deleteReferences();
return true;
}
+
void ChatHandler::HandleLearnSkillRecipesHelper(Player* player,uint32 skill_id)
{
uint32 classmask = player->getClassMask();
+
for (uint32 j = 0; j < sSkillLineAbilityStore.GetNumRows(); ++j)
{
SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(j);
if (!skillLine)
continue;
+
// wrong skill
if( skillLine->skillId != skill_id)
continue;
+
// not high rank
if(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,player,false))
continue;
+
player->learnSpell(skillLine->spellId,false);
}
}
+
bool ChatHandler::HandleLearnAllCraftsCommand(const char* /*args*/)
-{
+{
+
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) &&
skillInfo->canLink) // only prof. with recipes have
{
HandleLearnSkillRecipesHelper(m_session->GetPlayer(),skillInfo->id);
}
}
+
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 );
+
std::string name;
+
SkillLineEntry const *targetSkillInfo = NULL;
for (uint32 i = 1; i < sSkillLineStore.GetNumRows(); ++i)
{
SkillLineEntry const *skillInfo = sSkillLineStore.LookupEntry(i);
if (!skillInfo)
continue;
+
if ((skillInfo->categoryId != SKILL_CATEGORY_PROFESSION &&
skillInfo->categoryId != SKILL_CATEGORY_SECONDARY) ||
!skillInfo->canLink) // only prof with recipes have set
continue;
+
int loc = GetSessionDbcLocale();
name = skillInfo->name[loc];
if(name.empty())
continue;
+
if (!Utf8FitTo(name, wnamepart))
{
loc = 0;
@@ -3051,62 +3721,87 @@ bool ChatHandler::HandleLearnAllRecipesCommand(const char* args)
{
if(loc==GetSessionDbcLocale())
continue;
+
name = skillInfo->name[loc];
if(name.empty())
continue;
+
if (Utf8FitTo(name, wnamepart))
break;
}
}
+
if(loc < MAX_LOCALE)
{
targetSkillInfo = skillInfo;
break;
}
}
+
if(!targetSkillInfo)
return false;
+
HandleLearnSkillRecipesHelper(target,targetSkillInfo->id);
+
uint16 maxLevel = target->GetPureMaxSkillValue(targetSkillInfo->id);
target->SetSkill(targetSkillInfo->id, maxLevel, maxLevel);
PSendSysMessage(LANG_COMMAND_LEARN_ALL_RECIPES, name.c_str());
return true;
}
+
bool ChatHandler::HandleLookupPlayerIpCommand(const char* args)
{
+
if (!*args)
return false;
+
std::string ip = strtok ((char*)args, " ");
char* limit_str = strtok (NULL, " ");
int32 limit = limit_str ? atoi (limit_str) : -1;
+
loginDatabase.escape_string (ip);
+
QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE last_ip = '%s'", ip.c_str ());
+
return LookupPlayerSearchCommand (result,limit);
}
+
bool ChatHandler::HandleLookupPlayerAccountCommand(const char* args)
{
if (!*args)
return false;
+
std::string account = strtok ((char*)args, " ");
char* limit_str = strtok (NULL, " ");
int32 limit = limit_str ? atoi (limit_str) : -1;
+
if (!AccountMgr::normalizeString (account))
return false;
+
loginDatabase.escape_string (account);
+
QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE username = '%s'", account.c_str ());
+
return LookupPlayerSearchCommand (result,limit);
}
+
bool ChatHandler::HandleLookupPlayerEmailCommand(const char* args)
{
+
if (!*args)
return false;
+
std::string email = strtok ((char*)args, " ");
char* limit_str = strtok (NULL, " ");
int32 limit = limit_str ? atoi (limit_str) : -1;
+
loginDatabase.escape_string (email);
+
QueryResult* result = loginDatabase.PQuery ("SELECT id,username FROM account WHERE email = '%s'", email.c_str ());
+
return LookupPlayerSearchCommand (result,limit);
}
+
bool ChatHandler::LookupPlayerSearchCommand(QueryResult* result, int32 limit)
{
if(!result)
@@ -3115,73 +3810,93 @@ bool ChatHandler::LookupPlayerSearchCommand(QueryResult* result, int32 limit)
SetSentErrorMessage(true);
return false;
}
+
int i =0;
do
{
Field* fields = result->Fetch();
uint32 acc_id = fields[0].GetUInt32();
std::string acc_name = fields[1].GetCppString();
+
QueryResult* chars = CharacterDatabase.PQuery("SELECT guid,name FROM characters WHERE account = '%u'", acc_id);
if(chars)
{
PSendSysMessage(LANG_LOOKUP_PLAYER_ACCOUNT,acc_name.c_str(),acc_id);
+
uint64 guid = 0;
std::string name;
+
do
{
Field* charfields = chars->Fetch();
guid = charfields[0].GetUInt64();
name = charfields[1].GetCppString();
+
PSendSysMessage(LANG_LOOKUP_PLAYER_CHARACTER,name.c_str(),guid);
++i;
+
} while( chars->NextRow() && ( limit == -1 || i < limit ) );
+
delete chars;
}
} while(result->NextRow());
+
delete result;
+
if(i==0) // empty accounts only
{
PSendSysMessage(LANG_NO_PLAYERS_FOUND);
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
/// Triggering corpses expire check in world
bool ChatHandler::HandleServerCorpsesCommand(const char* /*args*/)
{
CorpsesErase();
return true;
}
+
bool ChatHandler::HandleRepairitemsCommand(const char* args)
{
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
// check online security
if (HasLowerSecurity(target, 0))
return false;
+
// Repair items
target->DurabilityRepairAll(false, 0, false);
+
PSendSysMessage(LANG_YOU_REPAIR_ITEMS, GetNameLink(target).c_str());
if(needReportToTarget(target))
ChatHandler(target).PSendSysMessage(LANG_YOUR_ITEMS_REPAIRED, GetNameLink().c_str());
return true;
}
+
bool ChatHandler::HandleWaterwalkCommand(const char* args)
{
if(!*args)
return false;
+
Player *player = getSelectedPlayer();
+
if(!player)
{
PSendSysMessage(LANG_NO_CHAR_SELECTED);
SetSentErrorMessage(true);
return false;
}
+
// check online security
if (HasLowerSecurity(player, 0))
return false;
+
if (strncmp(args, "on", 3) == 0)
player->SetMovement(MOVE_WATER_WALK); // ON
else if (strncmp(args, "off", 4) == 0)
@@ -3191,21 +3906,25 @@ bool ChatHandler::HandleWaterwalkCommand(const char* args)
SendSysMessage(LANG_USE_BOL);
return false;
}
+
PSendSysMessage(LANG_YOU_SET_WATERWALK, args, GetNameLink(player).c_str());
if(needReportToTarget(player))
ChatHandler(player).PSendSysMessage(LANG_YOUR_WATERWALK_SET, args, GetNameLink().c_str());
return true;
}
+
bool ChatHandler::HandleCreatePetCommand(const char* args)
{
Player *player = m_session->GetPlayer();
Creature *creatureTarget = getSelectedCreature();
+
if(!creatureTarget || creatureTarget->isPet() || creatureTarget->GetTypeId() == TYPEID_PLAYER)
{
PSendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(creatureTarget->GetEntry());
// Creatures with family 0 crashes the server
if(cInfo->family == 0)
@@ -3214,27 +3933,34 @@ bool ChatHandler::HandleCreatePetCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(player->GetPetGUID())
{
PSendSysMessage("You already have a pet");
SetSentErrorMessage(true);
return false;
}
+
// Everything looks OK, create new pet
Pet* pet = new Pet(player, HUNTER_PET);
+
if(!pet)
return false;
+
if(!pet->CreateBaseAtCreature(creatureTarget))
{
delete pet;
PSendSysMessage("Error 1");
return false;
}
+
creatureTarget->setDeathState(JUST_DIED);
creatureTarget->RemoveCorpse();
creatureTarget->SetHealth(0); // just for nice GM-mode view
+
pet->SetUInt64Value(UNIT_FIELD_CREATEDBY, player->GetGUID());
pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, player->getFaction());
+
if(!pet->InitStatsForLevel(creatureTarget->getLevel()))
{
sLog.outError("ERROR: InitStatsForLevel() in EffectTameCreature failed! Pet deleted.");
@@ -3242,35 +3968,47 @@ bool ChatHandler::HandleCreatePetCommand(const char* args)
delete pet;
return false;
}
+
// 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->InitPetCreateSpells();
pet->SetHealth(pet->GetMaxHealth());
+
pet->GetMap()->Add((Creature*)pet);
+
// visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL,creatureTarget->getLevel());
+
player->SetMinion(pet, true);
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
player->PetSpellInitialize();
+
return true;
}
+
bool ChatHandler::HandlePetLearnCommand(const char* args)
{
if(!*args)
return false;
+
Player *plr = m_session->GetPlayer();
Pet *pet = plr->GetPet();
+
if(!pet)
{
PSendSysMessage("You have no pet");
SetSentErrorMessage(true);
return false;
}
+
uint32 spellId = extractSpellIdFromLink((char*)args);
+
if(!spellId || !sSpellStore.LookupEntry(spellId))
return false;
+
// Check if pet already has it
if(pet->HasSpell(spellId))
{
@@ -3278,6 +4016,7 @@ bool ChatHandler::HandlePetLearnCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// Check if spell is valid
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo))
@@ -3286,72 +4025,96 @@ bool ChatHandler::HandlePetLearnCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
pet->learnSpell(spellId);
+
PSendSysMessage("Pet has learned spell %u", spellId);
return true;
}
+
bool ChatHandler::HandlePetUnlearnCommand(const char *args)
{
if(!*args)
return false;
+
Player *plr = m_session->GetPlayer();
Pet *pet = plr->GetPet();
+
if(!pet)
{
PSendSysMessage("You have no pet");
SetSentErrorMessage(true);
return false;
}
+
uint32 spellId = extractSpellIdFromLink((char*)args);
+
if(pet->HasSpell(spellId))
pet->removeSpell(spellId, false);
else
PSendSysMessage("Pet doesn't have that spell");
+
return true;
}
+
bool ChatHandler::HandlePetTpCommand(const char *args)
{
if(!*args)
return false;
+
Player *plr = m_session->GetPlayer();
Pet *pet = plr->GetPet();
+
if(!pet)
{
PSendSysMessage("You have no pet");
SetSentErrorMessage(true);
return false;
}
+
uint32 tp = atol(args);
+
//pet->SetTP(tp);
+
PSendSysMessage("Pet's tp changed to %u", tp);
return true;
}
+
bool ChatHandler::HandleActivateObjectCommand(const char *args)
{
if(!*args)
return false;
+
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;
}
+
// Activate
obj->SetLootState(GO_READY);
obj->UseDoorOrButton(10000);
+
PSendSysMessage("Object activated!");
+
return true;
}
+
// add creature, temp only
bool ChatHandler::HandleTempAddSpwCommand(const char* args)
{
@@ -3360,13 +4123,18 @@ bool ChatHandler::HandleTempAddSpwCommand(const char* args)
char* charID = strtok((char*)args, " ");
if (!charID)
return false;
+
Player *chr = m_session->GetPlayer();
+
uint32 id = atoi(charID);
if(!id)
return false;
+
chr->SummonCreature(id, *chr, TEMPSUMMON_CORPSE_DESPAWN, 120);
+
return true;
}
+
// add go, temp only
bool ChatHandler::HandleTempGameObjectCommand(const char* args)
{
@@ -3375,79 +4143,105 @@ bool ChatHandler::HandleTempGameObjectCommand(const char* args)
char* charID = strtok((char*)args, " ");
if (!charID)
return false;
+
Player *chr = m_session->GetPlayer();
+
char* spawntime = strtok(NULL, " ");
uint32 spawntm = 300;
+
if( spawntime )
spawntm = atoi((char*)spawntime);
+
float x = chr->GetPositionX();
float y = chr->GetPositionY();
float z = chr->GetPositionZ();
float ang = chr->GetOrientation();
+
float rot2 = sin(ang/2);
float rot3 = cos(ang/2);
+
uint32 id = atoi(charID);
+
chr->SummonGameObject(id,x,y,z,ang,0,0,rot2,rot3,spawntm);
+
return true;
}
+
bool ChatHandler::HandleNpcAddFormationCommand(const char* args)
{
if (!*args)
return false;
+
uint32 leaderGUID = (uint32) atoi((char*)args);
Creature *pCreature = getSelectedCreature();
+
if(!pCreature || !pCreature->GetDBTableGUIDLow())
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
uint32 lowguid = pCreature->GetDBTableGUIDLow();
if(pCreature->GetFormation())
{
PSendSysMessage("Selected creature is already member of group %u", pCreature->GetFormation()->GetId());
return false;
}
+
if (!lowguid)
return false;
+
Player *chr = m_session->GetPlayer();
FormationInfo *group_member;
+
group_member = new FormationInfo;
group_member->follow_angle = (pCreature->GetAngle(chr) - chr->GetOrientation()) * 180 / M_PI;
group_member->follow_dist = sqrtf(pow(chr->GetPositionX() - pCreature->GetPositionX(),int(2))+pow(chr->GetPositionY()-pCreature->GetPositionY(),int(2)));
group_member->leaderGUID = leaderGUID;
group_member->groupAI = 0;
+
CreatureGroupMap[lowguid] = group_member;
pCreature->SearchFormation();
+
WorldDatabase.PExecuteLog("INSERT INTO creature_formations (leaderGUID, memberGUID, dist, angle, groupAI) VALUES ('%u','%u','%f', '%f', '%u')",
leaderGUID, lowguid, group_member->follow_dist, group_member->follow_angle, group_member->groupAI);
+
PSendSysMessage("Creature %u added to formation with leader %u", lowguid, leaderGUID);
+
return true;
}
+
bool ChatHandler::HandleNpcSetLinkCommand(const char* args)
{
if (!*args)
return false;
+
uint32 linkguid = (uint32) atoi((char*)args);
+
Creature* pCreature = getSelectedCreature();
+
if(!pCreature)
{
SendSysMessage(LANG_SELECT_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
if(!pCreature->GetDBTableGUIDLow())
{
PSendSysMessage("Selected creature isn't in creature table", pCreature->GetGUIDLow());
SetSentErrorMessage(true);
return false;
}
+
if(!objmgr.SetCreatureLinkedRespawn(pCreature->GetDBTableGUIDLow(), linkguid))
{
PSendSysMessage("Selected creature can't link with guid '%u'", linkguid);
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage("LinkGUID '%u' added to creature with DBTableGUID: '%u'", linkguid, pCreature->GetDBTableGUIDLow());
return true;
}
diff --git a/src/game/Level3.cpp b/src/game/Level3.cpp
index d77d5424bd0..21f3c995dce 100644
--- a/src/game/Level3.cpp
+++ b/src/game/Level3.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -53,6 +54,7 @@
#include "InstanceData.h"
#include "AuctionHouseBot.h"
#include "CreatureEventAIMgr.h"
+
bool ChatHandler::HandleAHBotOptionsCommand(const char* args)
{
uint32 ahMapID = 0;
@@ -79,6 +81,7 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args)
return false;
}
int l = strlen(opt);
+
if (strncmp(opt,"help",l) == 0)
{
PSendSysMessage("AHBot commands:");
@@ -539,10 +542,12 @@ bool ChatHandler::HandleAHBotOptionsCommand(const char* args)
}
return true;
}
+
//reload commands
bool ChatHandler::HandleReloadAllCommand(const char*)
{
HandleReloadSkillFishingBaseLevelCommand("");
+
HandleReloadAllAchievementCommand("");
HandleReloadAllAreaCommand("");
HandleReloadAllEventAICommand("");
@@ -552,20 +557,24 @@ bool ChatHandler::HandleReloadAllCommand(const char*)
HandleReloadAllSpellCommand("");
HandleReloadAllItemCommand("");
HandleReloadAllLocalesCommand("");
+
HandleReloadAccessRequirementCommand("");
HandleReloadCommandCommand("");
HandleReloadReservedNameCommand("");
HandleReloadTrinityStringCommand("");
HandleReloadGameTeleCommand("");
+
HandleReloadAutobroadcastCommand("");
return true;
}
+
bool ChatHandler::HandleReloadAllAchievementCommand(const char*)
{
HandleReloadAchievementCriteriaDataCommand("");
HandleReloadAchievementRewardCommand("");
return true;
}
+
bool ChatHandler::HandleReloadAllAreaCommand(const char*)
{
//HandleReloadQuestAreaTriggersCommand(""); -- reloaded in HandleReloadAllQuestCommand
@@ -574,6 +583,7 @@ bool ChatHandler::HandleReloadAllAreaCommand(const char*)
HandleReloadGameGraveyardZoneCommand("");
return true;
}
+
bool ChatHandler::HandleReloadAllLootCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables..." );
@@ -581,6 +591,7 @@ bool ChatHandler::HandleReloadAllLootCommand(const char*)
SendGlobalGMSysMessage("DB tables `*_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/)
{
HandleReloadNpcGossipCommand("a");
@@ -591,15 +602,18 @@ bool ChatHandler::HandleReloadAllNpcCommand(const char* /*args*/)
HandleReloadSpellClickSpellsCommand("a");
return true;
}
+
bool ChatHandler::HandleReloadAllQuestCommand(const char* /*args*/)
{
HandleReloadQuestAreaTriggersCommand("a");
HandleReloadQuestTemplateCommand("a");
+
sLog.outString( "Re-Loading Quests Relations..." );
objmgr.LoadQuestRelations();
SendGlobalGMSysMessage("DB tables `*_questrelation` and `*_involvedrelation` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAllScriptsCommand(const char*)
{
if(sWorld.IsScriptScheduled())
@@ -608,6 +622,7 @@ bool ChatHandler::HandleReloadAllScriptsCommand(const char*)
SetSentErrorMessage(true);
return false;
}
+
sLog.outString( "Re-Loading Scripts..." );
HandleReloadGameObjectScriptsCommand("a");
HandleReloadEventScriptsCommand("a");
@@ -619,6 +634,7 @@ bool ChatHandler::HandleReloadAllScriptsCommand(const char*)
HandleReloadWpScriptsCommand("a");
return true;
}
+
bool ChatHandler::HandleReloadAllEventAICommand(const char*)
{
HandleReloadEventAITextsCommand("a");
@@ -626,6 +642,7 @@ bool ChatHandler::HandleReloadAllEventAICommand(const char*)
HandleReloadEventAIScriptsCommand("a");
return true;
}
+
bool ChatHandler::HandleReloadAllSpellCommand(const char*)
{
HandleReloadSkillDiscoveryTemplateCommand("a");
@@ -644,6 +661,7 @@ bool ChatHandler::HandleReloadAllSpellCommand(const char*)
HandleReloadSpellDisabledCommand("a");
return true;
}
+
bool ChatHandler::HandleReloadAllItemCommand(const char*)
{
HandleReloadPageTextsCommand("a");
@@ -651,6 +669,7 @@ bool ChatHandler::HandleReloadAllItemCommand(const char*)
HandleReloadItemRequiredTragetCommand("a");
return true;
}
+
bool ChatHandler::HandleReloadAllLocalesCommand(const char* /*args*/)
{
HandleReloadLocalesAchievementRewardCommand("a");
@@ -663,6 +682,7 @@ bool ChatHandler::HandleReloadAllLocalesCommand(const char* /*args*/)
HandleReloadLocalesQuestCommand("a");
return true;
}
+
bool ChatHandler::HandleReloadConfigCommand(const char* /*args*/)
{
sLog.outString( "Re-Loading config settings..." );
@@ -671,6 +691,7 @@ bool ChatHandler::HandleReloadConfigCommand(const char* /*args*/)
SendGlobalGMSysMessage("World config settings reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAccessRequirementCommand(const char*)
{
sLog.outString( "Re-Loading Access Requirement definitions..." );
@@ -678,6 +699,7 @@ bool ChatHandler::HandleReloadAccessRequirementCommand(const char*)
SendGlobalGMSysMessage("DB table `access_requirement` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAchievementCriteriaDataCommand(const char*)
{
sLog.outString( "Re-Loading Additional Achievement Criteria Data..." );
@@ -685,6 +707,7 @@ bool ChatHandler::HandleReloadAchievementCriteriaDataCommand(const char*)
SendGlobalGMSysMessage("DB table `achievement_criteria_data` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAchievementRewardCommand(const char*)
{
sLog.outString( "Re-Loading Achievement Reward Data..." );
@@ -692,6 +715,7 @@ bool ChatHandler::HandleReloadAchievementRewardCommand(const char*)
SendGlobalGMSysMessage("DB table `achievement_reward` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*)
{
sLog.outString( "Re-Loading Tavern Area Triggers..." );
@@ -699,6 +723,7 @@ bool ChatHandler::HandleReloadAreaTriggerTavernCommand(const char*)
SendGlobalGMSysMessage("DB table `areatrigger_tavern` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAreaTriggerTeleportCommand(const char*)
{
sLog.outString( "Re-Loading AreaTrigger teleport definitions..." );
@@ -706,6 +731,7 @@ bool ChatHandler::HandleReloadAreaTriggerTeleportCommand(const char*)
SendGlobalGMSysMessage("DB table `areatrigger_teleport` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadAutobroadcastCommand(const char*)
{
sLog.outString("Re-Loading Autobroadcast...");
@@ -713,12 +739,14 @@ bool ChatHandler::HandleReloadAutobroadcastCommand(const char*)
SendGlobalGMSysMessage("DB table `autobroadcast` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadCommandCommand(const char*)
{
load_command_table = true;
SendGlobalGMSysMessage("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`)" );
@@ -726,6 +754,7 @@ bool ChatHandler::HandleReloadCreatureQuestRelationsCommand(const char*)
SendGlobalGMSysMessage("DB table `creature_questrelation` (creature quest givers) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadCreatureLinkedRespawnCommand(const char* args)
{
sLog.outString( "Loading Linked Respawns... (`creature_linked_respawn`)" );
@@ -733,6 +762,7 @@ bool ChatHandler::HandleReloadCreatureLinkedRespawnCommand(const char* args)
SendGlobalGMSysMessage("DB table `creature_linked_respawn` (creature linked respawns) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
{
sLog.outString( "Loading Quests Relations... (`creature_involvedrelation`)" );
@@ -740,6 +770,7 @@ bool ChatHandler::HandleReloadCreatureQuestInvRelationsCommand(const char*)
SendGlobalGMSysMessage("DB table `creature_involvedrelation` (creature quest takers) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*)
{
sLog.outString( "Loading Quests Relations... (`gameobject_questrelation`)" );
@@ -747,6 +778,7 @@ bool ChatHandler::HandleReloadGOQuestRelationsCommand(const char*)
SendGlobalGMSysMessage("DB table `gameobject_questrelation` (gameobject quest givers) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadGOQuestInvRelationsCommand(const char*)
{
sLog.outString( "Loading Quests Relations... (`gameobject_involvedrelation`)" );
@@ -754,6 +786,7 @@ bool ChatHandler::HandleReloadGOQuestInvRelationsCommand(const char*)
SendGlobalGMSysMessage("DB table `gameobject_involvedrelation` (gameobject quest takers) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadQuestAreaTriggersCommand(const char*)
{
sLog.outString( "Re-Loading Quest Area Triggers..." );
@@ -761,17 +794,20 @@ bool ChatHandler::HandleReloadQuestAreaTriggersCommand(const char*)
SendGlobalGMSysMessage("DB table `areatrigger_involvedrelation` (quest area triggers) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadQuestTemplateCommand(const char*)
{
sLog.outString( "Re-Loading Quest Templates..." );
objmgr.LoadQuests();
SendGlobalGMSysMessage("DB table `quest_template` (quest definitions) reloaded.");
+
/// dependent also from `gameobject` but this table not reloaded anyway
sLog.outString( "Re-Loading GameObjects for quests..." );
objmgr.LoadGameObjectForQuests();
SendGlobalGMSysMessage("Data GameObjects for quests reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`creature_loot_template`)" );
@@ -780,6 +816,7 @@ bool ChatHandler::HandleReloadLootTemplatesCreatureCommand(const char*)
SendGlobalGMSysMessage("DB table `creature_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`disenchant_loot_template`)" );
@@ -788,6 +825,7 @@ bool ChatHandler::HandleReloadLootTemplatesDisenchantCommand(const char*)
SendGlobalGMSysMessage("DB table `disenchant_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`fishing_loot_template`)" );
@@ -796,6 +834,7 @@ bool ChatHandler::HandleReloadLootTemplatesFishingCommand(const char*)
SendGlobalGMSysMessage("DB table `fishing_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`gameobject_loot_template`)" );
@@ -804,6 +843,7 @@ bool ChatHandler::HandleReloadLootTemplatesGameobjectCommand(const char*)
SendGlobalGMSysMessage("DB table `gameobject_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`item_loot_template`)" );
@@ -812,6 +852,7 @@ bool ChatHandler::HandleReloadLootTemplatesItemCommand(const char*)
SendGlobalGMSysMessage("DB table `item_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesMillingCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`milling_loot_template`)" );
@@ -820,6 +861,7 @@ bool ChatHandler::HandleReloadLootTemplatesMillingCommand(const char*)
SendGlobalGMSysMessage("DB table `milling_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`pickpocketing_loot_template`)" );
@@ -828,6 +870,7 @@ bool ChatHandler::HandleReloadLootTemplatesPickpocketingCommand(const char*)
SendGlobalGMSysMessage("DB table `pickpocketing_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`prospecting_loot_template`)" );
@@ -836,6 +879,7 @@ bool ChatHandler::HandleReloadLootTemplatesProspectingCommand(const char*)
SendGlobalGMSysMessage("DB table `prospecting_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`quest_mail_loot_template`)" );
@@ -844,6 +888,7 @@ bool ChatHandler::HandleReloadLootTemplatesQuestMailCommand(const char*)
SendGlobalGMSysMessage("DB table `quest_mail_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`reference_loot_template`)" );
@@ -851,6 +896,7 @@ bool ChatHandler::HandleReloadLootTemplatesReferenceCommand(const char*)
SendGlobalGMSysMessage("DB table `reference_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`skinning_loot_template`)" );
@@ -859,6 +905,7 @@ bool ChatHandler::HandleReloadLootTemplatesSkinningCommand(const char*)
SendGlobalGMSysMessage("DB table `skinning_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLootTemplatesSpellCommand(const char*)
{
sLog.outString( "Re-Loading Loot Tables... (`spell_loot_template`)" );
@@ -867,6 +914,7 @@ bool ChatHandler::HandleReloadLootTemplatesSpellCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_loot_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadTrinityStringCommand(const char*)
{
sLog.outString( "Re-Loading trinity_string Table!" );
@@ -874,6 +922,7 @@ bool ChatHandler::HandleReloadTrinityStringCommand(const char*)
SendGlobalGMSysMessage("DB table `trinity_string` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadNpcOptionCommand(const char*)
{
sLog.outString( "Re-Loading `npc_option` Table!" );
@@ -881,6 +930,7 @@ bool ChatHandler::HandleReloadNpcOptionCommand(const char*)
SendGlobalGMSysMessage("DB table `npc_option` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadNpcGossipCommand(const char*)
{
sLog.outString( "Re-Loading `npc_gossip` Table!" );
@@ -888,6 +938,7 @@ bool ChatHandler::HandleReloadNpcGossipCommand(const char*)
SendGlobalGMSysMessage("DB table `npc_gossip` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadNpcTrainerCommand(const char*)
{
sLog.outString( "Re-Loading `npc_trainer` Table!" );
@@ -895,6 +946,7 @@ bool ChatHandler::HandleReloadNpcTrainerCommand(const char*)
SendGlobalGMSysMessage("DB table `npc_trainer` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadNpcVendorCommand(const char*)
{
sLog.outString( "Re-Loading `npc_vendor` Table!" );
@@ -902,6 +954,7 @@ bool ChatHandler::HandleReloadNpcVendorCommand(const char*)
SendGlobalGMSysMessage("DB table `npc_vendor` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadPointsOfInterestCommand(const char*)
{
sLog.outString( "Re-Loading `points_of_interest` Table!" );
@@ -909,6 +962,7 @@ bool ChatHandler::HandleReloadPointsOfInterestCommand(const char*)
SendGlobalGMSysMessage("DB table `points_of_interest` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellClickSpellsCommand(const char*)
{
sLog.outString( "Re-Loading `npc_spellclick_spells` Table!" );
@@ -916,6 +970,7 @@ bool ChatHandler::HandleReloadSpellClickSpellsCommand(const char*)
SendGlobalGMSysMessage("DB table `npc_spellclick_spells` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadReservedNameCommand(const char*)
{
sLog.outString( "Loading ReservedNames... (`reserved_name`)" );
@@ -923,6 +978,7 @@ bool ChatHandler::HandleReloadReservedNameCommand(const char*)
SendGlobalGMSysMessage("DB table `reserved_name` (player reserved names) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSkillDiscoveryTemplateCommand(const char* /*args*/)
{
sLog.outString( "Re-Loading Skill Discovery Table..." );
@@ -930,6 +986,7 @@ bool ChatHandler::HandleReloadSkillDiscoveryTemplateCommand(const char* /*args*/
SendGlobalGMSysMessage("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..." );
@@ -937,6 +994,7 @@ bool ChatHandler::HandleReloadSkillExtraItemTemplateCommand(const char* /*args*/
SendGlobalGMSysMessage("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..." );
@@ -944,6 +1002,7 @@ bool ChatHandler::HandleReloadSkillFishingBaseLevelCommand(const char* /*args*/)
SendGlobalGMSysMessage("DB table `skill_fishing_base_level` (fishing base level for zone/subzone) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellAreaCommand(const char*)
{
sLog.outString( "Re-Loading SpellArea Data..." );
@@ -951,6 +1010,7 @@ bool ChatHandler::HandleReloadSpellAreaCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_area` (spell dependences from area/quest/auras state) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellRequiredCommand(const char*)
{
sLog.outString( "Re-Loading Spell Required Data... " );
@@ -958,6 +1018,7 @@ bool ChatHandler::HandleReloadSpellRequiredCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_required` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellElixirCommand(const char*)
{
sLog.outString( "Re-Loading Spell Elixir types..." );
@@ -965,6 +1026,7 @@ bool ChatHandler::HandleReloadSpellElixirCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_elixir` (spell elixir types) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellLearnSpellCommand(const char*)
{
sLog.outString( "Re-Loading Spell Learn Spells..." );
@@ -972,6 +1034,7 @@ bool ChatHandler::HandleReloadSpellLearnSpellCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_learn_spell` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellLinkedSpellCommand(const char*)
{
sLog.outString( "Re-Loading Spell Linked Spells..." );
@@ -979,6 +1042,7 @@ bool ChatHandler::HandleReloadSpellLinkedSpellCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_linked_spell` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellProcEventCommand(const char*)
{
sLog.outString( "Re-Loading Spell Proc Event conditions..." );
@@ -986,6 +1050,7 @@ bool ChatHandler::HandleReloadSpellProcEventCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_proc_event` (spell proc trigger requirements) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellBonusesCommand(const char*)
{
sLog.outString( "Re-Loading Spell Bonus Data..." );
@@ -993,6 +1058,7 @@ bool ChatHandler::HandleReloadSpellBonusesCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_bonus_data` (spell damage/healing coefficients) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*)
{
sLog.outString( "Re-Loading SpellsScriptTarget..." );
@@ -1000,6 +1066,7 @@ bool ChatHandler::HandleReloadSpellScriptTargetCommand(const char*)
SendGlobalGMSysMessage("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..." );
@@ -1007,6 +1074,7 @@ bool ChatHandler::HandleReloadSpellTargetPositionCommand(const char*)
SendGlobalGMSysMessage("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...");
@@ -1014,6 +1082,7 @@ bool ChatHandler::HandleReloadSpellThreatsCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_threat` (spell aggro definitions) reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadSpellPetAurasCommand(const char*)
{
sLog.outString( "Re-Loading Spell pet auras...");
@@ -1021,6 +1090,7 @@ bool ChatHandler::HandleReloadSpellPetAurasCommand(const char*)
SendGlobalGMSysMessage("DB table `spell_pet_auras` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadPageTextsCommand(const char*)
{
sLog.outString( "Re-Loading Page Texts..." );
@@ -1028,6 +1098,7 @@ bool ChatHandler::HandleReloadPageTextsCommand(const char*)
SendGlobalGMSysMessage("DB table `page_texts` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*)
{
sLog.outString( "Re-Loading Item Random Enchantments Table..." );
@@ -1035,6 +1106,7 @@ bool ChatHandler::HandleReloadItemEnchantementsCommand(const char*)
SendGlobalGMSysMessage("DB table `item_enchantment_template` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadItemRequiredTragetCommand(const char*)
{
sLog.outString( "Re-Loading Item Required Targets Table..." );
@@ -1042,6 +1114,7 @@ bool ChatHandler::HandleReloadItemRequiredTragetCommand(const char*)
SendGlobalGMSysMessage("DB table `item_required_target` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg)
{
if(sWorld.IsScriptScheduled())
@@ -1050,13 +1123,18 @@ bool ChatHandler::HandleReloadGameObjectScriptsCommand(const char* arg)
SetSentErrorMessage(true);
return false;
}
+
if(*arg!='a')
sLog.outString( "Re-Loading Scripts from `gameobject_scripts`...");
+
objmgr.LoadGameObjectScripts();
+
if(*arg!='a')
SendGlobalGMSysMessage("DB table `gameobject_scripts` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadEventScriptsCommand(const char* arg)
{
if(sWorld.IsScriptScheduled())
@@ -1065,13 +1143,18 @@ bool ChatHandler::HandleReloadEventScriptsCommand(const char* arg)
SetSentErrorMessage(true);
return false;
}
+
if(*arg!='a')
sLog.outString( "Re-Loading Scripts from `event_scripts`...");
+
objmgr.LoadEventScripts();
+
if(*arg!='a')
SendGlobalGMSysMessage("DB table `event_scripts` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadWpScriptsCommand(const char* arg)
{
if(sWorld.IsScriptScheduled())
@@ -1080,20 +1163,27 @@ bool ChatHandler::HandleReloadWpScriptsCommand(const char* arg)
SetSentErrorMessage(true);
return false;
}
+
if(*arg!='a')
sLog.outString( "Re-Loading Scripts from `waypoint_scripts`...");
+
objmgr.LoadWaypointScripts();
+
if(*arg!='a')
SendGlobalGMSysMessage("DB table `waypoint_scripts` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadEventAITextsCommand(const char* arg)
{
+
sLog.outString( "Re-Loading Texts from `creature_ai_texts`...");
CreatureEAI_Mgr.LoadCreatureEventAI_Texts();
SendGlobalGMSysMessage("DB table `creature_ai_texts` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadEventAISummonsCommand(const char* arg)
{
sLog.outString( "Re-Loading Summons from `creature_ai_summons`...");
@@ -1101,6 +1191,7 @@ bool ChatHandler::HandleReloadEventAISummonsCommand(const char* arg)
SendGlobalGMSysMessage("DB table `creature_ai_summons` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadEventAIScriptsCommand(const char* arg)
{
sLog.outString( "Re-Loading Scripts from `creature_ai_scripts`...");
@@ -1108,6 +1199,7 @@ bool ChatHandler::HandleReloadEventAIScriptsCommand(const char* arg)
SendGlobalGMSysMessage("DB table `creature_ai_scripts` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadQuestEndScriptsCommand(const char* arg)
{
if(sWorld.IsScriptScheduled())
@@ -1116,13 +1208,18 @@ bool ChatHandler::HandleReloadQuestEndScriptsCommand(const char* arg)
SetSentErrorMessage(true);
return false;
}
+
if(*arg!='a')
sLog.outString( "Re-Loading Scripts from `quest_end_scripts`...");
+
objmgr.LoadQuestEndScripts();
+
if(*arg!='a')
SendGlobalGMSysMessage("DB table `quest_end_scripts` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadQuestStartScriptsCommand(const char* arg)
{
if(sWorld.IsScriptScheduled())
@@ -1131,13 +1228,18 @@ bool ChatHandler::HandleReloadQuestStartScriptsCommand(const char* arg)
SetSentErrorMessage(true);
return false;
}
+
if(*arg!='a')
sLog.outString( "Re-Loading Scripts from `quest_start_scripts`...");
+
objmgr.LoadQuestStartScripts();
+
if(*arg!='a')
SendGlobalGMSysMessage("DB table `quest_start_scripts` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadSpellScriptsCommand(const char* arg)
{
if(sWorld.IsScriptScheduled())
@@ -1146,13 +1248,18 @@ bool ChatHandler::HandleReloadSpellScriptsCommand(const char* arg)
SetSentErrorMessage(true);
return false;
}
+
if(*arg!='a')
sLog.outString( "Re-Loading Scripts from `spell_scripts`...");
+
objmgr.LoadSpellScripts();
+
if(*arg!='a')
SendGlobalGMSysMessage("DB table `spell_scripts` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadDbScriptStringCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Script strings from `db_script_string`...");
@@ -1160,27 +1267,40 @@ bool ChatHandler::HandleReloadDbScriptStringCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `db_script_string` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadGameGraveyardZoneCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Graveyard-zone links...");
+
objmgr.LoadGraveyardZones();
+
SendGlobalGMSysMessage("DB table `game_graveyard_zone` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadGameTeleCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Game Tele coordinates...");
+
objmgr.LoadGameTele();
+
SendGlobalGMSysMessage("DB table `game_tele` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadSpellDisabledCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading spell disabled table...");
+
objmgr.LoadSpellDisabledEntrys();
+
SendGlobalGMSysMessage("DB table `spell_disabled` reloaded.");
+
return true;
}
+
bool ChatHandler::HandleReloadLocalesAchievementRewardCommand(const char*)
{
sLog.outString( "Re-Loading Locales Achievement Reward Data..." );
@@ -1188,6 +1308,7 @@ bool ChatHandler::HandleReloadLocalesAchievementRewardCommand(const char*)
SendGlobalGMSysMessage("DB table `locales_achievement_reward` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesCreatureCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales Creature ...");
@@ -1195,6 +1316,7 @@ bool ChatHandler::HandleReloadLocalesCreatureCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `locales_creature` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesGameobjectCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales Gameobject ... ");
@@ -1202,6 +1324,7 @@ bool ChatHandler::HandleReloadLocalesGameobjectCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `locales_gameobject` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesItemCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales Item ... ");
@@ -1209,6 +1332,7 @@ bool ChatHandler::HandleReloadLocalesItemCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `locales_item` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesNpcTextCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales NPC Text ... ");
@@ -1216,6 +1340,7 @@ bool ChatHandler::HandleReloadLocalesNpcTextCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `locales_npc_text` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesPageTextCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales Page Text ... ");
@@ -1223,6 +1348,7 @@ bool ChatHandler::HandleReloadLocalesPageTextCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `locales_page_text` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesPointsOfInterestCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales Points Of Interest ... ");
@@ -1230,6 +1356,7 @@ bool ChatHandler::HandleReloadLocalesPointsOfInterestCommand(const char* /*arg*/
SendGlobalGMSysMessage("DB table `locales_points_of_interest` reloaded.");
return true;
}
+
bool ChatHandler::HandleReloadLocalesQuestCommand(const char* /*arg*/)
{
sLog.outString( "Re-Loading Locales Quest ... ");
@@ -1237,12 +1364,15 @@ bool ChatHandler::HandleReloadLocalesQuestCommand(const char* /*arg*/)
SendGlobalGMSysMessage("DB table `locales_quest` reloaded.");
return true;
}
+
bool ChatHandler::HandleLoadScriptsCommand(const char* args)
{
if(!LoadScriptingModule(args)) return true;
+
sWorld.SendGMText(LANG_SCRIPTS_RELOADED);
return true;
}
+
bool ChatHandler::HandleReloadAuctionsCommand(const char* args)
{
///- Reload dynamic data tables from the database
@@ -1252,22 +1382,26 @@ bool ChatHandler::HandleReloadAuctionsCommand(const char* args)
SendGlobalGMSysMessage("Auctions reloaded.");
return true;
}
+
bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
{
if(!*args)
return false;
+
std::string targetAccountName;
uint32 targetAccountId = 0;
uint32 targetSecurity = 0;
uint32 gm = 0;
char* arg1 = strtok((char*)args, " ");
char* arg2 = strtok(NULL, " ");
+
if (getSelectedPlayer() && arg1 && !arg2)
{
targetAccountId = getSelectedPlayer()->GetSession()->GetAccountId();
accmgr.GetName(targetAccountId, targetAccountName);
Player* targetPlayer = getSelectedPlayer();
gm = atoi(arg1);
+
// Check for invalid specified GM level.
if (gm < SEC_PLAYER || gm > SEC_ADMINISTRATOR)
{
@@ -1275,6 +1409,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// Check if targets GM level and specified GM level is not higher than current gm level
targetSecurity = targetPlayer->GetSession()->GetSecurity();
if (targetSecurity >= m_session->GetSecurity() || gm >= m_session->GetSecurity())
@@ -1283,11 +1418,13 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// Decide which string to show
if (m_session->GetPlayer() != targetPlayer)
PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm);
else
PSendSysMessage(LANG_YOURS_SECURITY_CHANGED, m_session->GetPlayer()->GetName(), gm);
+
loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId);
return true;
}
@@ -1296,6 +1433,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
// Check for second parameter
if (!arg2)
return false;
+
// Check for account
targetAccountName = arg1;
if (!AccountMgr::normalizeString(targetAccountName))
@@ -1304,6 +1442,7 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// Check for invalid specified GM level.
gm = atoi(arg2);
if (gm < SEC_PLAYER || gm > SEC_ADMINISTRATOR)
@@ -1312,9 +1451,11 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
targetAccountId = accmgr.GetId(arg1);
/// m_session==NULL only for console
uint32 plSecurity = m_session ? m_session->GetSecurity() : SEC_CONSOLE;
+
/// can set security level only for target with less security and to less security that we have
/// This is also reject self apply in fact
targetSecurity = accmgr.GetSecurity(targetAccountId);
@@ -1324,22 +1465,27 @@ bool ChatHandler::HandleAccountSetGmLevelCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_YOU_CHANGE_SECURITY, targetAccountName.c_str(), gm);
loginDatabase.PExecute("UPDATE account SET gmlevel = '%d' WHERE id = '%u'", gm, targetAccountId);
return true;
}
}
+
/// Set password for account
bool ChatHandler::HandleAccountSetPasswordCommand(const char* args)
{
if(!*args)
return false;
+
///- Get the command line arguments
char *szAccount = strtok ((char*)args," ");
char *szPassword1 = strtok (NULL," ");
char *szPassword2 = strtok (NULL," ");
+
if (!szAccount||!szPassword1 || !szPassword2)
return false;
+
std::string account_name = szAccount;
if (!AccountMgr::normalizeString(account_name))
{
@@ -1347,6 +1493,7 @@ bool ChatHandler::HandleAccountSetPasswordCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 targetAccountId = accmgr.GetId(account_name);
if (!targetAccountId)
{
@@ -1354,17 +1501,21 @@ bool ChatHandler::HandleAccountSetPasswordCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
/// can set password only for target with less security
/// This is also reject self apply in fact
if (HasLowerSecurityAccount (NULL,targetAccountId,true))
return false;
+
if (strcmp(szPassword1,szPassword2))
{
SendSysMessage (LANG_NEW_PASSWORDS_NOT_MATCH);
SetSentErrorMessage (true);
return false;
}
+
AccountOpResult result = accmgr.ChangePassword(targetAccountId, szPassword1);
+
switch (result)
{
case AOR_OK:
@@ -1383,8 +1534,10 @@ bool ChatHandler::HandleAccountSetPasswordCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/)
{
Player* SelectedPlayer = getSelectedPlayer();
@@ -1394,20 +1547,26 @@ bool ChatHandler::HandleMaxSkillCommand(const char* /*args*/)
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)
{
@@ -1415,7 +1574,9 @@ bool ChatHandler::HandleSetSkillCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
int32 level = atol (level_p);
+
Player * target = getSelectedPlayer();
if (!target)
{
@@ -1423,6 +1584,7 @@ bool ChatHandler::HandleSetSkillCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
SkillLineEntry const* sl = sSkillLineStore.LookupEntry(skill);
if (!sl)
{
@@ -1430,30 +1592,40 @@ bool ChatHandler::HandleSetSkillCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
std::string tNameLink = GetNameLink(target);
+
if (!target->GetSkillValue(skill))
{
PSendSysMessage(LANG_SET_SKILL_ERROR, tNameLink.c_str(), 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], tNameLink.c_str(), 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 spell_id = extractSpellIdFromLink((char*)args);
if (!spell_id)
return false;
+
char const* allStr = strtok(NULL," ");
bool allRanks = allStr ? (strncmp(allStr, "all", strlen(allStr)) == 0) : false;
+
Player* target = getSelectedPlayer();
if (!target)
{
@@ -1461,16 +1633,21 @@ bool ChatHandler::HandleUnLearnCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if (allRanks)
spell_id = spellmgr.GetFirstSpellInChain (spell_id);
+
if (target->HasSpell(spell_id))
target->removeSpell(spell_id,false,!allRanks);
else
SendSysMessage(LANG_FORGET_SPELL);
+
if (GetTalentSpellCost(spell_id))
target->SendTalentsInfoData(false);
+
return true;
}
+
bool ChatHandler::HandleCooldownCommand(const char* args)
{
Player* target = getSelectedPlayer();
@@ -1480,7 +1657,9 @@ bool ChatHandler::HandleCooldownCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
std::string tNameLink = GetNameLink(target);
+
if (!*args)
{
target->RemoveAllSpellCooldown();
@@ -1492,17 +1671,20 @@ bool ChatHandler::HandleCooldownCommand(const char* args)
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) : tNameLink.c_str());
SetSentErrorMessage(true);
return false;
}
+
target->RemoveSpellCooldown(spell_id,true);
PSendSysMessage(LANG_REMOVE_COOLDOWN, spell_id, target==m_session->GetPlayer() ? GetMangosString(LANG_YOU) : tNameLink.c_str());
}
return true;
}
+
bool ChatHandler::HandleLearnAllCommand(const char* /*args*/)
{
static const char *allSpellList[] =
@@ -2105,23 +2287,30 @@ bool ChatHandler::HandleLearnAllCommand(const char* /*args*/)
"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,false);
}
+
SendSysMessage(LANG_COMMAND_LEARN_MANY_SPELLS);
+
return true;
}
+
bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/)
{
static const char *gmSpellList[] =
@@ -2136,77 +2325,97 @@ bool ChatHandler::HandleLearnAllGMCommand(const char* /*args*/)
"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,false);
}
+
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 server-side/triggered spells
if(spellInfo->spellLevel==0)
continue;
+
// skip wrong class/race skills
if(!m_session->GetPlayer()->IsSpellFitByClassAndRace(spellInfo->Id))
continue;
+
// skip other spell families
if( spellInfo->SpellFamilyName != family)
continue;
+
// 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,false);
}
+
SendSysMessage(LANG_COMMAND_LEARN_CLASS_SPELLS);
return true;
}
+
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;
for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
@@ -2217,20 +2426,26 @@ bool ChatHandler::HandleLearnAllMyTalentsCommand(const char* /*args*/)
break;
}
}
+
if(!spellId) // ??? none spells in talent
continue;
+
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
continue;
+
// learn highest rank of talent and learn all non-talent spell ranks (recursive by tree)
player->learnSpellHighRank(spellId);
}
+
SendSysMessage(LANG_COMMAND_LEARN_CLASS_TALENTS);
return true;
}
+
bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/)
{
Player* player = m_session->GetPlayer();
+
Pet* pet = player->GetPet();
if(!pet)
{
@@ -2238,6 +2453,7 @@ bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
CreatureInfo const *ci = pet->GetCreatureInfo();
if(!ci)
{
@@ -2245,6 +2461,7 @@ bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
if(!pet_family)
{
@@ -2252,25 +2469,31 @@ bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
if(pet_family->petTalentType < 0) // not hunter pet
{
SendSysMessage(LANG_WRONG_PET_TYPE);
SetSentErrorMessage(true);
return false;
}
+
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;
+
// prevent learn talent for different family (cheating)
if(((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask)==0)
continue;
+
// search highest talent rank
uint32 spellid = 0;
+
for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
{
if(talentInfo->RankID[rank]!=0)
@@ -2279,50 +2502,64 @@ bool ChatHandler::HandleLearnAllMyPetTalentsCommand(const char* /*args*/)
break;
}
}
+
if(!spellid) // ??? none spells in talent
continue;
+
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid);
if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer(),false))
continue;
+
// learn highest rank of talent and learn all non-talent spell ranks (recursive by tree)
pet->learnSpellHighRank(spellid);
}
+
SendSysMessage(LANG_COMMAND_LEARN_PET_TALENTS);
return true;
}
+
bool ChatHandler::HandleLearnAllLangCommand(const char* /*args*/)
{
// skipping UNIVERSAL language (0)
for(uint8 i = 1; i < LANGUAGES_COUNT; ++i)
m_session->GetPlayer()->learnSpell(lang_description[i].spell_id,false);
+
SendSysMessage(LANG_COMMAND_LEARN_ALL_LANG);
return true;
}
+
bool ChatHandler::HandleLearnAllDefaultCommand(const char* args)
{
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
target->learnDefaultSpells();
target->learnQuestRewardedSpells();
+
PSendSysMessage(LANG_COMMAND_LEARN_ALL_DEFAULT_AND_QUEST,GetNameLink(target).c_str());
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;
+
char const* allStr = strtok(NULL," ");
bool allRanks = allStr ? (strncmp(allStr, "all", strlen(allStr)) == 0) : false;
+
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
if(!spellInfo || !SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
{
@@ -2330,6 +2567,7 @@ bool ChatHandler::HandleLearnCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if (!allRanks && targetPlayer->HasSpell(spell))
{
if(targetPlayer == m_session->GetPlayer())
@@ -2339,23 +2577,30 @@ bool ChatHandler::HandleLearnCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(allRanks)
targetPlayer->learnSpellHighRank(spell);
else
targetPlayer->learnSpell(spell,false);
+
uint32 first_spell = spellmgr.GetFirstSpellInChain(spell);
if(GetTalentSpellCost(first_spell))
targetPlayer->SendTalentsInfoData(false);
+
return true;
}
+
bool ChatHandler::HandleAddItemCommand(const char* args)
{
if (!*args)
return false;
+
uint32 itemId = 0;
+
if(args[0]=='[') // [name] manual form
{
char* citemName = strtok((char*)args, "]");
+
if(citemName && citemName[0])
{
std::string itemName = citemName+1;
@@ -2380,17 +2625,24 @@ bool ChatHandler::HandleAddItemCommand(const char* args)
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(GetTrinityString(LANG_ADDITEM), itemId, count);
+
ItemPrototype const *pProto = objmgr.GetItemPrototype(itemId);
if(!pProto)
{
@@ -2398,6 +2650,7 @@ bool ChatHandler::HandleAddItemCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
//Subtract
if (count < 0)
{
@@ -2405,43 +2658,55 @@ bool ChatHandler::HandleAddItemCommand(const char* args)
PSendSysMessage(LANG_REMOVEITEM, itemId, -count, GetNameLink(plTarget).c_str());
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)
{
@@ -2449,17 +2714,21 @@ bool ChatHandler::HandleAddItemSetCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
Player* pl = m_session->GetPlayer();
Player* plTarget = getSelectedPlayer();
if(!plTarget)
plTarget = pl;
+
sLog.outDetail(GetTrinityString(LANG_ADDITEMSET), itemsetId);
+
bool found = false;
for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
{
ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype>(id);
if (!pProto)
continue;
+
if (pProto->ItemSet == itemsetId)
{
found = true;
@@ -2468,9 +2737,11 @@ bool ChatHandler::HandleAddItemSetCommand(const char* args)
if (msg == EQUIP_ERR_OK)
{
Item* item = plTarget->StoreNewItem( dest, pProto->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);
@@ -2482,21 +2753,27 @@ bool ChatHandler::HandleAddItemSetCommand(const char* args)
}
}
}
+
if (!found)
{
PSendSysMessage(LANG_NO_ITEMS_FROM_ITEMSET_FOUND,itemsetId);
+
SetSentErrorMessage(true);
return false;
}
+
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);
if(!item_id)
{
@@ -2504,6 +2781,7 @@ bool ChatHandler::HandleListItemCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
ItemPrototype const* itemProto = objmgr.GetItemPrototype(item_id);
if(!itemProto)
{
@@ -2511,11 +2789,15 @@ bool ChatHandler::HandleListItemCommand(const char* args)
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);
@@ -2524,12 +2806,14 @@ bool ChatHandler::HandleListItemCommand(const char* args)
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
@@ -2541,6 +2825,7 @@ bool ChatHandler::HandleListItemCommand(const char* args)
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]";
@@ -2550,16 +2835,21 @@ bool ChatHandler::HandleListItemCommand(const char* args)
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);
@@ -2568,6 +2858,7 @@ bool ChatHandler::HandleListItemCommand(const char* args)
mail_count = (*result)[0].GetUInt32();
delete result;
}
+
if(count > 0)
{
result=CharacterDatabase.PQuery(
@@ -2579,6 +2870,7 @@ bool ChatHandler::HandleListItemCommand(const char* args)
}
else
result = NULL;
+
if(result)
{
do
@@ -2591,17 +2883,23 @@ bool ChatHandler::HandleListItemCommand(const char* args)
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);
@@ -2610,6 +2908,7 @@ bool ChatHandler::HandleListItemCommand(const char* args)
auc_count = (*result)[0].GetUInt32();
delete result;
}
+
if(count > 0)
{
result=CharacterDatabase.PQuery(
@@ -2620,6 +2919,7 @@ bool ChatHandler::HandleListItemCommand(const char* args)
}
else
result = NULL;
+
if(result)
{
do
@@ -2629,11 +2929,15 @@ bool ChatHandler::HandleListItemCommand(const char* args)
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;
}
+
// guild bank case
uint32 guild_count = 0;
result=CharacterDatabase.PQuery("SELECT COUNT(item_entry) FROM guild_bank_item WHERE item_entry='%u'",item_id);
@@ -2642,11 +2946,13 @@ bool ChatHandler::HandleListItemCommand(const char* args)
guild_count = (*result)[0].GetUInt32();
delete result;
}
+
result=CharacterDatabase.PQuery(
// 0 1 2
"SELECT gi.item_guid, gi.guildid, guild.name "
"FROM guild_bank_item AS gi, guild WHERE gi.item_entry='%u' AND gi.guildid = guild.guildid LIMIT %u ",
item_id,uint32(count));
+
if(result)
{
do
@@ -2655,33 +2961,44 @@ bool ChatHandler::HandleListItemCommand(const char* args)
uint32 item_guid = fields[0].GetUInt32();
uint32 guild_guid = fields[1].GetUInt32();
std::string guild_name = fields[2].GetCppString();
+
char const* item_pos = "[in guild bank]";
+
PSendSysMessage(LANG_ITEMLIST_GUILD,item_guid,guild_name.c_str(),guild_guid,item_pos);
} while (result->NextRow());
+
int64 res_count = result->GetRowCount();
+
delete result;
+
if(count > res_count)
count-=res_count;
else if(count)
count = 0;
}
+
if(inv_count+mail_count+auc_count+guild_count == 0)
{
SendSysMessage(LANG_COMMAND_NOITEMFOUND);
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_COMMAND_LISTITEMMESSAGE,item_id,inv_count+mail_count+auc_count+guild_count,inv_count,mail_count,auc_count,guild_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);
if(!go_id)
{
@@ -2689,6 +3006,7 @@ bool ChatHandler::HandleListObjectCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
GameObjectInfo const * gInfo = objmgr.GetGameObjectInfo(go_id);
if(!gInfo)
{
@@ -2696,11 +3014,15 @@ bool ChatHandler::HandleListObjectCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
char* c_count = strtok(NULL, " ");
int count = c_count ? atol(c_count) : 10;
+
if(count < 0)
return false;
+
QueryResult *result;
+
uint32 obj_count = 0;
result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM gameobject WHERE id='%u'",go_id);
if(result)
@@ -2708,6 +3030,7 @@ bool ChatHandler::HandleListObjectCommand(const char* args)
obj_count = (*result)[0].GetUInt32();
delete result;
}
+
if(m_session)
{
Player* pl = m_session->GetPlayer();
@@ -2717,6 +3040,7 @@ bool ChatHandler::HandleListObjectCommand(const char* args)
else
result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map FROM gameobject WHERE id = '%u' LIMIT %u",
go_id,uint32(count));
+
if (result)
{
do
@@ -2727,37 +3051,47 @@ bool ChatHandler::HandleListObjectCommand(const char* args)
float y = fields[2].GetFloat();
float z = fields[3].GetFloat();
int mapid = fields[4].GetUInt16();
+
if (m_session)
PSendSysMessage(LANG_GO_LIST_CHAT, guid, guid, gInfo->name, x, y, z, mapid);
else
PSendSysMessage(LANG_GO_LIST_CONSOLE, guid, gInfo->name, x, y, z, mapid);
} while (result->NextRow());
+
delete result;
}
+
PSendSysMessage(LANG_COMMAND_LISTOBJMESSAGE,go_id,obj_count);
return true;
}
+
bool ChatHandler::HandleGameObjectStateCommand(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* gobj = NULL;
+
if(GameObjectData const* goData = objmgr.GetGOData(lowguid))
gobj = GetObjectGlobalyWithGuidOrNearWithDbGuid(lowguid, goData->id);
+
if(!gobj)
{
PSendSysMessage(LANG_COMMAND_OBJNOTFOUND, lowguid);
SetSentErrorMessage(true);
return false;
}
+
char* ctype = strtok(NULL, " ");
if(!ctype)
return false;
+
int32 type = atoi(ctype);
if(type < 0)
{
@@ -2771,10 +3105,13 @@ bool ChatHandler::HandleGameObjectStateCommand(const char* args)
}
return true;
}
+
char* cstate = strtok(NULL, " ");
if(!cstate)
return false;
+
int32 state = atoi(cstate);
+
if(type < 4)
gobj->SetByteValue(GAMEOBJECT_BYTES_1, type, state);
else if(type == 4)
@@ -2785,16 +3122,20 @@ bool ChatHandler::HandleGameObjectStateCommand(const char* args)
gobj->SendMessageToSet(&data, true);
}
PSendSysMessage("Set gobject type %d state %d", type, state);
+
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);
if(!cr_id)
{
@@ -2802,6 +3143,7 @@ bool ChatHandler::HandleListCreatureCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
CreatureInfo const* cInfo = objmgr.GetCreatureTemplate(cr_id);
if(!cInfo)
{
@@ -2809,11 +3151,15 @@ bool ChatHandler::HandleListCreatureCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
char* c_count = strtok(NULL, " ");
int count = c_count ? atol(c_count) : 10;
+
if(count < 0)
return false;
+
QueryResult *result;
+
uint32 cr_count = 0;
result=WorldDatabase.PQuery("SELECT COUNT(guid) FROM creature WHERE id='%u'",cr_id);
if(result)
@@ -2821,6 +3167,7 @@ bool ChatHandler::HandleListCreatureCommand(const char* args)
cr_count = (*result)[0].GetUInt32();
delete result;
}
+
if(m_session)
{
Player* pl = m_session->GetPlayer();
@@ -2830,6 +3177,7 @@ bool ChatHandler::HandleListCreatureCommand(const char* args)
else
result = WorldDatabase.PQuery("SELECT guid, position_x, position_y, position_z, map FROM creature WHERE id = '%u' LIMIT %u",
cr_id,uint32(count));
+
if (result)
{
do
@@ -2840,33 +3188,43 @@ bool ChatHandler::HandleListCreatureCommand(const char* args)
float y = fields[2].GetFloat();
float z = fields[3].GetFloat();
int mapid = fields[4].GetUInt16();
+
if (m_session)
PSendSysMessage(LANG_CREATURE_LIST_CHAT, guid, guid, cInfo->Name, x, y, z, mapid);
else
PSendSysMessage(LANG_CREATURE_LIST_CONSOLE, 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);
+
bool found = false;
+
// Search in `item_template`
for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
{
ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype >(id);
if(!pProto)
continue;
+
int loc_idx = GetSessionDbLocaleIndex();
if ( loc_idx >= 0 )
{
@@ -2876,47 +3234,61 @@ bool ChatHandler::HandleLookupItemCommand(const char* args)
if (il->Name.size() > loc_idx && !il->Name[loc_idx].empty())
{
std::string name = il->Name[loc_idx];
+
if (Utf8FitTo(name, wnamepart))
{
if (m_session)
PSendSysMessage(LANG_ITEM_LIST_CHAT, id, id, name.c_str());
else
PSendSysMessage(LANG_ITEM_LIST_CONSOLE, id, name.c_str());
+
if(!found)
found = true;
+
continue;
}
}
}
}
+
std::string name = pProto->Name1;
if(name.empty())
continue;
+
if (Utf8FitTo(name, wnamepart))
{
if (m_session)
PSendSysMessage(LANG_ITEM_LIST_CHAT, id, id, name.c_str());
else
PSendSysMessage(LANG_ITEM_LIST_CONSOLE, id, name.c_str());
+
if(!found)
found = true;
}
}
+
if (!found)
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 );
+
bool found = false;
+
// Search in ItemSet.dbc
for (uint32 id = 0; id < sItemSetStore.GetNumRows(); id++)
{
@@ -2927,6 +3299,7 @@ bool ChatHandler::HandleLookupItemSetCommand(const char* args)
std::string name = set->name[loc];
if(name.empty())
continue;
+
if (!Utf8FitTo(name, wnamepart))
{
loc = 0;
@@ -2934,13 +3307,16 @@ bool ChatHandler::HandleLookupItemSetCommand(const char* args)
{
if(loc==GetSessionDbcLocale())
continue;
+
name = set->name[loc];
if(name.empty())
continue;
+
if (Utf8FitTo(name, wnamepart))
break;
}
}
+
if(loc < MAX_LOCALE)
{
// send item set in "id - [namedlink locale]" format
@@ -2948,6 +3324,7 @@ bool ChatHandler::HandleLookupItemSetCommand(const char* args)
PSendSysMessage(LANG_ITEMSET_LIST_CHAT,id,id,name.c_str(),localeNames[loc]);
else
PSendSysMessage(LANG_ITEMSET_LIST_CONSOLE,id,name.c_str(),localeNames[loc]);
+
if(!found)
found = true;
}
@@ -2957,19 +3334,26 @@ bool ChatHandler::HandleLookupItemSetCommand(const char* args)
SendSysMessage(LANG_COMMAND_NOITEMSETFOUND);
return true;
}
+
bool ChatHandler::HandleLookupSkillCommand(const char* args)
{
if(!*args)
return false;
+
// can be NULL in console call
Player* target = getSelectedPlayer();
+
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 );
+
bool found = false;
+
// Search in SkillLine.dbc
for (uint32 id = 0; id < sSkillLineStore.GetNumRows(); id++)
{
@@ -2980,6 +3364,7 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args)
std::string name = skillInfo->name[loc];
if(name.empty())
continue;
+
if (!Utf8FitTo(name, wnamepart))
{
loc = 0;
@@ -2987,13 +3372,16 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args)
{
if(loc==GetSessionDbcLocale())
continue;
+
name = skillInfo->name[loc];
if(name.empty())
continue;
+
if (Utf8FitTo(name, wnamepart))
break;
}
}
+
if(loc < MAX_LOCALE)
{
char valStr[50] = "";
@@ -3005,14 +3393,17 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args)
uint32 maxValue = target->GetPureMaxSkillValue(id);
uint32 permValue = target->GetSkillPermBonusValue(id);
uint32 tempValue = target->GetSkillTempBonusValue(id);
+
char const* valFormat = GetTrinityString(LANG_SKILL_VALUES);
snprintf(valStr,50,valFormat,curValue,maxValue,permValue,tempValue);
}
+
// send skill in "id - [namedlink locale]" format
if (m_session)
PSendSysMessage(LANG_SKILL_LIST_CHAT,id,id,name.c_str(),localeNames[loc],knownStr,valStr);
else
PSendSysMessage(LANG_SKILL_LIST_CONSOLE,id,name.c_str(),localeNames[loc],knownStr,valStr);
+
if(!found)
found = true;
}
@@ -3022,19 +3413,26 @@ bool ChatHandler::HandleLookupSkillCommand(const char* args)
SendSysMessage(LANG_COMMAND_NOSKILLFOUND);
return true;
}
+
bool ChatHandler::HandleLookupSpellCommand(const char* args)
{
if(!*args)
return false;
+
// can be NULL at console call
Player* target = getSelectedPlayer();
+
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 );
+
bool found = false;
+
// Search in Spell.dbc
for (uint32 id = 0; id < sSpellStore.GetNumRows(); id++)
{
@@ -3045,6 +3443,7 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args)
std::string name = spellInfo->SpellName[loc];
if(name.empty())
continue;
+
if (!Utf8FitTo(name, wnamepart))
{
loc = 0;
@@ -3052,37 +3451,47 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args)
{
if(loc==GetSessionDbcLocale())
continue;
+
name = spellInfo->SpellName[loc];
if(name.empty())
continue;
+
if (Utf8FitTo(name, wnamepart))
break;
}
}
+
if(loc < MAX_LOCALE)
{
bool known = target && target->HasSpell(id);
bool learn = (spellInfo->Effect[0] == SPELL_EFFECT_LEARN_SPELL);
+
uint32 talentCost = GetTalentSpellCost(id);
+
bool talent = (talentCost > 0);
bool passive = IsPassiveSpell(id);
bool active = target && target->HasAura(id);
+
// unit32 used to prevent interpreting uint8 as char at output
// find rank of learned spell for learning spell, or talent rank
uint32 rank = talentCost ? talentCost : spellmgr.GetSpellRank(learn ? spellInfo->EffectTriggerSpell[0] : id);
+
// send spell in "id - [name, rank N] [talent] [passive] [learn] [known]" format
std::ostringstream ss;
if (m_session)
ss << id << " - |cffffffff|Hspell:" << id << "|h[" << name;
else
ss << id << " - " << name;
+
// include rank in link name
if(rank)
ss << GetTrinityString(LANG_SPELL_RANK) << rank;
+
if (m_session)
ss << " " << localeNames[loc] << "]|h|r";
else
ss << " " << localeNames[loc];
+
if(talent)
ss << GetTrinityString(LANG_TALENT);
if(passive)
@@ -3093,7 +3502,9 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args)
ss << GetTrinityString(LANG_KNOWN);
if(active)
ss << GetTrinityString(LANG_ACTIVE);
+
SendSysMessage(ss.str().c_str());
+
if(!found)
found = true;
}
@@ -3103,23 +3514,31 @@ bool ChatHandler::HandleLookupSpellCommand(const char* args)
SendSysMessage(LANG_COMMAND_NOSPELLFOUND);
return true;
}
+
bool ChatHandler::HandleLookupQuestCommand(const char* args)
{
if(!*args)
return false;
+
// can be NULL at console call
Player* target = getSelectedPlayer();
+
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);
+
bool found = false;
+
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 = GetSessionDbLocaleIndex();
if ( loc_idx >= 0 )
{
@@ -3129,12 +3548,15 @@ bool ChatHandler::HandleLookupQuestCommand(const char* args)
if (il->Title.size() > loc_idx && !il->Title[loc_idx].empty())
{
std::string title = il->Title[loc_idx];
+
if (Utf8FitTo(title, wnamepart))
{
char const* statusStr = "";
+
if(target)
{
QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
+
if(status == QUEST_STATUS_COMPLETE)
{
if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
@@ -3145,26 +3567,33 @@ bool ChatHandler::HandleLookupQuestCommand(const char* args)
else if(status == QUEST_STATUS_INCOMPLETE)
statusStr = GetTrinityString(LANG_COMMAND_QUEST_ACTIVE);
}
+
if (m_session)
PSendSysMessage(LANG_QUEST_LIST_CHAT,qinfo->GetQuestId(),qinfo->GetQuestId(),qinfo->GetQuestLevel(),title.c_str(),statusStr);
else
PSendSysMessage(LANG_QUEST_LIST_CONSOLE,qinfo->GetQuestId(),title.c_str(),statusStr);
+
if(!found)
found = true;
+
continue;
}
}
}
}
+
std::string title = qinfo->GetTitle();
if(title.empty())
continue;
+
if (Utf8FitTo(title, wnamepart))
{
char const* statusStr = "";
+
if(target)
{
QuestStatus status = target->GetQuestStatus(qinfo->GetQuestId());
+
if(status == QUEST_STATUS_COMPLETE)
{
if(target->GetQuestRewardStatus(qinfo->GetQuestId()))
@@ -3175,34 +3604,45 @@ bool ChatHandler::HandleLookupQuestCommand(const char* args)
else if(status == QUEST_STATUS_INCOMPLETE)
statusStr = GetTrinityString(LANG_COMMAND_QUEST_ACTIVE);
}
+
if (m_session)
PSendSysMessage(LANG_QUEST_LIST_CHAT,qinfo->GetQuestId(),qinfo->GetQuestId(),qinfo->GetQuestLevel(),title.c_str(),statusStr);
else
PSendSysMessage(LANG_QUEST_LIST_CONSOLE,qinfo->GetQuestId(),title.c_str(),statusStr);
+
if(!found)
found = true;
}
}
+
if (!found)
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);
+
bool found = false;
+
for (uint32 id = 0; id< sCreatureStorage.MaxEntry; ++id)
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo> (id);
if(!cInfo)
continue;
+
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -3212,52 +3652,67 @@ bool ChatHandler::HandleLookupCreatureCommand(const char* args)
if (cl->Name.size() > loc_idx && !cl->Name[loc_idx].empty ())
{
std::string name = cl->Name[loc_idx];
+
if (Utf8FitTo (name, wnamepart))
{
if (m_session)
PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CHAT, id, id, name.c_str ());
else
PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CONSOLE, id, name.c_str ());
+
if(!found)
found = true;
+
continue;
}
}
}
}
+
std::string name = cInfo->Name;
if (name.empty ())
continue;
+
if (Utf8FitTo(name, wnamepart))
{
if (m_session)
PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CHAT, id, id, name.c_str ());
else
PSendSysMessage (LANG_CREATURE_ENTRY_LIST_CONSOLE, id, name.c_str ());
+
if(!found)
found = true;
}
}
+
if (!found)
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);
+
bool found = false;
+
for (uint32 id = 0; id< sGOStorage.MaxEntry; id++ )
{
GameObjectInfo const* gInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
if(!gInfo)
continue;
+
int loc_idx = GetSessionDbLocaleIndex();
if ( loc_idx >= 0 )
{
@@ -3267,59 +3722,76 @@ bool ChatHandler::HandleLookupObjectCommand(const char* args)
if (gl->Name.size() > loc_idx && !gl->Name[loc_idx].empty())
{
std::string name = gl->Name[loc_idx];
+
if (Utf8FitTo(name, wnamepart))
{
if (m_session)
PSendSysMessage(LANG_GO_ENTRY_LIST_CHAT, id, id, name.c_str());
else
PSendSysMessage(LANG_GO_ENTRY_LIST_CONSOLE, id, name.c_str());
+
if(!found)
found = true;
+
continue;
}
}
}
}
+
std::string name = gInfo->name;
if(name.empty())
continue;
+
if(Utf8FitTo(name, wnamepart))
{
if (m_session)
PSendSysMessage(LANG_GO_ENTRY_LIST_CHAT, id, id, name.c_str());
else
PSendSysMessage(LANG_GO_ENTRY_LIST_CONSOLE, id, name.c_str());
+
if(!found)
found = true;
}
}
+
if(!found)
SendSysMessage(LANG_COMMAND_NOGAMEOBJECTFOUND);
+
return true;
}
+
bool ChatHandler::HandleLookupFactionCommand(const char* args)
{
if (!*args)
return false;
+
// Can be NULL at console call
Player *target = getSelectedPlayer ();
+
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);
+
bool found = false;
+
for (uint32 id = 0; id < sFactionStore.GetNumRows(); ++id)
{
FactionEntry const *factionEntry = sFactionStore.LookupEntry (id);
if (factionEntry)
{
FactionState const* repState = target ? target->GetReputationMgr().GetState(factionEntry) : NULL;
+
int loc = GetSessionDbcLocale();
std::string name = factionEntry->name[loc];
if(name.empty())
continue;
+
if (!Utf8FitTo(name, wnamepart))
{
loc = 0;
@@ -3327,13 +3799,16 @@ bool ChatHandler::HandleLookupFactionCommand(const char* args)
{
if(loc==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
@@ -3343,11 +3818,14 @@ bool ChatHandler::HandleLookupFactionCommand(const char* args)
ss << id << " - |cffffffff|Hfaction:" << id << "|h[" << name << " " << localeNames[loc] << "]|h|r";
else
ss << id << " - " << name << " " << localeNames[loc];
+
if (repState) // and then target!=NULL also
{
ReputationRank rank = target->GetReputationMgr().GetRank(factionEntry);
std::string rankName = GetMangosString(ReputationRankStrIndex[rank]);
+
ss << " " << rankName << "|h|r (" << target->GetReputationMgr().GetReputation(factionEntry) << ")";
+
if(repState->Flags & FACTION_FLAG_VISIBLE)
ss << GetTrinityString(LANG_FACTION_VISIBLE);
if(repState->Flags & FACTION_FLAG_AT_WAR)
@@ -3363,27 +3841,36 @@ bool ChatHandler::HandleLookupFactionCommand(const char* args)
}
else
ss << GetTrinityString(LANG_FACTION_NOREPUTATION);
+
SendSysMessage(ss.str().c_str());
+
if(!found)
found = true;
}
}
}
+
if (!found)
SendSysMessage(LANG_COMMAND_FACTION_NOTFOUND);
return true;
}
+
bool ChatHandler::HandleLookupTaxiNodeCommand(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 );
+
bool found = false;
+
// Search in TaxiNodes.dbc
for (uint32 id = 0; id < sTaxiNodesStore.GetNumRows(); id++)
{
@@ -3394,6 +3881,7 @@ bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args)
std::string name = nodeEntry->name[loc];
if(name.empty())
continue;
+
if (!Utf8FitTo(name, wnamepart))
{
loc = 0;
@@ -3401,13 +3889,16 @@ bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args)
{
if(loc==GetSessionDbcLocale())
continue;
+
name = nodeEntry->name[loc];
if(name.empty())
continue;
+
if (Utf8FitTo(name, wnamepart))
break;
}
}
+
if(loc < MAX_LOCALE)
{
// send taxinode in "id - [name] (Map:m X:x Y:y Z:z)" format
@@ -3417,6 +3908,7 @@ bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args)
else
PSendSysMessage (LANG_TAXINODE_ENTRY_LIST_CONSOLE, id, name.c_str(), localeNames[loc],
nodeEntry->map_id,nodeEntry->x,nodeEntry->y,nodeEntry->z);
+
if(!found)
found = true;
}
@@ -3426,17 +3918,23 @@ bool ChatHandler::HandleLookupTaxiNodeCommand(const char * args)
SendSysMessage(LANG_COMMAND_NOTAXINODEFOUND);
return true;
}
+
bool ChatHandler::HandleLookupMapCommand(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);
+
bool found = false;
+
// search in Map.dbc
for(uint32 id = 0; id < sMapStore.GetNumRows(); id++)
{
@@ -3444,9 +3942,11 @@ bool ChatHandler::HandleLookupMapCommand(const char* args)
if(MapInfo)
{
uint8 loc = m_session ? m_session->GetSessionDbcLocale() : sWorld.GetDefaultDbcLocale();
+
std::string name = MapInfo->name[loc];
if(name.empty())
continue;
+
if(!Utf8FitTo(name, wnamepart))
{
loc = LOCALE_enUS;
@@ -3454,43 +3954,55 @@ bool ChatHandler::HandleLookupMapCommand(const char* args)
{
if(m_session && loc == m_session->GetSessionDbcLocale())
continue;
+
name = MapInfo->name[loc];
if(name.empty())
continue;
+
if(Utf8FitTo(name, wnamepart))
break;
}
}
+
if(loc < MAX_LOCALE)
{
// send map in "id - [name][Continent][Instance/Battleground/Arena][Raid reset time:][Heroic reset time:][Mountable]" format
std::ostringstream ss;
+
if(m_session)
ss << id << " - |cffffffff|Hmap:" << id << "|h[" << name << "]";
else // console
ss << id << " - [" << name << "]";
+
if(MapInfo->IsContinent())
ss << GetTrinityString(LANG_CONTINENT);
+
switch(MapInfo->map_type)
{
case MAP_INSTANCE: ss << GetTrinityString(LANG_INSTANCE); break;
case MAP_BATTLEGROUND: ss << GetTrinityString(LANG_BATTLEGROUND); break;
case MAP_ARENA: ss << GetTrinityString(LANG_ARENA); break;
}
+
if(MapInfo->IsRaid())
ss << GetTrinityString(LANG_RAID);
+
if(MapInfo->SupportsHeroicMode())
ss << GetTrinityString(LANG_HEROIC);
+
uint32 ResetTimeRaid = MapInfo->resetTimeRaid;
std::string ResetTimeRaidStr;
if(ResetTimeRaid)
ResetTimeRaidStr = secsToTimeString(ResetTimeRaid, true, false);
+
uint32 ResetTimeHeroic = MapInfo->resetTimeHeroic;
std::string ResetTimeHeroicStr;
if(ResetTimeHeroic)
ResetTimeHeroicStr = secsToTimeString(ResetTimeHeroic, true, false);
+
if(MapInfo->IsMountAllowed())
ss << GetTrinityString(LANG_MOUNTABLE);
+
if(ResetTimeRaid && !ResetTimeHeroic)
PSendSysMessage(ss.str().c_str(), ResetTimeRaidStr.c_str());
else if(!ResetTimeRaid && ResetTimeHeroic)
@@ -3499,15 +4011,19 @@ bool ChatHandler::HandleLookupMapCommand(const char* args)
PSendSysMessage(ss.str().c_str(), ResetTimeRaidStr.c_str(), ResetTimeHeroicStr.c_str());
else
SendSysMessage(ss.str().c_str());
+
if(!found)
found = true;
}
}
}
+
if(!found)
SendSysMessage(LANG_COMMAND_NOMAPFOUND);
+
return true;
}
+
/** \brief GM command level 3 - Create a guild.
*
* This command allows a GM (level 3) to create a guild.
@@ -3520,22 +4036,28 @@ bool ChatHandler::HandleGuildCreateCommand(const char* args)
{
if(!*args)
return false;
+
// if not guild name only (in "") then player name
Player* target;
if(!extractPlayerTarget(*args!='"' ? (char*)args : NULL, &target))
return false;
+
char* tailStr = *args!='"' ? strtok(NULL, "") : (char*)args;
if(!tailStr)
return false;
+
char* guildStr = extractQuotedArg(tailStr);
if(!guildStr)
return false;
+
std::string guildname = guildStr;
+
if (target->GetGuildId())
{
SendSysMessage (LANG_PLAYER_IN_GUILD);
return true;
}
+
Guild *guild = new Guild;
if (!guild->Create (target,guildname))
{
@@ -3544,47 +4066,60 @@ bool ChatHandler::HandleGuildCreateCommand(const char* args)
SetSentErrorMessage (true);
return false;
}
+
objmgr.AddGuild (guild);
return true;
}
+
bool ChatHandler::HandleGuildInviteCommand(const char *args)
{
if(!*args)
return false;
+
// if not guild name only (in "") then player name
uint64 target_guid;
if(!extractPlayerTarget(*args!='"' ? (char*)args : NULL, NULL, &target_guid))
return false;
+
char* tailStr = *args!='"' ? strtok(NULL, "") : (char*)args;
if(!tailStr)
return false;
+
char* guildStr = extractQuotedArg(tailStr);
if(!guildStr)
return false;
+
std::string glName = guildStr;
Guild* targetGuild = objmgr.GetGuildByName (glName);
if (!targetGuild)
return false;
+
// player's guild membership checked in AddMember before add
if (!targetGuild->AddMember (target_guid,targetGuild->GetLowestRank ()))
return false;
+
return true;
}
+
bool ChatHandler::HandleGuildUninviteCommand(const char *args)
{
Player* target;
uint64 target_guid;
if(!extractPlayerTarget((char*)args,&target,&target_guid))
return false;
+
uint32 glId = target ? target->GetGuildId () : Player::GetGuildIdFromDB (target_guid);
if (!glId)
return false;
+
Guild* targetGuild = objmgr.GetGuildById (glId);
if (!targetGuild)
return false;
+
targetGuild->DelMember (target_guid);
return true;
}
+
bool ChatHandler::HandleGuildRankCommand(const char *args)
{
char* nameStr;
@@ -3592,45 +4127,59 @@ bool ChatHandler::HandleGuildRankCommand(const char *args)
extractOptFirstArg((char*)args,&nameStr,&rankStr);
if(!rankStr)
return false;
+
Player* target;
uint64 target_guid;
std::string target_name;
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false;
+
uint32 glId = target ? target->GetGuildId () : Player::GetGuildIdFromDB (target_guid);
if (!glId)
return false;
+
Guild* targetGuild = objmgr.GetGuildById (glId);
if (!targetGuild)
return false;
+
uint32 newrank = uint32 (atoi (rankStr));
if (newrank > targetGuild->GetLowestRank ())
return false;
+
targetGuild->ChangeRank (target_guid,newrank);
return true;
}
+
bool ChatHandler::HandleGuildDeleteCommand(const char* args)
{
if (!*args)
return false;
+
char* guildStr = extractQuotedArg((char*)args);
if(!guildStr)
return false;
+
std::string gld = guildStr;
+
Guild* targetGuild = objmgr.GetGuildByName (gld);
if (!targetGuild)
return false;
+
targetGuild->Disband ();
+
return true;
}
+
bool ChatHandler::HandleGetDistanceCommand(const char* args)
{
WorldObject* obj = NULL;
+
if (*args)
{
uint64 guid = extractGuidFromLink((char*)args);
if(guid)
obj = (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*m_session->GetPlayer(),guid,TYPEMASK_UNIT|TYPEMASK_GAMEOBJECT);
+
if(!obj)
{
SendSysMessage(LANG_PLAYER_NOT_FOUND);
@@ -3641,6 +4190,7 @@ bool ChatHandler::HandleGetDistanceCommand(const char* args)
else
{
obj = getSelectedUnit();
+
if(!obj)
{
SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
@@ -3648,23 +4198,29 @@ bool ChatHandler::HandleGetDistanceCommand(const char* args)
return false;
}
}
+
PSendSysMessage(LANG_DISTANCE, m_session->GetPlayer()->GetDistance(obj),m_session->GetPlayer()->GetDistance2d(obj));
+
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->GetTypeId()==TYPEID_PLAYER)
{
if(HasLowerSecurity((Player*)target,0,false))
return false;
}
+
if( target->isAlive() )
{
if(sWorld.getConfig(CONFIG_DIE_COMMAND_MODE))
@@ -3672,29 +4228,39 @@ bool ChatHandler::HandleDieCommand(const char* /*args*/)
else
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_int = atoi((char*)damageStr);
if(damage_int <=0)
return true;
+
uint32 damage = damage_int;
+
char* schoolStr = strtok((char*)NULL, " ");
+
// flat melee damage without resistence/etc reduction
if (!schoolStr)
{
@@ -3703,39 +4269,53 @@ bool ChatHandler::HandleDamageCommand(const char * args)
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, NULL, BASE_ATTACK);
+
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()->DealDamageMods(target,damage,&absorb);
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);
return true;
}
+
bool ChatHandler::HandleModifyArenaCommand(const char * args)
{
if (!*args)
return false;
+
Player *target = getSelectedPlayer();
if(!target)
{
@@ -3743,17 +4323,23 @@ bool ChatHandler::HandleModifyArenaCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
int32 amount = (uint32)atoi(args);
+
target->ModifyArenaPoints(amount);
+
PSendSysMessage(LANG_COMMAND_MODIFY_ARENA, GetNameLink(target).c_str(), target->GetArenaPoints());
+
return true;
}
+
bool ChatHandler::HandleReviveCommand(const char* args)
{
Player* target;
uint64 target_guid;
if(!extractPlayerTarget((char*)args,&target,&target_guid))
return false;
+
if (target)
{
target->ResurrectPlayer(0.5f);
@@ -3763,8 +4349,10 @@ bool ChatHandler::HandleReviveCommand(const char* args)
else
// will resurrected at login without corpse
ObjectAccessor::Instance().ConvertCorpseForPlayer(target_guid);
+
return true;
}
+
bool ChatHandler::HandleAuraCommand(const char* args)
{
Unit *target = getSelectedUnit();
@@ -3774,8 +4362,10 @@ bool ChatHandler::HandleAuraCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
uint32 spellID = extractSpellIdFromLink((char*)args);
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry( spellID );
uint8 eff_mask=0;
if(spellInfo)
@@ -3795,8 +4385,10 @@ bool ChatHandler::HandleAuraCommand(const char* args)
Aura *Aur = new Aura(spellInfo, eff_mask, target, target, target);
target->AddAura(Aur);
}
+
return true;
}
+
bool ChatHandler::HandleUnAuraCommand(const char* args)
{
Unit *target = getSelectedUnit();
@@ -3806,29 +4398,39 @@ bool ChatHandler::HandleUnAuraCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
std::string argstr = args;
if (argstr == "all")
{
target->RemoveAllAuras();
return true;
}
+
// number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
uint32 spellID = extractSpellIdFromLink((char*)args);
if(!spellID)
return false;
+
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)
@@ -3837,15 +4439,20 @@ bool ChatHandler::HandleLinkGraveCommand(const char* args)
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 )
{
@@ -3853,16 +4460,21 @@ bool ChatHandler::HandleLinkGraveCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if(objmgr.AddGraveYardLink(g_id,zoneId,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)
@@ -3871,13 +4483,17 @@ bool ChatHandler::HandleNearGraveCommand(const char* args)
g_team = ALLIANCE;
else
return false;
+
Player* player = m_session->GetPlayer();
uint32 zone_id = player->GetZoneId();
+
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,zone_id);
if (!data)
{
@@ -3885,32 +4501,40 @@ bool ChatHandler::HandleNearGraveCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
g_team = data->team;
+
std::string team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_NOTEAM);
+
if(g_team == 0)
team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ANY);
else if(g_team == HORDE)
team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_HORDE);
else if(g_team == ALLIANCE)
team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
+
PSendSysMessage(LANG_COMMAND_GRAVEYARDNEAREST, g_id,team_name.c_str(),zone_id);
}
else
{
std::string team_name;
+
if(g_team == 0)
team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ANY);
else if(g_team == HORDE)
team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_HORDE);
else if(g_team == ALLIANCE)
team_name = GetTrinityString(LANG_COMMAND_GRAVEYARD_ALLIANCE);
+
if(g_team == ~uint32(0))
PSendSysMessage(LANG_COMMAND_ZONENOGRAVEYARDS, zone_id);
else
PSendSysMessage(LANG_COMMAND_ZONENOGRAFACTION, zone_id,team_name.c_str());
}
+
return true;
}
+
//-----------------------Npc Commands-----------------------
bool ChatHandler::HandleNpcAllowMovementCommand(const char* /*args*/)
{
@@ -3926,13 +4550,16 @@ bool ChatHandler::HandleNpcAllowMovementCommand(const char* /*args*/)
}
return true;
}
+
bool ChatHandler::HandleNpcChangeEntryCommand(const char *args)
{
if (!*args)
return false;
+
uint32 newEntryNum = atoi(args);
if(!newEntryNum)
return false;
+
Unit* unit = getSelectedUnit();
if(!unit || unit->GetTypeId() != TYPEID_UNIT)
{
@@ -3947,26 +4574,31 @@ bool ChatHandler::HandleNpcChangeEntryCommand(const char *args)
SendSysMessage(LANG_ERROR);
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());
@@ -3978,6 +4610,7 @@ bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/)
if(const CreatureData* const linked = target->GetLinkedRespawnCreatureData())
if(CreatureInfo const *master = GetCreatureInfo(linked->id))
PSendSysMessage(LANG_NPCINFO_LINKGUID, objmgr.GetLinkedRespawnGuid(target->GetDBTableGUIDLow()), linked->id, master->Name);
+
if ((npcflags & UNIT_NPC_FLAG_VENDOR) )
{
SendSysMessage(LANG_NPCINFO_VENDOR);
@@ -3986,12 +4619,15 @@ bool ChatHandler::HandleNpcInfoCommand(const char* /*args*/)
{
SendSysMessage(LANG_NPCINFO_TRAINER);
}
+
return true;
}
+
//play npc emote
bool ChatHandler::HandleNpcPlayEmoteCommand(const char* args)
{
uint32 emote = atoi((char*)args);
+
Creature* target = getSelectedCreature();
if(!target)
{
@@ -3999,35 +4635,47 @@ bool ChatHandler::HandleNpcPlayEmoteCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
target->SetUInt32Value(UNIT_NPC_EMOTESTATE,emote);
+
return true;
}
+
//TODO: NpcCommands that needs to be fixed :
+
bool ChatHandler::HandleNpcAddWeaponCommand(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)
{
@@ -4050,6 +4698,7 @@ bool ChatHandler::HandleNpcAddWeaponCommand(const char* /*args*/)
added = false;
break;
}
+
if(added)
PSendSysMessage(LANG_ITEM_ADDED_TO_SLOT,ItemID,tmpItem->Name1,SlotID);
}
@@ -4062,11 +4711,14 @@ bool ChatHandler::HandleNpcAddWeaponCommand(const char* /*args*/)
return true;
}
//----------------------------------------------------------
+
bool ChatHandler::HandleExploreCheatCommand(const char* args)
{
if (!*args)
return false;
+
int flag = atoi((char*)args);
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -4074,6 +4726,7 @@ bool ChatHandler::HandleExploreCheatCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
if (flag != 0)
{
PSendSysMessage(LANG_YOU_SET_EXPLORE_ALL, GetNameLink(chr).c_str());
@@ -4086,6 +4739,7 @@ bool ChatHandler::HandleExploreCheatCommand(const char* args)
if (needReportToTarget(chr))
ChatHandler(chr).PSendSysMessage(LANG_YOURS_EXPLORE_SET_NOTHING,GetNameLink().c_str());
}
+
for (uint8 i=0; i<128; ++i)
{
if (flag != 0)
@@ -4097,8 +4751,10 @@ bool ChatHandler::HandleExploreCheatCommand(const char* args)
m_session->GetPlayer()->SetFlag(PLAYER_EXPLORED_ZONES_1+i,0);
}
}
+
return true;
}
+
bool ChatHandler::HandleHoverCommand(const char* args)
{
char* px = strtok((char*)args, " ");
@@ -4107,13 +4763,17 @@ bool ChatHandler::HandleHoverCommand(const char* args)
flag = 1;
else
flag = atoi(px);
+
m_session->GetPlayer()->SetHover(flag);
+
if (flag)
SendSysMessage(LANG_HOVER_ENABLED);
else
SendSysMessage(LANG_HOVER_DISABLED);
+
return true;
}
+
void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint32 oldlevel, uint32 newlevel)
{
if(player)
@@ -4121,6 +4781,7 @@ void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint3
player->GiveLevel(newlevel);
player->InitTalentForLevel();
player->SetUInt32Value(PLAYER_XP,0);
+
if(needReportToTarget(player))
{
if(oldlevel == newlevel)
@@ -4137,6 +4798,7 @@ void ChatHandler::HandleCharacterLevel(Player* player, uint64 player_guid, uint3
CharacterDatabase.PExecute("UPDATE characters SET level = '%u', xp = 0 WHERE guid = '%u'", newlevel, GUID_LOPART(player_guid));
}
}
+
bool ChatHandler::HandleCharacterLevelCommand(const char* args)
{
char* nameStr;
@@ -4144,66 +4806,85 @@ bool ChatHandler::HandleCharacterLevelCommand(const char* args)
extractOptFirstArg((char*)args,&nameStr,&levelStr);
if(!levelStr)
return false;
+
// exception opt second arg: .character level $name
if(isalpha(levelStr[0]))
{
nameStr = levelStr;
levelStr = NULL; // current level will used
}
+
Player* target;
uint64 target_guid;
std::string target_name;
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false;
+
int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid);
int32 newlevel = levelStr ? atoi(levelStr) : oldlevel;
+
if(newlevel < 1)
return false; // invalid level
+
if(newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level
newlevel = STRONG_MAX_LEVEL;
+
HandleCharacterLevel(target,target_guid,oldlevel,newlevel);
+
if(!m_session || m_session->GetPlayer() != target) // including player==NULL
{
std::string nameLink = playerLink(target_name);
PSendSysMessage(LANG_YOU_CHANGE_LVL,nameLink.c_str(),newlevel);
}
+
return true;
}
+
bool ChatHandler::HandleLevelUpCommand(const char* args)
{
char* nameStr;
char* levelStr;
extractOptFirstArg((char*)args,&nameStr,&levelStr);
+
// exception opt second arg: .character level $name
if(levelStr && isalpha(levelStr[0]))
{
nameStr = levelStr;
levelStr = NULL; // current level will used
}
+
Player* target;
uint64 target_guid;
std::string target_name;
if(!extractPlayerTarget(nameStr,&target,&target_guid,&target_name))
return false;
+
int32 oldlevel = target ? target->getLevel() : Player::GetLevelFromDB(target_guid);
int32 addlevel = levelStr ? atoi(levelStr) : 1;
int32 newlevel = oldlevel + addlevel;
+
if(newlevel < 1)
newlevel = 1;
+
if(newlevel > STRONG_MAX_LEVEL) // hardcoded maximum level
newlevel = STRONG_MAX_LEVEL;
+
HandleCharacterLevel(target,target_guid,oldlevel,newlevel);
+
if(!m_session || m_session->GetPlayer() != target) // including chr==NULL
{
std::string nameLink = playerLink(target_name);
PSendSysMessage(LANG_YOU_CHANGE_LVL,nameLink.c_str(),newlevel);
}
+
return true;
}
+
bool ChatHandler::HandleShowAreaCommand(const char* args)
{
if (!*args)
return false;
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -4211,24 +4892,30 @@ bool ChatHandler::HandleShowAreaCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
int area = GetAreaFlagByAreaID(atoi((char*)args));
int offset = area / 32;
uint32 val = (uint32)(1 << (area % 32));
+
if(area<0 || 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;
+
Player *chr = getSelectedPlayer();
if (chr == NULL)
{
@@ -4236,29 +4923,37 @@ bool ChatHandler::HandleHideAreaCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
int area = GetAreaFlagByAreaID(atoi((char*)args));
int offset = area / 32;
uint32 val = (uint32)(1 << (area % 32));
+
if(area<0 || 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::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))
{
@@ -4266,16 +4961,22 @@ bool ChatHandler::HandleChangeWeather(const char* args)
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)
@@ -4284,13 +4985,17 @@ bool ChatHandler::HandleChangeWeather(const char* args)
SetSentErrorMessage(true);
return false;
}
+
wth->SetWeather(WeatherType(type), grade);
+
return true;
}
+
bool ChatHandler::HandleDebugSet32Bit(const char* args)
{
if(!*args)
return false;
+
WorldObject* target = getSelectedObject();
if(!target)
{
@@ -4298,34 +5003,45 @@ bool ChatHandler::HandleDebugSet32Bit(const char* args)
SetSentErrorMessage(true);
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(GetTrinityString(LANG_SET_32BIT), Opcode, Value);
+
uint32 iValue = Value ? 1 << (Value - 1) : 0;
target->SetUInt32Value( Opcode , iValue);
+
PSendSysMessage(LANG_SET_32BIT_FIELD, Opcode, iValue);
return true;
}
+
bool ChatHandler::HandleTeleAddCommand(const char * args)
{
if(!*args)
return false;
+
Player *player=m_session->GetPlayer();
if (!player)
return false;
+
std::string name = args;
+
if(objmgr.GetGameTele(name))
{
SendSysMessage(LANG_COMMAND_TP_ALREADYEXIST);
SetSentErrorMessage(true);
return false;
}
+
GameTele tele;
tele.position_x = player->GetPositionX();
tele.position_y = player->GetPositionY();
@@ -4333,6 +5049,7 @@ bool ChatHandler::HandleTeleAddCommand(const char * args)
tele.orientation = player->GetOrientation();
tele.mapId = player->GetMapId();
tele.name = name;
+
if(objmgr.AddGameTele(tele))
{
SendSysMessage(LANG_COMMAND_TP_ADDED);
@@ -4343,22 +5060,28 @@ bool ChatHandler::HandleTeleAddCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandleTeleDelCommand(const char * args)
{
if(!*args)
return false;
+
std::string name = args;
+
if(!objmgr.DeleteGameTele(name))
{
SendSysMessage(LANG_COMMAND_TELE_NOTFOUND);
SetSentErrorMessage(true);
return false;
}
+
SendSysMessage(LANG_COMMAND_TP_DELETED);
return true;
}
+
bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
{
Unit *unit = getSelectedUnit();
@@ -4368,18 +5091,23 @@ bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
SetSentErrorMessage(true);
return false;
}
+
char const* talentStr = GetTrinityString(LANG_TALENT);
char const* passiveStr = GetTrinityString(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;
+
char const* name = itr->second->GetSpellProto()->SpellName[GetSessionDbcLocale()];
+
if (m_session)
{
std::ostringstream ss_name;
ss_name << "|cffffffff|Hspell:" << itr->second->GetId() << "|h[" << name << "]|h|r";
+
PSendSysMessage(LANG_COMMAND_TARGET_AURADETAIL, itr->second->GetId(), itr->second->GetEffectMask(),
itr->second->GetAuraCharges(), itr->second->GetStackAmount(),itr->second->GetAuraSlot(),
itr->second->GetAuraDuration(), itr->second->GetAuraMaxDuration(),
@@ -4405,40 +5133,50 @@ bool ChatHandler::HandleListAurasCommand (const char * /*args*/)
for (Unit::AuraEffectList::const_iterator itr = uAuraList.begin(); itr != uAuraList.end(); ++itr)
{
bool talent = GetTalentSpellCost((*itr)->GetId()) > 0;
+
char const* name = (*itr)->GetSpellProto()->SpellName[GetSessionDbcLocale()];
+
std::ostringstream ss_name;
ss_name << "|cffffffff|Hspell:" << (*itr)->GetId() << "|h[" << name << "]|h|r";
+
PSendSysMessage(LANG_COMMAND_TARGET_AURASIMPLE, (*itr)->GetId(), (*itr)->GetEffIndex(),
(*itr)->GetAmount());
}
}
return true;
}
+
bool ChatHandler::HandleResetAchievementsCommand (const char * args)
{
Player* target;
uint64 target_guid;
if (!extractPlayerTarget((char*)args,&target,&target_guid))
return false;
+
if(target)
target->GetAchievementMgr().Reset();
else
AchievementMgr::DeleteFromDB(GUID_LOPART(target_guid));
+
return true;
}
+
bool ChatHandler::HandleResetHonorCommand (const char * args)
{
Player* target;
if (!extractPlayerTarget((char*)args,&target))
return false;
+
target->SetUInt32Value(PLAYER_FIELD_KILLS, 0);
target->SetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0);
target->SetUInt32Value(PLAYER_FIELD_HONOR_CURRENCY, 0);
target->SetUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, 0);
target->SetUInt32Value(PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0);
target->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EARN_HONORABLE_KILL);
+
return true;
}
+
static bool HandleResetStatsOrLevelHelper(Player* player)
{
ChrClassesEntry const* cEntry = sChrClassesStore.LookupEntry(player->getClass());
@@ -4447,37 +5185,52 @@ static bool HandleResetStatsOrLevelHelper(Player* player)
sLog.outError("Class %u not found in DBC (Wrong DBC files?)",player->getClass());
return false;
}
+
uint8 powertype = cEntry->powerType;
+
// 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, DEFAULT_COMBAT_REACH );
+
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)
player->InitDisplayIds();
+
player->SetByteValue(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP );
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)
{
Player* target;
if(!extractPlayerTarget((char*)args,&target))
return false;
+
if(!HandleResetStatsOrLevelHelper(target))
return false;
+
// set starting level
uint32 start_level = target->getClass() != CLASS_DEATH_KNIGHT
? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL)
: sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL);
+
target->_ApplyAllLevelScaleItemMods(false);
+
target->SetLevel(start_level);
target->InitRunes();
target->InitStatsForLevel(true);
@@ -4485,26 +5238,34 @@ bool ChatHandler::HandleResetLevelCommand(const char * args)
target->InitGlyphsForLevel();
target->InitTalentForLevel();
target->SetUInt32Value(PLAYER_XP,0);
+
target->_ApplyAllLevelScaleItemMods(true);
+
// reset level for pet
if(Pet* pet = target->GetPet())
pet->SynchronizeLevelWithOwner();
+
return true;
}
+
bool ChatHandler::HandleResetStatsCommand(const char * args)
{
Player* target;
if (!extractPlayerTarget((char*)args,&target))
return false;
+
if (!HandleResetStatsOrLevelHelper(target))
return false;
+
target->InitRunes();
target->InitStatsForLevel(true);
target->InitTaxiNodesForLevel();
target->InitGlyphsForLevel();
target->InitTalentForLevel();
+
return true;
}
+
bool ChatHandler::HandleResetSpellsCommand(const char * args)
{
Player* target;
@@ -4512,9 +5273,11 @@ bool ChatHandler::HandleResetSpellsCommand(const char * args)
std::string target_name;
if(!extractPlayerTarget((char*)args,&target,&target_guid,&target_name))
return false;
+
if(target)
{
target->resetSpells(/* bool myClassOnly */);
+
ChatHandler(target).SendSysMessage(LANG_RESET_SPELLS);
if(!m_session || m_session->GetPlayer() != target)
PSendSysMessage(LANG_RESET_SPELLS_ONLINE,GetNameLink(target).c_str());
@@ -4524,8 +5287,10 @@ bool ChatHandler::HandleResetSpellsCommand(const char * args)
CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE guid = '%u'",uint32(AT_LOGIN_RESET_SPELLS), GUID_LOPART(target_guid));
PSendSysMessage(LANG_RESET_SPELLS_OFFLINE,target_name.c_str());
}
+
return true;
}
+
bool ChatHandler::HandleResetTalentsCommand(const char * args)
{
Player* target;
@@ -4542,16 +5307,19 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args)
{
((Pet *)creature)->resetTalents(true);
((Player*)owner)->SendTalentsInfoData(true);
+
ChatHandler((Player*)owner).SendSysMessage(LANG_RESET_PET_TALENTS);
if(!m_session || m_session->GetPlayer()!=((Player*)owner))
PSendSysMessage(LANG_RESET_PET_TALENTS_ONLINE,GetNameLink((Player*)owner).c_str());
}
return true;
}
+
SendSysMessage(LANG_NO_CHAR_SELECTED);
SetSentErrorMessage(true);
return false;
}
+
if (target)
{
target->resetTalents(true);
@@ -4559,6 +5327,7 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args)
ChatHandler(target).SendSysMessage(LANG_RESET_TALENTS);
if (!m_session || m_session->GetPlayer()!=target)
PSendSysMessage(LANG_RESET_TALENTS_ONLINE,GetNameLink(target).c_str());
+
Pet* pet = target->GetPet();
Pet::resetTalentsForAllPetsOf(target,pet);
if(pet)
@@ -4573,16 +5342,21 @@ bool ChatHandler::HandleResetTalentsCommand(const char * args)
PSendSysMessage(LANG_RESET_TALENTS_OFFLINE,nameLink.c_str());
return true;
}
+
SendSysMessage(LANG_NO_CHAR_SELECTED);
SetSentErrorMessage(true);
return false;
}
+
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")
{
@@ -4604,125 +5378,161 @@ bool ChatHandler::HandleResetAllCommand(const char * args)
SetSentErrorMessage(true);
return false;
}
+
CharacterDatabase.PExecute("UPDATE characters SET at_login = at_login | '%u' WHERE (at_login & '%u') = '0'",atLogin,atLogin);
HashMapHolder<Player>::MapType const& plist = ObjectAccessor::Instance().GetPlayers();
for(HashMapHolder<Player>::MapType::const_iterator itr = plist.begin(); itr != plist.end(); ++itr)
itr->second->SetAtLoginFlag(atLogin);
+
return true;
}
+
bool ChatHandler::HandleServerShutDownCancelCommand(const char* /*args*/)
{
sWorld.ShutdownCancel();
return true;
}
+
bool ChatHandler::HandleServerShutDownCommand(const char* args)
{
if(!*args)
return false;
+
char* time_str = strtok ((char*) args, " ");
char* exitcode_str = strtok (NULL, "");
+
int32 time = atoi (time_str);
+
///- Prevent interpret wrong arg value as 0 secs shutdown time
if ((time == 0 && (time_str[0]!='0' || time_str[1]!='\0')) || time < 0)
return false;
+
if (exitcode_str)
{
int32 exitcode = atoi (exitcode_str);
+
// Handle atoi() errors
if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0'))
return false;
+
// Exit code should be in range of 0-125, 126-255 is used
// in many shells for their own return codes and code > 255
// is not supported in many others
if (exitcode < 0 || exitcode > 125)
return false;
+
sWorld.ShutdownServ (time, 0, exitcode);
}
else
sWorld.ShutdownServ(time,0,SHUTDOWN_EXIT_CODE);
return true;
}
+
bool ChatHandler::HandleServerRestartCommand(const char* args)
{
if(!*args)
return false;
+
char* time_str = strtok ((char*) args, " ");
char* exitcode_str = strtok (NULL, "");
+
int32 time = atoi (time_str);
+
///- Prevent interpret wrong arg value as 0 secs shutdown time
if ((time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0))
return false;
+
if (exitcode_str)
{
int32 exitcode = atoi (exitcode_str);
+
// Handle atoi() errors
if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0'))
return false;
+
// Exit code should be in range of 0-125, 126-255 is used
// in many shells for their own return codes and code > 255
// is not supported in many others
if (exitcode < 0 || exitcode > 125)
return false;
+
sWorld.ShutdownServ (time, SHUTDOWN_MASK_RESTART, exitcode);
}
else
sWorld.ShutdownServ(time, SHUTDOWN_MASK_RESTART, RESTART_EXIT_CODE);
return true;
}
+
bool ChatHandler::HandleServerIdleRestartCommand(const char* args)
{
if(!*args)
return false;
+
char* time_str = strtok ((char*) args, " ");
char* exitcode_str = strtok (NULL, "");
+
int32 time = atoi (time_str);
+
///- Prevent interpret wrong arg value as 0 secs shutdown time
if ((time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0))
return false;
+
if (exitcode_str)
{
int32 exitcode = atoi (exitcode_str);
+
// Handle atoi() errors
if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0'))
return false;
+
// Exit code should be in range of 0-125, 126-255 is used
// in many shells for their own return codes and code > 255
// is not supported in many others
if (exitcode < 0 || exitcode > 125)
return false;
+
sWorld.ShutdownServ (time, SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE, exitcode);
}
else
sWorld.ShutdownServ(time,SHUTDOWN_MASK_RESTART|SHUTDOWN_MASK_IDLE,RESTART_EXIT_CODE);
return true;
}
+
bool ChatHandler::HandleServerIdleShutDownCommand(const char* args)
{
if(!*args)
return false;
+
char* time_str = strtok ((char*) args, " ");
char* exitcode_str = strtok (NULL, "");
+
int32 time = atoi (time_str);
+
///- Prevent interpret wrong arg value as 0 secs shutdown time
if(time == 0 && (time_str[0]!='0' || time_str[1]!='\0') || time < 0)
return false;
+
if (exitcode_str)
{
int32 exitcode = atoi (exitcode_str);
+
// Handle atoi() errors
if (exitcode == 0 && (exitcode_str[0] != '0' || exitcode_str[1] != '\0'))
return false;
+
// Exit code should be in range of 0-125, 126-255 is used
// in many shells for their own return codes and code > 255
// is not supported in many others
if (exitcode < 0 || exitcode > 125)
return false;
+
sWorld.ShutdownServ (time, SHUTDOWN_MASK_IDLE, exitcode);
}
else
sWorld.ShutdownServ(time,SHUTDOWN_MASK_IDLE,SHUTDOWN_EXIT_CODE);
return true;
}
+
bool ChatHandler::HandleQuestAdd(const char* args)
{
Player* player = getSelectedPlayer();
@@ -4732,25 +5542,31 @@ bool ChatHandler::HandleQuestAdd(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// .addquest #entry'
// number or [name] Shift-click form |color|Hquest:quest_id:quest_level|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)
for (uint32 id = 0; id < sItemStorage.MaxEntry; id++)
{
ItemPrototype const *pProto = sItemStorage.LookupEntry<ItemPrototype>(id);
if (!pProto)
continue;
+
if (pProto->StartQuest == entry)
{
PSendSysMessage(LANG_COMMAND_QUEST_STARTFROMITEM, entry, pProto->ItemId);
@@ -4758,15 +5574,19 @@ bool ChatHandler::HandleQuestAdd(const char* args)
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::HandleQuestRemove(const char* args)
{
Player* player = getSelectedPlayer();
@@ -4776,19 +5596,24 @@ bool ChatHandler::HandleQuestRemove(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// .removequest #entry'
// number or [name] Shift-click form |color|Hquest:quest_id:quest_level|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 )
{
@@ -4796,17 +5621,22 @@ bool ChatHandler::HandleQuestRemove(const char* args)
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::HandleQuestComplete(const char* args)
{
Player* player = getSelectedPlayer();
@@ -4816,13 +5646,17 @@ bool ChatHandler::HandleQuestComplete(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// .quest complete #entry
// number or [name] Shift-click form |color|Hquest:quest_id:quest_level|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)
{
@@ -4830,6 +5664,7 @@ bool ChatHandler::HandleQuestComplete(const char* args)
SetSentErrorMessage(true);
return false;
}
+
// Add quest items for quests that require items
for(uint8 x = 0; x < QUEST_OBJECTIVES_COUNT; ++x)
{
@@ -4837,7 +5672,9 @@ bool ChatHandler::HandleQuestComplete(const char* args)
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 )
@@ -4846,11 +5683,13 @@ bool ChatHandler::HandleQuestComplete(const char* args)
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)
@@ -4868,6 +5707,7 @@ bool ChatHandler::HandleQuestComplete(const char* args)
player->CastedCreatureOrGO(creature,0,0);
}
}
+
// If the quest requires reputation to complete
if(uint32 repFaction = pQuest->GetRepObjectiveFaction())
{
@@ -4877,39 +5717,50 @@ bool ChatHandler::HandleQuestComplete(const char* args)
if(FactionEntry const *factionEntry = sFactionStore.LookupEntry(repFaction))
player->GetReputationMgr().SetReputation(factionEntry,repValue);
}
+
// If the quest requires money
int32 ReqOrRewMoney = pQuest->GetRewOrReqMoney();
if(ReqOrRewMoney < 0)
player->ModifyMoney(-ReqOrRewMoney);
+
player->CompleteQuest(entry);
return true;
}
+
bool ChatHandler::HandleBanAccountCommand(const char* args)
{
return HandleBanHelper(BAN_ACCOUNT,args);
}
+
bool ChatHandler::HandleBanCharacterCommand(const char* args)
{
return HandleBanHelper(BAN_CHARACTER,args);
}
+
bool ChatHandler::HandleBanIPCommand(const char* args)
{
return HandleBanHelper(BAN_IP,args);
}
+
bool ChatHandler::HandleBanHelper(BanMode mode, const char* args)
{
if (!*args)
return false;
+
char* cnameOrIP = strtok ((char*)args, " ");
if (!cnameOrIP)
return false;
+
std::string nameOrIP = cnameOrIP;
+
char* duration = strtok (NULL," ");
if(!duration || !atoi(duration))
return false;
+
char* reason = strtok (NULL,"");
if(!reason)
return false;
+
switch(mode)
{
case BAN_ACCOUNT:
@@ -4933,6 +5784,7 @@ bool ChatHandler::HandleBanHelper(BanMode mode, const char* args)
return false;
break;
}
+
switch(sWorld.BanAccount(mode, nameOrIP, duration, reason,m_session ? m_session->GetPlayerName() : ""))
{
case BAN_SUCCESS:
@@ -4959,28 +5811,36 @@ bool ChatHandler::HandleBanHelper(BanMode mode, const char* args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandleUnBanAccountCommand(const char* args)
{
return HandleUnBanHelper(BAN_ACCOUNT,args);
}
+
bool ChatHandler::HandleUnBanCharacterCommand(const char* args)
{
return HandleUnBanHelper(BAN_CHARACTER,args);
}
+
bool ChatHandler::HandleUnBanIPCommand(const char* args)
{
return HandleUnBanHelper(BAN_IP,args);
}
+
bool ChatHandler::HandleUnBanHelper(BanMode mode, const char* args)
{
if (!*args)
return false;
+
char* cnameOrIP = strtok ((char*)args, " ");
if(!cnameOrIP)
return false;
+
std::string nameOrIP = cnameOrIP;
+
switch(mode)
{
case BAN_ACCOUNT:
@@ -5004,19 +5864,24 @@ bool ChatHandler::HandleUnBanHelper(BanMode mode, const char* args)
return false;
break;
}
+
if(sWorld.RemoveBanAccount(mode,nameOrIP))
PSendSysMessage(LANG_UNBAN_UNBANNED,nameOrIP.c_str());
else
PSendSysMessage(LANG_UNBAN_ERROR,nameOrIP.c_str());
+
return true;
}
+
bool ChatHandler::HandleBanInfoAccountCommand(const char* args)
{
if (!*args)
return false;
+
char* cname = strtok((char*)args, "");
if(!cname)
return false;
+
std::string account_name = cname;
if(!AccountMgr::normalizeString(account_name))
{
@@ -5024,29 +5889,36 @@ bool ChatHandler::HandleBanInfoAccountCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 accountid = accmgr.GetId(account_name);
if(!accountid)
{
PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str());
return true;
}
+
return HandleBanInfoHelper(accountid,account_name.c_str());
}
+
bool ChatHandler::HandleBanInfoCharacterCommand(const char* args)
{
Player* target;
uint64 target_guid;
if(!extractPlayerTarget((char*)args,&target,&target_guid))
return false;
+
uint32 accountid = target ? target->GetSession()->GetAccountId() : objmgr.GetPlayerAccountIdByGUID(target_guid);
+
std::string accountname;
if(!accmgr.GetName(accountid,accountname))
{
PSendSysMessage(LANG_BANINFO_NOCHARACTER);
return true;
}
+
return HandleBanInfoHelper(accountid,accountname.c_str());
}
+
bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname)
{
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);
@@ -5055,10 +5927,12 @@ bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname)
PSendSysMessage(LANG_BANINFO_NOACCOUNTBAN, accountname);
return true;
}
+
PSendSysMessage(LANG_BANINFO_BANHISTORY,accountname);
do
{
Field* 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)) )
@@ -5068,19 +5942,25 @@ bool ChatHandler::HandleBanInfoHelper(uint32 accountid, char const* accountname)
PSendSysMessage(LANG_BANINFO_HISTORYENTRY,
fields[0].GetString(), bantime.c_str(), active ? GetTrinityString(LANG_BANINFO_YES):GetTrinityString(LANG_BANINFO_NO), fields[4].GetString(), fields[5].GetString());
}while (result->NextRow());
+
delete result;
return true;
}
+
bool ChatHandler::HandleBanInfoIPCommand(const char* args)
{
if (!*args)
return false;
+
char* cIP = strtok ((char*)args, "");
if(!cIP)
return false;
+
if (!IsIPAddress(cIP))
return false;
+
std::string IP = cIP;
+
loginDatabase.escape_string(IP);
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'",IP.c_str());
if(!result)
@@ -5088,6 +5968,7 @@ bool ChatHandler::HandleBanInfoIPCommand(const char* args)
PSendSysMessage(LANG_BANINFO_NOIP);
return true;
}
+
Field *fields = result->Fetch();
bool permanent = !fields[6].GetUInt64();
PSendSysMessage(LANG_BANINFO_IPENTRY,
@@ -5096,12 +5977,15 @@ bool ChatHandler::HandleBanInfoIPCommand(const char* args)
delete result;
return true;
}
+
bool ChatHandler::HandleBanListCharacterCommand(const char* args)
{
loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+
char* cFilter = strtok ((char*)args, " ");
if(!cFilter)
return false;
+
std::string filter = cFilter;
loginDatabase.escape_string(filter);
QueryResult* result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'"),filter.c_str());
@@ -5110,15 +5994,20 @@ bool ChatHandler::HandleBanListCharacterCommand(const char* args)
PSendSysMessage(LANG_BANLIST_NOCHARACTER);
return true;
}
+
return HandleBanListHelper(result);
}
+
bool ChatHandler::HandleBanListAccountCommand(const char* args)
{
loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+
char* cFilter = strtok((char*)args, " ");
std::string filter = cFilter ? cFilter : "";
loginDatabase.escape_string(filter);
+
QueryResult* result;
+
if(filter.empty())
{
result = loginDatabase.Query("SELECT account.id, username FROM account, account_banned"
@@ -5130,16 +6019,20 @@ bool ChatHandler::HandleBanListAccountCommand(const char* args)
" WHERE account.id = account_banned.id AND active = 1 AND username "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")" GROUP BY account.id",
filter.c_str());
}
+
if (!result)
{
PSendSysMessage(LANG_BANLIST_NOACCOUNT);
return true;
}
+
return HandleBanListHelper(result);
}
+
bool ChatHandler::HandleBanListHelper(QueryResult* result)
{
PSendSysMessage(LANG_BANLIST_MATCHINGACCOUNT);
+
// Chat short output
if(m_session)
{
@@ -5147,6 +6040,7 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result)
{
Field* 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.id=account.id",accountid);
if(banresult)
{
@@ -5167,13 +6061,16 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result)
SendSysMessage("-------------------------------------------------------------------------------");
Field *fields = result->Fetch();
uint32 account_id = fields[0].GetUInt32 ();
+
std::string account_name;
+
// "account" case, name can be get in same query
if(result->GetFieldCount() > 1)
account_name = fields[1].GetCppString();
// "character" case, name need extract from another DB
else
accmgr.GetName (account_id,account_name);
+
// No SQL injection. id is uint32.
QueryResult *banInfo = loginDatabase.PQuery("SELECT bandate,unbandate,bannedby,banreason FROM account_banned WHERE id = %u ORDER BY unbandate", account_id);
if (banInfo)
@@ -5183,6 +6080,7 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result)
{
time_t t_ban = fields2[0].GetUInt64();
tm* aTm_ban = localtime(&t_ban);
+
if (fields2[0].GetUInt64() == fields2[1].GetUInt64())
{
PSendSysMessage("|%-15.15s|%02d-%02d-%02d %02d:%02d| permanent |%-15.15s|%-15.15s|",
@@ -5204,16 +6102,21 @@ bool ChatHandler::HandleBanListHelper(QueryResult* result)
}while( result->NextRow() );
SendSysMessage("===============================================================================");
}
+
delete result;
return true;
}
+
bool ChatHandler::HandleBanListIPCommand(const char* args)
{
loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+
char* cFilter = strtok((char*)args, " ");
std::string filter = cFilter ? cFilter : "";
loginDatabase.escape_string(filter);
+
QueryResult* result;
+
if(filter.empty())
{
result = loginDatabase.Query ("SELECT ip,bandate,unbandate,bannedby,banreason FROM ip_banned"
@@ -5226,11 +6129,13 @@ bool ChatHandler::HandleBanListIPCommand(const char* args)
" WHERE (bandate=unbandate OR unbandate>UNIX_TIMESTAMP()) AND ip "_LIKE_" "_CONCAT3_("'%%'","'%s'","'%%'")
" ORDER BY unbandate",filter.c_str() );
}
+
if(!result)
{
PSendSysMessage(LANG_BANLIST_NOIP);
return true;
}
+
PSendSysMessage(LANG_BANLIST_MATCHINGIP);
// Chat short output
if(m_session)
@@ -5271,12 +6176,15 @@ bool ChatHandler::HandleBanListIPCommand(const char* args)
}while( result->NextRow() );
SendSysMessage("===============================================================================");
}
+
delete result;
return true;
}
+
bool ChatHandler::HandleRespawnCommand(const char* /*args*/)
{
Player* pl = m_session->GetPlayer();
+
// accept only explicitly selected target (not implicitly self targeting case)
Unit* target = getSelectedUnit();
if(pl->GetSelection() && target)
@@ -5287,28 +6195,36 @@ bool ChatHandler::HandleRespawnCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
if(target->isDead())
((Creature*)target)->Respawn();
return true;
}
+
CellPair p(Trinity::ComputeCellPair(pl->GetPositionX(), pl->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::RespawnDo u_do;
MaNGOS::WorldObjectWorker<MaNGOS::RespawnDo> worker(pl,u_do);
+
TypeContainerVisitor<Trinity::WorldObjectWorker<Trinity::RespawnDo>, GridTypeMapContainer > obj_worker(worker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, obj_worker, *pl->GetMap());
+
return true;
}
+
bool ChatHandler::HandleGMFlyCommand(const char* args)
{
if (!*args)
return false;
+
Player *target = getSelectedPlayer();
if (!target)
target = m_session->GetPlayer();
+
WorldPacket data(12);
if (strncmp(args, "on", 3) == 0)
data.SetOpcode(SMSG_MOVE_SET_CAN_FLY);
@@ -5325,16 +6241,20 @@ bool ChatHandler::HandleGMFlyCommand(const char* args)
PSendSysMessage(LANG_COMMAND_FLYMODE_STATUS, GetNameLink(target).c_str(), args);
return true;
}
+
bool ChatHandler::HandlePDumpLoadCommand(const char *args)
{
if (!*args)
return false;
+
char * file = strtok((char*)args, " ");
if(!file)
return false;
+
char * account = strtok(NULL, " ");
if(!account)
return false;
+
std::string account_name = account;
if(!AccountMgr::normalizeString(account_name))
{
@@ -5342,6 +6262,7 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
uint32 account_id = accmgr.GetId(account_name);
if(!account_id)
{
@@ -5353,14 +6274,17 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args)
return false;
}
}
+
if(!accmgr.GetName(account_id,account_name))
{
PSendSysMessage(LANG_ACCOUNT_NOT_EXIST,account_name.c_str());
SetSentErrorMessage(true);
return false;
}
+
char* guid_str = NULL;
char* name_str = strtok(NULL, " ");
+
std::string name;
if (name_str)
{
@@ -5372,15 +6296,19 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
if (ObjectMgr::CheckPlayerName(name,true) != CHAR_NAME_SUCCESS)
{
PSendSysMessage(LANG_INVALID_CHARACTER_NAME);
SetSentErrorMessage(true);
return false;
}
+
guid_str = strtok(NULL, " ");
}
+
uint32 guid = 0;
+
if (guid_str)
{
guid = atoi(guid_str);
@@ -5390,6 +6318,7 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
if (objmgr.GetPlayerAccountIdByGUID(guid))
{
PSendSysMessage(LANG_CHARACTER_GUID_IN_USE,guid);
@@ -5397,6 +6326,7 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args)
return false;
}
}
+
switch(PlayerDumpReader().LoadDump(file, account_id, name, guid))
{
case DUMP_SUCCESS:
@@ -5419,16 +6349,21 @@ bool ChatHandler::HandlePDumpLoadCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandlePDumpWriteCommand(const char *args)
{
if (!*args)
return false;
+
char* file = strtok((char*)args, " ");
char* p2 = strtok(NULL, " ");
+
if(!file || !p2)
return false;
+
uint32 guid;
// character name can't start from number
if (isNumeric(p2[0]))
@@ -5442,14 +6377,17 @@ bool ChatHandler::HandlePDumpWriteCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
guid = objmgr.GetPlayerGUIDByName(name);
}
+
if(!objmgr.GetPlayerAccountIdByGUID(guid))
{
PSendSysMessage(LANG_PLAYER_NOT_FOUND);
SetSentErrorMessage(true);
return false;
}
+
switch(PlayerDumpWriter().WriteDump(file, guid))
{
case DUMP_SUCCESS:
@@ -5464,8 +6402,10 @@ bool ChatHandler::HandlePDumpWriteCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandleMovegensCommand(const char* /*args*/)
{
Unit* unit = getSelectedUnit();
@@ -5475,7 +6415,9 @@ bool ChatHandler::HandleMovegensCommand(const char* /*args*/)
SetSentErrorMessage(true);
return false;
}
+
PSendSysMessage(LANG_MOVEGENS_LIST,(unit->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature" ),unit->GetGUIDLow());
+
MotionMaster* mm = unit->GetMotionMaster();
for(uint8 i = 0; i < MAX_MOTION_SLOT; ++i)
{
@@ -5541,6 +6483,7 @@ bool ChatHandler::HandleMovegensCommand(const char* /*args*/)
}
return true;
}
+
bool ChatHandler::HandleServerPLimitCommand(const char *args)
{
if(*args)
@@ -5548,7 +6491,9 @@ bool ChatHandler::HandleServerPLimitCommand(const char *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 )
@@ -5563,12 +6508,15 @@ bool ChatHandler::HandleServerPLimitCommand(const char *args)
{
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 = "";
@@ -5580,33 +6528,42 @@ bool ChatHandler::HandleServerPLimitCommand(const char *args)
case SEC_ADMINISTRATOR: secName = "Administrator"; break;
default: secName = "<unknown>"; break;
}
+
PSendSysMessage("Player limits: amount %u, min. security level %s.",pLimit,secName);
+
return true;
}
+
bool ChatHandler::HandleCastCommand(const char* args)
{
if(!*args)
return false;
+
Unit* target = getSelectedUnit();
+
if(!target)
{
SendSysMessage(LANG_SELECT_CHAR_OR_CREATURE);
SetSentErrorMessage(true);
return false;
}
+
// number or [name] Shift-click form |color|Hspell:spell_id|h[name]|h|r or Htalent form
uint32 spell = extractSpellIdFromLink((char*)args);
if(!spell)
return false;
+
SpellEntry const* spellInfo = sSpellStore.LookupEntry(spell);
if(!spellInfo)
return false;
+
if(!SpellMgr::IsSpellValid(spellInfo,m_session->GetPlayer()))
{
PSendSysMessage(LANG_COMMAND_SPELL_BROKEN,spell);
SetSentErrorMessage(true);
return false;
}
+
char* trig_str = strtok(NULL, " ");
if(trig_str)
{
@@ -5614,24 +6571,31 @@ bool ChatHandler::HandleCastCommand(const char* args)
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)
{
@@ -5639,37 +6603,50 @@ bool ChatHandler::HandleCastBackCommand(const char* args)
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,triggered);
+
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)
{
@@ -5677,31 +6654,39 @@ bool ChatHandler::HandleCastDistCommand(const char* args)
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)
{
@@ -5709,16 +6694,22 @@ bool ChatHandler::HandleCastTargetCommand(const char* args)
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,triggered);
+
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)
@@ -5727,9 +6718,12 @@ when attempting to use the PointMovementGenerator
bool ChatHandler::HandleComeToMeCommand(const char *args)
{
char* newFlagStr = strtok((char*)args, " ");
+
if(!newFlagStr)
return false;
+
uint32 newFlags = (uint32)strtoul(newFlagStr, NULL, 0);
+
Creature* caster = getSelectedCreature();
if(!caster)
{
@@ -5738,38 +6732,50 @@ bool ChatHandler::HandleComeToMeCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
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;
@@ -5779,6 +6785,7 @@ std::string GetTimeString(uint32 time)
ss << minute << "m";
return ss.str();
}
+
bool ChatHandler::HandleInstanceListBindsCommand(const char* /*args*/)
{
Player* player = getSelectedPlayer();
@@ -5813,12 +6820,15 @@ bool ChatHandler::HandleInstanceListBindsCommand(const char* /*args*/)
}
}
PSendSysMessage("group binds: %d", counter);
+
return true;
}
+
bool ChatHandler::HandleInstanceUnbindCommand(const char* args)
{
if(!*args)
return false;
+
std::string cmd = args;
if(cmd == "all")
{
@@ -5846,6 +6856,7 @@ bool ChatHandler::HandleInstanceUnbindCommand(const char* args)
}
return true;
}
+
bool ChatHandler::HandleInstanceStatsCommand(const char* /*args*/)
{
PSendSysMessage("instances loaded: %d", MapManager::Instance().GetNumInstances());
@@ -5855,9 +6866,11 @@ bool ChatHandler::HandleInstanceStatsCommand(const char* /*args*/)
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())
{
@@ -5865,15 +6878,18 @@ bool ChatHandler::HandleInstanceSaveDataCommand(const char * /*args*/)
SetSentErrorMessage(true);
return false;
}
+
if (!((InstanceMap*)map)->GetInstanceData())
{
PSendSysMessage("Map has no instance data.");
SetSentErrorMessage(true);
return false;
}
+
((InstanceMap*)map)->GetInstanceData()->SaveToDB();
return true;
}
+
/// Display the list of GMs
bool ChatHandler::HandleGMListFullCommand(const char* /*args*/)
{
@@ -5885,12 +6901,14 @@ bool ChatHandler::HandleGMListFullCommand(const char* /*args*/)
SendSysMessage("========================");
SendSysMessage(LANG_GMLIST_HEADER);
SendSysMessage("========================");
+
///- Circle through them. Display username and GM level
do
{
Field *fields = result->Fetch();
PSendSysMessage("|%15s|%6s|", fields[0].GetString(),fields[1].GetString());
}while( result->NextRow() );
+
PSendSysMessage("========================");
delete result;
}
@@ -5898,6 +6916,7 @@ bool ChatHandler::HandleGMListFullCommand(const char* /*args*/)
PSendSysMessage(LANG_GMLIST_EMPTY);
return true;
}
+
/// Define the 'Message of the day' for the realm
bool ChatHandler::HandleServerSetMotdCommand(const char* args)
{
@@ -5905,10 +6924,12 @@ bool ChatHandler::HandleServerSetMotdCommand(const char* args)
PSendSysMessage(LANG_MOTD_NEW, args);
return true;
}
+
/// Set whether we accept new clients
bool ChatHandler::HandleServerSetClosedCommand(const char* args)
{
std::string arg = args;
+
if(args == "on")
{
SendSysMessage(LANG_WORLD_CLOSED);
@@ -5921,25 +6942,31 @@ bool ChatHandler::HandleServerSetClosedCommand(const char* args)
sWorld.SetClosed(false);
return true;
}
+
SendSysMessage(LANG_USE_BOL);
SetSentErrorMessage(true);
return false;
}
+
/// Set/Unset the expansion level for an account
bool ChatHandler::HandleAccountSetAddonCommand(const char* args)
{
///- Get the command line arguments
char *szAcc = strtok((char*)args," ");
char *szExp = strtok(NULL," ");
+
if(!szAcc)
return false;
+
std::string account_name;
uint32 account_id;
+
if(!szExp)
{
Player* player = getSelectedPlayer();
if(!player)
return false;
+
account_id = player->GetSession()->GetAccountId();
accmgr.GetName(account_id,account_name);
szExp = szAcc;
@@ -5954,6 +6981,7 @@ bool ChatHandler::HandleAccountSetAddonCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
account_id = accmgr.GetId(account_name);
if(!account_id)
{
@@ -5961,20 +6989,25 @@ bool ChatHandler::HandleAccountSetAddonCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
}
+
// Let set addon state only for lesser (strong) security level
// or to self account
if (m_session && m_session->GetAccountId () != account_id &&
HasLowerSecurityAccount (NULL,account_id,true))
return false;
+
int expansion = atoi(szExp); //get int anyway (0 if error)
if(expansion < 0 || expansion > sWorld.getConfig(CONFIG_EXPANSION))
return false;
+
// No SQL injection
loginDatabase.PExecute("UPDATE account SET expansion = '%d' WHERE id = '%u'",expansion,account_id);
PSendSysMessage(LANG_ACCOUNT_SETADDON,account_name.c_str(),account_id,expansion);
return true;
}
+
//Send items by mail
bool ChatHandler::HandleSendItemsCommand(const char* args)
{
@@ -5984,38 +7017,49 @@ bool ChatHandler::HandleSendItemsCommand(const char* args)
std::string receiver_name;
if(!extractPlayerTarget((char*)args,&receiver,&receiver_guid,&receiver_name))
return false;
+
char* tail1 = strtok(NULL, "");
if(!tail1)
return false;
+
char* msgSubject = extractQuotedArg(tail1);
if (!msgSubject)
return false;
+
char* tail2 = strtok(NULL, "");
if(!tail2)
return false;
+
char* msgText = extractQuotedArg(tail2);
if (!msgText)
return false;
+
// msgSubject, msgText isn't NUL after prev. check
std::string subject = msgSubject;
std::string text = msgText;
+
// extract items
typedef std::pair<uint32,uint32> ItemPair;
typedef std::list< ItemPair > ItemPairs;
ItemPairs items;
+
// get all tail string
char* tail = strtok(NULL, "");
+
// get from tail next item str
while(char* itemStr = strtok(tail, " "))
{
// and get new tail
tail = strtok(NULL, "");
+
// parse item str
char* itemIdStr = strtok(itemStr, ":");
char* itemCountStr = strtok(NULL, " ");
+
uint32 item_id = atoi(itemIdStr);
if(!item_id)
return false;
+
ItemPrototype const* item_proto = objmgr.GetItemPrototype(item_id);
if(!item_proto)
{
@@ -6023,6 +7067,7 @@ bool ChatHandler::HandleSendItemsCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 item_count = itemCountStr ? atoi(itemCountStr) : 1;
if (item_count < 1 || (item_proto->MaxCount > 0 && item_count > uint32(item_proto->MaxCount)))
{
@@ -6030,12 +7075,15 @@ bool ChatHandler::HandleSendItemsCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
while(item_count > item_proto->GetMaxStackSize())
{
items.push_back(ItemPair(item_id,item_proto->GetMaxStackSize()));
item_count -= item_proto->GetMaxStackSize();
}
+
items.push_back(ItemPair(item_id,item_count));
+
if(items.size() > MAX_MAIL_ITEMS)
{
PSendSysMessage(LANG_COMMAND_MAIL_ITEMS_LIMIT, MAX_MAIL_ITEMS);
@@ -6043,13 +7091,17 @@ bool ChatHandler::HandleSendItemsCommand(const char* args)
return false;
}
}
+
// from console show not existed sender
uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0;
+
uint32 messagetype = MAIL_NORMAL;
uint32 stationery = MAIL_STATIONERY_GM;
uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
+
// fill mail
MailItemsInfo mi; // item list preparing
+
for(ItemPairs::const_iterator itr = items.begin(); itr != items.end(); ++itr)
{
if(Item* item = Item::CreateItem(itr->first,itr->second,m_session ? m_session->GetPlayer() : 0))
@@ -6058,49 +7110,64 @@ bool ChatHandler::HandleSendItemsCommand(const char* args)
mi.AddItem(item->GetGUIDLow(), item->GetEntry(), item);
}
}
+
WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, &mi, 0, 0, MAIL_CHECK_MASK_NONE);
+
std::string nameLink = playerLink(receiver_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
return true;
}
+
///Send money by mail
bool ChatHandler::HandleSendMoneyCommand(const char* args)
{
/// format: name "subject text" "mail text" money
+
Player* receiver;
uint64 receiver_guid;
std::string receiver_name;
if(!extractPlayerTarget((char*)args,&receiver,&receiver_guid,&receiver_name))
return false;
+
char* tail1 = strtok(NULL, "");
if (!tail1)
return false;
+
char* msgSubject = extractQuotedArg(tail1);
if (!msgSubject)
return false;
+
char* tail2 = strtok(NULL, "");
if (!tail2)
return false;
+
char* msgText = extractQuotedArg(tail2);
if (!msgText)
return false;
+
char* money_str = strtok(NULL, "");
int32 money = money_str ? atoi(money_str) : 0;
if (money <= 0)
return false;
+
// msgSubject, msgText isn't NUL after prev. check
std::string subject = msgSubject;
std::string text = msgText;
+
// from console show not existed sender
uint32 sender_guidlo = m_session ? m_session->GetPlayer()->GetGUIDLow() : 0;
+
uint32 messagetype = MAIL_NORMAL;
uint32 stationery = MAIL_STATIONERY_GM;
uint32 itemTextId = !text.empty() ? objmgr.CreateItemText( text ) : 0;
+
WorldSession::SendMailTo(receiver,messagetype, stationery, sender_guidlo, GUID_LOPART(receiver_guid), subject, itemTextId, NULL, money, 0, MAIL_CHECK_MASK_NONE);
+
std::string nameLink = playerLink(receiver_name);
PSendSysMessage(LANG_MAIL_SENT, nameLink.c_str());
return true;
}
+
/// Send a message to a player in game
bool ChatHandler::HandleSendMessageCommand(const char* args)
{
@@ -6108,9 +7175,11 @@ bool ChatHandler::HandleSendMessageCommand(const char* args)
Player *rPlayer;
if(!extractPlayerTarget((char*)args,&rPlayer))
return false;
+
char* msg_str = strtok(NULL, "");
if(!msg_str)
return false;
+
///- Check that he is not logging out.
if(rPlayer->GetSession()->isLogingOut())
{
@@ -6118,47 +7187,59 @@ bool ChatHandler::HandleSendMessageCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
///- Send the message
//Use SendAreaTriggerMessage for fastest delivery.
rPlayer->GetSession()->SendAreaTriggerMessage("%s", msg_str);
rPlayer->GetSession()->SendAreaTriggerMessage("|cffff0000[Message from administrator]:|r");
+
//Confirmation message
std::string nameLink = GetNameLink(rPlayer);
PSendSysMessage(LANG_SENDMESSAGE,nameLink.c_str(),msg_str);
return true;
}
+
bool ChatHandler::HandleFlushArenaPointsCommand(const char * /*args*/)
{
sBattleGroundMgr.DistributeArenaPoints();
return true;
}
+
bool ChatHandler::HandleModifyGenderCommand(const char *args)
{
if(!*args)
return false;
+
Player *player = getSelectedPlayer();
+
if(!player)
{
PSendSysMessage(LANG_PLAYER_NOT_FOUND);
SetSentErrorMessage(true);
return false;
}
+
PlayerInfo const* info = objmgr.GetPlayerInfo(player->getRace(), player->getClass());
if(!info)
return false;
+
char const* gender_str = (char*)args;
int gender_len = strlen(gender_str);
+
Gender gender;
+
if(!strncmp(gender_str, "male", gender_len)) // MALE
{
if(player->getGender() == GENDER_MALE)
return true;
+
gender = GENDER_MALE;
}
else if (!strncmp(gender_str, "female", gender_len)) // FEMALE
{
if(player->getGender() == GENDER_FEMALE)
return true;
+
gender = GENDER_FEMALE;
}
else
@@ -6167,37 +7248,50 @@ bool ChatHandler::HandleModifyGenderCommand(const char *args)
SetSentErrorMessage(true);
return false;
}
+
// Set gender
player->SetByteValue(UNIT_FIELD_BYTES_0, 2, gender);
player->SetByteValue(PLAYER_BYTES_3, 0, gender);
+
// Change display ID
player->InitDisplayIds();
+
char const* gender_full = gender ? "female" : "male";
+
PSendSysMessage(LANG_YOU_CHANGE_GENDER, GetNameLink(player).c_str(), gender_full);
+
if (needReportToTarget(player))
ChatHandler(player).PSendSysMessage(LANG_YOUR_GENDER_CHANGED, gender_full, GetNameLink().c_str());
+
return true;
}
+
/*------------------------------------------
*-------------TRINITY----------------------
*-------------------------------------*/
+
bool ChatHandler::HandlePlayAllCommand(const char* args)
{
if(!*args)
return false;
+
uint32 soundId = atoi((char*)args);
+
if(!sSoundEntriesStore.LookupEntry(soundId))
{
PSendSysMessage(LANG_SOUND_NOT_EXIST, soundId);
SetSentErrorMessage(true);
return false;
}
+
WorldPacket data(SMSG_PLAY_SOUND, 4);
data << uint32(soundId) << m_session->GetPlayer()->GetGUID();
sWorld.SendGlobalMessage(&data);
+
PSendSysMessage(LANG_COMMAND_PLAYED_TO_ALL, soundId);
return true;
}
+
bool ChatHandler::HandleFreezeCommand(const char *args)
{
std::string name;
@@ -6218,20 +7312,24 @@ bool ChatHandler::HandleFreezeCommand(const char *args)
normalizePlayerName(name);
player = objmgr.GetPlayer(name.c_str()); //get player by name
}
+
if (!player)
{
SendSysMessage(LANG_COMMAND_FREEZE_WRONG);
return true;
}
+
if (player==m_session->GetPlayer())
{
SendSysMessage(LANG_COMMAND_FREEZE_ERROR);
return true;
}
+
//effect
if ((player) && (!(player==m_session->GetPlayer())))
{
PSendSysMessage(LANG_COMMAND_FREEZE,name.c_str());
+
//stop combat + make player unattackable + duel stop + stop some spells
player->setFaction(35);
player->CombatStop();
@@ -6239,6 +7337,7 @@ bool ChatHandler::HandleFreezeCommand(const char *args)
player->InterruptNonMeleeSpells(true);
player->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
player->SetUInt32Value(PLAYER_DUEL_TEAM, 1);
+
//if player class = hunter || warlock remove pet if alive
if((player->getClass() == CLASS_HUNTER) || (player->getClass() == CLASS_WARLOCK))
{
@@ -6250,15 +7349,18 @@ bool ChatHandler::HandleFreezeCommand(const char *args)
player->RemovePet(pet,PET_SAVE_NOT_IN_SLOT);
}
}
+
//m_session->GetPlayer()->CastSpell(player,spellID,false);
SpellEntry const *spellInfo = sSpellStore.LookupEntry( 9454 );
Aura *Aur = new Aura(spellInfo, 1, player, player, player);
player->AddAura(Aur);
+
//save player
player->SaveToDB();
}
return true;
}
+
bool ChatHandler::HandleUnFreezeCommand(const char *args)
{
std::string name;
@@ -6272,24 +7374,30 @@ bool ChatHandler::HandleUnFreezeCommand(const char *args)
name = player->GetName();
}
}
+
else // if name entered
{
name = TargetName;
normalizePlayerName(name);
player = objmgr.GetPlayer(name.c_str()); //get player by name
}
+
//effect
if (player)
{
PSendSysMessage(LANG_COMMAND_UNFREEZE,name.c_str());
+
//Reset player faction + allow combat + allow duels
player->setFactionForRace(player->getRace());
player->RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
//allow movement and spells
player->RemoveAurasDueToSpell(9454);
+
//save player
player->SaveToDB();
}
+
if (!player)
{
if (TargetName)
@@ -6315,8 +7423,10 @@ bool ChatHandler::HandleUnFreezeCommand(const char *args)
return true;
}
}
+
return true;
}
+
bool ChatHandler::HandleListFreezeCommand(const char* args)
{
//Get names from DB
@@ -6328,6 +7438,7 @@ bool ChatHandler::HandleListFreezeCommand(const char* args)
}
//Header of the names
PSendSysMessage(LANG_COMMAND_LIST_FREEZE);
+
//Output of the results
do
{
@@ -6335,69 +7446,88 @@ bool ChatHandler::HandleListFreezeCommand(const char* args)
std::string fplayers = fields[0].GetCppString();
PSendSysMessage(LANG_COMMAND_FROZEN_PLAYERS,fplayers.c_str());
} while (result->NextRow());
+
delete result;
return true;
}
+
bool ChatHandler::HandleGroupLeaderCommand(const char* args)
{
Player* plr = NULL;
Group* group = NULL;
uint64 guid = 0;
char* cname = strtok((char*)args, " ");
+
if(GetPlayerGroupAndGUIDByName(cname, plr, group, guid))
if(group && group->GetLeaderGUID() != guid)
group->ChangeLeader(guid);
+
return true;
}
+
bool ChatHandler::HandleGroupDisbandCommand(const char* args)
{
Player* plr = NULL;
Group* group = NULL;
uint64 guid = 0;
char* cname = strtok((char*)args, " ");
+
if(GetPlayerGroupAndGUIDByName(cname, plr, group, guid))
if(group)
group->Disband();
+
return true;
}
+
bool ChatHandler::HandleGroupRemoveCommand(const char* args)
{
Player* plr = NULL;
Group* group = NULL;
uint64 guid = 0;
char* cname = strtok((char*)args, " ");
+
if(GetPlayerGroupAndGUIDByName(cname, plr, group, guid, true))
if(group)
group->RemoveMember(guid, 0);
+
return true;
}
+
bool ChatHandler::HandlePossessCommand(const char* args)
{
Unit* pUnit = getSelectedUnit();
if(!pUnit)
return false;
+
m_session->GetPlayer()->CastSpell(pUnit, 530, true);
return true;
}
+
bool ChatHandler::HandleUnPossessCommand(const char* args)
{
Unit* pUnit = getSelectedUnit();
if(!pUnit) pUnit = m_session->GetPlayer();
+
pUnit->RemoveCharmAuras();
+
return true;
}
+
bool ChatHandler::HandleBindSightCommand(const char* args)
{
Unit* pUnit = getSelectedUnit();
if (!pUnit)
return false;
+
m_session->GetPlayer()->CastSpell(pUnit, 6277, true);
return true;
}
+
bool ChatHandler::HandleUnbindSightCommand(const char* args)
{
if (m_session->GetPlayer()->isPossessing())
return false;
+
m_session->GetPlayer()->StopCastingBindSight();
return true;
}
diff --git a/src/game/LootHandler.cpp b/src/game/LootHandler.cpp
index c1bfcfe3fe0..e4ee8bffc6f 100644
--- a/src/game/LootHandler.cpp
+++ b/src/game/LootHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -30,6 +31,7 @@
#include "Group.h"
#include "World.h"
#include "Util.h"
+
void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_AUTOSTORE_LOOT_ITEM");
@@ -37,26 +39,32 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
uint64 lguid = player->GetLootGUID();
Loot *loot;
uint8 lootSlot;
+
recv_data >> lootSlot;
+
if (IS_GAMEOBJECT_GUID(lguid))
{
GameObject *go = player->GetMap()->GetGameObject(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 if (IS_CORPSE_GUID(lguid))
@@ -72,34 +80,43 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
else
{
Creature* pCreature = GetPlayer()->GetMap()->GetCreature(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;
@@ -125,10 +142,13 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
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);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item->itemid, item->count);
player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, loot->loot_type, item->count);
@@ -136,29 +156,37 @@ void WorldSession::HandleAutostoreLootItemOpcode( WorldPacket & recv_data )
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 = GetPlayer()->GetMap()->GetGameObject(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:
@@ -170,19 +198,24 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
case HIGHGUID_UNIT:
{
Creature* pCreature = GetPlayer()->GetMap()->GetCreature(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<Player*> playersNear;
for(GroupReference *itr = group->GetFirstMember(); itr != NULL; itr = itr->next())
{
@@ -192,7 +225,9 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
if (player->IsWithinDist(playerGroup,sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE),false))
playersNear.push_back(playerGroup);
}
+
uint32 money_per_player = uint32((pLoot->gold)/(playersNear.size()));
+
for (std::vector<Player*>::const_iterator i = playersNear.begin(); i != playersNear.end(); ++i)
{
(*i)->ModifyMoney( money_per_player );
@@ -212,41 +247,56 @@ void WorldSession::HandleLootMoneyOpcode( WorldPacket & /*recv_data*/ )
pLoot->NotifyMoneyRemoved();
}
}
+
void WorldSession::HandleLootOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_LOOT");
+
uint64 guid;
recv_data >> guid;
+
// Check possible cheat
if(!_player->isAlive())
return;
+
GetPlayer()->SendLoot(guid, LOOT_CORPSE);
}
+
void WorldSession::HandleLootReleaseOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_LOOT_RELEASE");
+
// cheaters can modify lguid to prevent correct apply loot release code and re-loot
// use internal stored guid
recv_data.read_skip<uint64>(); // guid;
+
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(!player->IsInWorld())
return;
+
if (IS_GAMEOBJECT_GUID(lguid))
{
GameObject *go = GetPlayer()->GetMap()->GetGameObject(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
@@ -259,19 +309,23 @@ void WorldSession::DoLootRelease( uint64 lguid )
{
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)
@@ -306,6 +360,7 @@ void WorldSession::DoLootRelease( uint64 lguid )
}
else // not chest (or vein/herb/etc)
go->SetLootState(GO_JUST_DEACTIVATED);
+
loot->clear();
}
else
@@ -317,7 +372,9 @@ void WorldSession::DoLootRelease( uint64 lguid )
Corpse *corpse = ObjectAccessor::GetCorpse(*player, lguid);
if (!corpse || !corpse->IsWithinDistInMap(_player,INTERACTION_DISTANCE) )
return;
+
loot = &corpse->loot;
+
if (loot->isLooted())
{
loot->clear();
@@ -329,17 +386,22 @@ void WorldSession::DoLootRelease( uint64 lguid )
Item *pItem = player->GetItemByGuid(lguid );
if(!pItem)
return;
+
ItemPrototype const* proto = pItem->GetProto();
+
// destroy only 5 items from stack in case prospecting and milling
if( (proto->BagFamily & (BAG_FAMILY_MASK_MINING_SUPP|BAG_FAMILY_MASK_HERBS)) &&
proto->Class == ITEM_CLASS_TRADE_GOODS)
{
pItem->m_lootGenerated = false;
pItem->loot.clear();
+
uint32 count = pItem->GetCount();
+
// >=5 checked in spell code, but will work for cheating cases also with removing from another stacks.
if(count > 5)
count = 5;
+
player->DestroyItemCount(pItem, count, true);
}
else
@@ -350,49 +412,64 @@ void WorldSession::DoLootRelease( uint64 lguid )
else
{
Creature* pCreature = GetPlayer()->GetMap()->GetCreature(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 )
{
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_CRE_OR_VEH_GUID(GetPlayer()->GetLootGUID()))
{
Creature *pCreature = GetPlayer()->GetMap()->GetCreature(lootguid);
if(!pCreature)
return;
+
pLoot = &pCreature->loot;
}
else if(IS_GAMEOBJECT_GUID(GetPlayer()->GetLootGUID()))
@@ -400,16 +477,21 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data )
GameObject *pGO = GetPlayer()->GetMap()->GetGameObject(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 %lu)",GetPlayer()->GetName(), slotid, (unsigned long)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 )
@@ -418,15 +500,18 @@ void WorldSession::HandleLootMasterGiveOpcode( WorldPacket & recv_data )
_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 );
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_ITEM, item.itemid, item.count);
target->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOOT_TYPE, pLoot->loot_type, item.count);
+
// 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
index e43ab44c184..df11d909dd0 100644
--- a/src/game/LootMgr.cpp
+++ b/src/game/LootMgr.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -25,6 +26,7 @@
#include "Util.h"
#include "SharedDefines.h"
#include "SpellMgr.h"
+
static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
RATE_DROP_ITEM_POOR, // ITEM_QUALITY_POOR
RATE_DROP_ITEM_NORMAL, // ITEM_QUALITY_NORMAL
@@ -34,6 +36,7 @@ static Rates const qualityToRate[MAX_ITEM_QUALITY] = {
RATE_DROP_ITEM_LEGENDARY, // ITEM_QUALITY_LEGENDARY
RATE_DROP_ITEM_ARTIFACT, // ITEM_QUALITY_ARTIFACT
};
+
LootStore LootTemplates_Creature( "creature_loot_template", "creature entry", true);
LootStore LootTemplates_Disenchant( "disenchant_loot_template", "item disenchant id", true);
LootStore LootTemplates_Fishing( "fishing_loot_template", "area id", true);
@@ -47,6 +50,7 @@ LootStore LootTemplates_Reference( "reference_loot_template", "reference i
LootStore LootTemplates_Skinning( "skinning_loot_template", "creature skinning id", true);
LootStore LootTemplates_Spell( "spell_loot_template", "spell id (random item creating)",false);
+
class LootTemplate::LootGroup // A set of loot definitions for items (refs are not allowed)
{
public:
@@ -57,14 +61,17 @@ class LootTemplate::LootGroup // A set of loot def
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()
{
@@ -72,6 +79,7 @@ void LootStore::Clear()
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
@@ -79,24 +87,31 @@ 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::const_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();
@@ -106,22 +121,28 @@ void LootStore::LoadLootTable()
ConditionType condition = (ConditionType)fields[6].GetUInt8();
uint32 cond_value1 = fields[7].GetUInt32();
uint32 cond_value2 = fields[8].GetUInt32();
+
if(maxcount > std::numeric_limits<uint8>::max())
{
sLog.outErrorDb("Table '%s' entry %d item %d: maxcount value (%u) to large. must be less %u - skipped", GetName(), entry, item, maxcount,std::numeric_limits<uint8>::max());
continue; // error already printed to log/console.
}
+
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)
@@ -136,12 +157,17 @@ void LootStore::LoadLootTable()
}
// else is empty - template Id and iter are the same
// finally iter refers to already existed or just created <entry, LootTemplate>
+
// 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 (%lu templates)", count, (unsigned long)m_LootTemplates.size());
}
@@ -151,65 +177,84 @@ void LootStore::LoadLootTable()
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(bool rate) const
{
if(chance>=100.0f)
return true;
+
if(mincountOrRef < 0) // reference case
return roll_chance_f(chance* (rate ? sWorld.getRate(RATE_DROP_ITEM_REFERENCED) : 1.0f));
+
ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
+
float qualityModifier = pProto && rate ? 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
{
@@ -218,11 +263,13 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
sLog.outErrorDb("Table '%s' entry %d item %d: group (%u) must be less %u - skipped", store.GetName(), entry, itemid, group, 1 << 7);
return false;
}
+
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);
@@ -231,22 +278,26 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
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 (%f) - skipped",
store.GetName(), entry, itemid, chance);
return false;
}
+
if( maxcount < mincountOrRef) // wrong max count
{
sLog.outErrorDb("Table '%s' entry %d item %d: max count (%u) less that min count (%i) - skipped", store.GetName(), entry, itemid, uint32(maxcount), mincountOrRef);
return false;
}
+
}
else // mincountOrRef < 0
{
@@ -260,17 +311,22 @@ bool LootStoreItem::IsValid(LootStore const& store, uint32 entry) const
}
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);
@@ -279,12 +335,14 @@ LootItem::LootItem(LootStoreItem const& li)
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)
@@ -298,11 +356,14 @@ bool LootItem::AllowedForPlayer(Player const * player) const
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)
{
@@ -314,6 +375,7 @@ void Loot::AddItem(LootStoreItem const & 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()
@@ -325,21 +387,27 @@ void Loot::AddItem(LootStoreItem const & item)
}
}
}
+
// Calls processor of corresponding LootTemplate (which handles everything including references)
void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner, bool personal)
{
// Must be provided
if(!loot_owner)
return;
+
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,store.IsRatesAllowed ()); // Processing is done there, callback via Loot::AddItem()
+
// Setting access rights for group loot case
Group * pGroup=loot_owner->GetGroup();
if(!personal && pGroup)
@@ -352,22 +420,28 @@ void Loot::FillLoot(uint32 loot_id, LootStore const& store, Player* loot_owner,
else
FillNotNormalLootFor(loot_owner);
}
+
void Loot::FillNotNormalLootFor(Player* pl)
{
uint32 plguid = pl->GetGUIDLow();
+
QuestItemMap::const_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];
@@ -382,26 +456,32 @@ QuestItemList* Loot::FillFFALoot(Player* player)
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;
}
@@ -411,12 +491,15 @@ QuestItemList* Loot::FillQuestLoot(Player* player)
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];
@@ -435,10 +518,13 @@ QuestItemList* Loot::FillNonQuestNonFFAConditionalLoot(Player* player)
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
@@ -454,6 +540,7 @@ void Loot::NotifyItemRemoved(uint8 lootIndex)
PlayersLooting.erase(i);
}
}
+
void Loot::NotifyMoneyRemoved()
{
// notify all players that are looting this that the money was removed
@@ -468,12 +555,14 @@ void Loot::NotifyMoneyRemoved()
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 isn't called often
+
std::set<uint64>::iterator i_next;
for(std::set<uint64>::iterator i = PlayersLooting.begin(); i != PlayersLooting.end(); i = i_next)
{
@@ -486,10 +575,12 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex)
{
// 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);
}
@@ -498,6 +589,7 @@ void Loot::NotifyQuestItemRemoved(uint8 questIndex)
PlayersLooting.erase(i);
}
}
+
void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
{
if (maxAmount > 0)
@@ -510,6 +602,7 @@ void Loot::generateMoneyLoot( uint32 minAmount, uint32 maxAmount )
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;
@@ -566,15 +659,19 @@ LootItem* Loot::LootItemInSlot(uint32 lootSlot, Player* player, QuestItem **qite
}
}
}
+
if(is_looted)
return NULL;
+
return item;
}
+
uint32 Loot::GetMaxSlotInLootFor(Player* player) const
{
QuestItemMap::const_iterator itr = PlayerQuestItems.find(player->GetGUIDLow());
return items.size() + (itr != PlayerQuestItems.end() ? itr->second->size() : 0);
}
+
ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
{
b << uint32(li.itemid);
@@ -585,6 +682,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li)
//b << uint8(0); // slot type - will send after this function call
return b;
}
+
ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
{
if (lv.permission == NONE_PERMISSION)
@@ -593,12 +691,17 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
b << uint8(0); // item count
return b; // nothing output more
}
+
Loot &l = lv.loot;
+
uint8 itemsShown = 0;
+
//gold
b << uint32(l.gold);
+
size_t count_pos = b.wpos(); // pos of item count byte
b << uint8(0); // item count placeholder
+
switch (lv.permission)
{
case GROUP_PERMISSION:
@@ -610,6 +713,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
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;
@@ -635,6 +739,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
default:
return b; // nothing output more
}
+
QuestItemMap const& lootPlayerQuestItems = l.GetPlayerQuestItems();
QuestItemMap::const_iterator q_itr = lootPlayerQuestItems.find(lv.viewer->GetGUIDLow());
if (q_itr != lootPlayerQuestItems.end())
@@ -652,6 +757,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
}
}
}
+
QuestItemMap const& lootPlayerFFAItems = l.GetPlayerFFAItems();
QuestItemMap::const_iterator ffa_itr = lootPlayerFFAItems.find(lv.viewer->GetGUIDLow());
if (ffa_itr != lootPlayerFFAItems.end())
@@ -668,6 +774,7 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
}
}
}
+
QuestItemMap const& lootPlayerNonQuestNonFFAConditionalItems = l.GetPlayerNonQuestNonFFAConditionalItems();
QuestItemMap::const_iterator nn_itr = lootPlayerNonQuestNonFFAConditionalItems.find(lv.viewer->GetGUIDLow());
if (nn_itr != lootPlayerNonQuestNonFFAConditionalItems.end())
@@ -684,13 +791,17 @@ ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv)
}
}
}
+
//update number of items shown
b.put<uint8>(count_pos,itemsShown);
+
return b;
}
+
//
// --------- LootTemplate::LootGroup ---------
//
+
// Adds an entry to the group (at loading stage)
void LootTemplate::LootGroup::AddEntry(LootStoreItem& item)
{
@@ -699,16 +810,19 @@ void LootTemplate::LootGroup::AddEntry(LootStoreItem& 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<ExplicitlyChanced.size(); ++i) //check each explicitly chanced entry in the template and modify its chance based on quality.
{
if(ExplicitlyChanced[i].chance>=100.0f)
return &ExplicitlyChanced[i];
+
Roll -= ExplicitlyChanced[i].chance;
if (Roll < 0)
return &ExplicitlyChanced[i];
@@ -716,8 +830,10 @@ LootStoreItem const * LootTemplate::LootGroup::Roll() const
}
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
{
@@ -729,6 +845,7 @@ bool LootTemplate::LootGroup::HasQuestDrop() const
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
{
@@ -740,6 +857,7 @@ bool LootTemplate::LootGroup::HasQuestDropForPlayer(Player const * player) const
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
{
@@ -747,23 +865,30 @@ void LootTemplate::LootGroup::Process(Loot& loot) const
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();
@@ -771,11 +896,13 @@ void LootTemplate::LootGroup::Verify(LootStore const& lootstore, uint32 id, uint
{
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)
@@ -788,6 +915,7 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootId
ref_set->erase(-ieItr->mincountOrRef);
}
}
+
for (LootStoreItemList::const_iterator ieItr=EqualChanced.begin(); ieItr != EqualChanced.end(); ++ieItr)
{
if(ieItr->mincountOrRef < 0)
@@ -799,9 +927,11 @@ void LootTemplate::LootGroup::CheckLootRefs(LootTemplateMap const& store, LootId
}
}
}
+
//
// --------- LootTemplate ---------
//
+
// Adds an entry to the group (at loading stage)
void LootTemplate::AddEntry(LootStoreItem& item)
{
@@ -814,6 +944,7 @@ void LootTemplate::AddEntry(LootStoreItem& item)
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, bool rate, uint8 groupId) const
{
@@ -821,29 +952,36 @@ void LootTemplate::Process(Loot& loot, LootStore const& store, bool rate, uint8
{
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(rate))
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, rate, i->group);
}
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
{
@@ -853,6 +991,7 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con
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
@@ -866,12 +1005,15 @@ bool LootTemplate::HasQuestDrop(LootTemplateMap const& store, uint8 groupId) con
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
{
@@ -881,6 +1023,7 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
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 )
{
@@ -895,20 +1038,25 @@ bool LootTemplate::HasQuestDropForPlayer(LootTemplateMap const& store, Player co
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->HasQuestDropForPlayer(player))
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)
@@ -921,13 +1069,16 @@ void LootTemplate::CheckLootRefs(LootTemplateMap const& store, LootIdSet* ref_se
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 )
{
@@ -944,13 +1095,16 @@ void LoadLootTemplates_Creature()
}
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 )
{
@@ -970,10 +1124,12 @@ void LoadLootTemplates_Disenchant()
// 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 )
{
@@ -981,13 +1137,16 @@ void LoadLootTemplates_Fishing()
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 )
{
@@ -1004,43 +1163,54 @@ void LoadLootTemplates_Gameobject()
}
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<ItemPrototype>(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_Milling()
{
LootIdSet ids_set;
LootTemplates_Milling.LoadAndCollectLootIds(ids_set);
+
// remove real entries and check existence loot
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i )
{
ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i);
if(!proto)
continue;
+
if((proto->BagFamily & BAG_FAMILY_MASK_HERBS)==0)
continue;
+
if(ids_set.count(proto->ItemId))
ids_set.erase(proto->ItemId);
}
+
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Milling.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 )
{
@@ -1057,37 +1227,46 @@ void LoadLootTemplates_Pickpocketing()
}
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 )
{
ItemPrototype const* proto = sItemStorage.LookupEntry<ItemPrototype>(i);
if(!proto)
continue;
+
if((proto->BagFamily & BAG_FAMILY_MASK_MINING_SUPP)==0)
continue;
+
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(!itr->second->GetRewMailTemplateId())
continue;
+
if(ids_set.count(itr->first))
ids_set.erase(itr->first);
/* disabled reporting: some quest mails not include items
@@ -1095,13 +1274,16 @@ void LoadLootTemplates_QuestMail()
LootTemplates_QuestMail.ReportNotExistedId(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 )
{
@@ -1118,22 +1300,27 @@ void LoadLootTemplates_Skinning()
}
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_Spell()
{
LootIdSet ids_set;
LootTemplates_Spell.LoadAndCollectLootIds(ids_set);
+
// remove real entries and check existence loot
for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry (spell_id);
if(!spellInfo)
continue;
+
// possible cases
if( !IsLootCraftingSpell(spellInfo))
continue;
+
if(!ids_set.count(spell_id))
{
// not report about not trainable spells (optionally supported by DB)
@@ -1146,13 +1333,16 @@ void LoadLootTemplates_Spell()
else
ids_set.erase(spell_id);
}
+
// output error for any still listed (not referenced from appropriate table) ids
LootTemplates_Spell.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);
@@ -1165,6 +1355,7 @@ void LoadLootTemplates_Reference()
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
index e7fb4daed36..c629977ea67 100644
--- a/src/game/LootMgr.h
+++ b/src/game/LootMgr.h
@@ -17,23 +17,29 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_LOOTMGR_H
#define TRINITY_LOOTMGR_H
+
#include "ItemEnchantmentMgr.h"
#include "ByteBuffer.h"
#include "Utilities/LinkedReference/RefManager.h"
+
#include <map>
#include <vector>
+
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,
@@ -42,6 +48,7 @@ enum LootMethod
GROUP_LOOT = 3,
NEED_BEFORE_GREED = 4
};
+
enum PermissionTypes
{
ALL_PERMISSION = 0,
@@ -49,6 +56,7 @@ enum PermissionTypes
MASTER_PERMISSION = 2,
NONE_PERMISSION = 3
};
+
enum LootType
{
LOOT_CORPSE = 1,
@@ -59,11 +67,14 @@ enum LootType
LOOT_SKINNING = 6,
LOOT_PROSPECTING = 7,
LOOT_MILLING = 8,
+
LOOT_FISHINGHOLE = 20, // unsupported by client, sending LOOT_FISHING instead
LOOT_INSIGNIA = 21 // unsupported by client, sending LOOT_CORPSE instead
};
+
class Player;
class LootStore;
+
struct LootStoreItem
{
uint32 itemid; // id of the item
@@ -73,16 +84,19 @@ struct LootStoreItem
bool needs_quest :1; // quest drop (negative ChanceOrQuestChance in DB)
uint8 maxcount :8; // max drop count for the item (mincountOrRef positive) or Ref multiplicator (mincountOrRef negative)
uint16 conditionId :16; // additional loot condition Id
+
// 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), needs_quest(_chanceOrQuestChance < 0), maxcount(_maxcount), conditionId(_conditionId)
{}
+
bool Roll(bool rate) 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;
@@ -96,43 +110,57 @@ struct LootItem
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<QuestItem> QuestItemList;
typedef std::map<uint32, QuestItemList *> QuestItemMap;
typedef std::vector<LootStoreItem> LootStoreItemList;
typedef UNORDERED_MAP<uint32, LootTemplate*> LootTemplateMap;
+
typedef std::set<uint32> LootIdSet;
+
class LootStore
{
public:
explicit LootStore(char const* name, char const* entryName, bool ratesAllowed)
: m_name(name), m_entryName(entryName), m_ratesAllowed(ratesAllowed) {}
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; }
bool IsRatesAllowed() const { return m_ratesAllowed; }
@@ -145,19 +173,23 @@ class LootStore
char const* m_entryName;
bool m_ratesAllowed;
};
+
class LootTemplate
{
class LootGroup; // A set of loot definitions for items (refs are not allowed inside)
typedef std::vector<LootGroup> 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, bool rate, 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;
@@ -165,7 +197,9 @@ class LootTemplate
LootStoreItemList Entries; // not grouped only
LootGroups Groups; // groups have own (optimised) processing, grouped entries go there
};
+
//=====================================================
+
class LootValidatorRef : public Reference<Loot, LootValidatorRef>
{
public:
@@ -173,51 +207,66 @@ class LootValidatorRef : public Reference<Loot, LootValidatorRef>
void targetObjectDestroyLink() {}
void sourceObjectDestroyLink() {}
};
+
//=====================================================
+
class LootValidatorRefManager : public RefManager<Loot, LootValidatorRef>
{
public:
typedef LinkedListHead::Iterator< LootValidatorRef > iterator;
+
LootValidatorRef* getFirst() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getFirst(); }
LootValidatorRef* getLast() { return (LootValidatorRef*)RefManager<Loot, LootValidatorRef>::getLast(); }
+
iterator begin() { return iterator(getFirst()); }
iterator end() { return iterator(NULL); }
iterator rbegin() { return iterator(getLast()); }
iterator rend() { return iterator(NULL); }
};
+
//=====================================================
struct LootView;
+
ByteBuffer& operator<<(ByteBuffer& b, LootItem const& li);
ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
+
struct Loot
{
friend ByteBuffer& operator<<(ByteBuffer& b, LootView const& lv);
+
QuestItemMap const& GetPlayerQuestItems() const { return PlayerQuestItems; }
QuestItemMap const& GetPlayerFFAItems() const { return PlayerFFAItems; }
QuestItemMap const& GetPlayerNonQuestNonFFAConditionalItems() const { return PlayerNonQuestNonFFAConditionalItems; }
+
std::vector<LootItem> items;
uint32 gold;
uint8 unlootedCount;
LootType loot_type; // required for achievement system
+
Loot(uint32 _gold = 0) : gold(_gold), unlootedCount(0), loot_type(LOOT_CORPSE) {}
~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()
{
for (QuestItemMap::const_iterator itr = PlayerQuestItems.begin(); itr != PlayerQuestItems.end(); ++itr)
delete itr->second;
PlayerQuestItems.clear();
+
for (QuestItemMap::const_iterator itr = PlayerFFAItems.begin(); itr != PlayerFFAItems.end(); ++itr)
delete itr->second;
PlayerFFAItems.clear();
+
for (QuestItemMap::const_iterator itr = PlayerNonQuestNonFFAConditionalItems.begin(); itr != PlayerNonQuestNonFFAConditionalItems.end(); ++itr)
delete itr->second;
PlayerNonQuestNonFFAConditionalItems.clear();
+
PlayersLooting.clear();
items.clear();
quest_items.clear();
@@ -225,32 +274,41 @@ struct Loot
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, bool personal);
+
// 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);
uint32 GetMaxSlotInLootFor(Player* player) const;
+
private:
void FillNotNormalLootFor(Player* player);
QuestItemList* FillFFALoot(Player* player);
QuestItemList* FillQuestLoot(Player* player);
QuestItemList* FillNonQuestNonFFAConditionalLoot(Player* player);
+
std::vector<LootItem> quest_items;
std::set<uint64> 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;
@@ -259,6 +317,7 @@ struct LootView
LootView(Loot &_loot, Player *_viewer,PermissionTypes _permission = ALL_PERMISSION)
: loot(_loot), viewer(_viewer), permission(_permission) {}
};
+
extern LootStore LootTemplates_Creature;
extern LootStore LootTemplates_Fishing;
extern LootStore LootTemplates_Gameobject;
@@ -270,6 +329,7 @@ extern LootStore LootTemplates_Disenchant;
extern LootStore LootTemplates_Prospecting;
extern LootStore LootTemplates_QuestMail;
extern LootStore LootTemplates_Spell;
+
void LoadLootTemplates_Creature();
void LoadLootTemplates_Fishing();
void LoadLootTemplates_Gameobject();
@@ -280,8 +340,10 @@ void LoadLootTemplates_Skinning();
void LoadLootTemplates_Disenchant();
void LoadLootTemplates_Prospecting();
void LoadLootTemplates_QuestMail();
+
void LoadLootTemplates_Spell();
void LoadLootTemplates_Reference();
+
inline void LoadLootTables()
{
LoadLootTemplates_Creature();
@@ -295,7 +357,9 @@ inline void LoadLootTables()
LoadLootTemplates_Prospecting();
LoadLootTemplates_QuestMail();
LoadLootTemplates_Spell();
+
LoadLootTemplates_Reference();
}
+
#endif
diff --git a/src/game/Mail.cpp b/src/game/Mail.cpp
index e52006b9c37..c50b16104f2 100644
--- a/src/game/Mail.cpp
+++ b/src/game/Mail.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -30,6 +31,7 @@
#include "Language.h"
#include "AuctionHouseBot.h"
#include "DBCStores.h"
+
enum MailShowFlags
{
MAIL_SHOW_UNK0 = 0x0001,
@@ -38,16 +40,19 @@ enum MailShowFlags
MAIL_SHOW_UNK2 = 0x0008, // unknown, COD will be shown even without that flag
MAIL_SHOW_RETURN = 0x0010,
};
+
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 )
{
uint64 mailbox, unk3;
@@ -56,20 +61,28 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
uint8 unk4;
recv_data >> mailbox;
recv_data >> receiver;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
recv_data >> subject;
+
recv_data >> body;
+
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
{
GetPlayer()->SendMailResult(0, MAIL_SEND, MAIL_ERR_TOO_MANY_ATTACHMENTS);
return;
}
+
if(items_count)
{
for(uint8 i = 0; i < items_count; ++i)
@@ -81,16 +94,22 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
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",
@@ -98,22 +117,30 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
pl->SendMailResult(0, MAIL_SEND, 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, MAIL_SEND, MAIL_ERR_CANNOT_SEND_TO_SELF);
return;
}
+
uint32 cost = items_count ? 30 * items_count : 30; // price hardcoded in client
+
uint32 reqmoney = cost + money;
+
if (pl->GetMoney() < reqmoney)
{
pl->SendMailResult(0, MAIL_SEND, 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();
@@ -142,21 +169,25 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_NOT_YOUR_TEAM);
return;
}
+
uint32 rc_account = 0;
if(receive)
rc_account = receive->GetSession()->GetAccountId();
else
rc_account = objmgr.GetPlayerAccountIdByGUID(rc);
+
if (items_count)
{
for(MailItemMap::iterator mailItemIter = mi.begin(); mailItemIter != mi.end(); ++mailItemIter)
{
MailItem& mailItem = mailItemIter->second;
+
if(!mailItem.item_guidlow)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
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)
@@ -164,21 +195,25 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_MAIL_ATTACHMENT_INVALID);
return;
}
+
if(!mailItem.item->CanBeTraded(true))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
+
if(mailItem.item->IsBoundAccountWide() && mailItem.item->IsSoulBound() && pl->GetSession()->GetAccountId() != rc_account)
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_ARTEFACTS_ONLY_FOR_OWN_CHARACTERS);
return;
}
+
if (mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_CONJURED) || mailItem.item->GetUInt32Value(ITEM_FIELD_DURATION))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_EQUIP_ERROR, EQUIP_ERR_MAIL_BOUND_ITEM);
return;
}
+
if(COD && mailItem.item->HasFlag(ITEM_FIELD_FLAGS, ITEM_FLAGS_WRAPPED))
{
pl->SendMailResult(0, MAIL_SEND, MAIL_ERR_CANT_SEND_WRAPPED_COD);
@@ -187,14 +222,18 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
}
}
pl->SendMailResult(0, MAIL_SEND, MAIL_OK);
+
uint32 itemTextId = 0;
if (!body.empty())
{
itemTextId = objmgr.CreateItemText( body );
}
+
pl->ModifyMoney( -int32(reqmoney) );
pl->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_MAIL, cost);
+
bool needItemDelay = false;
+
if(items_count > 0 || money > 0)
{
if (items_count > 0)
@@ -204,12 +243,15 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
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(GetAccountId(), "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
@@ -218,31 +260,39 @@ void WorldSession::HandleSendMail(WorldPacket & recv_data )
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(GetAccountId(),"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::HandleMailMarkAsRead(WorldPacket & recv_data )
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
recv_data >> mailId;
Player *pl = _player;
Mail *m = pl->GetMail(mailId);
@@ -256,6 +306,7 @@ void WorldSession::HandleMailMarkAsRead(WorldPacket & recv_data )
m->state = MAIL_STATE_CHANGED;
}
}
+
//called when client deletes mail
void WorldSession::HandleMailDelete(WorldPacket & recv_data )
{
@@ -263,8 +314,10 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data )
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
Mail *m = _player->GetMail(mailId);
Player* pl = _player;
pl->m_mailsUpdated = true;
@@ -276,17 +329,21 @@ void WorldSession::HandleMailDelete(WorldPacket & recv_data )
pl->SendMailResult(mailId, MAIL_DELETED, MAIL_ERR_INTERNAL_ERROR);
return;
}
+
m->state = MAIL_STATE_DELETED;
}
pl->SendMailResult(mailId, MAIL_DELETED, MAIL_OK);
}
+
void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
recv_data >> mailId;
Player *pl = _player;
Mail *m = pl->GetMail(mailId);
@@ -303,7 +360,9 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
CharacterDatabase.PExecute("DELETE FROM mail_items WHERE mail_id = '%u'", mailId);
CharacterDatabase.CommitTransaction();
pl->RemoveMail(mailId);
+
MailItemsInfo mi;
+
if(m->HasItems())
{
for(std::vector<MailItemInfo>::iterator itr2 = m->items.begin(); itr2 != m->items.end(); ++itr2)
@@ -315,9 +374,11 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
{
//WTF?
}
+
pl->RemoveMItem(itr2->item_guid);
}
}
+
if (m->sender == auctionbot.GetAHBplayerGUID())
{
SendReturnToSender(MAIL_CREATURE, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId);
@@ -326,9 +387,11 @@ void WorldSession::HandleMailReturnToSender(WorldPacket & recv_data )
{
SendReturnToSender(MAIL_NORMAL, GetAccountId(), m->receiver, m->sender, m->subject, m->itemTextId, &mi, m->money, m->mailTemplateId);
}
+
delete m; //we can deallocate old mail
pl->SendMailResult(mailId, MAIL_RETURNED_TO_SENDER, MAIL_OK);
}
+
void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint32 sender_guid, uint32 receiver_guid, const std::string& subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, uint16 mailTemplateId )
{
if(messageType != MAIL_NORMAL) // return only to players
@@ -336,21 +399,27 @@ void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint
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;
}
+
// prepare 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)
@@ -362,11 +431,14 @@ void WorldSession::SendReturnToSender(uint8 messageType, uint32 sender_acc, uint
}
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::HandleMailTakeItem(WorldPacket & recv_data )
{
@@ -374,35 +446,44 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
uint32 mailId;
uint32 itemId;
recv_data >> mailbox;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
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;
@@ -415,6 +496,7 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
{
// can be calculated early
sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid);
+
if(!objmgr.GetPlayerNameByGUID(sender_guid,sender_name))
sender_name = objmgr.GetTrinityStringForDBCLocale(LANG_UNKNOWN);
}
@@ -423,80 +505,104 @@ void WorldSession::HandleMailTakeItem(WorldPacket & recv_data )
}
else if(!receive)
sender_accId = objmgr.GetPlayerAccountIdByGUID(sender_guid);
+
// check player existence
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_EQUIP_ERROR, msg);
}
+
void WorldSession::HandleMailTakeMoney(WorldPacket & recv_data )
{
uint64 mailbox;
uint32 mailId;
recv_data >> mailbox;
recv_data >> mailId;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
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, MAIL_OK);
+
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->SaveGoldToDB();
pl->_SaveMail();
CharacterDatabase.CommitTransaction();
}
+
//called when player lists his received mails
void WorldSession::HandleGetMailList(WorldPacket & recv_data )
{
uint64 mailbox;
recv_data >> mailbox;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
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;
+
uint32 show_flags = 0;
if ((*itr)->messageType != MAIL_NORMAL)
show_flags |= MAIL_SHOW_DELETE;
@@ -504,9 +610,11 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
show_flags |= MAIL_SHOW_AUCTION;
if ((*itr)->HasItems() && (*itr)->messageType == MAIL_NORMAL)
show_flags |= MAIL_SHOW_RETURN;
+
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
@@ -520,6 +628,7 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
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
@@ -530,7 +639,9 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
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; // client limit is 0x10
+
for(uint8 i = 0; i < item_count; ++i)
{
Item *item = pl->GetMItem((*itr)->items[i].item_guid);
@@ -564,51 +675,68 @@ void WorldSession::HandleGetMailList(WorldPacket & recv_data )
// unknown wotlk
data << (uint8) 0;
}
+
mails_count += 1;
}
+
data.put<uint8>(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 )
{
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 )
{
uint64 mailbox;
uint32 mailId;
+
recv_data >> mailbox >> mailId;
+
if (!GetPlayer()->GetGameObjectIfCanInteractWith(mailbox, GAMEOBJECT_TYPE_MAILBOX))
return;
+
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 )
@@ -616,6 +744,7 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
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, MAIL_OK);
@@ -626,16 +755,20 @@ void WorldSession::HandleMailCreateTextItem(WorldPacket & recv_data )
delete bodyItem;
}
}
+
//TODO Fix me! ... this void has probably bad condition, but good data are sent
void WorldSession::HandleQueryNextMailTime(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;
time_t now = time(NULL);
for(PlayerMails::iterator itr = _player->GetmailBegin(); itr != _player->GetmailEnd(); ++itr)
@@ -644,10 +777,13 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ )
// must be not checked yet
if(m->checked & MAIL_CHECK_MASK_READ)
continue;
+
// and already delivered
if(now < m->deliver_time)
continue;
+
data << (uint64) m->sender; // sender guid
+
switch(m->messageType)
{
case MAIL_AUCTION:
@@ -662,6 +798,7 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ )
break;
}
data << (uint32) 0xC6000000; // float unk, time or something
+
++count;
if(count == 2) // do not display more than 2 mails
break;
@@ -675,6 +812,7 @@ void WorldSession::HandleQueryNextMailTime(WorldPacket & /*recv_data*/ )
}
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)
{
if (receiver_guidlow == auctionbot.GetAHBplayerGUID())
@@ -686,22 +824,28 @@ void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 station
return;
}
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 = sWorld.getConfig(CONFIG_MAIL_DELIVERY_DELAY);
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;
@@ -713,15 +857,19 @@ void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 station
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)
@@ -737,11 +885,13 @@ void WorldSession::SendMailTo(Player* receiver, uint8 messageType, uint8 station
}
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', '" UI64FMTD "','" UI64FMTD "', '%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)
diff --git a/src/game/Mail.h b/src/game/Mail.h
index 5dc8cb5fd3d..8cbe46e6396 100644
--- a/src/game/Mail.h
+++ b/src/game/Mail.h
@@ -19,11 +19,15 @@
*/
#ifndef TRINITY_MAIL_H
#define TRINITY_MAIL_H
+
#include "Common.h"
#include <map>
+
class Item;
+
#define MAIL_BODY_ITEM_TEMPLATE 8383 // - plain letter, A Dusty Unsent Letter: 889
#define MAX_MAIL_ITEMS 12
+
enum MailCheckMask
{
MAIL_CHECK_MASK_NONE = 0,
@@ -32,6 +36,7 @@ enum MailCheckMask
MAIL_CHECK_MASK_COD_PAYMENT = 8,
MAIL_CHECK_MASK_RETURNED = 16
};
+
enum MailMessageType
{
MAIL_NORMAL = 0,
@@ -40,12 +45,14 @@ enum 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,
@@ -56,6 +63,7 @@ enum MailAuctionAnswers
AUCTION_CANCELED = 5,
AUCTION_SALE_PENDING = 6
};
+
// gathered from Stationery.dbc
enum MailStationery
{
@@ -66,21 +74,27 @@ enum MailStationery
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<uint32, MailItem> MailItemMap;
+
class MailItemsInfo
{
public:
@@ -88,6 +102,7 @@ class MailItemsInfo
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;
@@ -97,6 +112,7 @@ class MailItemsInfo
mailItem.item = item;
i_MailItemMap[guidlow] = mailItem;
}
+
void AddItem(uint32 guidlow, uint8 slot = 0)
{
MailItem mailItem;
@@ -104,8 +120,10 @@ class MailItemsInfo
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)
@@ -117,6 +135,7 @@ class MailItemsInfo
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;
@@ -135,6 +154,7 @@ struct Mail
uint32 COD;
uint32 checked;
MailState state;
+
void AddItem(uint32 itemGuidLow, uint32 item_template)
{
MailItemInfo mii;
@@ -142,6 +162,7 @@ struct Mail
mii.item_template = item_template;
items.push_back(mii);
}
+
void AddAllItems(MailItemsInfo& pMailItemsInfo)
{
for(MailItemMap::iterator mailItemIter = pMailItemsInfo.begin(); mailItemIter != pMailItemsInfo.end(); ++mailItemIter)
@@ -150,6 +171,7 @@ struct Mail
AddItem(mailItem.item_guidlow, mailItem.item_template);
}
}
+
bool RemoveItem(uint32 item_guid)
{
for(std::vector<MailItemInfo>::iterator itr = items.begin(); itr != items.end(); ++itr)
@@ -162,6 +184,7 @@ struct Mail
}
return false;
}
+
bool HasItems() const { return !items.empty(); }
};
#endif
diff --git a/src/game/Map.cpp b/src/game/Map.cpp
index ca139c13d2d..2f8c4d0e152 100644
--- a/src/game/Map.cpp
+++ b/src/game/Map.cpp
@@ -17,6 +17,7 @@
* 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 "Player.h"
#include "Vehicle.h"
@@ -37,13 +38,17 @@
#include "MapRefManager.h"
#include "Vehicle.h"
#include "WaypointManager.h"
+
#include "MapInstanced.h"
#include "InstanceSaveMgr.h"
#include "VMapFactory.h"
+
#define DEFAULT_GRID_EXPIRY 300
#define MAX_GRID_LOAD_TIME 50
#define MAX_CREATURE_ATTACK_RADIUS (45.0f * sWorld.getRate(RATE_CREATURE_AGGRO))
+
GridState* si_GridStates[MAX_GRID_STATE];
+
struct ScriptAction
{
uint64 sourceGUID;
@@ -51,9 +56,11 @@ struct ScriptAction
uint64 ownerGUID; // owner of source if source is item
ScriptInfo const* script; // pointer to static script data
};
+
Map::~Map()
{
UnloadAll();
+
while(!i_worldObjects.empty())
{
WorldObject *obj = *i_worldObjects.begin();
@@ -62,21 +69,26 @@ Map::~Map()
obj->RemoveFromWorld();
obj->ResetMap();
}
+
if(!m_scriptSchedule.empty())
sWorld.DecreaseScheduledScriptCount(m_scriptSchedule.size());
}
+
bool Map::ExistMap(uint32 mapid,int gx,int gy)
{
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,gx,gy);
+
FILE *pf=fopen(tmp,"rb");
+
if(!pf)
{
sLog.outError("Check existing of map file '%s': not exist!",tmp);
delete[] tmp;
return false;
}
+
map_fileheader header;
fread(&header, sizeof(header), 1, pf);
if (header.mapMagic != uint32(MAP_MAGIC) ||
@@ -87,10 +99,12 @@ bool Map::ExistMap(uint32 mapid,int gx,int gy)
fclose(pf); //close file before return
return false;
}
+
delete [] tmp;
fclose(pf);
return true;
}
+
bool Map::ExistVMap(uint32 mapid,int gx,int gy)
{
if(VMAP::IVMapManager* vmgr = VMAP::VMapFactory::createOrGetVMapManager())
@@ -107,8 +121,10 @@ bool Map::ExistVMap(uint32 mapid,int gx,int gy)
}
}
}
+
return true;
}
+
void Map::LoadVMap(int gx,int gy)
{
// x and y are swapped !!
@@ -126,21 +142,26 @@ void Map::LoadVMap(int gx,int gy)
break;
}
}
+
void Map::LoadMap(int gx,int gy, bool reload)
{
if( i_InstanceId != 0 )
{
if(GridMaps[gx][gy])
return;
+
// load grid map for base map
if (!m_parentMap->GridMaps[gx][gy])
m_parentMap->EnsureGridCreated(GridPair(63-gx,63-gy));
+
((MapInstanced*)(m_parentMap))->AddGridMapReference(GridPair(gx,gy));
GridMaps[gx][gy] = m_parentMap->GridMaps[gx][gy];
return;
}
+
if(GridMaps[gx][gy] && !reload)
return;
+
//map already load, delete it before reloading (Is it necessary? Do we really need the ability the reload maps during runtime?)
if(GridMaps[gx][gy])
{
@@ -148,6 +169,7 @@ void Map::LoadMap(int gx,int gy, bool reload)
delete (GridMaps[gx][gy]);
GridMaps[gx][gy]=NULL;
}
+
// map file name
char *tmp=NULL;
int len = sWorld.GetDataPath().length()+strlen("maps/%03u%02u%02u.map")+1;
@@ -162,12 +184,14 @@ void Map::LoadMap(int gx,int gy, bool reload)
}
delete [] tmp;
}
+
void Map::LoadMapAndVMap(int gx,int gy)
{
LoadMap(gx,gy);
if(i_InstanceId == 0)
LoadVMap(gx, gy); // Only load the data for the base map
}
+
void Map::InitStateMachine()
{
si_GridStates[GRID_STATE_INVALID] = new InvalidState;
@@ -175,6 +199,7 @@ void Map::InitStateMachine()
si_GridStates[GRID_STATE_IDLE] = new IdleState;
si_GridStates[GRID_STATE_REMOVAL] = new RemovalState;
}
+
void Map::DeleteStateMachine()
{
delete si_GridStates[GRID_STATE_INVALID];
@@ -182,6 +207,7 @@ void Map::DeleteStateMachine()
delete si_GridStates[GRID_STATE_IDLE];
delete si_GridStates[GRID_STATE_REMOVAL];
}
+
Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _parent)
: i_mapEntry (sMapStore.LookupEntry(id)), i_spawnMode(SpawnMode), i_InstanceId(InstanceId), m_unloadTimer(0),
m_activeNonPlayersIter(m_activeNonPlayers.end()),
@@ -190,6 +216,7 @@ Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _par
, i_notifyLock(false), i_scriptLock(false)
{
m_notifyTimer.SetInterval(IN_MILISECONDS/2);
+
for(unsigned int idx=0; idx < MAX_NUMBER_OF_GRIDS; ++idx)
{
for(unsigned int j=0; j < MAX_NUMBER_OF_GRIDS; ++j)
@@ -199,14 +226,17 @@ Map::Map(uint32 id, time_t expiry, uint32 InstanceId, uint8 SpawnMode, Map* _par
setNGrid(NULL, idx, j);
}
}
+
//lets initialize visibility distance for map
Map::InitVisibilityDistance();
}
+
void Map::InitVisibilityDistance()
{
//init visibility for continents
m_VisibleDistance = sWorld.GetMaxVisibleDistanceOnContinents();
}
+
// Template specialization of utility methods
template<class T>
void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell)
@@ -216,6 +246,7 @@ void Map::AddToGrid(T* obj, NGridType *grid, Cell const& cell)
else
(*grid)(cell.CellX(), cell.CellY()).template AddGridObject<T>(obj, obj->GetGUID());
}
+
template<>
void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell)
{
@@ -223,8 +254,10 @@ void Map::AddToGrid(Creature* obj, NGridType *grid, Cell const& cell)
(*grid)(cell.CellX(), cell.CellY()).AddWorldObject(obj, obj->GetGUID());
else
(*grid)(cell.CellX(), cell.CellY()).AddGridObject(obj, obj->GetGUID());
+
obj->SetCurrentCell(cell);
}
+
template<class T>
void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell)
{
@@ -233,6 +266,7 @@ void Map::RemoveFromGrid(T* obj, NGridType *grid, Cell const& cell)
else
(*grid)(cell.CellX(), cell.CellY()).template RemoveGridObject<T>(obj, obj->GetGUID());
}
+
template<class T>
void Map::SwitchGridContainers(T* obj, bool on)
{
@@ -242,13 +276,17 @@ void Map::SwitchGridContainers(T* obj, bool on)
sLog.outError("Map::SwitchGridContainers: Object " I64FMT " 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("Switch object " I64FMT " from grid[%u,%u] %u", obj->GetGUID(), cell.data.Part.grid_x, cell.data.Part.grid_y, on);
NGridType *ngrid = getNGrid(cell.GridX(), cell.GridY());
assert( ngrid != NULL );
+
GridType &grid = (*ngrid)(cell.CellX(), cell.CellY());
+
if(on)
{
grid.RemoveGridObject<T>(obj, obj->GetGUID());
@@ -271,24 +309,29 @@ void Map::SwitchGridContainers(T* obj, bool on)
}
obj->m_isWorldObject = on;
}
+
template void Map::SwitchGridContainers(Creature *, bool);
//template void Map::SwitchGridContainers(DynamicObject *, bool);
+
template<class T>
void Map::DeleteFromWorld(T* obj)
{
// Note: In case resurrectable corpse and pet its removed from global lists in own destructor
delete obj;
}
+
template<>
void Map::DeleteFromWorld(Player* pl)
{
ObjectAccessor::Instance().RemoveObject(pl);
delete pl;
}
+
template<class T>
void Map::AddNotifier(T*)
{
}
+
template<>
void Map::AddNotifier(Player* obj)
{
@@ -296,6 +339,7 @@ void Map::AddNotifier(Player* obj)
//obj->m_IsInNotifyList = false;
AddUnitToNotify(obj);
}
+
template<>
void Map::AddNotifier(Creature* obj)
{
@@ -303,6 +347,7 @@ void Map::AddNotifier(Creature* obj)
//obj->m_IsInNotifyList = false;
AddUnitToNotify(obj);
}
+
void
Map::EnsureGridCreated(const GridPair &p)
{
@@ -312,26 +357,34 @@ Map::EnsureGridCreated(const GridPair &p)
if(!getNGrid(p.x_coord, p.y_coord))
{
sLog.outDebug("Creating grid[%u,%u] for map %u instance %u", p.x_coord, p.y_coord, GetId(), i_InstanceId);
+
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 = (MAX_NUMBER_OF_GRIDS - 1) - p.x_coord;
int gy = (MAX_NUMBER_OF_GRIDS - 1) - p.y_coord;
+
if(!GridMaps[gx][gy])
LoadMapAndVMap(gx,gy);
}
}
}
+
void
Map::EnsureGridLoadedAtEnter(const Cell &cell, Player *player)
{
NGridType *grid;
+
if(EnsureGridLoaded(cell))
{
grid = getNGrid(cell.GridX(), cell.GridY());
+
if (player)
{
player->SendDelayResponse(MAX_GRID_LOAD_TIME);
@@ -341,37 +394,47 @@ Map::EnsureGridLoadedAtEnter(const Cell &cell, Player *player)
{
DEBUG_LOG("Active object nearby triggers of loading grid [%u,%u] on map %u", cell.GridX(), cell.GridY(), GetId());
}
+
ResetGridExpiry(*getNGrid(cell.GridX(), cell.GridY()), 0.1f);
grid->SetGridState(GRID_STATE_ACTIVE);
}
else
grid = getNGrid(cell.GridX(), cell.GridY());
+
if (player)
AddToGrid(player,grid,cell);
}
+
bool Map::EnsureGridLoaded(const Cell &cell)
{
EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
+
assert(grid != NULL);
if( !isGridObjectDataLoaded(cell.GridX(), cell.GridY()) )
{
sLog.outDebug("Loading grid[%u,%u] for map %u instance %u", cell.GridX(), cell.GridY(), GetId(), i_InstanceId);
+
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());
return true;
}
+
return false;
}
+
void Map::LoadGrid(float x, float y)
{
CellPair pair = Trinity::ComputeCellPair(x, y);
Cell cell(pair);
EnsureGridLoaded(cell);
}
+
bool Map::Add(Player *player)
{
// Check if we are adding to correct map
@@ -381,12 +444,16 @@ bool Map::Add(Player *player)
Cell cell(p);
EnsureGridLoadedAtEnter(cell, player);
player->AddToWorld();
+
SendInitSelf(player);
SendInitTransports(player);
+
player->m_clientGUIDs.clear();
//AddNotifier(player);
+
return true;
}
+
template<class T>
void
Map::Add(T *obj)
@@ -397,6 +464,7 @@ Map::Add(T *obj)
sLog.outError("Map::Add: Object " UI64FMTD " 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(obj->IsInWorld()) // need some clean up later
{
@@ -404,56 +472,72 @@ Map::Add(T *obj)
AddNotifier(obj);
return;
}
+
if(obj->isActiveObject())
EnsureGridLoadedAtEnter(cell);
else
EnsureGridCreated(GridPair(cell.GridX(), cell.GridY()));
+
NGridType *grid = getNGrid(cell.GridX(), cell.GridY());
assert( grid != NULL );
+
AddToGrid(obj,grid,cell);
//obj->SetMap(this);
obj->AddToWorld();
+
if(obj->isActiveObject())
AddToActive(obj);
+
DEBUG_LOG("Object %u enters grid[%u,%u]", GUID_LOPART(obj->GetGUID()), cell.GridX(), cell.GridY());
+
//something, such as vehicle, needs to be update immediately
//also, trigger needs to cast spell, if not update, cannot see visual
//if(obj->GetTypeId() != TYPEID_UNIT)
UpdateObjectVisibility(obj,cell,p);
AddNotifier(obj);
}
+
/*
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;
cell.SetNoCreate();
+
if( !loaded(GridPair(cell.data.Part.grid_x, cell.data.Part.grid_y)) )
return;
+
MaNGOS::MessageDeliverer post_man(*player, msg, to_self);
TypeContainerVisitor<MaNGOS::MessageDeliverer, WorldTypeMapContainer > message(post_man);
CellLock<ReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *this, *player, GetVisibilityDistance());
}
+
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 (GUID: %u TypeId: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), 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;
+
//TODO: currently on continents when Visibility.Distance.InFlight > Visibility.Distance.Continents
//we have alot of blinking mobs because monster move packet send is broken...
MaNGOS::ObjectMessageDeliverer post_man(*obj,msg);
@@ -461,60 +545,77 @@ void Map::MessageBroadcast(WorldObject *obj, WorldPacket *msg)
CellLock<ReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *this, *obj, GetVisibilityDistance());
}
+
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;
cell.SetNoCreate();
+
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<MaNGOS::MessageDistDeliverer , WorldTypeMapContainer > message(post_man);
CellLock<ReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *this, *player, dist);
}
+
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 (GUID: %u TypeId: %u) have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), 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<MaNGOS::ObjectMessageDistDeliverer, WorldTypeMapContainer > message(post_man);
CellLock<ReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *this, *obj, dist);
}
*/
+
bool Map::loaded(const GridPair &p) const
{
return ( getNGrid(p.x_coord, p.y_coord) && isGridObjectDataLoaded(p.x_coord, p.y_coord) );
}
+
void Map::RelocationNotify()
{
i_notifyLock = true;
+
//Notify
for(std::vector<Unit*>::iterator iter = i_unitsToNotify.begin(); iter != i_unitsToNotify.end(); ++iter)
{
Unit *unit = *iter;
if(!unit)
continue;
+
unit->m_NotifyListPos = -1;
+
if(unit->m_Notified)
continue;
+
unit->m_Notified = true;
+
float dist = abs(unit->GetPositionX() - unit->oldX) + abs(unit->GetPositionY() - unit->oldY);
if(dist > 10.0f)
{
@@ -522,6 +623,7 @@ void Map::RelocationNotify()
VisitWorld(unit->oldX, unit->oldY, GetVisibilityDistance(), notifier);
dist = 0;
}
+
if(unit->GetTypeId() == TYPEID_PLAYER)
{
Trinity::PlayerRelocationNotifier notifier(*((Player*)unit));
@@ -541,19 +643,23 @@ void Map::RelocationNotify()
if(*iter)
(*iter)->m_Notified = false;
i_unitsToNotify.clear();
+
i_notifyLock = false;
+
if(!i_unitsToNotifyBacklog.empty())
{
i_unitsToNotify.insert(i_unitsToNotify.end(), i_unitsToNotifyBacklog.begin(), i_unitsToNotifyBacklog.end());
i_unitsToNotifyBacklog.clear();
}
}
+
void Map::AddUnitToNotify(Unit* u)
{
if(u->m_NotifyListPos < 0 && u->IsInWorld())
{
u->oldX = u->GetPositionX();
u->oldY = u->GetPositionY();
+
if(i_notifyLock)
{
u->m_NotifyListPos = i_unitsToNotifyBacklog.size();
@@ -566,6 +672,7 @@ void Map::AddUnitToNotify(Unit* u)
}
}
}
+
void Map::RemoveUnitFromNotify(Unit *unit)
{
int32 slot = unit->m_NotifyListPos;
@@ -583,8 +690,10 @@ void Map::RemoveUnitFromNotify(Unit *unit)
assert(slot < i_unitsToNotify.size());
i_unitsToNotify[slot] = NULL;
}
+
unit->m_NotifyListPos = -1;
}
+
void Map::Update(const uint32 &t_diff)
{
/// update players at tick
@@ -594,36 +703,45 @@ void Map::Update(const uint32 &t_diff)
if(plr && plr->IsInWorld())
plr->Update(t_diff);
}
+
m_notifyTimer.Update(t_diff);
if(m_notifyTimer.Passed())
{
m_notifyTimer.Reset();
RelocationNotify();
}
+
/// update active cells around players and active objects
resetMarkedCells();
+
Trinity::ObjectUpdater updater(t_diff);
// for creature
TypeContainerVisitor<Trinity::ObjectUpdater, GridTypeMapContainer > grid_object_update(updater);
// for pets
TypeContainerVisitor<Trinity::ObjectUpdater, WorldTypeMapContainer > world_object_update(updater);
+
// the player iterator is stored in the map object
// to make sure calls to Map::Remove don't invalidate it
for(m_mapRefIter = m_mapRefManager.begin(); m_mapRefIter != m_mapRefManager.end(); ++m_mapRefIter)
{
Player* plr = m_mapRefIter->getSource();
+
if(!plr->IsInWorld())
continue;
+
CellPair standing_cell(Trinity::ComputeCellPair(plr->GetPositionX(), plr->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);
//lets update mobs/objects in ALL visible cells around player!
CellArea area = Cell::CalculateCellArea(*plr, GetVisibilityDistance());
area.ResizeBorders(begin_cell, end_cell);
+
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)
@@ -644,9 +762,11 @@ void Map::Update(const uint32 &t_diff)
}
}
}
+
if(plr->m_seer != plr && !plr->GetVehicle())
AddUnitToNotify(plr);
}
+
// non-player active objects
if(!m_activeNonPlayers.empty())
{
@@ -654,20 +774,26 @@ void Map::Update(const uint32 &t_diff)
{
// skip not in world
WorldObject* obj = *m_activeNonPlayersIter;
+
// step before processing, in this case if Map::Remove remove next object we correctly
// step to next-next, and if we step to end() then newly added objects can wait next update.
++m_activeNonPlayersIter;
+
if(!obj->IsInWorld())
continue;
+
CellPair standing_cell(Trinity::ComputeCellPair(obj->GetPositionX(), obj->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)
@@ -690,6 +816,7 @@ void Map::Update(const uint32 &t_diff)
}
}
}
+
///- Process necessary scripts
if (!m_scriptSchedule.empty())
{
@@ -697,12 +824,15 @@ void Map::Update(const uint32 &t_diff)
ScriptsProcess();
i_scriptLock = false;
}
+
MoveAllCreaturesInMoveList();
}
+
void Map::Remove(Player *player, bool remove)
{
player->RemoveFromWorld();
SendRemoveTransports(player);
+
CellPair p = Trinity::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.outCrash("Map::Remove: Player is in invalid cell!");
@@ -716,13 +846,16 @@ void Map::Remove(Player *player, bool remove)
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);
+
RemoveFromGrid(player,grid,cell);
UpdateObjectVisibility(player,cell,p);
}
}
+
if( remove )
DeleteFromWorld(player);
}
+
bool Map::RemoveBones(uint64 guid, float x, float y)
{
if (IsRemovalGrid(x, y))
@@ -735,6 +868,7 @@ bool Map::RemoveBones(uint64 guid, float x, float y)
}
return true;
}
+
template<class T>
void
Map::Remove(T *obj, bool remove)
@@ -742,6 +876,7 @@ Map::Remove(T *obj, bool remove)
obj->RemoveFromWorld();
if(obj->isActiveObject())
RemoveFromActive(obj);
+
CellPair p = Trinity::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 " I64FMT " have invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUID(), obj->GetPositionX(), obj->GetPositionY(), p.x_coord, p.y_coord);
@@ -753,11 +888,14 @@ Map::Remove(T *obj, bool remove)
DEBUG_LOG("Remove object " I64FMT " 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 );
+
RemoveFromGrid(obj,grid,cell);
UpdateObjectVisibility(obj,cell,p);
}
}
+
obj->ResetMap();
+
if( remove )
{
// if option set then object already saved at this moment
@@ -766,23 +904,30 @@ Map::Remove(T *obj, bool remove)
DeleteFromWorld(obj);
}
}
+
void
Map::PlayerRelocation(Player *player, float x, float y, float z, float orientation)
{
assert(player);
+
CellPair old_val = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
CellPair new_val = Trinity::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) )
@@ -790,7 +935,9 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati
else
EnsureGridLoadedAtEnter(new_cell, player);
}
+
AddUnitToNotify(player);
+
NGridType* newGrid = getNGrid(new_cell.GridX(), new_cell.GridY());
if( !same_cell && newGrid->GetGridState()!= GRID_STATE_ACTIVE )
{
@@ -798,13 +945,17 @@ Map::PlayerRelocation(Player *player, float x, float y, float z, float orientati
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 = Trinity::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) )
{
@@ -820,6 +971,7 @@ Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang
creature->Relocate(x, y, z, ang);
AddUnitToNotify(creature);
}
+
if(creature->IsVehicle())
{
for(SeatMap::iterator itr = creature->GetVehicleKit()->m_Seats.begin(); itr != creature->GetVehicleKit()->m_Seats.end(); ++itr)
@@ -839,14 +991,18 @@ Map::CreatureRelocation(Creature *creature, float x, float y, float z, float ang
ang + passenger->m_movementInfo.t_o);
}
}
+
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())
@@ -856,9 +1012,11 @@ void Map::MoveAllCreaturesInMoveList()
Creature* c = iter->first;
CreatureMover cm = iter->second;
i_creaturesToMove.erase(iter);
+
// calculate cells
CellPair new_val = Trinity::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))
{
@@ -883,6 +1041,7 @@ void Map::MoveAllCreaturesInMoveList()
}
}
}
+
bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
{
Cell const& old_cell = c->GetCurrentCell();
@@ -895,6 +1054,7 @@ bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
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
+
RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
}
@@ -905,20 +1065,26 @@ bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
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
}
+
return true;
}
+
// in diff. grids but active creature
if(c->isActiveObject())
{
EnsureGridLoadedAtEnter(new_cell);
+
#ifdef TRINITY_DEBUG
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
sLog.outDebug("Active creature (GUID: %u Entry: %u) moved from grid[%u,%u]cell[%u,%u] to grid[%u,%u]cell[%u,%u].", c->GetGUIDLow(), c->GetEntry(), old_cell.GridX(), old_cell.GridY(), old_cell.CellX(), old_cell.CellY(), new_cell.GridX(), new_cell.GridY(), new_cell.CellX(), new_cell.CellY());
#endif
+
RemoveFromGrid(c,getNGrid(old_cell.GridX(), old_cell.GridY()),old_cell);
AddToGrid(c,getNGrid(new_cell.GridX(), new_cell.GridY()),new_cell);
+
return true;
}
+
// in diff. loaded grid normal creature
if(loaded(GridPair(new_cell.GridX(), new_cell.GridY())))
{
@@ -926,11 +1092,14 @@ bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
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);
+
return true;
}
+
// fail to move: normal creature attempt move to unloaded grid
#ifdef TRINITY_DEBUG
if((sLog.getLogFilter() & LOG_FILTER_CREATURE_MOVES)==0)
@@ -938,18 +1107,23 @@ bool Map::CreatureCellRelocation(Creature *c, Cell new_cell)
#endif
return false;
}
+
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 = Trinity::ComputeCellPair(resp_x, resp_y);
Cell resp_cell(resp_val);
+
c->CombatStop();
c->GetMotionMaster()->Clear();
+
#ifdef TRINITY_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))
{
@@ -962,35 +1136,48 @@ bool Map::CreatureRespawnRelocation(Creature *c)
else
return false;
}
+
bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool unloadAll)
{
NGridType *grid = getNGrid(x, y);
assert( grid != NULL);
+
{
if(!unloadAll && ActiveObjectsNearGrid(x, y) )
return false;
+
sLog.outDebug("Unloading grid[%u,%u] for map %u", x,y, GetId());
+
ObjectGridUnloader unloader(*grid);
+
if(!unloadAll)
{
// Finish creature moves, remove and delete all creatures with delayed remove before moving to respawn grids
// Must know real mob position before move
MoveAllCreaturesInMoveList();
+
// 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
MoveAllCreaturesInMoveList();
}
+
ObjectGridCleaner cleaner(*grid);
cleaner.CleanN();
+
RemoveAllObjectsInRemoveList();
+
unloader.UnloadN();
+
assert(i_objectsToRemove.empty());
+
delete grid;
setNGrid(NULL, x, y);
}
int gx = (MAX_NUMBER_OF_GRIDS - 1) - x;
int gy = (MAX_NUMBER_OF_GRIDS - 1) - 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
{
@@ -1006,11 +1193,13 @@ bool Map::UnloadGrid(const uint32 &x, const uint32 &y, bool unloadAll)
}
else
((MapInstanced*)m_parentMap)->RemoveGridMapReference(GridPair(gx, gy));
+
GridMaps[gx][gy] = NULL;
}
DEBUG_LOG("Unloading grid[%u,%u] for map %u finished", x,y, GetId());
return true;
}
+
void Map::RemoveAllPlayers()
{
if(HavePlayers())
@@ -1027,10 +1216,12 @@ void Map::RemoveAllPlayers()
}
}
}
+
void Map::UnloadAll()
{
// clear all delayed moves, useless anyway do this moves before map unload.
i_creaturesToMove.clear();
+
for (GridRefManager<NGridType>::iterator i = GridRefManager<NGridType>::begin(); i != GridRefManager<NGridType>::end(); )
{
NGridType &grid(*i->getSource());
@@ -1038,6 +1229,7 @@ void Map::UnloadAll()
UnloadGrid(grid.getX(), grid.getY(), true); // deletes the grid and removes it from the GridRefManager
}
}
+
//*****************************
// Grid function
//*****************************
@@ -1062,14 +1254,17 @@ GridMap::GridMap()
m_liquid_type = NULL;
m_liquid_map = NULL;
}
+
GridMap::~GridMap()
{
unloadData();
}
+
bool GridMap::loadData(char *filename)
{
// Unload old data if exist
unloadData();
+
map_fileheader header;
// Not return error if file not found
FILE *in = fopen(filename, "rb");
@@ -1107,6 +1302,7 @@ bool GridMap::loadData(char *filename)
fclose(in);
return false;
}
+
void GridMap::unloadData()
{
if (m_area_map) delete[] m_area_map;
@@ -1121,6 +1317,7 @@ void GridMap::unloadData()
m_liquid_map = NULL;
m_gridGetHeight = &GridMap::getHeightFromFlat;
}
+
bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size)
{
map_areaHeader header;
@@ -1128,6 +1325,7 @@ bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size)
fread(&header, sizeof(header), 1, in);
if (header.fourcc != uint32(MAP_AREA_MAGIC))
return false;
+
m_gridArea = header.gridArea;
if (!(header.flags & MAP_AREA_NO_AREA))
{
@@ -1136,6 +1334,7 @@ bool GridMap::loadAreaData(FILE *in, uint32 offset, uint32 size)
}
return true;
}
+
bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size)
{
map_heightHeader header;
@@ -1143,6 +1342,7 @@ bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size)
fread(&header, sizeof(header), 1, in);
if (header.fourcc != uint32(MAP_HEIGHT_MAGIC))
return false;
+
m_gridHeight = header.gridHeight;
if (!(header.flags & MAP_HEIGHT_NO_HEIGHT))
{
@@ -1177,6 +1377,7 @@ bool GridMap::loadHeihgtData(FILE *in, uint32 offset, uint32 size)
m_gridGetHeight = &GridMap::getHeightFromFlat;
return true;
}
+
bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size)
{
map_liquidHeader header;
@@ -1184,12 +1385,14 @@ bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size)
fread(&header, sizeof(header), 1, in);
if (header.fourcc != uint32(MAP_LIQUID_MAGIC))
return false;
+
m_liquidType = header.liquidType;
m_liquid_offX = header.offsetX;
m_liquid_offY = header.offsetY;
m_liquid_width = header.width;
m_liquid_height= header.height;
m_liquidLevel = header.liquidLevel;
+
if (!(header.flags & MAP_LIQUID_NO_TYPE))
{
m_liquid_type = new uint8 [16*16];
@@ -1202,32 +1405,39 @@ bool GridMap::loadLiquidData(FILE *in, uint32 offset, uint32 size)
}
return true;
}
+
uint16 GridMap::getArea(float x, float y)
{
if (!m_area_map)
return m_gridArea;
+
x = 16 * (32 - x/SIZE_OF_GRIDS);
y = 16 * (32 - y/SIZE_OF_GRIDS);
int lx = (int)x & 15;
int ly = (int)y & 15;
return m_area_map[lx*16 + ly];
}
+
float GridMap::getHeightFromFlat(float /*x*/, float /*y*/) const
{
return m_gridHeight;
}
+
float GridMap::getHeightFromFloat(float x, float y) const
{
if (!m_V8 || !m_V9)
return m_gridHeight;
+
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
+
int x_int = (int)x;
int y_int = (int)y;
x -= x_int;
y -= y_int;
x_int&=(MAP_RESOLUTION - 1);
y_int&=(MAP_RESOLUTION - 1);
+
// Height stored as: h5 - its v8 grid, h1-h4 - its v9 grid
// +--------------> X
// | h1-------h2 Coordinates is:
@@ -1242,6 +1452,7 @@ float GridMap::getHeightFromFloat(float x, float y) const
// 1 - detect triangle
// 2 - solve linear equation from triangle points
// Calculate coefficients for solve h = a*x + b*y + c
+
float a,b,c;
// Select triangle:
if (x+y < 1)
@@ -1293,18 +1504,22 @@ float GridMap::getHeightFromFloat(float x, float y) const
// Calculate height
return a * x + b * y + c;
}
+
float GridMap::getHeightFromUint8(float x, float y) const
{
if (!m_uint8_V8 || !m_uint8_V9)
return m_gridHeight;
+
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
+
int x_int = (int)x;
int y_int = (int)y;
x -= x_int;
y -= y_int;
x_int&=(MAP_RESOLUTION - 1);
y_int&=(MAP_RESOLUTION - 1);
+
int32 a, b, c;
uint8 *V9_h1_ptr = &m_uint8_V9[x_int*128 + x_int + y_int];
if (x+y < 1)
@@ -1356,18 +1571,22 @@ float GridMap::getHeightFromUint8(float x, float y) const
// Calculate height
return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight;
}
+
float GridMap::getHeightFromUint16(float x, float y) const
{
if (!m_uint16_V8 || !m_uint16_V9)
return m_gridHeight;
+
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
+
int x_int = (int)x;
int y_int = (int)y;
x -= x_int;
y -= y_int;
x_int&=(MAP_RESOLUTION - 1);
y_int&=(MAP_RESOLUTION - 1);
+
int32 a, b, c;
uint16 *V9_h1_ptr = &m_uint16_V9[x_int*128 + x_int + y_int];
if (x+y < 1)
@@ -1419,48 +1638,61 @@ float GridMap::getHeightFromUint16(float x, float y) const
// Calculate height
return (float)((a * x) + (b * y) + c)*m_gridIntHeightMultiplier + m_gridHeight;
}
+
float GridMap::getLiquidLevel(float x, float y)
{
if (!m_liquid_map)
return m_liquidLevel;
+
x = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
y = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
+
int cx_int = ((int)x & (MAP_RESOLUTION-1)) - m_liquid_offY;
int cy_int = ((int)y & (MAP_RESOLUTION-1)) - m_liquid_offX;
+
if (cx_int < 0 || cx_int >=m_liquid_height)
return INVALID_HEIGHT;
if (cy_int < 0 || cy_int >=m_liquid_width )
return INVALID_HEIGHT;
+
return m_liquid_map[cx_int*m_liquid_width + cy_int];
}
+
uint8 GridMap::getTerrainType(float x, float y)
{
if (!m_liquid_type)
return m_liquidType;
+
x = 16 * (32 - x/SIZE_OF_GRIDS);
y = 16 * (32 - y/SIZE_OF_GRIDS);
int lx = (int)x & 15;
int ly = (int)y & 15;
return m_liquid_type[lx*16 + ly];
}
+
// Get water state on map
inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data)
{
// Check water type (if no water return)
if (!m_liquid_type && !m_liquidType)
return LIQUID_MAP_NO_WATER;
+
// Get cell
float cx = MAP_RESOLUTION * (32 - x/SIZE_OF_GRIDS);
float cy = MAP_RESOLUTION * (32 - y/SIZE_OF_GRIDS);
+
int x_int = (int)cx & (MAP_RESOLUTION-1);
int y_int = (int)cy & (MAP_RESOLUTION-1);
+
// Check water type in cell
uint8 type = m_liquid_type ? m_liquid_type[(x_int>>3)*16 + (y_int>>3)] : m_liquidType;
if (type == 0)
return LIQUID_MAP_NO_WATER;
+
// Check req liquid type mask
if (ReqLiquidType && !(ReqLiquidType&type))
return LIQUID_MAP_NO_WATER;
+
// Check water level:
// Check water height map
int lx_int = x_int - m_liquid_offY;
@@ -1469,13 +1701,16 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R
return LIQUID_MAP_NO_WATER;
if (ly_int < 0 || ly_int >=m_liquid_width )
return LIQUID_MAP_NO_WATER;
+
// Get water level
float liquid_level = m_liquid_map ? m_liquid_map[lx_int*m_liquid_width + ly_int] : m_liquidLevel;
// Get ground level (sub 0.2 for fix some errors)
float ground_level = getHeight(x, y);
+
// Check water level and ground level
if (liquid_level < ground_level || z < ground_level - 2)
return LIQUID_MAP_NO_WATER;
+
// All ok in water -> store data
if (data)
{
@@ -1483,8 +1718,10 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R
data->level = liquid_level;
data->depth_level = ground_level;
}
+
// For speed check as int values
int delta = int((liquid_level - z) * 10);
+
// Get position delta
if (delta > 20) // Under water
return LIQUID_MAP_UNDER_WATER;
@@ -1495,15 +1732,19 @@ inline ZLiquidStatus GridMap::getLiquidStatus(float x, float y, float z, uint8 R
// Above water
return LIQUID_MAP_ABOVE_WATER;
}
+
inline GridMap *Map::GetGrid(float x, float y)
{
// half opt method
int gx=(int)(32-x/SIZE_OF_GRIDS); //grid x
int gy=(int)(32-y/SIZE_OF_GRIDS); //grid y
+
// ensure GridMap is loaded
EnsureGridCreated(GridPair(63-gx,63-gy));
+
return GridMaps[gx][gy];
}
+
float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
{
// find raw .map surface under Z coordinates
@@ -1511,6 +1752,7 @@ float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
{
float _mapheight = gmap->getHeight(x,y);
+
// look from a bit higher pos to find the floor, ignore under surface case
if(z + 2.0f > _mapheight)
mapHeight = _mapheight;
@@ -1519,6 +1761,7 @@ float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
}
else
mapHeight = VMAP_INVALID_HEIGHT_VALUE;
+
float vmapHeight;
if(pUseVmaps)
{
@@ -1533,19 +1776,23 @@ float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
}
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)
@@ -1560,6 +1807,7 @@ float Map::GetHeight(float x, float y, float z, bool pUseVmaps) const
return VMAP_INVALID_HEIGHT_VALUE; // we not have any height
}
}
+
float Map::GetVmapHeight(float x, float y, float z, bool useMaps) const
{
float mapHeight;
@@ -1579,6 +1827,7 @@ float Map::GetVmapHeight(float x, float y, float z, bool useMaps) const
return INVALID_HEIGHT;
return vmapHeight;
}
+
uint16 Map::GetAreaFlag(float x, float y, float z) const
{
uint16 areaflag;
@@ -1587,6 +1836,7 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const
// this used while not all *.map files generated (instances)
else
areaflag = GetAreaFlagByMapId(GetId());
+
//FIXME: some hacks for areas above or underground for ground area
// required for area specific spells/etc, until map/vmap data
// not provided correct areaflag with this hacks
@@ -1615,12 +1865,14 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const
break;
}
}
+
// The Violet Hold (Dalaran), fast check
if (x < 5791.0f && y > 404.0f && y < 595.0f)
{
areaflag = 2540;
break;
}
+
// Dalaran
areaflag = 2153;
}
@@ -1641,18 +1893,21 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const
break;
}
}
+
// The Eventide (Dalaran), fast check against diagonal box with lower limit
if (z > 635.0f && x+y < 6375.0f && x+y > 6295.0f && x-y < 5106.0f && x-y > 4972.0f)
{
areaflag = 2543;
break;
}
+
// The Violet Hold (Dalaran), fast check
if (x < 5791.0f && y > 404.0f && y < 595.0f)
{
areaflag = 2540;
break;
}
+
// Dalaran
areaflag = 2153;
}
@@ -1671,6 +1926,7 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const
areaflag = 2748;
break;
}
+
// The Violet Citadel (Dalaran)
if ((x-5803.0f)*(x-5803.0f)+(y-846.18f)*(y-846.18f) < 6690.0f)
{
@@ -1678,6 +1934,7 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const
break;
}
}
+
// Dalaran
areaflag = 2153;
}
@@ -1742,8 +1999,10 @@ uint16 Map::GetAreaFlag(float x, float y, float z) const
}
break;
}
+
return areaflag;
}
+
uint8 Map::GetTerrainType(float x, float y ) const
{
if(GridMap *gmap = const_cast<Map*>(this)->GetGrid(x, y))
@@ -1751,6 +2010,7 @@ uint8 Map::GetTerrainType(float x, float y ) const
else
return 0;
}
+
ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data) const
{
if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
@@ -1758,6 +2018,7 @@ ZLiquidStatus Map::getLiquidStatus(float x, float y, float z, uint8 ReqLiquidTyp
else
return LIQUID_MAP_NO_WATER;
}
+
float Map::GetWaterLevel(float x, float y ) const
{
if(GridMap* gmap = const_cast<Map*>(this)->GetGrid(x, y))
@@ -1765,28 +2026,35 @@ float Map::GetWaterLevel(float x, float y ) const
else
return 0;
}
+
uint32 Map::GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id)
{
AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
+
if (entry)
return entry->ID;
else
return 0;
}
+
uint32 Map::GetZoneIdByAreaFlag(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;
}
+
void Map::GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag,uint32 map_id)
{
AreaTableEntry const *entry = GetAreaEntryByAreaFlagAndMap(areaflag,map_id);
+
areaid = entry ? entry->ID : 0;
zoneid = entry ? (( entry->zone != 0 ) ? entry->zone : entry->ID) : 0;
}
+
bool Map::IsInWater(float x, float y, float pZ, float min_depth) const
{
// Check surface in x, y point for liquid
@@ -1801,6 +2069,7 @@ bool Map::IsInWater(float x, float y, float pZ, float min_depth) const
}
return false;
}
+
bool Map::IsUnderWater(float x, float y, float z) const
{
if (const_cast<Map*>(this)->GetGrid(x, y))
@@ -1810,9 +2079,11 @@ bool Map::IsUnderWater(float x, float y, float z) const
}
return false;
}
+
bool Map::CheckGridIntegrity(Creature* c, bool moved) const
{
Cell const& cur_cell = c->GetCurrentCell();
+
CellPair xy_val = Trinity::ComputeCellPair(c->GetPositionX(), c->GetPositionY());
Cell xy_cell(xy_val);
if(xy_cell != cur_cell)
@@ -1824,12 +2095,15 @@ bool Map::CheckGridIntegrity(Creature* c, bool moved) const
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;
@@ -1839,18 +2113,23 @@ void Map::UpdateObjectVisibility( WorldObject* obj, Cell cell, CellPair cellpair
CellLock<GridReadGuard> cell_lock(cell, cellpair);
cell_lock->Visit(cell_lock, player_notifier, *this, *obj, GetVisibilityDistance());
}
+
/*
void Map::UpdatePlayerVisibility( Player* player, Cell cell, CellPair cellpair )
{
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::PlayerNotifier pl_notifier(*player);
TypeContainerVisitor<MaNGOS::PlayerNotifier, WorldTypeMapContainer > player_notifier(pl_notifier);
+
CellLock<ReadGuard> cell_lock(cell, cellpair);
cell_lock->Visit(cell_lock, player_notifier, *this, *player, GetVisibilityDistance());
}
+
void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpair )
{
MaNGOS::VisibleNotifier notifier(*player);
+
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
TypeContainerVisitor<MaNGOS::VisibleNotifier, WorldTypeMapContainer > world_notifier(notifier);
@@ -1858,42 +2137,54 @@ void Map::UpdateObjectsVisibilityFor( Player* player, Cell cell, CellPair cellpa
CellLock<GridReadGuard> cell_lock(cell, cellpair);
cell_lock->Visit(cell_lock, world_notifier, *this, *player, GetVisibilityDistance());
cell_lock->Visit(cell_lock, grid_notifier, *this, *player, GetVisibilityDistance());
+
// send data
notifier.Notify();
}
+
void Map::PlayerRelocationNotify( Player* player, Cell cell, CellPair cellpair )
{
CellLock<ReadGuard> cell_lock(cell, cellpair);
MaNGOS::PlayerRelocationNotifier relocationNotifier(*player);
cell.data.Part.reserved = ALL_DISTRICT;
+
TypeContainerVisitor<MaNGOS::PlayerRelocationNotifier, GridTypeMapContainer > p2grid_relocation(relocationNotifier);
TypeContainerVisitor<MaNGOS::PlayerRelocationNotifier, WorldTypeMapContainer > p2world_relocation(relocationNotifier);
+
cell_lock->Visit(cell_lock, p2grid_relocation, *this, *player, MAX_CREATURE_ATTACK_RADIUS);
cell_lock->Visit(cell_lock, p2world_relocation, *this, *player, MAX_CREATURE_ATTACK_RADIUS);
}
+
void Map::CreatureRelocationNotify(Creature *creature, Cell cell, CellPair cellpair)
{
CellLock<ReadGuard> 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<MaNGOS::CreatureRelocationNotifier, WorldTypeMapContainer > c2world_relocation(relocationNotifier);
TypeContainerVisitor<MaNGOS::CreatureRelocationNotifier, GridTypeMapContainer > c2grid_relocation(relocationNotifier);
+
cell_lock->Visit(cell_lock, c2world_relocation, *this, *creature, MAX_CREATURE_ATTACK_RADIUS);
cell_lock->Visit(cell_lock, c2grid_relocation, *this, *creature, MAX_CREATURE_ATTACK_RADIUS);
}
*/
+
void Map::SendInitSelf( Player * player )
{
sLog.outDetail("Creating player data for himself %u", player->GetGUIDLow());
+
UpdateData data;
+
// attach to player data current transport data
if(Transport* transport = player->GetTransport())
{
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())
{
@@ -1905,19 +2196,25 @@ void Map::SendInitSelf( Player * player )
}
}
}
+
WorldPacket packet;
data.BuildPacket(&packet);
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()];
+
for (MapManager::TransportSet::const_iterator i = tset.begin(); i != tset.end(); ++i)
{
// send data for current transport in other place
@@ -1926,27 +2223,35 @@ void Map::SendInitTransports( Player * player )
(*i)->BuildCreateUpdateBlockForPlayer(&transData, player);
}
}
+
WorldPacket packet;
transData.BuildPacket(&packet);
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::const_iterator i = tset.begin(); i != tset.end(); ++i)
if((*i) != player->GetTransport() && (*i)->GetMapId()!=GetId())
(*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)
@@ -1956,9 +2261,11 @@ inline void Map::setNGrid(NGridType *grid, uint32 x, uint32 y)
}
i_grids[x][y] = grid;
}
+
void Map::DelayedUpdate(const uint32 t_diff)
{
RemoveAllObjectsInRemoveList();
+
// 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())
@@ -1973,16 +2280,21 @@ void Map::DelayedUpdate(const uint32 t_diff)
}
}
}
+
void Map::AddObjectToRemoveList(WorldObject *obj)
{
assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId());
+
obj->CleanupsBeforeDelete(); // remove or simplify at least cross referenced links
+
i_objectsToRemove.insert(obj);
//sLog.outDebug("Object (GUID: %u TypeId: %u ) added to removing list.",obj->GetGUIDLow(),obj->GetTypeId());
}
+
void Map::AddObjectToSwitchList(WorldObject *obj, bool on)
{
assert(obj->GetMapId()==GetId() && obj->GetInstanceId()==GetInstanceId());
+
std::map<WorldObject*, bool>::iterator itr = i_objectsToSwitch.find(obj);
if(itr == i_objectsToSwitch.end())
i_objectsToSwitch.insert(itr, std::make_pair(obj, on));
@@ -1991,6 +2303,7 @@ void Map::AddObjectToSwitchList(WorldObject *obj, bool on)
else
assert(false);
}
+
void Map::RemoveAllObjectsInRemoveList()
{
while(!i_objectsToSwitch.empty())
@@ -1999,6 +2312,7 @@ void Map::RemoveAllObjectsInRemoveList()
WorldObject *obj = itr->first;
bool on = itr->second;
i_objectsToSwitch.erase(itr);
+
switch(obj->GetTypeId())
{
case TYPEID_UNIT:
@@ -2007,11 +2321,13 @@ void Map::RemoveAllObjectsInRemoveList()
break;
}
}
+
//sLog.outDebug("Object remover 1 check.");
while(!i_objectsToRemove.empty())
{
std::set<WorldObject*>::iterator itr = i_objectsToRemove.begin();
WorldObject* obj = *itr;
+
switch(obj->GetTypeId())
{
case TYPEID_CORPSE:
@@ -2039,10 +2355,12 @@ void Map::RemoveAllObjectsInRemoveList()
sLog.outError("Non-grid object (TypeId: %u) in grid object removing list, ignored.",obj->GetTypeId());
break;
}
+
i_objectsToRemove.erase(itr);
}
//sLog.outDebug("Object remover 2 check.");
}
+
uint32 Map::GetPlayersCountExceptGMs() const
{
uint32 count = 0;
@@ -2051,45 +2369,57 @@ uint32 Map::GetPlayersCountExceptGMs() const
++count;
return count;
}
+
void Map::SendToPlayers(WorldPacket const* data) const
{
for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
itr->getSource()->GetSession()->SendPacket(data);
}
+
bool Map::ActiveObjectsNearGrid(uint32 x, uint32 y) const
{
ASSERT(x < MAX_NUMBER_OF_GRIDS);
ASSERT(y < MAX_NUMBER_OF_GRIDS);
+
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);
+
//we must find visible range in cells so we unload only non-visible cells...
float viewDist = GetVisibilityDistance();
int cell_range = (int)ceilf(viewDist / SIZE_OF_GRID_CELL) + 1;
+
cell_min << cell_range;
cell_min -= cell_range;
cell_max >> cell_range;
cell_max += cell_range;
+
for(MapRefManager::const_iterator iter = m_mapRefManager.begin(); iter != m_mapRefManager.end(); ++iter)
{
Player* plr = iter->getSource();
+
CellPair p = Trinity::ComputeCellPair(plr->GetPositionX(), plr->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;
}
+
for(ActiveNonPlayers::const_iterator iter = m_activeNonPlayers.begin(); iter != m_activeNonPlayers.end(); ++iter)
{
WorldObject* obj = *iter;
+
CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->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 Map::AddToActive( Creature* c )
{
AddToActiveHelper(c);
+
// also not allow unloading spawn grid to prevent creating creature clone at load
if(!c->isPet() && c->GetDBTableGUIDLow())
{
@@ -2106,9 +2436,11 @@ void Map::AddToActive( Creature* c )
}
}
}
+
void Map::RemoveFromActive( Creature* c )
{
RemoveFromActiveHelper(c);
+
// also allow unloading spawn grid
if(!c->isPet() && c->GetDBTableGUIDLow())
{
@@ -2125,15 +2457,19 @@ void Map::RemoveFromActive( Creature* c )
}
}
}
+
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* _parent)
: Map(id, expiry, InstanceId, SpawnMode, _parent),
m_resetAfterUnload(false), m_unloadWhenEmpty(false),
@@ -2141,10 +2477,12 @@ InstanceMap::InstanceMap(uint32 id, time_t expiry, uint32 InstanceId, uint8 Spaw
{
//lets initialize visibility distance for dungeons
InstanceMap::InitVisibilityDistance();
+
// 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)
@@ -2153,11 +2491,13 @@ InstanceMap::~InstanceMap()
i_data = NULL;
}
}
+
void InstanceMap::InitVisibilityDistance()
{
//init visibility distance for instances
m_VisibleDistance = sWorld.GetMaxVisibleDistanceInInstances();
}
+
/*
Do map specific checks to see if the player can enter
*/
@@ -2169,6 +2509,7 @@ bool InstanceMap::CanEnter(Player *player)
assert(false);
return false;
}
+
// cannot enter if the instance is full (player cap), GMs don't count
uint32 maxPlayers = GetMaxPlayers();
if (!player->isGameMaster() && GetPlayersCountExceptGMs() >= maxPlayers)
@@ -2177,6 +2518,7 @@ bool InstanceMap::CanEnter(Player *player)
player->SendTransferAborted(GetId(), TRANSFER_ABORT_MAX_PLAYERS);
return false;
}
+
// cannot enter while players in the instance are in combat
Group *pGroup = player->GetGroup();
if(!player->isGameMaster() && pGroup && pGroup->InCombatToInstance(GetInstanceId()) && player->GetMapId() != GetId())
@@ -2184,8 +2526,10 @@ bool InstanceMap::CanEnter(Player *player)
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.
*/
@@ -2194,11 +2538,13 @@ 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);
// Check moved to void WorldSession::HandleMoveWorldportAckOpcode()
//if(!CanEnter(player))
//return false;
+
// Dungeon only code
if(IsDungeon())
{
@@ -2209,6 +2555,7 @@ bool InstanceMap::Add(Player *player)
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)
@@ -2275,26 +2622,32 @@ bool InstanceMap::Add(Player *player)
}
}
}
+
if(i_data) i_data->OnPlayerEnter(player);
// for normal instances cancel the reset schedule when the
// first player enters (no players yet)
SetResetSchedule(false);
+
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::Update(const uint32& t_diff)
{
Map::Update(t_diff);
+
if(i_data)
i_data->Update(t_diff);
}
+
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());
@@ -2305,19 +2658,24 @@ void InstanceMap::Remove(Player *player, bool remove)
// for normal instances schedule the reset after all players have left
SetResetSchedule(true);
}
+
void InstanceMap::CreateInstanceData(bool load)
{
if(i_data != NULL)
return;
+
InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(GetId());
if (mInstance)
{
i_script_id = mInstance->script_id;
i_data = Script->CreateInstanceData(this);
}
+
if(!i_data)
return;
+
i_data->Initialize();
+
if(load)
{
// TODO: make a global storage for this
@@ -2335,6 +2693,7 @@ void InstanceMap::CreateInstanceData(bool load)
}
}
}
+
/*
Returns true if there are no players in the instance
*/
@@ -2342,6 +2701,7 @@ 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(HavePlayers())
{
if(method == INSTANCE_RESET_ALL)
@@ -2358,6 +2718,7 @@ bool InstanceMap::Reset(uint8 method)
for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
itr->getSource()->m_InstanceValid = false;
}
+
// the unload timer is not started
// instead the map will unload immediately after the players have left
m_unloadWhenEmpty = true;
@@ -2370,18 +2731,22 @@ bool InstanceMap::Reset(uint8 method)
m_unloadTimer = MIN_UNLOAD_DELAY;
m_resetAfterUnload = true;
}
+
return m_mapRefManager.isEmpty();
}
+
void InstanceMap::PermBindAllPlayers(Player *player)
{
if(!IsDungeon())
return;
+
InstanceSave *save = sInstanceSaveManager.GetInstanceSave(GetInstanceId());
if(!save)
{
sLog.outError("Cannot bind players, no instance save available for map!");
return;
}
+
Group *group = player->GetGroup();
// group members outside the instance group don't get bound
for(MapRefManager::iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
@@ -2397,23 +2762,29 @@ void InstanceMap::PermBindAllPlayers(Player *player)
data << uint32(0);
plr->GetSession()->SendPacket(&data);
}
+
// if the leader is not in the instance the group will not get a perm bind
if(group && group->GetLeaderGUID() == plr->GetGUID())
group->BindToInstance(save, true);
}
}
+
void InstanceMap::UnloadAll()
{
assert(!HavePlayers());
+
if(m_resetAfterUnload == true)
objmgr.DeleteRespawnTimeForInstance(GetInstanceId());
+
Map::UnloadAll();
}
+
void InstanceMap::SendResetWarnings(uint32 timeLeft) const
{
for(MapRefManager::const_iterator itr = m_mapRefManager.begin(); itr != m_mapRefManager.end(); ++itr)
itr->getSource()->SendInstanceResetWarning(GetId(), itr->getSource()->GetDifficulty(), timeLeft);
}
+
void InstanceMap::SetResetSchedule(bool on)
{
// only for normal instances
@@ -2426,6 +2797,7 @@ void InstanceMap::SetResetSchedule(bool on)
else sInstanceSaveManager.ScheduleReset(on, save->GetResetTime(), InstanceSaveManager::InstResetEvent(0, GetId(), GetInstanceId()));
}
}
+
uint32 InstanceMap::GetMaxPlayers() const
{
InstanceTemplate const* iTemplate = objmgr.GetInstanceTemplate(GetId());
@@ -2433,21 +2805,26 @@ uint32 InstanceMap::GetMaxPlayers() const
return 0;
return IsHeroic() ? iTemplate->maxPlayersHeroic : iTemplate->maxPlayers;
}
+
/* ******* Battleground Instance Maps ******* */
+
BattleGroundMap::BattleGroundMap(uint32 id, time_t expiry, uint32 InstanceId, Map* _parent)
: Map(id, expiry, InstanceId, DIFFICULTY_NORMAL, _parent)
{
//lets initialize visibility distance for BG/Arenas
BattleGroundMap::InitVisibilityDistance();
}
+
BattleGroundMap::~BattleGroundMap()
{
}
+
void BattleGroundMap::InitVisibilityDistance()
{
//init visibility distance for BG/Arenas
m_VisibleDistance = sWorld.GetMaxVisibleDistanceInBGArenas();
}
+
bool BattleGroundMap::CanEnter(Player * player)
{
if(player->GetMapRef().getTarget() == this)
@@ -2456,11 +2833,15 @@ bool BattleGroundMap::CanEnter(Player * player)
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)
{
{
@@ -2473,15 +2854,18 @@ bool BattleGroundMap::Add(Player * player)
}
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());
Map::Remove(player, remove);
}
+
void BattleGroundMap::SetUnload()
{
m_unloadTimer = MIN_UNLOAD_DELAY;
}
+
void BattleGroundMap::RemoveAllPlayers()
{
if(HavePlayers())
@@ -2494,6 +2878,7 @@ void BattleGroundMap::RemoveAllPlayers()
}
}
}
+
/// Put scripts in the execution queue
void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, Object* target)
{
@@ -2501,10 +2886,12 @@ void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, O
ScriptMapMap::const_iterator s = scripts.find(id);
if (s == scripts.end())
return;
+
// prepare static data
uint64 sourceGUID = source ? source->GetGUID() : (uint64)0; //some script commands doesn't have source
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;
@@ -2514,10 +2901,12 @@ void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, O
sa.sourceGUID = sourceGUID;
sa.targetGUID = targetGUID;
sa.ownerGUID = ownerGUID;
+
sa.script = &iter->second;
m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(time_t(sWorld.GetGameTime() + iter->first), sa));
if (iter->first == 0)
immedScript = true;
+
sWorld.IncreaseScheduledScriptsCount();
}
///- If one of the effects should be immediate, launch the script execution
@@ -2528,20 +2917,26 @@ void Map::ScriptsStart(ScriptMapMap const& scripts, uint32 id, Object* source, O
i_scriptLock = false;
}
}
+
void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target)
{
// NOTE: script record _must_ exist until command executed
+
// prepare static data
uint64 sourceGUID = source ? source->GetGUID() : (uint64)0;
uint64 targetGUID = target ? target->GetGUID() : (uint64)0;
uint64 ownerGUID = (source->GetTypeId()==TYPEID_ITEM) ? ((Item*)source)->GetOwnerGUID() : (uint64)0;
+
ScriptAction sa;
sa.sourceGUID = sourceGUID;
sa.targetGUID = targetGUID;
sa.ownerGUID = ownerGUID;
+
sa.script = &script;
m_scriptSchedule.insert(std::pair<time_t, ScriptAction>(time_t(sWorld.GetGameTime() + delay), sa));
+
sWorld.IncreaseScheduledScriptsCount();
+
///- If effects should be immediate, launch the script execution
if(delay == 0 && !i_scriptLock)
{
@@ -2550,18 +2945,22 @@ void Map::ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* sou
i_scriptLock = false;
}
}
+
/// Process queued scripts
void Map::ScriptsProcess()
{
if (m_scriptSchedule.empty())
return;
+
///- Process overdue queued scripts
std::multimap<time_t, ScriptAction>::iterator iter = m_scriptSchedule.begin();
// ok as multimap is a *sorted* associative container
while (!m_scriptSchedule.empty() && (iter->first <= sWorld.GetGameTime()))
{
ScriptAction const& step = iter->second;
+
Object* source = NULL;
+
if(step.sourceGUID)
{
switch(GUID_HIPART(step.sourceGUID))
@@ -2607,8 +3006,11 @@ void Map::ScriptsProcess()
break;
}
}
+
//if(source && !source->IsInWorld()) source = NULL;
+
Object* target = NULL;
+
if(step.targetGUID)
{
switch(GUID_HIPART(step.targetGUID))
@@ -2636,7 +3038,9 @@ void Map::ScriptsProcess()
break;
}
}
+
//if(target && !target->IsInWorld()) target = NULL;
+
switch (step.script->command)
{
case SCRIPT_COMMAND_TALK:
@@ -2646,6 +3050,7 @@ void Map::ScriptsProcess()
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, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
@@ -2656,7 +3061,9 @@ void Map::ScriptsProcess()
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, 4=boss emote text
switch(step.script->datalong)
{
@@ -2685,17 +3092,20 @@ void Map::ScriptsProcess()
}
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, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
((Creature *)source)->HandleEmoteCommand(step.script->datalong);
break;
case SCRIPT_COMMAND_FIELD_SET:
@@ -2710,6 +3120,7 @@ void Map::ScriptsProcess()
step.script->datalong,source->GetValuesCount(),source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
source->SetUInt32Value(step.script->datalong, step.script->datalong2);
break;
case SCRIPT_COMMAND_MOVE_TO:
@@ -2718,6 +3129,7 @@ void Map::ScriptsProcess()
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, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
@@ -2738,6 +3150,7 @@ void Map::ScriptsProcess()
step.script->datalong,source->GetValuesCount(),source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
source->SetFlag(step.script->datalong, step.script->datalong2);
break;
case SCRIPT_COMMAND_FLAG_REMOVE:
@@ -2752,8 +3165,10 @@ void Map::ScriptsProcess()
step.script->datalong,source->GetValuesCount(),source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
source->RemoveFlag(step.script->datalong, step.script->datalong2);
break;
+
case SCRIPT_COMMAND_TELEPORT_TO:
{
// accept player in any one from target/source arg
@@ -2762,16 +3177,20 @@ void Map::ScriptsProcess()
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
@@ -2779,29 +3198,36 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL creature.");
break;
}
+
if(!source)
{
sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for NULL world object.");
break;
}
+
WorldObject* summoner = dynamic_cast<WorldObject*>(source);
+
if(!summoner)
{
sLog.outError("SCRIPT_COMMAND_TEMP_SUMMON_CREATURE call for non-WorldObject (TypeId: %u, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
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
@@ -2809,32 +3235,41 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL gameobject.");
break;
}
+
if(!source)
{
sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for NULL world object.");
break;
}
+
WorldObject* summoner = dynamic_cast<WorldObject*>(source);
+
if(!summoner)
{
sLog.outError("SCRIPT_COMMAND_RESPAWN_GAMEOBJECT call for non-WorldObject (TypeId: %u, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
GameObject *go = NULL;
int32 time_to_despawn = step.script->datalong2<5 ? 5 : (int32)step.script->datalong2;
+
CellPair p(MaNGOS::ComputeCellPair(summoner->GetPositionX(), summoner->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::GameObjectWithDbGUIDCheck go_check(*summoner,step.script->datalong);
MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(summoner, go,go_check);
+
TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *summoner->GetMap());
+
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_DOOR ||
go->GetGoType()==GAMEOBJECT_TYPE_BUTTON ||
@@ -2843,10 +3278,13 @@ void Map::ScriptsProcess()
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
+
go->GetMap()->Add(go);
break;
}
@@ -2857,27 +3295,35 @@ void Map::ScriptsProcess()
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, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
Unit* caster = (Unit*)source;
+
GameObject *door = NULL;
int32 time_to_close = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2;
+
CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong);
MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check);
+
TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *caster->GetMap());
+
if (!door)
{
sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for gameobject(guid: %u).", step.script->datalong);
@@ -2888,9 +3334,12 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_OPEN_DOOR failed for non-door(GoType: %u, Entry: %u, GUID: %u).", door->GetGoType(),door->GetEntry(),door->GetGUIDLow());
break;
}
+
if (door->GetGoState() != GO_STATE_READY)
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;
@@ -2902,27 +3351,35 @@ void Map::ScriptsProcess()
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, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
Unit* caster = (Unit*)source;
+
GameObject *door = NULL;
int32 time_to_open = step.script->datalong2 < 15 ? 15 : (int32)step.script->datalong2;
+
CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(), caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::GameObjectWithDbGUIDCheck go_check(*caster,step.script->datalong);
MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck> checker(caster,door,go_check);
+
TypeContainerVisitor<MaNGOS::GameObjectSearcher<MaNGOS::GameObjectWithDbGUIDCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *caster->GetMap());
+
if ( !door )
{
sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for gameobject(guid: %u).", step.script->datalong);
@@ -2933,11 +3390,15 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_CLOSE_DOOR failed for non-door(GoType: %u, Entry: %u, GUID: %u).", door->GetGoType(),door->GetEntry(),door->GetGUIDLow());
break;
}
+
if( door->GetGoState() == GO_STATE_READY )
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:
@@ -2947,14 +3408,17 @@ void Map::ScriptsProcess()
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)
@@ -2962,6 +3426,7 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
worldObject = (WorldObject*)source;
player = (Player*)target;
}
@@ -2972,22 +3437,27 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-creature and non-gameobject (TypeId: %u, Entry: %u, GUID: %u), skipping.",target->GetTypeId(),target->GetEntry(),target->GetGUIDLow());
break;
}
+
if(source->GetTypeId()!=TYPEID_PLAYER)
{
sLog.outError("SCRIPT_COMMAND_QUEST_EXPLORED call for non-player(TypeId: %u, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
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)
@@ -2995,42 +3465,53 @@ void Map::ScriptsProcess()
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, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
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, Entry: %u, GUID: %u), skipping.",target->GetTypeId(),target->GetEntry(),target->GetGUIDLow());
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, Entry: %u, GUID: %u), skipping.",step.script->datalong2 ? "source" : "target",cmdTarget->GetTypeId(),cmdTarget->GetEntry(),cmdTarget->GetGUIDLow());
break;
}
+
((Unit*)cmdTarget)->RemoveAurasDueToSpell(step.script->datalong);
break;
}
+
case SCRIPT_COMMAND_CAST_SPELL:
{
if(!source)
@@ -3038,29 +3519,39 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_CAST_SPELL must have source caster.");
break;
}
+
Object* cmdTarget = step.script->datalong2 & 0x01 ? source : target;
+
if(cmdTarget && !cmdTarget->isType(TYPEMASK_UNIT))
{
sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u, Entry: %u. GUID: %u), skipping.",step.script->datalong2 & 0x01 ? "source" : "target",cmdTarget->GetTypeId(),cmdTarget->GetEntry(),cmdTarget->GetGUIDLow());
break;
}
+
Unit* spellTarget = (Unit*)cmdTarget;
+
Object* cmdSource = step.script->datalong2 & 0x02 ? target : source;
+
if(!cmdSource)
{
sLog.outError("SCRIPT_COMMAND_CAST_SPELL %u call for NULL %s.", step.script->datalong, step.script->datalong2 & 0x02 ? "target" : "source");
break;
}
+
if(!cmdSource->isType(TYPEMASK_UNIT))
{
sLog.outError("SCRIPT_COMMAND_CAST_SPELL %s isn't unit (TypeId: %u, Entry: %u, GUID: %u), skipping.",step.script->datalong2 & 0x02 ? "target" : "source", cmdSource->GetTypeId(),cmdSource->GetEntry(),cmdSource->GetGUIDLow());
break;
}
+
Unit* spellSource = (Unit*)cmdSource;
+
//TODO: when GO cast implemented, code below must be updated accordingly to also allow GO spell cast
spellSource->CastSpell(spellTarget,step.script->datalong,false);
+
break;
}
+
case SCRIPT_COMMAND_LOAD_PATH:
{
if(!source)
@@ -3068,19 +3559,23 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_START_MOVE is tried to apply to NON-existing unit.");
break;
}
+
if(!source->isType(TYPEMASK_UNIT))
{
sLog.outError("SCRIPT_COMMAND_START_MOVE source mover isn't unit (TypeId: %u, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
if(!WaypointMgr.GetPath(step.script->datalong))
{
sLog.outError("SCRIPT_COMMAND_START_MOVE source mover has an invallid path, skipping.", step.script->datalong2);
break;
}
+
dynamic_cast<Unit*>(source)->GetMotionMaster()->MovePath(step.script->datalong, step.script->datalong2);
break;
}
+
case SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT:
{
if(!step.script->datalong || !step.script->datalong2)
@@ -3090,14 +3585,17 @@ void Map::ScriptsProcess()
}
//our target
Creature* target = NULL;
+
if(source) //using grid searcher
{
CellPair p(Trinity::ComputeCellPair(((Unit*)source)->GetPositionX(), ((Unit*)source)->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
//sLog.outDebug("Attempting to find Creature: Db GUID: %i", step.script->datalong);
Trinity::CreatureWithDbGUIDCheck target_check(((Unit*)source), step.script->datalong);
Trinity::CreatureSearcher<Trinity::CreatureWithDbGUIDCheck> checker(((Unit*)source), target, target_check);
+
TypeContainerVisitor<Trinity::CreatureSearcher <Trinity::CreatureWithDbGUIDCheck>, GridTypeMapContainer > unit_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, unit_checker, *(((Unit*)source)->GetMap()));
@@ -3140,16 +3638,20 @@ void Map::ScriptsProcess()
//if no scriptmap present...
if(!datamap)
break;
+
uint32 script_id = step.script->datalong2;
//insert script into schedule but do not start it
ScriptsStart(*datamap, script_id, target, NULL/*, false*/);
break;
}
+
case SCRIPT_COMMAND_KILL:
{
if(!source || ((Creature*)source)->isDead())
break;
+
((Creature*)source)->DealDamage(((Creature*)source), ((Creature*)source)->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+
switch(step.script->dataint)
{
case 0: break; //return false not remove corpse
@@ -3157,6 +3659,7 @@ void Map::ScriptsProcess()
}
break;
}
+
case SCRIPT_COMMAND_PLAY_SOUND:
{
if(!source)
@@ -3164,12 +3667,14 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for NULL creature.");
break;
}
+
WorldObject* pSource = dynamic_cast<WorldObject*>(source);
if(!pSource)
{
sLog.outError("SCRIPT_COMMAND_PLAY_SOUND call for non-world object (TypeId: %u, Entry: %u, GUID: %u), skipping.",source->GetTypeId(),source->GetEntry(),source->GetGUIDLow());
break;
}
+
// bitmask: 0/1=anyone/target, 0/2=with distance dependent
Player* pTarget = NULL;
if(step.script->datalong2 & 1)
@@ -3179,13 +3684,16 @@ void Map::ScriptsProcess()
sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for NULL target.");
break;
}
+
if(target->GetTypeId()!=TYPEID_PLAYER)
{
sLog.outError("SCRIPT_COMMAND_PLAY_SOUND in targeted mode call for non-player (TypeId: %u, Entry: %u, GUID: %u), skipping.",target->GetTypeId(),target->GetEntry(),target->GetGUIDLow());
break;
}
+
pTarget = (Player*)target;
}
+
// bitmask: 0/1=anyone/target, 0/2=with distance dependent
if(step.script->datalong2 & 2)
pSource->PlayDistanceSound(step.script->datalong,pTarget);
@@ -3197,25 +3705,33 @@ void Map::ScriptsProcess()
sLog.outError("Unknown script command %u called.",step.script->command);
break;
}
+
m_scriptSchedule.erase(iter);
sWorld.DecreaseScheduledScriptCount();
+
iter = m_scriptSchedule.begin();
}
}
+
Creature*
Map::GetCreature(uint64 guid)
{
Creature * ret = NULL;
if(IS_CRE_OR_VEH_GUID(guid))
ret = ObjectAccessor::GetObjectInWorld(guid, (Creature*)NULL);
+
if(!ret)
return NULL;
+
if(ret->GetMapId() != GetId())
return NULL;
+
if(ret->GetInstanceId() != GetInstanceId())
return NULL;
+
return ret;
}
+
GameObject*
Map::GetGameObject(uint64 guid)
{
@@ -3228,6 +3744,7 @@ Map::GetGameObject(uint64 guid)
return NULL;
return ret;
}
+
DynamicObject*
Map::GetDynamicObject(uint64 guid)
{
@@ -3240,6 +3757,7 @@ Map::GetDynamicObject(uint64 guid)
return NULL;
return ret;
}
+
void Map::UpdateIteratorBack(Player *player)
{
if(m_mapRefIter == player->GetMapRef())
diff --git a/src/game/Map.h b/src/game/Map.h
index 8ab318cc2c4..a8d94d350e7 100644
--- a/src/game/Map.h
+++ b/src/game/Map.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_MAP_H
#define TRINITY_MAP_H
+
#include "Platform/Define.h"
#include "Policies/ThreadingModel.h"
#include "ace/RW_Thread_Mutex.h"
#include "ace/Thread_Mutex.h"
+
#include "DBCStructure.h"
#include "GridDefines.h"
#include "Cell.h"
@@ -31,8 +34,10 @@
#include "GameSystem/GridRefManager.h"
#include "MapRefManager.h"
#include "mersennetwister/MersenneTwister.h"
+
#include <bitset>
#include <list>
+
class Unit;
class WorldPacket;
class InstanceData;
@@ -47,22 +52,27 @@ struct ScriptInfo;
struct ScriptAction;
struct Position;
+
typedef ACE_RW_Thread_Mutex GridRWLock;
+
template<class MUTEX, class LOCK_TYPE>
struct RGuard
{
RGuard(MUTEX &l) : i_lock(l.getReadLock()) {}
Trinity::GeneralLock<LOCK_TYPE> i_lock;
};
+
template<class MUTEX, class LOCK_TYPE>
struct WGuard
{
WGuard(MUTEX &l) : i_lock(l.getWriteLock()) {}
Trinity::GeneralLock<LOCK_TYPE> i_lock;
};
+
typedef RGuard<GridRWLock, ACE_Thread_Mutex> GridReadGuard;
typedef WGuard<GridRWLock, ACE_Thread_Mutex> GridWriteGuard;
typedef MaNGOS::SingleThreaded<GridRWLock>::Lock NullGuard;
+
//******************************************
// Map file format defines
//******************************************
@@ -71,6 +81,7 @@ typedef MaNGOS::SingleThreaded<GridRWLock>::Lock NullGuard;
#define MAP_AREA_MAGIC 'AERA'
#define MAP_HEIGHT_MAGIC 'TGHM'
#define MAP_LIQUID_MAGIC 'QILM'
+
struct map_fileheader
{
uint32 mapMagic;
@@ -82,16 +93,20 @@ struct map_fileheader
uint32 liquidMapOffset;
uint32 liquidMapSize;
};
+
#define MAP_AREA_NO_AREA 0x0001
+
struct map_areaHeader
{
uint32 fourcc;
uint16 flags;
uint16 gridArea;
};
+
#define MAP_HEIGHT_NO_HEIGHT 0x0001
#define MAP_HEIGHT_AS_INT16 0x0002
#define MAP_HEIGHT_AS_INT8 0x0004
+
struct map_heightHeader
{
uint32 fourcc;
@@ -99,8 +114,10 @@ struct map_heightHeader
float gridHeight;
float gridMaxHeight;
};
+
#define MAP_LIQUID_NO_TYPE 0x0001
#define MAP_LIQUID_NO_HEIGHT 0x0002
+
struct map_liquidHeader
{
uint32 fourcc;
@@ -112,6 +129,7 @@ struct map_liquidHeader
uint8 height;
float liquidLevel;
};
+
enum ZLiquidStatus
{
LIQUID_MAP_NO_WATER = 0x00000000,
@@ -120,20 +138,25 @@ enum ZLiquidStatus
LIQUID_MAP_IN_WATER = 0x00000004,
LIQUID_MAP_UNDER_WATER = 0x00000008
};
+
#define MAP_LIQUID_TYPE_NO_WATER 0x00
#define MAP_LIQUID_TYPE_WATER 0x01
#define MAP_LIQUID_TYPE_OCEAN 0x02
#define MAP_LIQUID_TYPE_MAGMA 0x04
#define MAP_LIQUID_TYPE_SLIME 0x08
+
#define MAP_ALL_LIQUIDS (MAP_LIQUID_TYPE_WATER | MAP_LIQUID_TYPE_OCEAN | MAP_LIQUID_TYPE_MAGMA | MAP_LIQUID_TYPE_SLIME)
+
#define MAP_LIQUID_TYPE_DARK_WATER 0x10
#define MAP_LIQUID_TYPE_WMO_WATER 0x20
+
struct LiquidData
{
uint32 type;
float level;
float depth_level;
};
+
class GridMap
{
uint32 m_flags;
@@ -162,9 +185,11 @@ class GridMap
float m_liquidLevel;
uint8 *m_liquid_type;
float *m_liquid_map;
+
bool loadAreaData(FILE *in, uint32 offset, uint32 size);
bool loadHeihgtData(FILE *in, uint32 offset, uint32 size);
bool loadLiquidData(FILE *in, uint32 offset, uint32 size);
+
// Get height functions and pointers
typedef float (GridMap::*pGetHeightPtr) (float x, float y) const;
pGetHeightPtr m_gridGetHeight;
@@ -172,29 +197,35 @@ class GridMap
float getHeightFromUint16(float x, float y) const;
float getHeightFromUint8(float x, float y) const;
float getHeightFromFlat(float x, float y) const;
+
public:
GridMap();
~GridMap();
bool loadData(char *filaname);
void unloadData();
+
uint16 getArea(float x, float y);
inline float getHeight(float x, float y) {return (this->*m_gridGetHeight)(x, y);}
float getLiquidLevel(float x, float y);
uint8 getTerrainType(float x, float y);
ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0);
};
+
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;
@@ -209,26 +240,33 @@ struct InstanceTemplate
float startLocO;
uint32 script_id;
};
+
enum LevelRequirementVsMode
{
LEVELREQUIREMENT_HEROIC = 70
};
+
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
+
typedef UNORDERED_MAP<Creature*, CreatureMover> 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
+
typedef std::map<uint32/*leaderDBGUID*/, CreatureGroup*> CreatureGroupHolderType;
+
class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::ObjectLevelLockable<Map, ACE_Thread_Mutex>
{
friend class MapReference;
public:
Map(uint32 id, time_t, uint32 InstanceId, uint8 SpawnMode, Map* _parent = NULL);
virtual ~Map();
+
// currently unused for normal maps
bool CanUnload(uint32 diff)
{
@@ -237,80 +275,105 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
m_unloadTimer -= diff;
return false;
}
+
virtual bool Add(Player *);
virtual void Remove(Player *, bool);
template<class T> void Add(T *);
template<class T> 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);
*/
+
float GetVisibilityDistance() const { return m_VisibleDistance; }
//function for setting up visibility distance for maps on per-type/per-Id basis
virtual void InitVisibilityDistance();
+
void PlayerRelocation(Player *, float x, float y, float z, float angl);
void CreatureRelocation(Creature *creature, float x, float y, float, float);
+
template<class LOCK_TYPE, class T, class CONTAINER> void Visit(const CellLock<LOCK_TYPE> &cell, TypeContainerVisitor<T, CONTAINER> &visitor);
+
bool IsRemovalGrid(float x, float y) const
{
GridPair p = Trinity::ComputeGridPair(x, y);
return( !getNGrid(p.x_coord, p.y_coord) || getNGrid(p.x_coord, p.y_coord)->GetGridState() == GRID_STATE_REMOVAL );
}
+
bool GetUnloadLock(const GridPair &p) const { return getNGrid(p.x_coord, p.y_coord)->getUnloadLock(); }
void SetUnloadLock(const GridPair &p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadExplicitLock(on); }
void LoadGrid(float x, float y);
bool UnloadGrid(const uint32 &x, const uint32 &y, bool pForce);
virtual void UnloadAll();
+
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_mapEntry->MapID; }
+
static bool ExistMap(uint32 mapid, int gx, int gy);
static bool ExistVMap(uint32 mapid, int gx, int gy);
+
static void InitStateMachine();
static void DeleteStateMachine();
+
Map const * GetParent() const { return m_parentMap; }
+
// 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;
float GetVmapHeight(float x, float y, float z, bool useMaps) const;
bool IsInWater(float x, float y, float z, float min_depth = 2.0f) const;
+
ZLiquidStatus getLiquidStatus(float x, float y, float z, uint8 ReqLiquidType, LiquidData *data = 0) const;
+
uint16 GetAreaFlag(float x, float y, float z) 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 GetAreaIdByAreaFlag(uint16 areaflag,uint32 map_id);
static uint32 GetZoneIdByAreaFlag(uint16 areaflag,uint32 map_id);
static void GetZoneAndAreaIdByAreaFlag(uint32& zoneid, uint32& areaid, uint16 areaflag,uint32 map_id);
+
uint32 GetAreaId(float x, float y, float z) const
{
return GetAreaIdByAreaFlag(GetAreaFlag(x,y,z),GetId());
}
+
uint32 GetZoneId(float x, float y, float z) const
{
return GetZoneIdByAreaFlag(GetAreaFlag(x,y,z),GetId());
}
+
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid, float x, float y, float z) const
{
GetZoneAndAreaIdByAreaFlag(zoneid,areaid,GetAreaFlag(x,y,z),GetId());
}
+
void MoveAllCreaturesInMoveList();
void RemoveAllObjectsInRemoveList();
void RelocationNotify();
virtual void RemoveAllPlayers();
+
bool CreatureRespawnRelocation(Creature *c); // used only in MoveAllCreaturesInMoveList and ObjectGridUnloader
+
// assert print helper
bool CheckGridIntegrity(Creature* c, bool moved) const;
+
uint32 GetInstanceId() const { return i_InstanceId; }
uint8 GetSpawnMode() const { 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(); }
@@ -330,41 +393,57 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
y = i_mapEntry->entrance_y;
return true;
}
+
void AddObjectToRemoveList(WorldObject *obj);
void AddObjectToSwitchList(WorldObject *obj, bool on);
virtual void DelayedUpdate(const uint32 diff);
+
virtual bool RemoveBones(uint64 guid, float x, float y);
+
void UpdateObjectVisibility(WorldObject* obj, 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); }
+
bool HavePlayers() const { return !m_mapRefManager.isEmpty(); }
uint32 GetPlayersCountExceptGMs() const;
bool ActiveObjectsNearGrid(uint32 x, uint32 y) const;
+
void AddUnitToNotify(Unit* unit);
void RemoveUnitFromNotify(Unit *unit);
void AddWorldObject(WorldObject *obj) { i_worldObjects.insert(obj); }
void RemoveWorldObject(WorldObject *obj) { i_worldObjects.erase(obj); }
+
void SendToPlayers(WorldPacket const* data) const;
+
typedef MapRefManager PlayerList;
PlayerList const& GetPlayers() const { return m_mapRefManager; }
+
//per-map script storage
void ScriptsStart(std::map<uint32, std::multimap<uint32, ScriptInfo> > const& scripts, uint32 id, Object* source, Object* target);
void ScriptCommandStart(ScriptInfo const& script, uint32 delay, Object* source, Object* target);
+
// must called with AddToWorld
template<class T>
void AddToActive(T* obj) { AddToActiveHelper(obj); }
+
void AddToActive(Creature* obj);
+
// must called with RemoveFromWorld
template<class T>
void RemoveFromActive(T* obj) { RemoveFromActiveHelper(obj); }
+
void RemoveFromActive(Creature* obj);
+
template<class T> void SwitchGridContainers(T* obj, bool active);
template<class NOTIFIER> void VisitAll(const float &x, const float &y, float radius, NOTIFIER &notifier);
template<class NOTIFIER> void VisitWorld(const float &x, const float &y, float radius, NOTIFIER &notifier);
template<class NOTIFIER> void VisitGrid(const float &x, const float &y, float radius, NOTIFIER &notifier);
CreatureGroupHolderType CreatureGroupHolder;
+
void UpdateIteratorBack(Player *player);
+
#ifdef MAP_BASED_RAND_GEN
MTRand mtRand;
int32 irand(int32 min, int32 max) { return int32 (mtRand.randInt(max - min)) + min; }
@@ -373,6 +452,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
double rand_norm() { return mtRand.randExc(); }
double rand_chance() { return mtRand.randExc(100.0); }
#endif
+
TempSummon *SummonCreature(uint32 entry, const Position &pos, SummonPropertiesEntry const *properties = NULL, uint32 duration = 0, Unit *summoner = NULL, uint32 vehId = 0);
Creature* GetCreature(uint64 guid);
GameObject* GetGameObject(uint64 guid);
@@ -382,56 +462,78 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
void LoadVMap(int gx, int gy);
void LoadMap(int gx,int gy, bool reload = false);
GridMap *GetGrid(float x, float y);
+
void SetTimer(uint32 t) { i_gridExpiry = t < MIN_GRID_DELAY ? MIN_GRID_DELAY : t; }
+
void SendInitSelf( Player * player );
+
void SendInitTransports( Player * player );
void SendRemoveTransports( Player * player );
+
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 EnsureGridCreated(const GridPair &);
bool EnsureGridLoaded(Cell const&);
void EnsureGridLoadedAtEnter(Cell const&, Player* player = NULL);
+
void buildNGridLinkage(NGridType* pNGridType) { pNGridType->link(this); }
+
template<class T> void AddType(T *obj);
template<class T> void RemoveType(T *obj, bool);
+
NGridType* getNGrid(uint32 x, uint32 y) const
{
ASSERT(x < MAX_NUMBER_OF_GRIDS);
ASSERT(y < MAX_NUMBER_OF_GRIDS);
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); }
+
void setNGrid(NGridType* grid, uint32 x, uint32 y);
void ScriptsProcess();
+
void UpdateActiveCells(const float &x, const float &y, const uint32 &t_diff);
protected:
void SetUnloadReferenceLock(const GridPair &p, bool on) { getNGrid(p.x_coord, p.y_coord)->setUnloadReferenceLock(on); }
+
typedef MaNGOS::ObjectLevelLockable<Map, ACE_Thread_Mutex>::Lock Guard;
+
MapEntry const* i_mapEntry;
uint8 i_spawnMode;
uint32 i_InstanceId;
uint32 m_unloadTimer;
float m_VisibleDistance;
+
MapRefManager m_mapRefManager;
MapRefManager::iterator m_mapRefIter;
+
typedef std::set<WorldObject*> ActiveNonPlayers;
ActiveNonPlayers m_activeNonPlayers;
ActiveNonPlayers::iterator m_activeNonPlayersIter;
+
private:
time_t i_gridExpiry;
+
//used for fast base_map (e.g. MapInstanced class object) search for
//InstanceMaps and BattleGroundMaps...
Map* m_parentMap;
+
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<TOTAL_NUMBER_OF_CELLS_PER_MAP*TOTAL_NUMBER_OF_CELLS_PER_MAP> marked_cells;
+
IntervalTimer m_notifyTimer;
+
bool i_notifyLock, i_scriptLock;
std::vector<Unit*> i_unitsToNotifyBacklog;
std::vector<Unit*> i_unitsToNotify;
@@ -439,20 +541,26 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
std::map<WorldObject*, bool> i_objectsToSwitch;
std::set<WorldObject*> i_worldObjects;
std::multimap<time_t, ScriptAction> m_scriptSchedule;
+
// Type specific code for add/remove to/from grid
template<class T>
void AddToGrid(T*, NGridType *, Cell const&);
+
template<class T>
void AddNotifier(T*);
+
template<class T>
void RemoveFromGrid(T*, NGridType *, Cell const&);
+
template<class T>
void DeleteFromWorld(T*);
+
template<class T>
void AddToActiveHelper(T* obj)
{
m_activeNonPlayers.insert(obj);
}
+
template<class T>
void RemoveFromActiveHelper(T* obj)
{
@@ -470,6 +578,7 @@ class MANGOS_DLL_SPEC Map : public GridRefManager<NGridType>, public MaNGOS::Obj
m_activeNonPlayers.erase(obj);
}
};
+
enum InstanceResetMethod
{
INSTANCE_RESET_ALL,
@@ -479,6 +588,7 @@ enum InstanceResetMethod
INSTANCE_RESET_GROUP_JOIN,
INSTANCE_RESET_RESPAWN_DELAY
};
+
class TRINITY_DLL_SPEC InstanceMap : public Map
{
public:
@@ -497,6 +607,7 @@ class TRINITY_DLL_SPEC InstanceMap : public Map
void SendResetWarnings(uint32 timeLeft) const;
void SetResetSchedule(bool on);
uint32 GetMaxPlayers() const;
+
virtual void InitVisibilityDistance();
private:
bool m_resetAfterUnload;
@@ -504,19 +615,23 @@ class TRINITY_DLL_SPEC InstanceMap : public Map
InstanceData* i_data;
uint32 i_script_id;
};
+
class TRINITY_DLL_SPEC BattleGroundMap : public Map
{
public:
BattleGroundMap(uint32 id, time_t, uint32 InstanceId, Map* _parent);
~BattleGroundMap();
+
bool Add(Player *);
void Remove(Player *, bool);
bool CanEnter(Player* player);
void SetUnload();
//void UnloadAll(bool pForce);
void RemoveAllPlayers();
+
virtual void InitVisibilityDistance();
};
+
/*inline
uint64
Map::CalculateGridMask(const uint32 &y) const
@@ -526,6 +641,7 @@ Map::CalculateGridMask(const uint32 &y) const
return mask;
}
*/
+
template<class LOCK_TYPE, class T, class CONTAINER>
inline void
Map::Visit(const CellLock<LOCK_TYPE> &cell, TypeContainerVisitor<T, CONTAINER> &visitor)
@@ -534,6 +650,7 @@ Map::Visit(const CellLock<LOCK_TYPE> &cell, TypeContainerVisitor<T, CONTAINER> &
const uint32 y = cell->GridY();
const uint32 cell_x = cell->CellX();
const uint32 cell_y = cell->CellY();
+
if( !cell->NoCreate() || loaded(GridPair(x,y)) )
{
EnsureGridLoaded(cell);
@@ -541,6 +658,7 @@ Map::Visit(const CellLock<LOCK_TYPE> &cell, TypeContainerVisitor<T, CONTAINER> &
getNGrid(x, y)->Visit(cell_x, cell_y, visitor);
}
}
+
template<class NOTIFIER>
inline void
Map::VisitAll(const float &x, const float &y, float radius, NOTIFIER &notifier)
@@ -551,11 +669,13 @@ Map::VisitAll(const float &x, const float &y, float radius, NOTIFIER &notifier)
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
CellLock<GridReadGuard> cell_lock(cell, p);
+
TypeContainerVisitor<NOTIFIER, WorldTypeMapContainer> world_object_notifier(notifier);
cell_lock->Visit(cell_lock, world_object_notifier, *this, radius, x_off, y_off);
TypeContainerVisitor<NOTIFIER, GridTypeMapContainer > grid_object_notifier(notifier);
cell_lock->Visit(cell_lock, grid_object_notifier, *this, radius, x_off, y_off);
}
+
template<class NOTIFIER>
inline void
Map::VisitWorld(const float &x, const float &y, float radius, NOTIFIER &notifier)
@@ -566,9 +686,11 @@ Map::VisitWorld(const float &x, const float &y, float radius, NOTIFIER &notifier
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
CellLock<GridReadGuard> cell_lock(cell, p);
+
TypeContainerVisitor<NOTIFIER, WorldTypeMapContainer> world_object_notifier(notifier);
cell_lock->Visit(cell_lock, world_object_notifier, *this, radius, x_off, y_off);
}
+
template<class NOTIFIER>
inline void
Map::VisitGrid(const float &x, const float &y, float radius, NOTIFIER &notifier)
@@ -579,6 +701,7 @@ Map::VisitGrid(const float &x, const float &y, float radius, NOTIFIER &notifier)
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
CellLock<GridReadGuard> cell_lock(cell, p);
+
TypeContainerVisitor<NOTIFIER, GridTypeMapContainer > grid_object_notifier(notifier);
cell_lock->Visit(cell_lock, grid_object_notifier, *this, radius, x_off, y_off);
}
diff --git a/src/game/MapInstanced.cpp b/src/game/MapInstanced.cpp
index 22ea5ecef2c..28faf52bf19 100644
--- a/src/game/MapInstanced.cpp
+++ b/src/game/MapInstanced.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,6 +25,7 @@
#include "VMapFactory.h"
#include "InstanceSaveMgr.h"
#include "World.h"
+
MapInstanced::MapInstanced(uint32 id, time_t expiry) : Map(id, expiry, 0, 0)
{
// initialize instanced maps list
@@ -31,6 +33,7 @@ MapInstanced::MapInstanced(uint32 id, time_t expiry) : Map(id, expiry, 0, 0)
// fill with zero
memset(&GridMapReference, 0, MAX_NUMBER_OF_GRIDS*MAX_NUMBER_OF_GRIDS*sizeof(uint16));
}
+
void MapInstanced::InitVisibilityDistance()
{
if(m_InstancedMaps.empty())
@@ -41,12 +44,15 @@ void MapInstanced::InitVisibilityDistance()
(*i).second->InitVisibilityDistance();
}
}
+
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))
@@ -64,12 +70,15 @@ void MapInstanced::Update(const uint32& t)
}
}
}
+
void MapInstanced::DelayedUpdate(const uint32 diff)
{
for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
i->second->DelayedUpdate(diff);
+
Map::DelayedUpdate(diff); // this may be removed
}
+
/*
void MapInstanced::RelocationNotify()
{
@@ -77,27 +86,35 @@ void MapInstanced::RelocationNotify()
i->second->RelocationNotify();
}
*/
+
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()
{
// Unload instanced maps
for (InstancedMaps::iterator i = m_InstancedMaps.begin(); i != m_InstancedMaps.end(); ++i)
i->second->UnloadAll();
+
// 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();
}
+
/*
- return the right instance for the object, based on its InstanceId
- create the instance if it's not created already
@@ -108,6 +125,7 @@ Map* MapInstanced::CreateInstance(const uint32 mapId, Player * player, uint32 in
if(instanceId)
if(Map *map = _FindMap(instanceId))
return map;
+
if(IsBattleGroundOrArena())
{
instanceId = player->GetBattleGroundId();
@@ -127,20 +145,25 @@ Map* MapInstanced::CreateInstance(const uint32 mapId, Player * player, uint32 in
else if(instanceId != pSave->GetInstanceId()) // cannot go from one instance to another
return NULL;
// else log in at a saved instance
+
return CreateInstance(instanceId, pSave, pSave->GetDifficulty());
}
else if(!player->GetSession()->PlayerLoading())
{
if(!instanceId)
instanceId = MapManager::Instance().GenerateInstanceId();
+
return CreateInstance(instanceId, NULL, player->GetDifficulty());
}
+
return NULL;
}
+
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)
@@ -154,26 +177,36 @@ InstanceMap* MapInstanced::CreateInstance(uint32 InstanceId, InstanceSave *save,
sLog.outError("CreateInstance: no instance template for map %d", GetId());
assert(false);
}
+
// some instances only have one difficulty
if (entry && !entry->SupportsHeroicMode()) difficulty = DIFFICULTY_NORMAL;
+
sLog.outDebug("MapInstanced::CreateInstance: %s map instance %d for %d created with difficulty %s", save?"":"new ", InstanceId, GetId(), difficulty?"heroic":"normal");
+
InstanceMap *map = new InstanceMap(GetId(), GetGridExpiry(), InstanceId, difficulty, this);
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, this);
ASSERT(map->IsBattleGroundOrArena());
+
m_InstancedMaps[InstanceId] = map;
return map;
}
+
// increments the iterator after erase
bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
{
@@ -183,6 +216,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
++itr;
return false;
}
+
itr->second->UnloadAll();
// 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))
@@ -197,6 +231,7 @@ bool MapInstanced::DestroyInstance(InstancedMaps::iterator &itr)
m_InstancedMaps.erase(itr++);
return true;
}
+
bool MapInstanced::CanEnter(Player *player)
{
//assert(false);
diff --git a/src/game/MapInstanced.h b/src/game/MapInstanced.h
index 80501b8a747..22622d362ce 100644
--- a/src/game/MapInstanced.h
+++ b/src/game/MapInstanced.h
@@ -17,17 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_MAP_INSTANCED_H
#define TRINITY_MAP_INSTANCED_H
+
#include "Map.h"
#include "InstanceSaveMgr.h"
+
class TRINITY_DLL_DECL MapInstanced : public Map
{
friend class MapManager;
public:
typedef UNORDERED_MAP< uint32, Map* > InstancedMaps;
+
MapInstanced(uint32 id, time_t expiry);
~MapInstanced() {}
+
// functions overwrite Map versions
void Update(const uint32&);
void DelayedUpdate(const uint32 diff);
@@ -35,31 +40,40 @@ class TRINITY_DLL_DECL MapInstanced : public Map
bool RemoveBones(uint64 guid, float x, float y);
void UnloadAll();
bool CanEnter(Player* player);
+
Map* CreateInstance(const uint32 mapId, Player * player, uint32 instanceId);
Map* FindMap(uint32 InstanceId) const { return _FindMap(InstanceId); }
bool DestroyInstance(InstancedMaps::iterator &itr);
+
void AddGridMapReference(const GridPair &p)
{
++GridMapReference[p.x_coord][p.y_coord];
SetUnloadReferenceLock(GridPair(63-p.x_coord, 63-p.y_coord), true);
}
+
void RemoveGridMapReference(GridPair const& p)
{
--GridMapReference[p.x_coord][p.y_coord];
if (!GridMapReference[p.x_coord][p.y_coord])
SetUnloadReferenceLock(GridPair(63-p.x_coord, 63-p.y_coord), false);
}
+
InstancedMaps &GetInstancedMaps() { return m_InstancedMaps; }
virtual void InitVisibilityDistance();
+
private:
+
InstanceMap* CreateInstance(uint32 InstanceId, InstanceSave *save, uint8 difficulty);
BattleGroundMap* CreateBattleGround(uint32 InstanceId);
+
InstancedMaps m_InstancedMaps;
+
Map* _FindMap(uint32 InstanceId) const
{
InstancedMaps::const_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
index c6ece7c426e..0da5b19eb93 100644
--- a/src/game/MapManager.cpp
+++ b/src/game/MapManager.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifdef MULTI_THREAD_MAP
#include <omp.h>
#endif
@@ -35,26 +36,34 @@
#include "Corpse.h"
#include "ObjectMgr.h"
#include "Language.h"
+
#define CLASS_LOCK MaNGOS::ClassLevelLockable<MapManager, ACE_Thread_Mutex>
INSTANTIATE_SINGLETON_2(MapManager, CLASS_LOCK);
INSTANTIATE_CLASS_MUTEX(MapManager, ACE_Thread_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;i<MAX_GRID_STATE; i++)
@@ -63,13 +72,16 @@ MapManager::Initialize()
}
i_GridStateErrorCount = 0;
}
+
InitMaxInstanceId();
}
+
void MapManager::InitializeVisibilityDistanceInfo()
{
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
(*iter).second->InitVisibilityDistance();
}
+
// debugging code, should be deleted some day
void MapManager::checkAndCorrectGridStatesArray()
{
@@ -96,13 +108,16 @@ void MapManager::checkAndCorrectGridStatesArray()
if(i_GridStateErrorCount > 2)
assert(false); // force a crash. Too many errors
}
+
Map*
MapManager::_createBaseMap(uint32 id)
{
Map *m = _findMap(id);
+
if( m == NULL )
{
Guard guard(*this);
+
const MapEntry* entry = sMapStore.LookupEntry(id);
if (entry && entry->Instanceable())
{
@@ -118,26 +133,34 @@ MapManager::_createBaseMap(uint32 id)
}
i_maps[id] = m;
}
+
assert(m != NULL);
return m;
}
+
Map* MapManager::CreateMap(uint32 id, const WorldObject* obj, uint32 instanceId)
{
ASSERT(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 = _createBaseMap(id);
+
if (m && (obj->GetTypeId() == TYPEID_PLAYER) && m->Instanceable()) m = ((MapInstanced*)m)->CreateInstance(id, (Player*)obj, instanceId);
+
return m;
}
+
Map* MapManager::FindMap(uint32 mapid, uint32 instanceId) const
{
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
@@ -147,6 +170,7 @@ 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)
@@ -166,6 +190,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
}
}
}
+
//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)
{
@@ -173,6 +198,7 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
player->SendTransferAborted(mapid, TRANSFER_ABORT_DIFFICULTY, DIFFICULTY_HEROIC);
return false;
}
+
if (!player->isAlive())
{
if(Corpse *corpse = player->GetCorpse())
@@ -183,10 +209,12 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
{
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(player->GetSession()->GetTrinityString(811), mapName);
@@ -200,29 +228,35 @@ bool MapManager::CanPlayerEnter(uint32 mapid, Player* player)
sLog.outDebug("Map::CanEnter - player '%s' is dead but doesn't have a corpse!", player->GetName());
}
}
+
// Requirements
InstanceTemplate const* instance = objmgr.GetInstanceTemplate(mapid);
if(!instance)
return false;
+
return player->Satisfy(objmgr.GetAccessRequirement(instance->access_id), mapid, true);
}
else
return true;
}
+
void MapManager::RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y)
{
bool remove_result = _createBaseMap(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(uint32 diff)
{
i_timer.Update(diff);
if( !i_timer.Passed() )
return;
+
#ifdef MULTI_THREAD_MAP
uint32 i=0;
MapMapType::iterator iter;
@@ -249,15 +283,19 @@ MapManager::Update(uint32 diff)
sWorld.RecordTimeDiff("UpdateMap %u", iter->second->GetId());
}
#endif
+
for(MapMapType::iterator iter = i_maps.begin(); iter != i_maps.end(); ++iter)
iter->second->DelayedUpdate(i_timer.GetCurrent());
+
ObjectAccessor::Instance().Update(i_timer.GetCurrent());
sWorld.RecordTimeDiff("UpdateObjectAccessor");
for (TransportSet::iterator iter = m_Transports.begin(); iter != m_Transports.end(); ++iter)
(*iter)->Update(i_timer.GetCurrent());
sWorld.RecordTimeDiff("UpdateTransports");
+
i_timer.SetCurrent(0);
}
+
void MapManager::DoDelayedMovesAndRemoves()
{
/*
@@ -266,38 +304,48 @@ void MapManager::DoDelayedMovesAndRemoves()
MapMapType::iterator iter;
for(iter = i_maps.begin();iter != i_maps.end(); ++iter, i++)
update_queue[i] = iter->second;
+
omp_set_num_threads(sWorld.getConfig(CONFIG_NUMTHREADS));
+
#pragma omp parallel for schedule(dynamic) private(i) shared(update_queue)
for(i=0;i<i_maps.size();i++)
update_queue[i]->DoDelayedMovesAndRemoves();
*/
}
+
bool MapManager::ExistMapAndVMap(uint32 mapid, float x,float y)
{
GridPair p = Trinity::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->IsDungeon() || objmgr.GetInstanceTemplate(mapid));
// TODO: add check for battleground template
}
+
void MapManager::UnloadAll()
{
for(MapMapType::iterator iter=i_maps.begin(); iter != i_maps.end(); ++iter)
iter->second->UnloadAll();
+
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 )
{
@@ -305,6 +353,7 @@ void MapManager::InitMaxInstanceId()
delete result;
}
}
+
uint32 MapManager::GetNumInstances()
{
uint32 ret = 0;
@@ -318,6 +367,7 @@ uint32 MapManager::GetNumInstances()
}
return ret;
}
+
uint32 MapManager::GetNumPlayersInInstances()
{
uint32 ret = 0;
diff --git a/src/game/MapManager.h b/src/game/MapManager.h
index 5e7db0e4263..f9e64240686 100644
--- a/src/game/MapManager.h
+++ b/src/game/MapManager.h
@@ -17,24 +17,32 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_MAPMANAGER_H
#define TRINITY_MAPMANAGER_H
+
#include "Platform/Define.h"
#include "Policies/Singleton.h"
#include "ace/Thread_Mutex.h"
#include "Common.h"
#include "Map.h"
#include "GridStates.h"
+
class Transport;
+
class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::ClassLevelLockable<MapManager, ACE_Thread_Mutex> >
{
+
friend class Trinity::OperatorNew<MapManager>;
typedef UNORDERED_MAP<uint32, Map*> MapMapType;
typedef std::pair<UNORDERED_MAP<uint32, Map*>::iterator, bool> MapMapPair;
+
public:
+
Map* CreateMap(uint32, const WorldObject* obj, uint32 instanceId);
Map const* CreateBaseMap(uint32 id) const { return const_cast<MapManager*>(this)->_createBaseMap(id); }
Map* FindMap(uint32 mapid, uint32 instanceId = 0) const;
+
uint16 GetAreaFlag(uint32 mapid, float x, float y, float z) const
{
Map const* m = CreateBaseMap(mapid);
@@ -52,8 +60,10 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::
{
Map::GetZoneAndAreaIdByAreaFlag(zoneid,areaid,GetAreaFlag(mapid, x, y, z),mapid);
}
+
void Initialize(void);
void Update(uint32);
+
void SetGridCleanUpDelay(uint32 t)
{
if( t < MIN_GRID_DELAY )
@@ -61,47 +71,62 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::
else
i_gridCleanUpDelay = t;
}
+
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, int instId, 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) && Trinity::IsValidMapCoord(x,y);
}
+
static bool IsValidMapCoord(uint32 mapid, float x,float y,float z)
{
return IsValidMAP(mapid) && Trinity::IsValidMapCoord(x,y,z);
}
+
static bool IsValidMapCoord(uint32 mapid, float x,float y,float z,float o)
{
return IsValidMAP(mapid) && Trinity::IsValidMapCoord(x,y,z,o);
}
+
static bool IsValidMapCoord(WorldLocation const& loc)
{
return IsValidMapCoord(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation());
}
+
void DoDelayedMovesAndRemoves();
+
void LoadTransports();
+
typedef std::set<Transport *> TransportSet;
TransportSet m_Transports;
+
typedef std::map<uint32, TransportSet> TransportMap;
TransportMap m_TransportsByMap;
+
bool CanPlayerEnter(uint32 mapid, Player* player);
void RemoveBonesFromMap(uint32 mapid, uint64 guid, float x, float y);
uint32 GenerateInstanceId() { return ++i_MaxInstanceId; }
void InitMaxInstanceId();
void InitializeVisibilityDistanceInfo();
+
/* statistics */
uint32 GetNumInstances();
uint32 GetNumPlayersInInstances();
+
private:
// debugging code, should be deleted some day
void checkAndCorrectGridStatesArray(); // just for debugging to find some memory overwrites
@@ -110,18 +135,22 @@ class MANGOS_DLL_DECL MapManager : public MaNGOS::Singleton<MapManager, MaNGOS::
private:
MapManager();
~MapManager();
+
MapManager(const MapManager &);
MapManager& operator=(const MapManager &);
+
Map* _createBaseMap(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<MapManager, ACE_Thread_Mutex>::Lock Guard;
uint32 i_gridCleanUpDelay;
MapMapType i_maps;
IntervalTimer i_timer;
+
uint32 i_MaxInstanceId;
};
#endif
diff --git a/src/game/MapRefManager.h b/src/game/MapRefManager.h
index 8f94412fb34..4337aa75fd9 100644
--- a/src/game/MapRefManager.h
+++ b/src/game/MapRefManager.h
@@ -15,19 +15,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _MAPREFMANAGER
#define _MAPREFMANAGER
+
#include "Utilities/LinkedReference/RefManager.h"
+
class MapReference;
+
class MapRefManager : public RefManager<Map, Player>
{
public:
typedef LinkedListHead::Iterator< MapReference > iterator;
typedef LinkedListHead::Iterator< MapReference const > const_iterator;
+
MapReference* getFirst() { return (MapReference*)RefManager<Map, Player>::getFirst(); }
MapReference const* getFirst() const { return (MapReference const*)RefManager<Map, Player>::getFirst(); }
MapReference* getLast() { return (MapReference*)RefManager<Map, Player>::getLast(); }
MapReference const* getLast() const { return (MapReference const*)RefManager<Map, Player>::getLast(); }
+
iterator begin() { return iterator(getFirst()); }
iterator end() { return iterator(NULL); }
iterator rbegin() { return iterator(getLast()); }
diff --git a/src/game/MapReference.h b/src/game/MapReference.h
index f997d852331..ae485af7487 100644
--- a/src/game/MapReference.h
+++ b/src/game/MapReference.h
@@ -15,10 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _MAPREFERENCE_H
#define _MAPREFERENCE_H
+
#include "Utilities/LinkedReference/Reference.h"
#include "Map.h"
+
class TRINITY_DLL_SPEC MapReference : public Reference<Map, Player>
{
protected:
diff --git a/src/game/MiscHandler.cpp b/src/game/MiscHandler.cpp
index 72c47eb5a1f..56070f27e19 100644
--- a/src/game/MiscHandler.cpp
+++ b/src/game/MiscHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -46,12 +47,16 @@
#include "AccountMgr.h"
#include "Vehicle.h"
#include "CreatureAI.h"
+
void WorldSession::HandleRepopRequestOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Recvd CMSG_REPOP_REQUEST Message" );
+
recv_data.read_skip<uint8>();
+
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
@@ -62,19 +67,24 @@ void WorldSession::HandleRepopRequestOpcode( WorldPacket & recv_data )
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::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
{
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
@@ -82,6 +92,7 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
recv_data >> code;
sLog.outBasic("string read: %s", code.c_str());
}
+
Creature *unit = NULL;
GameObject *go = NULL;
if(IS_CRE_OR_VEH_GUID(guid))
@@ -107,9 +118,11 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
sLog.outDebug( "WORLD: HandleGossipSelectOptionOpcode - unsupported GUID type for highguid %u. lowpart %u.", uint32(GUID_HIPART(guid)), uint32(GUID_LOPART(guid)) );
return;
}
+
// remove fake death
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
if(!code.empty())
{
if(unit)
@@ -131,23 +144,31 @@ void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
Script->GOSelect( _player, go, _player->PlayerTalkClass->GossipOptionSender( option ), _player->PlayerTalkClass->GossipOptionAction( option ));
}
}
+
void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
{
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 (MAX_LEVEL)
recv_data >> player_name; // player name, case sensitive...
+
recv_data >> guild_name; // guild name, case sensitive...
+
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
+
for(uint32 i = 0; i < zones_count; ++i)
{
uint32 temp;
@@ -155,37 +176,49 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
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
+
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)
{
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, temp.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 >= MAX_LEVEL)
level_max = STRONG_MAX_LEVEL;
+
uint32 team = _player->GetTeam();
uint32 security = GetSecurity();
bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
uint32 gmLevelInWhoList = sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST);
+
WorldPacket data( SMSG_WHO, 50 ); // guess size
data << clientcount; // clientcount place holder
data << clientcount; // clientcount place holder
+
//TODO: Guard Player map
HashMapHolder<Player>::MapType& m = ObjectAccessor::Instance().GetPlayers();
for(HashMapHolder<Player>::MapType::const_iterator itr = m.begin(); itr != m.end(); ++itr)
@@ -195,29 +228,37 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
// 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() > gmLevelInWhoList))
continue;
}
+
//do not process players which are not in world
if(!(itr->second->IsInWorld()))
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)
{
@@ -226,27 +267,34 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
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)
{
@@ -264,6 +312,7 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
}
if (!s_show)
continue;
+
data << pname; // player name
data << gname; // guild name
data << uint32( lvl ); // player level
@@ -271,21 +320,27 @@ void WorldSession::HandleWhoOpcode( WorldPacket & recv_data )
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 - can be overridden
// through config, but is unstable
if ((++clientcount) == sWorld.getConfig(CONFIG_MAX_WHO))
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);
+
//Can not logout if...
if( GetPlayer()->isInCombat() || //...is in combat
GetPlayer()->duel || //...is in Duel
@@ -301,6 +356,7 @@ void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ )
LogoutRequest(0);
return;
}
+
//instant logout in taverns/cities or on taxi or for admins, gm's, mod's if its enabled in mangosd.conf
if (GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) || GetPlayer()->isInFlight() ||
GetSecurity() >= sWorld.getConfig(CONFIG_INSTANT_LOGOUT))
@@ -308,32 +364,40 @@ void WorldSession::HandleLogoutRequestOpcode( WorldPacket & /*recv_data*/ )
LogoutPlayer(true);
return;
}
+
// not set flags if player can't free move to prevent lost state at logout cancel
if(GetPlayer()->CanFreeMove())
{
GetPlayer()->SetStandState(UNIT_STAND_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_STUNNED);
}
+
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())
{
@@ -342,13 +406,17 @@ void WorldSession::HandleLogoutCancelOpcode( WorldPacket & /*recv_data*/ )
data.append(GetPlayer()->GetPackGUID());
data << uint32(0);
SendPacket( &data );
+
//! Stand Up
GetPlayer()->SetStandState(UNIT_STAND_STATE_STAND);
+
//! DISABLE_ROTATE
GetPlayer()->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
}
+
sLog.outDebug( "WORLD: sent SMSG_LOGOUT_CANCEL_ACK Message" );
}
+
void WorldSession::HandleTogglePvP( WorldPacket & recv_data )
{
// this opcode can be used in two ways: Either set explicit new status or toggle old status
@@ -364,6 +432,7 @@ void WorldSession::HandleTogglePvP( WorldPacket & recv_data )
GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP);
GetPlayer()->ToggleFlag(PLAYER_FLAGS, PLAYER_FLAGS_PVP_TIMER);
}
+
if(GetPlayer()->HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_IN_PVP))
{
if(!GetPlayer()->IsPvP() || GetPlayer()->pvpInfo.endTimer != 0)
@@ -374,52 +443,67 @@ void WorldSession::HandleTogglePvP( WorldPacket & recv_data )
if(!GetPlayer()->pvpInfo.inHostileArea && GetPlayer()->IsPvP())
GetPlayer()->pvpInfo.endTimer = time(NULL); // start toggle-off
}
+
//if(OutdoorPvP * pvp = _player->GetOutdoorPvP())
// pvp->HandlePlayerActivityChanged(_player);
}
+
void WorldSession::HandleZoneUpdateOpcode( WorldPacket & recv_data )
{
uint32 newZone;
recv_data >> newZone;
+
sLog.outDetail("WORLD: Recvd ZONE_UPDATE: %u", newZone);
+
// use server size data
uint32 newzone, newarea;
GetPlayer()->GetZoneAndAreaId(newzone,newarea);
GetPlayer()->UpdateZone(newzone,newarea);
//GetPlayer()->SendInitWorldStates(true,newZone);
}
+
void WorldSession::HandleSetTargetOpcode( WorldPacket & recv_data )
{
// When this packet send?
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;
+
if(FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction()))
_player->GetReputationMgr().SetVisible(factionTemplateEntry);
}
+
void WorldSession::HandleSetSelectionOpcode( WorldPacket & recv_data )
{
uint64 guid;
recv_data >> guid;
+
_player->SetSelection(guid);
+
// update reputation list if need
Unit* unit = ObjectAccessor::GetUnit(*_player, guid );
if(!unit)
return;
+
if(FactionTemplateEntry const* factionTemplateEntry = sFactionTemplateStore.LookupEntry(unit->getFaction()))
_player->GetReputationMgr().SetVisible(factionTemplateEntry);
}
+
void WorldSession::HandleStandStateChangeOpcode( WorldPacket & recv_data )
{
// sLog.outDebug( "WORLD: Received CMSG_STANDSTATECHANGE" ); -- too many spam in log at lags/debug stop
uint32 animstate;
recv_data >> animstate;
+
_player->SetStandState(animstate);
}
+
void WorldSession::HandleContactListOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_CONTACT_LIST" );
@@ -428,37 +512,52 @@ void WorldSession::HandleContactListOpcode( WorldPacket & recv_data )
sLog.outDebug("unk value is %u", unk);
_player->GetSocial()->SendSocialList();
}
+
void WorldSession::HandleAddFriendOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_ADD_FRIEND" );
+
std::string friendName = GetTrinityString(LANG_FRIEND_IGNORE_UNKNOWN);
std::string friendNote;
+
recv_data >> friendName;
+
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() );
+
CharacterDatabase.AsyncPQuery(&WorldSession::HandleAddFriendOpcodeCallBack, GetAccountId(), friendNote, "SELECT guid, race, account FROM characters WHERE name = '%s'", friendName.c_str());
}
+
void WorldSession::HandleAddFriendOpcodeCallBack(QueryResult *result, uint32 accountId, std::string friendNote)
{
uint64 friendGuid;
uint64 friendAcctid;
uint32 team;
FriendsResult friendResult;
+
WorldSession * session = sWorld.FindSession(accountId);
+
if(!session || !session->GetPlayer())
return;
+
friendResult = FRIEND_NOT_FOUND;
friendGuid = 0;
+
if(result)
{
friendGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
team = Player::TeamForRace((*result)[1].GetUInt8());
friendAcctid = (*result)[2].GetUInt32();
+
delete result;
+
if ( session->GetSecurity() >= SEC_MODERATOR || sWorld.getConfig(CONFIG_ALLOW_GM_FRIEND) || accmgr.GetSecurity(friendAcctid) < SEC_MODERATOR)
{
if(friendGuid)
@@ -486,43 +585,65 @@ void WorldSession::HandleAddFriendOpcodeCallBack(QueryResult *result, uint32 acc
}
}
}
+
sSocialMgr.SendFriendStatus(session->GetPlayer(), friendResult, GUID_LOPART(friendGuid), false);
+
sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
}
+
void WorldSession::HandleDelFriendOpcode( WorldPacket & recv_data )
{
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 )
{
sLog.outDebug( "WORLD: Received CMSG_ADD_IGNORE" );
+
std::string IgnoreName = GetTrinityString(LANG_FRIEND_IGNORE_UNKNOWN);
+
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() );
+
CharacterDatabase.AsyncPQuery(&WorldSession::HandleAddIgnoreOpcodeCallBack, GetAccountId(), "SELECT guid FROM characters WHERE name = '%s'", IgnoreName.c_str());
}
+
void WorldSession::HandleAddIgnoreOpcodeCallBack(QueryResult *result, uint32 accountId)
{
uint64 IgnoreGuid;
FriendsResult ignoreResult;
+
WorldSession * session = sWorld.FindSession(accountId);
+
if(!session || !session->GetPlayer())
return;
+
ignoreResult = FRIEND_IGNORE_NOT_FOUND;
IgnoreGuid = 0;
+
if(result)
{
IgnoreGuid = MAKE_NEW_GUID((*result)[0].GetUInt32(), 0, HIGHGUID_PLAYER);
+
delete result;
+
if(IgnoreGuid)
{
if(IgnoreGuid==session->GetPlayer()->GetGUID()) //not add yourself
@@ -532,24 +653,34 @@ void WorldSession::HandleAddIgnoreOpcodeCallBack(QueryResult *result, uint32 acc
else
{
ignoreResult = FRIEND_IGNORE_ADDED;
+
// ignore list full
if(!session->GetPlayer()->GetSocial()->AddToSocialList(GUID_LOPART(IgnoreGuid), true))
ignoreResult = FRIEND_IGNORE_FULL;
}
}
}
+
sSocialMgr.SendFriendStatus(session->GetPlayer(), ignoreResult, GUID_LOPART(IgnoreGuid), false);
+
sLog.outDebug( "WORLD: Sent (SMSG_FRIEND_STATUS)" );
}
+
void WorldSession::HandleDelIgnoreOpcode( WorldPacket & recv_data )
{
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::HandleSetContactNotesOpcode( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_SET_CONTACT_NOTES");
@@ -558,96 +689,127 @@ void WorldSession::HandleSetContactNotesOpcode( WorldPacket & recv_data )
recv_data >> guid >> note;
_player->GetSocial()->SetFriendNote(guid, note);
}
+
void WorldSession::HandleBugOpcode( WorldPacket & recv_data )
{
uint32 suggestion, contentlen;
std::string content;
uint32 typelen;
std::string type;
+
recv_data >> suggestion >> contentlen >> content;
+
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("%s", type.c_str() );
sLog.outDebug("%s", 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::HandleReclaimCorpseOpcode(WorldPacket &recv_data)
{
sLog.outDetail("WORLD: Received CMSG_RECLAIM_CORPSE");
if (GetPlayer()->isAlive())
return;
+
// do not allow corpse reclaim in arena
if (GetPlayer()->InArena())
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;
+
if (!corpse->IsWithinDist(GetPlayer(), CORPSE_RECLAIM_RADIUS, true))
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)
{
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)
{
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
@@ -662,16 +824,21 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data)
else
{
// we have only extent
+
// rotate the players position instead of rotating the whole cube, that way we can make a simplified
// is-in-cube check and we have to calculate only one point instead of 4
+
// 2PI = 360°, keep in mind that ingame orientation is counter-clockwise
double rotation = 2*M_PI-atEntry->box_orientation;
double sinVal = sin(rotation);
double cosVal = cos(rotation);
+
float playerBoxDistX = pl->GetPositionX() - atEntry->x;
float playerBoxDistY = pl->GetPositionY() - atEntry->y;
+
float rotPlayerX = atEntry->x + playerBoxDistX * cosVal - playerBoxDistY*sinVal;
float rotPlayerY = atEntry->y + playerBoxDistY * cosVal + playerBoxDistX*sinVal;
+
// box edges are parallel to coordiante axis, so we can treat every dimension independently :D
float dz = pl->GetPositionZ() - atEntry->z;
float dx = rotPlayerX - atEntry->x;
@@ -685,8 +852,10 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data)
return;
}
}
+
if(Script->scriptAreaTrigger(GetPlayer(), atEntry))
return;
+
uint32 quest_id = objmgr.GetQuestForAreaTrigger( Trigger_ID );
if( quest_id && GetPlayer()->isAlive() && GetPlayer()->IsActiveQuest(quest_id) )
{
@@ -697,62 +866,81 @@ void WorldSession::HandleAreaTriggerOpcode(WorldPacket & recv_data)
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()->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
+
return;
}
+
if(GetPlayer()->InBattleGround())
{
BattleGround* bg = GetPlayer()->GetBattleGround();
if(bg)
if(bg->GetStatus() == STATUS_IN_PROGRESS)
bg->HandleAreaTrigger(GetPlayer(), Trigger_ID);
+
return;
}
+
if(OutdoorPvP * pvp = GetPlayer()->GetOutdoorPvP())
{
if(pvp->HandleAreaTrigger(_player, Trigger_ID))
return;
}
+
// NULL if all values default (non teleport trigger)
AreaTrigger const* at = objmgr.GetAreaTrigger(Trigger_ID);
if(!at)
return;
+
if(!GetPlayer()->Satisfy(objmgr.GetAccessRequirement(at->access_id), at->target_mapId, true))
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");
+
uint32 type, timestamp, decompressedSize;
recv_data >> type >> timestamp >> decompressedSize;
+
sLog.outDebug("UAD: type %u, time %u, decompressedSize %u", type, timestamp, decompressedSize);
+
if(type > NUM_ACCOUNT_DATA_TYPES)
return;
+
if(decompressedSize == 0) // erase
{
SetAccountData(AccountDataType(type), 0, "");
+
WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4);
data << uint32(type);
data << uint32(0);
SendPacket(&data);
+
return;
}
+
if(decompressedSize > 0xFFFF)
{
recv_data.rpos(recv_data.wpos()); // unnneded warning spam in this case
sLog.outError("UAD: Account data packet too big, size %u", decompressedSize);
return;
}
+
ByteBuffer dest;
dest.resize(decompressedSize);
+
uLongf realSize = decompressedSize;
if(uncompress(const_cast<uint8*>(dest.contents()), &realSize, const_cast<uint8*>(recv_data.contents() + recv_data.rpos()), recv_data.size() - recv_data.rpos()) != Z_OK)
{
@@ -760,34 +948,49 @@ void WorldSession::HandleUpdateAccountData(WorldPacket &recv_data)
sLog.outError("UAD: Failed to decompress account data");
return;
}
+
recv_data.rpos(recv_data.wpos()); // uncompress read (recv_data.size() - recv_data.rpos())
+
std::string adata;
dest >> adata;
+
SetAccountData(AccountDataType(type), timestamp, adata);
+
WorldPacket data(SMSG_UPDATE_ACCOUNT_DATA_COMPLETE, 4+4);
data << uint32(type);
data << uint32(0);
SendPacket(&data);
}
+
void WorldSession::HandleRequestAccountData(WorldPacket& recv_data)
{
sLog.outDetail("WORLD: Received CMSG_REQUEST_ACCOUNT_DATA");
+
uint32 type;
recv_data >> type;
+
sLog.outDebug("RAD: type %u", type);
+
if(type > NUM_ACCOUNT_DATA_TYPES)
return;
+
AccountData *adata = GetAccountData(AccountDataType(type));
+
uint32 size = adata->Data.size();
+
uLongf destSize = compressBound(size);
+
ByteBuffer dest;
dest.resize(destSize);
+
if(size && compress(const_cast<uint8*>(dest.contents()), &destSize, (uint8*)adata->Data.c_str(), size) != Z_OK)
{
sLog.outDebug("RAD: Failed to compress account data");
return;
}
+
dest.resize(destSize);
+
WorldPacket data (SMSG_UPDATE_ACCOUNT_DATA, 8+4+4+4+destSize);
data << uint64(_player->GetGUID()); // player guid
data << uint32(type); // type (0-7)
@@ -796,14 +999,17 @@ void WorldSession::HandleRequestAccountData(WorldPacket& recv_data)
data.append(dest); // compressed data
SendPacket(&data);
}
+
void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data)
{
sLog.outDebug( "WORLD: Received CMSG_SET_ACTION_BUTTON" );
uint8 button;
uint32 packetData;
recv_data >> button >> packetData;
+
uint32 action = ACTION_BUTTON_ACTION(packetData);
uint8 type = ACTION_BUTTON_TYPE(packetData);
+
sLog.outDetail( "BUTTON: %u ACTION: %u TYPE: %u", button, action, type );
if (!packetData)
{
@@ -834,18 +1040,22 @@ void WorldSession::HandleSetActionButtonOpcode(WorldPacket& recv_data)
GetPlayer()->addActionButton(button,action,type);
}
}
+
void WorldSession::HandleCompleteCinematic( 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" );
+
recv_data.read_skip<uint64>();
recv_data.read_skip<uint32>();
/*
@@ -854,6 +1064,7 @@ void WorldSession::HandleMoveTimeSkippedOpcode( WorldPacket & recv_data )
recv_data >> guid;
recv_data >> time_skipped;
sLog.outDebug( "WORLD: CMSG_MOVE_TIME_SKIPPED" );
+
/// TODO
must be need use in Trinity
We substract server Lags to move time ( AntiLags )
@@ -861,10 +1072,12 @@ void WorldSession::HandleMoveTimeSkippedOpcode( WorldPacket & recv_data )
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)
{
// no used
@@ -872,18 +1085,23 @@ void WorldSession::HandleMoveUnRootAck(WorldPacket& recv_data)
/*
uint64 guid;
recv_data >> guid;
+
// now can skip not our packet
if(_player->GetGUID() != guid)
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_UNROOT_ACK" );
+
recv_data.read_skip<uint32>(); // unk
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
*/
}
+
void WorldSession::HandleMoveRootAck(WorldPacket& recv_data)
{
// no used
@@ -891,30 +1109,39 @@ void WorldSession::HandleMoveRootAck(WorldPacket& recv_data)
/*
uint64 guid;
recv_data >> guid;
+
// now can skip not our packet
if(_player->GetGUID() != guid)
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
sLog.outDebug( "WORLD: CMSG_FORCE_MOVE_ROOT_ACK" );
+
recv_data.read_skip<uint32>(); // unk
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
*/
}
+
void WorldSession::HandleSetActionBarToggles(WorldPacket& recv_data)
{
uint8 ActionBar;
+
recv_data >> ActionBar;
+
if(!GetPlayer()) // ignore until not logged (check needed because STATUS_AUTHED)
{
if(ActionBar!=0)
sLog.outError("WorldSession::HandleSetActionBarToggles in not logged state with value: %u, ignored",uint32(ActionBar));
return;
}
+
GetPlayer()->SetByteValue(PLAYER_FIELD_BYTES, 2, ActionBar);
}
+
void WorldSession::HandleWardenDataOpcode(WorldPacket& recv_data)
{
recv_data.read_skip<uint8>();
@@ -924,29 +1151,36 @@ void WorldSession::HandleWardenDataOpcode(WorldPacket& recv_data)
sLog.outDebug("Received opcode CMSG_WARDEN_DATA, not resolve.uint8 = %u",tmp);
*/
}
+
void WorldSession::HandlePlayedTime(WorldPacket& recv_data)
{
uint8 unk1;
recv_data >> unk1; // 0 or 1 expected
+
WorldPacket data(SMSG_PLAYED_TIME, 4 + 4 + 1);
data << uint32(_player->GetTotalPlayedTime());
data << uint32(_player->GetLevelPlayedTime());
data << uint8(unk1); // 0 - will not show in chat frame
SendPacket(&data);
}
+
void WorldSession::HandleInspectOpcode(WorldPacket& recv_data)
{
uint64 guid;
recv_data >> guid;
DEBUG_LOG("Inspected guid is " UI64FMTD, guid);
+
_player->SetSelection(guid);
+
Player *plr = objmgr.GetPlayer(guid);
if(!plr) // wrong player
return;
+
uint32 talent_points = 0x47;
- uint32 guid_size = plr->GetPackGUID().wpos();
- WorldPacket data(SMSG_INSPECT_TALENT, guid_size+4+talent_points);
+ uint32 guid_size = plr->GetPackGUID().wpos();
+ WorldPacket data(SMSG_INSPECT_TALENT, guid_size+4+talent_points);
data.append(plr->GetPackGUID());
+
if(sWorld.getConfig(CONFIG_TALENTS_INSPECTING) || _player->isGameMaster())
{
plr->BuildPlayerTalentsInfoData(&data);
@@ -958,19 +1192,24 @@ void WorldSession::HandleInspectOpcode(WorldPacket& recv_data)
data << uint8(0); // talentGroupIndex
data << uint32(0); // slotUsedMask
}
+
plr->BuildEnchantmentsInfoData(&data);
SendPacket(&data);
}
+
void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data)
{
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));
@@ -980,23 +1219,28 @@ void WorldSession::HandleInspectHonorStatsOpcode(WorldPacket& recv_data)
data << uint32(player->GetUInt32Value(PLAYER_FIELD_LIFETIME_HONORABLE_KILLS));
SendPacket(&data);
}
+
void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data)
{
// 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;
@@ -1004,40 +1248,49 @@ void WorldSession::HandleWorldTeleportOpcode(WorldPacket& recv_data)
recv_data >> PositionZ;
recv_data >> Orientation; // o (3.141593 = 180 degrees)
DEBUG_LOG("Time %u sec, map=%u, x=%f, y=%f, z=%f, orient=%f", time/1000, mapid, PositionX, PositionY, PositionZ, Orientation);
+
if (GetSecurity() >= SEC_ADMINISTRATOR)
GetPlayer()->TeleportTo(mapid,PositionX,PositionY,PositionZ,Orientation);
else
SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
sLog.outDebug("Received worldport command from player %s", GetPlayer()->GetName());
}
+
void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data)
{
sLog.outDebug("Received opcode CMSG_WHOIS");
std::string charname;
recv_data >> charname;
+
if (GetSecurity() < SEC_ADMINISTRATOR)
{
SendNotification(LANG_YOU_NOT_HAVE_PERMISSION);
return;
}
+
if(charname.empty() || !normalizePlayerName (charname))
{
SendNotification(LANG_NEED_CHARACTER_NAME);
return;
}
+
Player *plr = objmgr.GetPlayer(charname.c_str());
+
if(!plr)
{
SendNotification(LANG_PLAYER_NOT_EXIST_OR_OFFLINE, charname.c_str());
return;
}
+
uint32 accid = plr->GetSession()->GetAccountId();
+
QueryResult *result = loginDatabase.PQuery("SELECT username,email,last_ip FROM account WHERE id=%u", accid);
if(!result)
{
SendNotification(LANG_ACCOUNT_FOR_PLAYER_NOT_FOUND, charname.c_str());
return;
}
+
Field *fields = result->Fetch();
std::string acc = fields[0].GetCppString();
if(acc.empty())
@@ -1048,17 +1301,23 @@ void WorldSession::HandleWhoisOpcode(WorldPacket& recv_data)
std::string lastip = fields[2].GetCppString();
if(lastip.empty())
lastip = "Unknown";
+
std::string msg = charname + "'s " + "account is " + acc + ", e-mail: " + email + ", last ip: " + lastip;
+
WorldPacket data(SMSG_WHOIS, msg.size()+1);
data << msg;
_player->GetSession()->SendPacket(&data);
+
delete result;
+
sLog.outDebug("Received whois command from player %s for character %s", GetPlayer()->GetName(), charname.c_str());
}
+
void WorldSession::HandleComplainOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_COMPLAIN");
recv_data.hexlike();
+
uint8 spam_type; // 0 - mail, 1 - chat
uint64 spammer_guid;
uint32 unk1 = 0;
@@ -1083,20 +1342,26 @@ void WorldSession::HandleComplainOpcode( WorldPacket & recv_data )
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::HandleRealmSplitOpcode( WorldPacket & recv_data )
{
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
@@ -1108,12 +1373,15 @@ void WorldSession::HandleRealmSplitOpcode( WorldPacket & recv_data )
SendPacket(&data);
//sLog.outDebug("response sent %u", unk);
}
+
void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_FAR_SIGHT");
//recv_data.hexlike();
+
uint8 apply;
recv_data >> apply;
+
switch(apply)
{
case 0:
@@ -1136,11 +1404,14 @@ void WorldSession::HandleFarSightOpcode( WorldPacket & recv_data )
//GetPlayer()->GetMap()->UpdateObjectsVisibilityFor(_player, cell, pair);
GetPlayer()->SetToNotify();
}
+
void WorldSession::HandleSetTitleOpcode( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_SET_TITLE");
+
int32 title;
recv_data >> title;
+
// -1 at none
if(title > 0 && title < MAX_TITLE_INDEX)
{
@@ -1149,17 +1420,23 @@ void WorldSession::HandleSetTitleOpcode( WorldPacket & recv_data )
}
else
title = 0;
+
GetPlayer()->SetUInt32Value(PLAYER_CHOSEN_TITLE, title);
}
+
void WorldSession::HandleTimeSyncResp( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_TIME_SYNC_RESP");
+
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");
@@ -1172,18 +1449,23 @@ void WorldSession::HandleResetInstancesOpcode( WorldPacket & /*recv_data*/ )
else
_player->ResetInstances(INSTANCE_RESET_ALL);
}
+
void WorldSession::HandleSetDungeonDifficultyOpcode( WorldPacket & recv_data )
{
sLog.outDebug("MSG_SET_DUNGEON_DIFFICULTY");
+
uint32 mode;
recv_data >> mode;
+
if(mode == _player->GetDifficulty())
return;
+
if(mode > DIFFICULTY_HEROIC)
{
sLog.outError("WorldSession::HandleSetDungeonDifficultyOpcode: 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())
@@ -1191,6 +1473,7 @@ void WorldSession::HandleSetDungeonDifficultyOpcode( WorldPacket & recv_data )
sLog.outError("WorldSession::HandleSetDungeonDifficultyOpcode: player %d tried to reset the instance while inside!", _player->GetGUIDLow());
return;
}
+
if(_player->getLevel() < LEVELREQUIREMENT_HEROIC)
return;
Group *pGroup = _player->GetGroup();
@@ -1210,36 +1493,46 @@ void WorldSession::HandleSetDungeonDifficultyOpcode( WorldPacket & recv_data )
_player->SetDifficulty(mode);
}
}
+
void WorldSession::HandleCancelMountAuraOpcode( 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->RemoveAurasByType(SPELL_AURA_MOUNTED);
}
+
void WorldSession::HandleMoveSetCanFlyAckOpcode( WorldPacket & recv_data )
{
// fly mode on/off
sLog.outDebug("WORLD: CMSG_MOVE_SET_CAN_FLY_ACK");
//recv_data.hexlike();
+
recv_data.read_skip<uint64>(); // guid
recv_data.read_skip<uint32>(); // unk
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
+
recv_data.read_skip<uint32>(); // unk2
+
_player->m_mover->m_movementInfo.flags = movementInfo.GetMovementFlags();
}
+
void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */)
{
/*
@@ -1247,19 +1540,24 @@ void WorldSession::HandleRequestPetInfoOpcode( WorldPacket & /*recv_data */)
recv_data.hexlike();
*/
}
+
void WorldSession::HandleSetTaxiBenchmarkOpcode( WorldPacket & recv_data )
{
uint8 mode;
recv_data >> mode;
+
sLog.outDebug("Client used \"/timetest %d\" command", mode);
}
+
void WorldSession::HandleQueryInspectAchievements( WorldPacket & recv_data )
{
uint64 guid;
if(!recv_data.readPackGUID(guid))
return;
+
Player *player = objmgr.GetPlayer(guid);
if(!player)
return;
+
player->GetAchievementMgr().SendRespondInspectAchievements(_player);
}
diff --git a/src/game/MotionMaster.cpp b/src/game/MotionMaster.cpp
index 1b0b57219b8..e32fffaa8ac 100644
--- a/src/game/MotionMaster.cpp
+++ b/src/game/MotionMaster.cpp
@@ -17,10 +17,12 @@
* 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"
@@ -29,11 +31,14 @@
#include "TargetedMovementGenerator.h"
#include "WaypointMovementGenerator.h"
#include "RandomMovementGenerator.h"
+
#include <cassert>
+
inline bool isStatic(MovementGenerator *mv)
{
return (mv == &si_idleMovement);
}
+
void
MotionMaster::Initialize()
{
@@ -44,8 +49,10 @@ MotionMaster::Initialize()
pop();
if(curr) DirectDelete(curr);
}
+
InitDefault();
}
+
// set new default movement generator
void MotionMaster::InitDefault()
{
@@ -59,6 +66,7 @@ void MotionMaster::InitDefault()
Mutate(&si_idleMovement, MOTION_SLOT_IDLE);
}
}
+
MotionMaster::~MotionMaster()
{
// clear ALL movement generators (including default)
@@ -69,6 +77,7 @@ MotionMaster::~MotionMaster()
if(curr) DirectDelete(curr);
}
}
+
void
MotionMaster::UpdateMotion(uint32 diff)
{
@@ -83,6 +92,7 @@ MotionMaster::UpdateMotion(uint32 diff)
}
else
m_cleanFlag &= ~MMCF_UPDATE;
+
if (m_expList)
{
for (size_t i = 0; i < m_expList->size(); ++i)
@@ -90,17 +100,21 @@ MotionMaster::UpdateMotion(uint32 diff)
MovementGenerator* mg = (*m_expList)[i];
DirectDelete(mg);
}
+
delete m_expList;
m_expList = NULL;
+
if(empty())
Initialize();
else if(needInitTop())
InitTop();
else if (m_cleanFlag & MMCF_RESET)
top()->Reset(*i_owner);
+
m_cleanFlag &= ~MMCF_RESET;
}
}
+
void
MotionMaster::DirectClean(bool reset)
{
@@ -110,11 +124,13 @@ MotionMaster::DirectClean(bool reset)
pop();
if(curr) DirectDelete(curr);
}
+
if(needInitTop())
InitTop();
else if(reset)
top()->Reset(*i_owner);
}
+
void
MotionMaster::DelayedClean()
{
@@ -125,6 +141,7 @@ MotionMaster::DelayedClean()
if(curr) DelayedDelete(curr);
}
}
+
void
MotionMaster::DirectExpire(bool reset)
{
@@ -134,8 +151,10 @@ MotionMaster::DirectExpire(bool reset)
pop();
DirectDelete(curr);
}
+
while(!top())
--i_top;
+
if(empty())
Initialize();
else if(needInitTop())
@@ -143,6 +162,7 @@ MotionMaster::DirectExpire(bool reset)
else if(reset)
top()->Reset(*i_owner);
}
+
void
MotionMaster::DelayedExpire()
{
@@ -152,9 +172,11 @@ MotionMaster::DelayedExpire()
pop();
DelayedDelete(curr);
}
+
while(!top())
--i_top;
}
+
void MotionMaster::MoveIdle(MovementSlot slot)
{
//if( empty() || !isStatic( top() ) )
@@ -162,6 +184,7 @@ void MotionMaster::MoveIdle(MovementSlot slot)
if(!isStatic(Impl[slot]))
Mutate(&si_idleMovement, slot);
}
+
void
MotionMaster::MoveRandom(float spawndist)
{
@@ -171,12 +194,15 @@ MotionMaster::MoveRandom(float spawndist)
Mutate(new RandomMovementGenerator<Creature>(spawndist), MOTION_SLOT_IDLE);
}
}
+
void
MotionMaster::MoveTargetedHome()
{
//if(i_owner->hasUnitState(UNIT_STAT_FLEEING))
// return;
+
Clear(false);
+
if(i_owner->GetTypeId() == TYPEID_UNIT)
{
DEBUG_LOG("Creature (Entry: %u GUID: %u) targeted home", i_owner->GetEntry(), i_owner->GetGUIDLow());
@@ -187,6 +213,7 @@ MotionMaster::MoveTargetedHome()
sLog.outError("Player (GUID: %u) attempt targeted home", i_owner->GetGUIDLow() );
}
}
+
void
MotionMaster::MoveConfused()
{
@@ -202,12 +229,14 @@ MotionMaster::MoveConfused()
Mutate(new ConfusedMovementGenerator<Creature>(), MOTION_SLOT_CONTROLLED);
}
}
+
void
MotionMaster::MoveChase(Unit* target, float dist, float angle)
{
// ignore movement request if target not exist
if(!target || target == i_owner)
return;
+
i_owner->clearUnitState(UNIT_STAT_FOLLOW);
if(i_owner->GetTypeId()==TYPEID_PLAYER)
{
@@ -226,12 +255,14 @@ MotionMaster::MoveChase(Unit* target, float dist, float angle)
Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle), MOTION_SLOT_ACTIVE);
}
}
+
void
MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slot)
{
// ignore movement request if target not exist
if(!target || target == i_owner)
return;
+
i_owner->addUnitState(UNIT_STAT_FOLLOW);
if(i_owner->GetTypeId()==TYPEID_PLAYER)
{
@@ -249,6 +280,7 @@ MotionMaster::MoveFollow(Unit* target, float dist, float angle, MovementSlot slo
Mutate(new TargetedMovementGenerator<Creature>(*target,dist,angle), slot);
}
}
+
void
MotionMaster::MovePoint(uint32 id, float x, float y, float z)
{
@@ -264,30 +296,36 @@ MotionMaster::MovePoint(uint32 id, float x, float y, float z)
Mutate(new PointMovementGenerator<Creature>(id,x,y,z), MOTION_SLOT_ACTIVE);
}
}
+
void MotionMaster::MoveKnockbackFrom(float srcX, float srcY, float speedXY, float speedZ)
{
//this function may make players fall below map
if(i_owner->GetTypeId()==TYPEID_PLAYER)
return;
+
float x, y, z;
float dist = speedXY * speedZ * 0.1f;
i_owner->GetNearPoint(i_owner, x, y, z, i_owner->GetObjectSize(), dist, i_owner->GetAngle(srcX, srcY) + M_PI);
MoveJump(x, y, z, speedXY, speedZ);
}
+
void MotionMaster::MoveJumpTo(float angle, float speedXY, float speedZ)
{
//this function may make players fall below map
if(i_owner->GetTypeId()==TYPEID_PLAYER)
return;
+
float x, y, z;
float dist = speedXY * speedZ * 0.1f;
i_owner->GetClosePoint(x, y, z, i_owner->GetObjectSize(), dist, angle);
MoveJump(x, y, z, speedXY, speedZ);
}
+
void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float speedZ)
{
uint32 moveFlag = MOVEFLAG_JUMP | MOVEFLAG_WALK;
uint32 time = speedZ * 100;
+
i_owner->addUnitState(UNIT_STAT_CHARGING | UNIT_STAT_JUMPING);
i_owner->m_TempSpeed = speedXY;
if(i_owner->GetTypeId()==TYPEID_PLAYER)
@@ -301,13 +339,16 @@ void MotionMaster::MoveJump(float x, float y, float z, float speedXY, float spee
i_owner->GetEntry(), i_owner->GetGUIDLow(), x, y, z );
Mutate(new PointMovementGenerator<Creature>(0,x,y,z), MOTION_SLOT_CONTROLLED);
}
+
i_owner->SendMonsterMove(x, y, z, moveFlag, time, speedZ);
}
+
void
MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
{
if(Impl[MOTION_SLOT_CONTROLLED] && Impl[MOTION_SLOT_CONTROLLED]->GetMovementGeneratorType() != DISTRACT_MOTION_TYPE)
return;
+
i_owner->addUnitState(UNIT_STAT_CHARGING);
i_owner->m_TempSpeed = speed;
if(i_owner->GetTypeId()==TYPEID_PLAYER)
@@ -322,6 +363,7 @@ MotionMaster::MoveCharge(float x, float y, float z, float speed, uint32 id)
Mutate(new PointMovementGenerator<Creature>(id,x,y,z), MOTION_SLOT_CONTROLLED);
}
}
+
void MotionMaster::MoveFall(float z, uint32 id)
{
i_owner->SetFlying(false);
@@ -329,6 +371,7 @@ void MotionMaster::MoveFall(float z, uint32 id)
//AddUnitMovementFlag(MOVEMENTFLAG_FALLING);
MoveCharge(i_owner->GetPositionX(), i_owner->GetPositionY(), z, SPEED_CHARGE, id);
}
+
void
MotionMaster::MoveSeekAssistance(float x, float y, float z)
{
@@ -345,6 +388,7 @@ MotionMaster::MoveSeekAssistance(float x, float y, float z)
Mutate(new AssistanceMovementGenerator(x,y,z), MOTION_SLOT_ACTIVE);
}
}
+
void
MotionMaster::MoveSeekAssistanceDistract(uint32 time)
{
@@ -359,13 +403,16 @@ MotionMaster::MoveSeekAssistanceDistract(uint32 time)
Mutate(new AssistanceDistractMovementGenerator(time), MOTION_SLOT_ACTIVE);
}
}
+
void
MotionMaster::MoveFleeing(Unit* enemy, uint32 time)
{
if(!enemy)
return;
+
if(i_owner->HasAuraType(SPELL_AURA_PREVENTS_FLEEING))
return;
+
if(i_owner->GetTypeId()==TYPEID_PLAYER)
{
DEBUG_LOG("Player (GUID: %u) flee from %s (GUID: %u)", i_owner->GetGUIDLow(),
@@ -386,6 +433,7 @@ MotionMaster::MoveFleeing(Unit* enemy, uint32 time)
Mutate(new FleeingMovementGenerator<Creature>(enemy->GetGUID()), MOTION_SLOT_CONTROLLED);
}
}
+
void
MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
{
@@ -401,11 +449,13 @@ MotionMaster::MoveTaxiFlight(uint32 path, uint32 pathnode)
i_owner->GetEntry(), i_owner->GetGUIDLow(), path, pathnode );
}
}
+
void
MotionMaster::MoveDistract(uint32 timer)
{
if(Impl[MOTION_SLOT_CONTROLLED])
return;
+
if(i_owner->GetTypeId()==TYPEID_PLAYER)
{
DEBUG_LOG("Player (GUID: %u) distracted (timer: %u)", i_owner->GetGUIDLow(), timer);
@@ -415,9 +465,11 @@ MotionMaster::MoveDistract(uint32 timer)
DEBUG_LOG("Creature (Entry: %u GUID: %u) (timer: %u)",
i_owner->GetEntry(), i_owner->GetGUIDLow(), timer);
}
+
DistractMovementGenerator* mgen = new DistractMovementGenerator(timer);
Mutate(mgen, MOTION_SLOT_CONTROLLED);
}
+
void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
{
if(MovementGenerator *curr = Impl[slot])
@@ -432,6 +484,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
{
i_top = slot;
}
+
if(i_top > slot)
needInit[slot] = true;
else
@@ -441,6 +494,7 @@ void MotionMaster::Mutate(MovementGenerator *m, MovementSlot slot)
}
Impl[slot] = m;
}
+
void MotionMaster::MovePath(uint32 path_id, bool repeatable)
{
if(!path_id)
@@ -455,19 +509,24 @@ void MotionMaster::MovePath(uint32 path_id, bool repeatable)
if( !isStatic( curr ) )
delete curr;
}*/
+
//i_owner->GetTypeId()==TYPEID_PLAYER ?
//Mutate(new WaypointMovementGenerator<Player>(path_id, repeatable)):
Mutate(new WaypointMovementGenerator<Creature>(path_id, repeatable), MOTION_SLOT_IDLE);
+
DEBUG_LOG("%s (GUID: %u) start moving over path(Id:%u, repeatable: %s)",
i_owner->GetTypeId()==TYPEID_PLAYER ? "Player" : "Creature",
i_owner->GetGUIDLow(), path_id, repeatable ? "YES" : "NO" );
}
+
void MotionMaster::MoveRotate(uint32 time, RotateDirection direction)
{
if(!time)
return;
+
Mutate(new RotateMovementGenerator(time, direction), MOTION_SLOT_ACTIVE);
}
+
void MotionMaster::propagateSpeedChange()
{
/*Impl::container_type::iterator it = Impl::c.begin();
@@ -481,12 +540,15 @@ void MotionMaster::propagateSpeedChange()
Impl[i]->unitSpeedChanged();
}
}
+
MovementGeneratorType MotionMaster::GetCurrentMovementGeneratorType() const
{
if(empty())
return IDLE_MOTION_TYPE;
+
return top()->GetMovementGeneratorType();
}
+
MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
{
if(!Impl[slot])
@@ -494,11 +556,13 @@ MovementGeneratorType MotionMaster::GetMotionSlotType(int slot) const
else
return Impl[slot]->GetMovementGeneratorType();
}
+
void MotionMaster::InitTop()
{
top()->Initialize(*i_owner);
needInit[i_top] = false;
}
+
void MotionMaster::DirectDelete(_Ty curr)
{
if(isStatic(curr))
@@ -506,6 +570,7 @@ void MotionMaster::DirectDelete(_Ty curr)
curr->Finalize(*i_owner);
delete curr;
}
+
void MotionMaster::DelayedDelete(_Ty curr)
{
sLog.outCrash("Unit (Entry %u) is trying to delete its updating MG (Type %u)!", i_owner->GetEntry(), curr->GetMovementGeneratorType());
@@ -515,10 +580,12 @@ void MotionMaster::DelayedDelete(_Ty curr)
m_expList = new ExpireList();
m_expList->push_back(curr);
}
+
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
index 554defe6b7e..c6755bfa167 100644
--- a/src/game/MotionMaster.h
+++ b/src/game/MotionMaster.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_MOTIONMASTER_H
#define TRINITY_MOTIONMASTER_H
+
#include "Common.h"
#include <vector>
#include "SharedDefines.h"
#include "Object.h"
+
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
{
@@ -48,6 +53,7 @@ enum MovementGeneratorType
ROTATE_MOTION_TYPE = 14,
NULL_MOTION_TYPE = 15,
};
+
enum MovementSlot
{
MOTION_SLOT_IDLE,
@@ -55,19 +61,23 @@ enum MovementSlot
MOTION_SLOT_CONTROLLED,
MAX_MOTION_SLOT,
};
+
enum MMCleanFlag
{
MMCF_NONE = 0,
MMCF_UPDATE = 1, // Clear or Expire called from update
MMCF_RESET = 2 // Flag if need top()->Reset()
};
+
enum RotateDirection
{
ROTATE_DIRECTION_LEFT,
ROTATE_DIRECTION_RIGHT,
};
+
// assume it is 25 yard per 0.6 second
#define SPEED_CHARGE 42.0f
+
class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *>
{
private:
@@ -77,12 +87,15 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *>
bool needInit[MAX_MOTION_SLOT];
typedef std::vector<_Ty> ExpireList;
int i_top;
+
bool empty() const { return (i_top < 0); }
void pop() { Impl[i_top] = NULL; --i_top; }
void push(_Ty _Val) { ++i_top; Impl[i_top] = _Val; }
+
bool needInitTop() const { return needInit[i_top]; }
void InitTop();
public:
+
explicit MotionMaster(Unit *unit) : i_owner(unit), m_expList(NULL), m_cleanFlag(MMCF_NONE), i_top(-1)
{
for(uint8 i = 0; i < MAX_MOTION_SLOT; ++i)
@@ -92,13 +105,17 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *>
}
}
~MotionMaster();
+
void Initialize();
void InitDefault();
+
int size() const { return i_top + 1; }
_Ty top() const { return Impl[i_top]; }
_Ty GetMotionSlot(int slot) const { return Impl[slot]; }
+
void DirectDelete(_Ty curr);
void DelayedDelete(_Ty curr);
+
void UpdateMotion(uint32 diff);
void Clear(bool reset = true)
{
@@ -126,6 +143,7 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *>
else
DirectExpire(reset);
}
+
void MoveIdle(MovementSlot slot = MOTION_SLOT_ACTIVE);
void MoveTargetedHome();
void MoveRandom(float spawndist = 0.0f);
@@ -147,16 +165,22 @@ class TRINITY_DLL_SPEC MotionMaster //: private std::stack<MovementGenerator *>
void MoveDistract(uint32 time);
void MovePath(uint32 path_id, bool repeatable);
void MoveRotate(uint32 time, RotateDirection direction);
+
MovementGeneratorType GetCurrentMovementGeneratorType() const;
MovementGeneratorType GetMotionSlotType(int slot) const;
+
void propagateSpeedChange();
+
bool GetDestination(float &x, float &y, float &z);
private:
void Mutate(MovementGenerator *m, MovementSlot slot); // use Move* functions instead
+
void DirectClean(bool reset);
void DelayedClean();
+
void DirectExpire(bool reset);
void DelayedExpire();
+
Unit *i_owner;
ExpireList *m_expList;
uint8 m_cleanFlag;
diff --git a/src/game/MovementGenerator.cpp b/src/game/MovementGenerator.cpp
index 76efd901019..7784df315c9 100644
--- a/src/game/MovementGenerator.cpp
+++ b/src/game/MovementGenerator.cpp
@@ -17,7 +17,9 @@
* 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
index 270479bfa29..cb352b7f1c6 100644
--- a/src/game/MovementGenerator.h
+++ b/src/game/MovementGenerator.h
@@ -17,27 +17,38 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_MOVEMENTGENERATOR_H
#define TRINITY_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 TRINITY_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 T, class D>
class TRINITY_DLL_SPEC MovementGeneratorMedium : public MovementGenerator
{
@@ -69,16 +80,20 @@ class TRINITY_DLL_SPEC MovementGeneratorMedium : public MovementGenerator
void Reset(T &u);
bool Update(T &u, const uint32 &time_diff);
};
+
struct SelectableMovement : public FactoryHolder<MovementGenerator,MovementGeneratorType>
{
SelectableMovement(MovementGeneratorType mgt) : FactoryHolder<MovementGenerator,MovementGeneratorType>(mgt) {}
};
+
template<class REAL_MOVEMENT>
struct MovementGeneratorFactory : public SelectableMovement
{
MovementGeneratorFactory(MovementGeneratorType mgt) : SelectableMovement(mgt) {}
+
MovementGenerator* Create(void *) const;
};
+
typedef FactoryHolder<MovementGenerator,MovementGeneratorType> MovementGeneratorCreator;
typedef FactoryHolder<MovementGenerator,MovementGeneratorType>::FactoryHolderRegistry MovementGeneratorRegistry;
typedef FactoryHolder<MovementGenerator,MovementGeneratorType>::FactoryHolderRepository MovementGeneratorRepository;
diff --git a/src/game/MovementHandler.cpp b/src/game/MovementHandler.cpp
index 822818273ed..e3cc490adb4 100644
--- a/src/game/MovementHandler.cpp
+++ b/src/game/MovementHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -32,31 +33,39 @@
#include "WaypointMovementGenerator.h"
#include "InstanceSaveMgr.h"
#include "ObjectMgr.h"
+
void WorldSession::HandleMoveWorldportAckOpcode( WorldPacket & /*recv_data*/ )
{
sLog.outDebug( "WORLD: got MSG_MOVE_WORLDPORT_ACK." );
HandleMoveWorldportAckOpcode();
}
+
void WorldSession::HandleMoveWorldportAckOpcode()
{
// ignore unexpected far teleports
if(!GetPlayer()->IsBeingTeleportedFar())
return;
+
// get the teleport destination
WorldLocation &loc = GetPlayer()->GetTeleportDest();
+
// possible errors in the coordinate validity check
if(!MapManager::IsValidMapCoord(loc))
{
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.GetMapId());
InstanceTemplate const* mInstance = objmgr.GetInstanceTemplate(loc.GetMapId());
+
// reset instance validity, except if going to an instance inside an instance
if(GetPlayer()->m_InstanceValid == false && !mInstance)
GetPlayer()->m_InstanceValid = true;
+
GetPlayer()->SetSemaphoreTeleportFar(false);
+
Map * oldMap = GetPlayer()->GetMap();
assert(oldMap);
if(GetPlayer()->IsInWorld())
@@ -64,6 +73,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
sLog.outCrash("Player is still in world when teleported out of map %u! to new map %u", oldMap->GetId(), loc.GetMapId());
oldMap->Remove(GetPlayer(), false);
}
+
// relocate the player to the teleport destination
Map * newMap = MapManager::Instance().CreateMap(loc.GetMapId(), GetPlayer(), 0);
// the CanEnter checks are done in TeleporTo but conditions may change
@@ -76,8 +86,10 @@ void WorldSession::HandleMoveWorldportAckOpcode()
}
else
GetPlayer()->Relocate(&loc);
+
GetPlayer()->ResetMap();
GetPlayer()->SetMap(newMap);
+
GetPlayer()->SendInitialPacketsBeforeAddToMap();
if(!GetPlayer()->GetMap()->Add(GetPlayer()))
{
@@ -87,6 +99,7 @@ void WorldSession::HandleMoveWorldportAckOpcode()
GetPlayer()->TeleportTo(GetPlayer()->m_homebindMapId, GetPlayer()->m_homebindX, GetPlayer()->m_homebindY, GetPlayer()->m_homebindZ, GetPlayer()->GetOrientation());
return;
}
+
// battleground state prepare (in case join to BG), at relogin/tele player not invited
// only add to bg group and object, if the player was invited (else he entered through command)
if(_player->InBattleGround())
@@ -106,7 +119,9 @@ void WorldSession::HandleMoveWorldportAckOpcode()
bg->AddPlayer(_player);
}
}
+
GetPlayer()->SendInitialPacketsAfterAddToMap();
+
// flight fast teleport case
if(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE)
{
@@ -117,10 +132,12 @@ void WorldSession::HandleMoveWorldportAckOpcode()
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())
@@ -132,48 +149,65 @@ void WorldSession::HandleMoveWorldportAckOpcode()
GetPlayer()->SaveToDB();
}
}
+
if((mEntry->IsRaid() || (mEntry->IsNonRaidDungeon() && mEntry->SupportsHeroicMode() && GetPlayer()->IsHeroic())) && mInstance)
{
uint32 timeleft = sInstanceSaveManager.GetResetTimeFor(GetPlayer()->GetMapId()) - time(NULL);
GetPlayer()->SendInstanceResetWarning(GetPlayer()->GetMapId(), GetPlayer()->GetDifficulty(), timeleft);
}
+
// mount allow check
if(!mEntry->IsMountAllowed())
_player->RemoveAurasByType(SPELL_AURA_MOUNTED);
+
// update zone immediately, otherwise leave channel will cause crash in mtmap
uint32 newzone, newarea;
GetPlayer()->GetZoneAndAreaId(newzone, newarea);
GetPlayer()->UpdateZone(newzone, newarea);
+
// honorless target
if(GetPlayer()->pvpInfo.inHostileArea)
GetPlayer()->CastSpell(GetPlayer(), SPELL_ID_HONORLESS_TARGET, true);
+
// resummon pet
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
+
//lets process all delayed operations on successful teleport
GetPlayer()->ProcessDelayedOperations();
}
+
void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
{
sLog.outDebug("MSG_MOVE_TELEPORT_ACK");
uint64 guid;
uint32 flags, time;
+
recv_data >> guid;
recv_data >> flags >> time;
DEBUG_LOG("Guid " UI64FMTD, guid);
DEBUG_LOG("Flags %u, time %u", flags, time/IN_MILISECONDS);
+
Unit *mover = _player->m_mover;
Player *plMover = mover->GetTypeId() == TYPEID_PLAYER ? (Player*)mover : NULL;
+
if(!plMover || !plMover->IsBeingTeleportedNear())
return;
+
if(guid != plMover->GetGUID())
return;
+
plMover->SetSemaphoreTeleportNear(false);
+
uint32 old_zone = plMover->GetZoneId();
+
WorldLocation const& dest = plMover->GetTeleportDest();
+
plMover->SetPosition(dest, true);
+
uint32 newzone, newarea;
plMover->GetZoneAndAreaId(newzone, newarea);
plMover->UpdateZone(newzone, newarea);
+
// new zone
if(old_zone != newzone)
{
@@ -181,27 +215,34 @@ void WorldSession::HandleMoveTeleportAck(WorldPacket& recv_data)
if(plMover->pvpInfo.inHostileArea)
plMover->CastSpell(plMover, SPELL_ID_HONORLESS_TARGET, true);
}
+
// resummon pet
GetPlayer()->ResummonPetTemporaryUnSummonedIfAny();
+
//lets process all delayed operations on successful teleport
GetPlayer()->ProcessDelayedOperations();
}
+
void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
{
uint32 opcode = recv_data.GetOpcode();
//sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(opcode), opcode, opcode);
+
Unit *mover = _player->m_mover;
Player *plMover = mover->GetTypeId()==TYPEID_PLAYER ? (Player*)mover : NULL;
+
// ignore, waiting processing in WorldSession::HandleMoveWorldportAckOpcode and WorldSession::HandleMoveTeleportAck
if(plMover && plMover->IsBeingTeleported())
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
/* extract packet */
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
/*----------------*/
+
if(recv_data.size() != recv_data.rpos())
{
sLog.outError("MovementHandler: player %s (guid %d, account %u) sent a packet (opcode %u) that is " SIZEFMTD " 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());
@@ -209,11 +250,13 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
if (!MaNGOS::IsValidMapCoord(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o))
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
/* handle special cases */
if (movementInfo.flags & MOVEMENTFLAG_ONTRANSPORT)
{
@@ -224,12 +267,14 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
if( !MaNGOS::IsValidMapCoord(movementInfo.x+movementInfo.t_x, movementInfo.y + movementInfo.t_y,
movementInfo.z + movementInfo.t_z, movementInfo.o + movementInfo.t_o) )
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
// if we boarded a transport, add us to it
if (plMover && !plMover->m_transport)
{
@@ -244,6 +289,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
}
}
}
+
if(!mover->GetTransport() && !mover->GetVehicle())
movementInfo.flags &= ~MOVEMENTFLAG_ONTRANSPORT;
}
@@ -258,31 +304,39 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
movementInfo.t_time = 0;
movementInfo.t_seat = -1;
}
+
// fall damage generation (ignore in flight case that can be triggered also at lags in moment teleportation to another map).
if (opcode == MSG_MOVE_FALL_LAND && plMover && !plMover->isInFlight())
plMover->HandleFall(movementInfo);
+
if (plMover && ((movementInfo.flags & MOVEMENTFLAG_SWIMMING) != 0) != plMover->IsInWater())
{
// now client not include swimming flag in case jumping under water
plMover->SetInWater( !plMover->IsInWater() || plMover->GetBaseMap()->IsUnderWater(movementInfo.x, movementInfo.y, movementInfo.z) );
}
+
/*----------------------*/
+
/* process position-change */
recv_data.put<uint32>(6, getMSTime()); // fix time, offset flags(4) + unk(2)
WorldPacket data(recv_data.GetOpcode(), (mover->GetPackGUID().size()+recv_data.size()));
data.append(mover->GetPackGUID()); // use mover guid
data.append(recv_data.contents(), recv_data.size());
GetPlayer()->SendMessageToSet(&data, false);
+
mover->m_movementInfo = movementInfo;
+
if(mover->GetVehicle())
{
mover->SetOrientation(movementInfo.o);
return;
}
+
if(plMover) // nothing is charmed, or player charmed
{
plMover->SetPosition(movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
plMover->UpdateFallInformationIfNeed(movementInfo, recv_data.GetOpcode());
+
if(movementInfo.z < -500.0f)
{
if(plMover->InBattleGround()
@@ -308,6 +362,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
plMover->BuildPlayerRepop();
}
}
+
// cancel the death timer here if started
plMover->RepopAtGraveyard();
}
@@ -316,6 +371,7 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
else // creature charmed
{
mover->GetMap()->CreatureRelocation((Creature*)mover, movementInfo.x, movementInfo.y, movementInfo.z, movementInfo.o);
+
/*if(mover->canFly())
{
bool flying = mover->IsFlying();
@@ -323,34 +379,46 @@ void WorldSession::HandleMovementOpcodes( WorldPacket & recv_data )
mover->SetFlying(flying);
}*/
}
+
//sLog.outString("Receive Movement Packet %s:", opcodeTable[recv_data.GetOpcode()]);
//mover->OutMovementInfo();
}
+
void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
{
//sLog.outDebug("WORLD: Recvd %s (%u, 0x%X) opcode", LookupOpcodeName(recv_data.GetOpcode()), recv_data.GetOpcode(), recv_data.GetOpcode());
+
/* extract packet */
uint64 guid;
uint32 unk1;
float newspeed;
+
recv_data >> guid;
+
// now can skip not our packet
if(_player->GetGUID() != guid)
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
// continue parse packet
+
recv_data >> unk1; // counter or moveEvent
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
+
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", "RunBack", "Swim", "SwimBack", "TurnRate", "Flight", "FlightBack", "PitchRate" };
+
uint16 opcode = recv_data.GetOpcode();
switch(opcode)
{
@@ -367,6 +435,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
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)
@@ -375,6 +444,7 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
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
@@ -391,13 +461,17 @@ void WorldSession::HandleForceSpeedChangeAck(WorldPacket &recv_data)
}
}
}
+
void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_SET_ACTIVE_MOVER");
+
uint64 guid;
recv_data >> guid;
+
if(guid == GetPlayer()->m_mover->GetGUID())
return;
+
if(Unit *mover = ObjectAccessor::GetUnit(*GetPlayer(), guid))
{
GetPlayer()->SetMover(mover);
@@ -412,41 +486,52 @@ void WorldSession::HandleSetActiveMoverOpcode(WorldPacket &recv_data)
else
{
sLog.outError("HandleSetActiveMoverOpcode: incorrect mover guid: mover is " UI64FMTD " and should be " UI64FMTD, guid, _player->m_mover->GetGUID());
- GetPlayer()->SetMover(GetPlayer());
+ GetPlayer()->SetMover(GetPlayer());
}
}
+
void WorldSession::HandleMoveNotActiveMover(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_MOVE_NOT_ACTIVE_MOVER");
+
uint64 old_mover_guid;
recv_data >> old_mover_guid;
+
/*if(_player->m_mover->GetGUID() == old_mover_guid)
{
sLog.outError("HandleMoveNotActiveMover: incorrect mover guid: mover is " I64FMT " and should be " I64FMT " instead of " I64FMT, _player->m_mover->GetGUID(), _player->GetGUID(), old_mover_guid);
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}*/
+
ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo);
}
+
void WorldSession::HandleDismissControlledVehicle(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_DISMISS_CONTROLLED_VEHICLE");
recv_data.hexlike();
+
uint64 vehicleGUID = _player->GetCharmGUID();
+
if(!vehicleGUID) // something wrong here...
{
recv_data.rpos(recv_data.wpos()); // prevent warnings spam
return;
}
+
ReadMovementInfo(recv_data, &_player->m_mover->m_movementInfo);
_player->ExitVehicle();
}
+
void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE");
recv_data.hexlike();
+
if(!GetPlayer()->GetVehicle())
return;
+
if(recv_data.GetOpcode() == CMSG_REQUEST_VEHICLE_PREV_SEAT)
{
GetPlayer()->ChangeSeat(-1, false);
@@ -459,11 +544,14 @@ void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
}
else if(recv_data.GetOpcode() == CMSG_CHANGE_SEATS_ON_CONTROLLED_VEHICLE)
ReadMovementInfo(recv_data, &GetPlayer()->GetVehicleBase()->m_movementInfo);
+
uint64 guid;
if(!recv_data.readPackGUID(guid))
return;
+
int8 seatId;
recv_data >> seatId;
+
if(!guid)
GetPlayer()->ChangeSeat(-1, seatId > 0); // prev/next
else if(Unit *vehUnit = ObjectAccessor::GetUnit(*GetPlayer(), guid))
@@ -471,53 +559,71 @@ void WorldSession::HandleChangeSeatsOnControlledVehicle(WorldPacket &recv_data)
if(vehicle->HasEmptySeat(seatId))
GetPlayer()->EnterVehicle(vehicle, seatId);
}
+
void WorldSession::HandleRequestVehicleExit(WorldPacket &recv_data)
{
sLog.outDebug("WORLD: Recvd CMSG_REQUEST_VEHICLE_EXIT");
recv_data.hexlike();
GetPlayer()->ExitVehicle();
}
+
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 )
{
sLog.outDebug("CMSG_MOVE_KNOCK_BACK_ACK");
+
recv_data.read_skip<uint64>(); // guid
recv_data.read_skip<uint32>(); // unk
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
}
+
void WorldSession::HandleMoveHoverAck( WorldPacket& recv_data )
{
sLog.outDebug("CMSG_MOVE_HOVER_ACK");
+
recv_data.read_skip<uint64>(); // guid
recv_data.read_skip<uint32>(); // unk
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
+
recv_data.read_skip<uint32>(); // unk2
}
+
void WorldSession::HandleMoveWaterWalkAck(WorldPacket& recv_data)
{
sLog.outDebug("CMSG_MOVE_WATER_WALK_ACK");
+
recv_data.read_skip<uint64>(); // guid
recv_data.read_skip<uint32>(); // unk
+
MovementInfo movementInfo;
ReadMovementInfo(recv_data, &movementInfo);
+
recv_data.read_skip<uint32>(); // unk2
}
+
void WorldSession::HandleSummonResponseOpcode(WorldPacket& recv_data)
{
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
index 81729a0c78b..4e15fd77595 100644
--- a/src/game/NPCHandler.cpp
+++ b/src/game/NPCHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -36,81 +37,103 @@
#include "BattleGroundMgr.h"
#include "BattleGround.h"
#include "Guild.h"
+
void WorldSession::HandleTabardVendorActivateOpcode( WorldPacket & recv_data )
{
uint64 guid;
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(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 )
{
uint64 guid;
+
sLog.outDebug( "WORLD: Received CMSG_BANKER_ACTIVATE" );
+
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(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 )
{
uint64 guid;
+
recv_data >> guid;
SendTrainerList( guid );
}
+
void WorldSession::SendTrainerList( uint64 guid )
{
std::string str = GetTrinityString(LANG_NPC_TAINER_HELLO);
SendTrainerList( guid, str );
}
+
void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle )
{
sLog.outDebug( "WORLD: SendTrainerList" );
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
// trainer list loaded at check;
if(!unit->isCanTrainingOf(_player,true))
return;
+
CreatureInfo const *ci = unit->GetCreatureInfo();
+
if (!ci)
{
sLog.outDebug( "WORLD: SendTrainerList - (GUID: %u) NO CREATUREINFO!",GUID_LOPART(guid) );
return;
}
+
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if(!trainer_spells)
{
@@ -118,27 +141,35 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle )
GUID_LOPART(guid), unit->GetEntry());
return;
}
+
WorldPacket data( SMSG_TRAINER_LIST, 8+4+4+trainer_spells->spellList.size()*38 + strTitle.size()+1);
data << guid;
data << uint32(trainer_spells->trainerType);
+
size_t count_pos = data.wpos();
data << uint32(trainer_spells->spellList.size());
+
// reputation discount
float fDiscountMod = _player->GetReputationPriceDiscount(unit);
bool can_learn_primary_prof = GetPlayer()->GetFreePrimaryProfessionPoints() > 0;
+
uint32 count = 0;
for(TrainerSpellMap::const_iterator itr = trainer_spells->spellList.begin(); itr != trainer_spells->spellList.end(); ++itr)
{
TrainerSpell const* tSpell = &itr->second;
+
if(!_player->IsSpellFitByClassAndRace(tSpell->learnedSpell))
continue;
+
bool primary_prof_first_rank = spellmgr.IsPrimaryProfessionFirstRankSpell(tSpell->learnedSpell);
SpellChainNode const* chain_node = spellmgr.GetSpellChainNode(tSpell->learnedSpell);
uint32 req_spell = spellmgr.GetSpellRequired(tSpell->spell);
TrainerSpellState state = _player->GetTrainerSpellState(tSpell);
+
data << uint32(tSpell->spell); // learned spell (or cast-spell in profession case)
data << uint8(state==TRAINER_SPELL_GREEN_DISABLED ? TRAINER_SPELL_GREEN : state);
data << uint32(floor(tSpell->spellCost * fDiscountMod));
+
data << uint32(primary_prof_first_rank && can_learn_primary_prof ? 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
@@ -149,81 +180,105 @@ void WorldSession::SendTrainerList( uint64 guid, const std::string& strTitle )
data << uint32(!tSpell->IsCastable() && chain_node && chain_node->prev ? chain_node->prev : req_spell);
data << uint32(!tSpell->IsCastable() && chain_node && chain_node->prev ? req_spell : 0);
data << uint32(0);
+
++count;
}
+
data << strTitle;
+
data.put<uint32>(count_pos,count);
SendPacket( &data );
}
+
void WorldSession::HandleTrainerBuySpellOpcode( WorldPacket & recv_data )
{
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 = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
if(!unit->isCanTrainingOf(_player,true))
return;
+
// check present spell in trainer spell list
TrainerSpellData const* trainer_spells = unit->GetTrainerSpells();
if(!trainer_spells)
return;
+
// not found, cheat?
TrainerSpell const* trainer_spell = trainer_spells->Find(spellId);
if(!trainer_spell)
return;
+
// can't be learn, cheat? Or double learn with lags...
if(_player->GetTrainerSpellState(trainer_spell) != TRAINER_SPELL_GREEN)
return;
+
// apply reputation discount
uint32 nSpellCost = uint32(floor(trainer_spell->spellCost * _player->GetReputationPriceDiscount(unit)));
+
// check money requirement
if(_player->GetMoney() < nSpellCost )
return;
+
_player->ModifyMoney( -int32(nSpellCost) );
+
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);
+
// learn explicitly or cast explicitly
if(trainer_spell->IsCastable ())
//FIXME: prof. spell entry in trainer list not marked gray until list re-open.
_player->CastSpell(_player,trainer_spell->spell,true);
else
_player->learnSpell(spellId,false);
+
data.Initialize(SMSG_TRAINER_BUY_SUCCEEDED, 12);
data << uint64(guid) << uint32(trainer_spell->spell);
SendPacket(&data);
}
+
void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_GOSSIP_HELLO" );
+
uint64 guid;
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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;
}
+
GetPlayer()->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
// remove fake death
//if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
// GetPlayer()->RemoveAurasByType(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())
{
@@ -235,6 +290,7 @@ void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
return;
}
}
+
if(!Script->GossipHello( _player, unit ))
{
_player->TalkedToCreature(unit->GetEntry(),unit->GetGUID());
@@ -242,29 +298,36 @@ void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
unit->sendPreparedGossip(_player);
}
}
+
/*void WorldSession::HandleGossipSelectOptionOpcode( WorldPacket & recv_data )
{
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 ))
{
sLog.outDebug("reading string");
recv_data >> code;
sLog.outDebug("string read: %s", code.c_str());
}
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
if(!code.empty())
{
if (!Script->GossipSelectWithCode(_player, unit, _player->PlayerTalkClass->GossipOptionSender (option), _player->PlayerTalkClass->GossipOptionAction( option ), code.c_str()))
@@ -276,39 +339,51 @@ void WorldSession::HandleGossipHelloOpcode( WorldPacket & recv_data )
unit->OnGossipSelect (_player, option);
}
}*/
+
void WorldSession::HandleSpiritHealerActivateOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_SPIRIT_HEALER_ACTIVATE");
+
uint64 guid;
+
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
SendSpiritResurrect();
}
+
void WorldSession::SendSpiritResurrect()
{
_player->ResurrectPlayer(0.5f, 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
@@ -320,32 +395,41 @@ void WorldSession::SendSpiritResurrect()
else
//ObjectAccessor::UpdateVisibilityForPlayer(_player);
_player->SetToNotify();
+
_player->SaveToDB();
}
+
void WorldSession::HandleBinderActivateOpcode( WorldPacket & recv_data )
{
uint64 npcGUID;
recv_data >> npcGUID;
+
if(!GetPlayer()->IsInWorld() || !GetPlayer()->isAlive())
return;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
SendBindPoint(unit);
}
+
void WorldSession::SendBindPoint(Creature *npc)
{
// prevent set homebind to instances in any case
if(GetPlayer()->GetMap()->Instanceable())
return;
+
uint32 bindspell = 3286;
uint32 zone_id = _player->GetZoneId();
+
// 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(), zone_id, _player->GetPositionX(), _player->GetPositionY(), _player->GetPositionZ(), _player->GetGUIDLow());
_player->m_homebindMapId = _player->GetMapId();
@@ -353,12 +437,15 @@ void WorldSession::SendBindPoint(Creature *npc)
_player->m_homebindX = _player->GetPositionX();
_player->m_homebindY = _player->GetPositionY();
_player->m_homebindZ = _player->GetPositionZ();
+
// send spell for bind 3286 bind magic
_player->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());
@@ -367,47 +454,63 @@ void WorldSession::SendBindPoint(Creature *npc)
data << uint32(_player->GetMapId());
data << uint32(zone_id);
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",zone_id);
+
// zone update
data.Initialize( SMSG_PLAYERBOUND, 8+4 );
data << uint64(_player->GetGUID());
data << uint32(zone_id);
SendPacket( &data );
+
_player->PlayerTalkClass->CloseGossip();
}
+
void WorldSession::HandleListStabledPetsOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Recv MSG_LIST_STABLED_PETS");
uint64 npcGUID;
+
recv_data >> npcGUID;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
// remove mounts this fix bug where getting pet from stable while mounted deletes pet.
if(GetPlayer()->IsMounted())
GetPlayer()->RemoveAurasByType(SPELL_AURA_MOUNTED);
+
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();
+
size_t wpos = data.wpos();
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)
{
@@ -418,43 +521,56 @@ void WorldSession::SendStablePet(uint64 guid )
data << uint8(1); // 1 = current, 2/3 = in stable (any from 4,5,... create problems with proper show)
++num;
}
+
// 0 1 2 3 4
QueryResult* result = CharacterDatabase.PQuery("SELECT owner, id, entry, level, name FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot",
_player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
+
if(result)
{
do
{
Field *fields = result->Fetch();
+
data << uint32(fields[1].GetUInt32()); // petnumber
data << uint32(fields[2].GetUInt32()); // creature entry
data << uint32(fields[3].GetUInt32()); // level
data << fields[4].GetString(); // name
data << uint8(2); // 1 = current, 2/3 = in stable (any from 4,5,... create problems with proper show)
+
++num;
}while( result->NextRow() );
+
delete result;
}
+
data.put<uint8>(wpos, num); // set real data to placeholder
SendPacket(&data);
}
+
void WorldSession::HandleStablePet( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Recv CMSG_STABLE_PET");
uint64 npcGUID;
+
recv_data >> npcGUID;
+
if(!GetPlayer()->isAlive())
return;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
Pet *pet = _player->GetPet();
+
// can't place in stable dead pet
if(!pet||!pet->isAlive()||pet->getPetType()!=HUNTER_PET)
{
@@ -463,7 +579,9 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
uint32 free_slot = 1;
+
QueryResult *result = CharacterDatabase.PQuery("SELECT owner,slot,id FROM character_pet WHERE owner = '%u' AND slot >= '%u' AND slot <= '%u' ORDER BY slot ",
_player->GetGUIDLow(),PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
if(result)
@@ -471,15 +589,20 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data )
do
{
Field *fields = result->Fetch();
+
uint32 slot = fields[1].GetUInt32();
+
// slots ordered in query, and if not equal then free
if(slot!=free_slot)
break;
+
// this slot not free, skip
++free_slot;
}while( result->NextRow() );
+
delete result;
}
+
WorldPacket data(SMSG_STABLE_RESULT, 1);
if( free_slot > 0 && free_slot <= GetPlayer()->m_stableSlots)
{
@@ -488,24 +611,31 @@ void WorldSession::HandleStablePet( WorldPacket & recv_data )
}
else
data << uint8(0x06);
+
SendPacket(&data);
}
+
void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Recv CMSG_UNSTABLE_PET.");
uint64 npcGUID;
uint32 petnumber;
+
recv_data >> npcGUID >> petnumber;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
uint32 creature_id = 0;
+
{
QueryResult *result = CharacterDatabase.PQuery("SELECT entry FROM character_pet WHERE owner = '%u' AND id = '%u' AND slot >='%u' AND slot <= '%u'",
_player->GetGUIDLow(),petnumber,PET_SAVE_FIRST_STABLE_SLOT,PET_SAVE_LAST_STABLE_SLOT);
@@ -516,6 +646,7 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
delete result;
}
}
+
if(!creature_id)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
@@ -523,6 +654,7 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(creature_id);
if(!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
@@ -531,6 +663,7 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
Pet* pet = _player->GetPet();
if(pet && pet->isAlive())
{
@@ -539,9 +672,11 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
// delete dead pet
if(pet)
_player->RemovePet(pet,PET_SAVE_AS_DELETED);
+
Pet *newpet = new Pet(_player, HUNTER_PET);
if(!newpet->LoadPetFromDB(_player,creature_id,petnumber))
{
@@ -552,25 +687,32 @@ void WorldSession::HandleUnstablePet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
WorldPacket data(SMSG_STABLE_RESULT, 1);
data << uint8(0x09);
SendPacket(&data);
}
+
void WorldSession::HandleBuyStableSlot( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Recv CMSG_BUY_STABLE_SLOT.");
uint64 npcGUID;
+
recv_data >> npcGUID;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
WorldPacket data(SMSG_STABLE_RESULT, 200);
+
if(GetPlayer()->m_stableSlots < MAX_PET_STABLES)
{
StableSlotPricesEntry const *SlotPrice = sStableSlotPricesStore.LookupEntry(GetPlayer()->m_stableSlots+1);
@@ -585,40 +727,53 @@ void WorldSession::HandleBuyStableSlot( WorldPacket & recv_data )
}
else
data << uint8(0x06);
+
SendPacket(&data);
}
+
void WorldSession::HandleStableRevivePet( WorldPacket &/* recv_data */)
{
sLog.outDebug("HandleStableRevivePet: Not implemented");
}
+
void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: Recv CMSG_STABLE_SWAP_PET.");
uint64 npcGUID;
uint32 pet_number;
+
recv_data >> npcGUID >> pet_number;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(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 creature_id = fields[1].GetUInt32();
delete result;
+
if(!creature_id)
{
WorldPacket data(SMSG_STABLE_RESULT, 1);
@@ -626,6 +781,7 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
CreatureInfo const* creatureInfo = objmgr.GetCreatureTemplate(creature_id);
if(!creatureInfo || !creatureInfo->isTameable(_player->CanTameExoticPets()))
{
@@ -634,8 +790,10 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
SendPacket(&data);
return;
}
+
// move alive pet to slot or delete dead pet
_player->RemovePet(pet,pet->isAlive() ? PetSaveMode(slot) : PET_SAVE_AS_DELETED);
+
// summon unstabled pet
Pet *newpet = new Pet(_player);
if(!newpet->LoadPetFromDB(_player,creature_id,pet_number))
@@ -645,36 +803,47 @@ void WorldSession::HandleStableSwapPet( WorldPacket & recv_data )
}
else
data << uint8(0x09);
+
SendPacket(&data);
}
+
void WorldSession::HandleRepairItemOpcode( WorldPacket & recv_data )
{
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 = GetPlayer()->GetNPCIfCanInteractWith(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()->RemoveAurasByType(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)
diff --git a/src/game/NPCHandler.h b/src/game/NPCHandler.h
index c959e9957e2..4d0395a0c87 100644
--- a/src/game/NPCHandler.h
+++ b/src/game/NPCHandler.h
@@ -17,31 +17,38 @@
* 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;
@@ -50,17 +57,21 @@ struct GossipTextOption
float Probability;
QEmote Emotes[3];
};
+
struct GossipText
{
GossipTextOption Options[8];
};
+
struct PageTextLocale
{
std::vector<std::string> Text;
};
+
struct NpcTextLocale
{
NpcTextLocale() { Text_0.resize(8); Text_1.resize(8); }
+
std::vector<std::vector<std::string> > Text_0;
std::vector<std::vector<std::string> > Text_1;
};
diff --git a/src/game/Object.cpp b/src/game/Object.cpp
index cb28fd40df5..6c892ff1d5d 100644
--- a/src/game/Object.cpp
+++ b/src/game/Object.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -41,9 +42,11 @@
#include "CellImpl.h"
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
+
#include "TemporarySummon.h"
#include "Totem.h"
#include "OutdoorPvPMgr.h"
+
uint32 GuidHigh2TypeId(uint32 guid_hi)
{
switch(guid_hi)
@@ -61,17 +64,22 @@ uint32 GuidHigh2TypeId(uint32 guid_hi)
}
return NUM_CLIENT_OBJECT_TYPES; // unknown
}
+
Object::Object( ) : m_PackGUID(sizeof(uint64)+1)
{
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.appendPackGUID(0);
}
+
WorldObject::~WorldObject()
{
// this may happen because there are many !create/delete
@@ -85,6 +93,7 @@ WorldObject::~WorldObject()
ResetMap();
}
}
+
Object::~Object( )
{
if(IsInWorld())
@@ -95,12 +104,14 @@ Object::~Object( )
assert(false);
RemoveFromWorld();
}
+
if(m_objectUpdated)
{
sLog.outCrash("Object::~Object - guid="UI64FMTD", typeid=%d, entry=%u deleted but still in update list!!", GetGUID(), GetTypeId(), GetEntry());
assert(false);
ObjectAccessor::Instance().RemoveUpdateObject(this);
}
+
if(m_uint32Values)
{
//DEBUG_LOG("Object desctr 1 check (%p)",(void*)this);
@@ -109,17 +120,22 @@ Object::~Object( )
//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);
SetUInt64Value( OBJECT_FIELD_GUID, guid );
uint32 type = 0;
@@ -139,31 +155,41 @@ void Object::_Create( uint32 guidlow, uint32 entry, HighGuid guidhigh )
m_PackGUID.wpos(0);
m_PackGUID.appendPackGUID(GetGUID());
}
+
void Object::BuildMovementUpdateBlock(UpdateData * data, uint32 flags ) const
{
ByteBuffer buf(500);
+
buf << uint8( UPDATETYPE_MOVEMENT );
buf.append(GetPackGUID());
+
_BuildMovementUpdate(&buf, flags);
+
data->AddUpdateBlock(buf);
}
+
void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) const
{
if(!target)
return;
+
uint8 updatetype = UPDATETYPE_CREATE_OBJECT;
uint16 flags = m_updateFlag;
+
/** lower flag1 **/
if(target == this) // building packet for yourself
flags |= UPDATEFLAG_SELF;
+
if(flags & UPDATEFLAG_HAS_POSITION)
{
// 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))
{
@@ -182,70 +208,90 @@ void Object::BuildCreateUpdateBlockForPlayer(UpdateData *data, Player *target) c
break;
}
}
+
if(isType(TYPEMASK_UNIT))
{
if(((Unit*)this)->getVictim())
flags |= UPDATEFLAG_HAS_TARGET;
}
}
+
//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());
buf << (uint8)m_objectTypeId;
+
_BuildMovementUpdate(&buf, flags);
+
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;
+
BuildCreateUpdateBlockForPlayer(&upd, player);
upd.BuildPacket(&packet);
player->GetSession()->SendPacket(&packet);
}
+
void Object::BuildValuesUpdateBlockForPlayer(UpdateData *data, Player *target) const
{
ByteBuffer buf(500);
+
buf << (uint8) UPDATETYPE_VALUES;
buf.append(GetPackGUID());
+
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, bool anim ) const
{
ASSERT(target);
+
WorldPacket data(SMSG_DESTROY_OBJECT, 8);
data << uint64(GetGUID());
data << uint8(anim ? 1 : 0); // WotLK (bool), may be despawn animation
target->GetSession()->SendPacket( &data );
}
+
void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
{
*data << (uint16)flags; // update flags
+
// 0x20
if (flags & UPDATEFLAG_LIVING)
{
((Unit*)this)->BuildMovementPacket(data);
+
*data << ((Unit*)this)->GetSpeed( MOVE_WALK );
*data << ((Unit*)this)->GetSpeed( MOVE_RUN );
*data << ((Unit*)this)->GetSpeed( MOVE_SWIM_BACK );
@@ -255,13 +301,18 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
*data << ((Unit*)this)->GetSpeed( MOVE_FLIGHT_BACK );
*data << ((Unit*)this)->GetSpeed( MOVE_TURN_RATE );
*data << ((Unit*)this)->GetSpeed( MOVE_PITCH_RATE );
+
// 0x08000000
if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->isInFlight())
{
WPAssert(((Player*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == FLIGHT_MOTION_TYPE);
+
FlightPathMovementGenerator *fmg = (FlightPathMovementGenerator*)(((Player*)this)->GetMotionMaster()->top());
+
uint32 flags3 = MOVEFLAG_GLIDE;
+
*data << uint32(flags3); // splines flag?
+
if(flags3 & 0x20000) // may be orientation
{
*data << (float)0;
@@ -274,32 +325,43 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
*data << (float)0;
*data << (float)0;
}
+
if(flags3 & 0x10000) // probably guid there
{
*data << uint64(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?
+
*data << float(0); // added in 3.1
*data << float(0); // added in 3.1
*data << float(0); // added in 3.1
+
*data << uint32(0); // added in 3.1
+
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;
}
+
*data << uint8(0); // added in 3.0.8
+
*data << path.GetNodes()[poscount-1].x;
*data << path.GetNodes()[poscount-1].y;
*data << path.GetNodes()[poscount-1].z;
@@ -317,6 +379,7 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
*data << ((WorldObject*)this)->GetPositionY();
*data << ((WorldObject*)this)->GetPositionZ();
*data << ((WorldObject*)this)->GetOrientation();
+
if(GetTypeId() == TYPEID_CORPSE)
*data << float(((WorldObject*)this)->GetOrientation());
else
@@ -345,6 +408,7 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
}
}
}
+
// 0x8
if(flags & UPDATEFLAG_LOWGUID)
{
@@ -372,12 +436,14 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
break;
}
}
+
// 0x10
if(flags & UPDATEFLAG_HIGHGUID)
{
// not high guid
*data << uint32(0x00000000); // unk
}
+
// 0x4
if(flags & UPDATEFLAG_HAS_TARGET) // packed guid (current target guid)
{
@@ -386,27 +452,32 @@ void Object::_BuildMovementUpdate(ByteBuffer * data, uint16 flags) const
else
*data << uint8(0);
}
+
// 0x2
if(flags & UPDATEFLAG_TRANSPORT)
{
*data << uint32(getMSTime()); // ms time
}
+
// 0x80
if(flags & UPDATEFLAG_VEHICLE) // unused for now
{
*data << uint32(((Unit*)this)->GetVehicleKit()->GetVehicleInfo()->m_ID); // vehicle id
*data << float(0); // facing adjustment
}
+
// 0x200
if(flags & UPDATEFLAG_ROTATION)
{
*data << uint64(((GameObject*)this)->GetRotation());
}
}
+
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)
{
@@ -414,7 +485,9 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask
{
if ( ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster())
IsActivateToQuest = true;
+
updateMask->SetBit(GAMEOBJECT_DYNAMIC);
+
if (((GameObject*)this)->GetGoArtKit())
updateMask->SetBit(GAMEOBJECT_BYTES_1);
}
@@ -445,9 +518,12 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask
}
}
}
+
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
{
@@ -459,8 +535,10 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask
{
// remove custom flag before sending
uint32 appendValue = m_uint32Values[ index ] & ~(UNIT_NPC_FLAG_GUARD + UNIT_NPC_FLAG_OUTDOORPVP);
+
if (GetTypeId() == TYPEID_UNIT && !target->canSeeSpellClickOn((Creature*)this))
appendValue &= ~UNIT_NPC_FLAG_SPELLCLICK;
+
*data << uint32(appendValue);
}
else if (index == UNIT_FIELD_AURASTATE)
@@ -561,7 +639,7 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask
}
else
*data << m_uint32Values[ index ];
- }
+ }
else
{
// send in current format (float as float, uint32 as uint32)
@@ -623,9 +701,11 @@ void Object::_BuildValuesUpdate(uint8 updatetype, ByteBuffer * data, UpdateMask
}
}
}
+
void Object::ClearUpdateMask(bool remove)
{
memcpy(m_uint32Values_mirror, m_uint32Values, m_valuesCount*sizeof(uint32));
+
if(m_objectUpdated)
{
if(remove)
@@ -633,56 +713,71 @@ void Object::ClearUpdateMask(bool remove)
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
{
uint32 *value = m_uint32Values;
uint32 *mirror = m_uint32Values_mirror;
+
for(uint16 index = 0; index < m_valuesCount; ++index, ++value, ++mirror)
{
if(*mirror != *value)
updateMask->SetBit(index);
}
}
+
void Object::_SetCreateBits(UpdateMask *updateMask, Player* /*target*/) const
{
uint32 *value = m_uint32Values;
+
for(uint16 index = 0; index < m_valuesCount; ++index, ++value)
{
if(*value)
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)
@@ -693,12 +788,15 @@ void Object::SetInt32Value( uint16 index, int32 value )
}
}
}
+
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)
@@ -709,11 +807,14 @@ void Object::SetUInt32Value( uint16 index, uint32 value )
}
}
}
+
void Object::UpdateUInt32Value( uint16 index, uint32 value )
{
ASSERT( index < m_valuesCount || PrintIndexError( index, true ) );
+
m_uint32Values[ index ] = value;
}
+
void Object::SetUInt64Value( uint16 index, const uint64 &value )
{
ASSERT( index + 1 < m_valuesCount || PrintIndexError( index, true ) );
@@ -721,6 +822,7 @@ void Object::SetUInt64Value( uint16 index, const uint64 &value )
{
m_uint32Values[ index ] = *((uint32*)&value);
m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1);
+
if(m_inWorld)
{
if(!m_objectUpdated)
@@ -731,6 +833,7 @@ void Object::SetUInt64Value( uint16 index, const uint64 &value )
}
}
}
+
bool Object::AddUInt64Value(uint16 index, const uint64 &value)
{
ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) );
@@ -738,6 +841,7 @@ bool Object::AddUInt64Value(uint16 index, const uint64 &value)
{
m_uint32Values[ index ] = *((uint32*)&value);
m_uint32Values[ index + 1 ] = *(((uint32*)&value) + 1);
+
if(m_inWorld)
{
if(!m_objectUpdated)
@@ -750,6 +854,7 @@ bool Object::AddUInt64Value(uint16 index, const uint64 &value)
}
return false;
}
+
bool Object::RemoveUInt64Value(uint16 index, const uint64 &value)
{
ASSERT( index + 1 < m_valuesCount || PrintIndexError( index , true ) );
@@ -757,6 +862,7 @@ bool Object::RemoveUInt64Value(uint16 index, const uint64 &value)
{
m_uint32Values[ index ] = 0;
m_uint32Values[ index + 1 ] = 0;
+
if(m_inWorld)
{
if(!m_objectUpdated)
@@ -769,12 +875,15 @@ bool Object::RemoveUInt64Value(uint16 index, const uint64 &value)
}
return false;
}
+
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)
@@ -785,18 +894,22 @@ void Object::SetFloatValue( uint16 index, float value )
}
}
}
+
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)
@@ -807,18 +920,22 @@ void Object::SetByteValue( uint16 index, uint8 offset, uint8 value )
}
}
}
+
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(uint16(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)
@@ -829,18 +946,23 @@ void Object::SetUInt16Value( uint16 index, uint8 offset, uint16 value )
}
}
}
+
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);
@@ -849,18 +971,21 @@ void Object::ApplyModUInt32Value(uint16 index, int32 val, bool apply)
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);
@@ -869,14 +994,17 @@ void Object::ApplyModPositiveFloatValue(uint16 index, float val, bool apply)
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)
@@ -887,14 +1015,17 @@ void Object::SetFlag( uint16 index, uint32 newFlag )
}
}
}
+
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)
@@ -905,17 +1036,21 @@ void Object::RemoveFlag( uint16 index, uint32 oldFlag )
}
}
}
+
void Object::SetByteFlag( uint16 index, uint8 offset, uint8 newFlag )
{
ASSERT( index < m_valuesCount || PrintIndexError( index, true ) );
+
if(offset > 4)
{
sLog.outError("Object::SetByteFlag: wrong offset %u", offset);
return;
}
+
if(!(uint8(m_uint32Values[ index ] >> (offset * 8)) & newFlag))
{
m_uint32Values[ index ] |= uint32(uint32(newFlag) << (offset * 8));
+
if(m_inWorld)
{
if(!m_objectUpdated)
@@ -926,17 +1061,21 @@ void Object::SetByteFlag( uint16 index, uint8 offset, uint8 newFlag )
}
}
}
+
void Object::RemoveByteFlag( uint16 index, uint8 offset, uint8 oldFlag )
{
ASSERT( index < m_valuesCount || PrintIndexError( index, true ) );
+
if(offset > 4)
{
sLog.outError("Object::RemoveByteFlag: wrong offset %u", offset);
return;
}
+
if(uint8(m_uint32Values[ index ] >> (offset * 8)) & oldFlag)
{
m_uint32Values[ index ] &= ~uint32(uint32(oldFlag) << (offset * 8));
+
if(m_inWorld)
{
if(!m_objectUpdated)
@@ -947,12 +1086,15 @@ void Object::RemoveByteFlag( uint16 index, uint8 offset, uint8 oldFlag )
}
}
}
+
bool Object::PrintIndexError(uint32 index, bool set) const
{
sLog.outError("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;
}
+
bool Position::HasInLine(const Unit * const target, float distance, float width) const
{
if(!HasInArc(M_PI, target) || !target->IsWithinDist3d(m_positionX, m_positionY, m_positionZ, distance)) return false;
@@ -960,6 +1102,7 @@ bool Position::HasInLine(const Unit * const target, float distance, float width)
float angle = GetRelativeAngle(target);
return abs(sin(angle)) * GetExactDist2d(target->GetPositionX(), target->GetPositionY()) < width;
}
+
WorldObject::WorldObject()
: WorldLocation(), m_InstanceId(0), m_phaseMask(PHASEMASK_NORMAL), m_currMap(NULL)
, m_zoneScript(NULL)
@@ -967,24 +1110,32 @@ WorldObject::WorldObject()
, m_name("")
{
}
+
void WorldObject::SetWorldObject(bool on)
{
if(!IsInWorld())
return;
+
GetMap()->AddObjectToSwitchList(this, on);
}
+
void WorldObject::setActive( bool on )
{
if(m_isActive == on)
return;
+
if(GetTypeId() == TYPEID_PLAYER)
return;
+
m_isActive = on;
+
if(!IsInWorld())
return;
+
Map *map = FindMap();
if(!map)
return;
+
if(on)
{
if(GetTypeId() == TYPEID_UNIT)
@@ -1000,31 +1151,38 @@ void WorldObject::setActive( bool on )
map->RemoveFromActive((DynamicObject*)this);
}
}
+
void WorldObject::CleanupsBeforeDelete()
{
}
+
void WorldObject::_Create( uint32 guidlow, HighGuid guidhigh, uint32 phaseMask )
{
Object::_Create(guidlow, 0, guidhigh);
m_phaseMask = phaseMask;
}
+
uint32 WorldObject::GetZoneId() const
{
return GetBaseMap()->GetZoneId(m_positionX, m_positionY, m_positionZ);
}
+
uint32 WorldObject::GetAreaId() const
{
return GetBaseMap()->GetAreaId(m_positionX, m_positionY, m_positionZ);
}
+
void WorldObject::GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const
{
GetBaseMap()->GetZoneAndAreaId(zoneid, areaid, m_positionX, m_positionY, m_positionZ);
}
+
InstanceData* WorldObject::GetInstanceData()
{
Map *map = GetMap();
return map->IsDungeon() ? ((InstanceMap*)map)->GetInstanceData() : NULL;
}
+
float WorldObject::GetDistanceZ(const WorldObject* obj) const
{
float dz = fabs(GetPositionZ() - obj->GetPositionZ());
@@ -1032,6 +1190,7 @@ float WorldObject::GetDistanceZ(const WorldObject* obj) const
float dist = dz - sizefactor;
return ( dist > 0 ? dist : 0);
}
+
bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool is3D) const
{
float dx = GetPositionX() - obj->GetPositionX();
@@ -1044,8 +1203,10 @@ bool WorldObject::_IsWithinDist(WorldObject const* obj, float dist2compare, bool
}
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;
@@ -1053,6 +1214,7 @@ bool WorldObject::IsWithinLOSInMap(const WorldObject* obj) const
obj->GetPosition(ox,oy,oz);
return(IsWithinLOS(ox, oy, oz ));
}
+
bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const
{
float x,y,z;
@@ -1060,6 +1222,7 @@ bool WorldObject::IsWithinLOS(float ox, float oy, float oz) const
VMAP::IVMapManager *vMapManager = VMAP::VMapFactory::createOrGetVMapManager();
return vMapManager->isInLineOfSight(GetMapId(), x, y, z+2.0f, ox, oy, oz+2.0f);
}
+
bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* obj2, bool is3D /* = true */) const
{
float dx1 = GetPositionX() - obj1->GetPositionX();
@@ -1070,6 +1233,7 @@ bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* o
float dz1 = GetPositionZ() - obj1->GetPositionZ();
distsq1 += dz1*dz1;
}
+
float dx2 = GetPositionX() - obj2->GetPositionX();
float dy2 = GetPositionY() - obj2->GetPositionY();
float distsq2 = dx2*dx2 + dy2*dy2;
@@ -1078,8 +1242,10 @@ bool WorldObject::GetDistanceOrder(WorldObject const* obj1, WorldObject const* o
float dz2 = GetPositionZ() - obj2->GetPositionZ();
distsq2 += dz2*dz2;
}
+
return distsq1 < distsq2;
}
+
bool WorldObject::IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D /* = true */) const
{
float dx = GetPositionX() - obj->GetPositionX();
@@ -1090,7 +1256,9 @@ bool WorldObject::IsInRange(WorldObject const* obj, float minRange, float maxRan
float dz = GetPositionZ() - obj->GetPositionZ();
distsq += dz*dz;
}
+
float sizefactor = GetObjectSize() + obj->GetObjectSize();
+
// check only for real range
if(minRange > 0.0f)
{
@@ -1098,15 +1266,19 @@ bool WorldObject::IsInRange(WorldObject const* obj, float minRange, float maxRan
if(distsq < mindist * mindist)
return false;
}
+
float maxdist = maxRange + sizefactor;
return distsq < maxdist * maxdist;
}
+
bool WorldObject::IsInRange2d(float x, float y, float minRange, float maxRange) const
{
float dx = GetPositionX() - x;
float dy = GetPositionY() - y;
float distsq = dx*dx + dy*dy;
+
float sizefactor = GetObjectSize();
+
// check only for real range
if(minRange > 0.0f)
{
@@ -1114,16 +1286,20 @@ bool WorldObject::IsInRange2d(float x, float y, float minRange, float maxRange)
if(distsq < mindist * mindist)
return false;
}
+
float maxdist = maxRange + sizefactor;
return distsq < maxdist * maxdist;
}
+
bool WorldObject::IsInRange3d(float x, float y, float z, float minRange, float maxRange) const
{
float dx = GetPositionX() - x;
float dy = GetPositionY() - y;
float dz = GetPositionZ() - z;
float distsq = dx*dx + dy*dy + dz*dz;
+
float sizefactor = GetObjectSize();
+
// check only for real range
if(minRange > 0.0f)
{
@@ -1131,27 +1307,33 @@ bool WorldObject::IsInRange3d(float x, float y, float z, float minRange, float m
if(distsq < mindist * mindist)
return false;
}
+
float maxdist = maxRange + sizefactor;
return distsq < maxdist * maxdist;
}
+
float Position::GetAngle(const Position *obj) const
{
if(!obj) return 0;
return GetAngle( obj->GetPositionX(), obj->GetPositionY() );
}
+
// Return angle in range 0..2*pi
float Position::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;
}
+
void Position::GetSinCos(const float x, const float y, float &vsin, float &vcos) const
{
float dx = GetPositionX() - x;
float dy = GetPositionY() - y;
+
if(dx < 0.001f && dy < 0.001f)
{
float angle = rand_norm()*2*M_PI;
@@ -1165,18 +1347,22 @@ void Position::GetSinCos(const float x, const float y, float &vsin, float &vcos)
vsin = dy / dist;
}
}
+
bool Position::HasInArc(float arc, const Position *obj) const
{
// always have self in arc
if(obj == this)
return true;
+
// 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;
+
//if(angle > 100 || angle < -100)
//{
// sLog.outCrash("Invalid Angle %f: this %u %u %f %f %f %f, that %u %u %f %f %f %f", angle,
@@ -1185,15 +1371,18 @@ bool Position::HasInArc(float arc, const Position *obj) const
// assert(false);
// return false;
//}
+
// 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 ));
}
+
bool WorldObject::IsInBetween(const WorldObject *obj1, const WorldObject *obj2, float size) const
{
if(GetPositionX() > std::max(obj1->GetPositionX(), obj2->GetPositionX())
@@ -1201,11 +1390,14 @@ bool WorldObject::IsInBetween(const WorldObject *obj1, const WorldObject *obj2,
|| GetPositionY() > std::max(obj1->GetPositionY(), obj2->GetPositionY())
|| GetPositionY() < std::min(obj1->GetPositionY(), obj2->GetPositionY()))
return false;
+
if(!size)
size = GetObjectSize() / 2;
+
float angle = obj1->GetAngle(this) - obj1->GetAngle(obj2);
return abs(sin(angle)) * GetExactDist2d(obj1->GetPositionX(), obj1->GetPositionY()) < size;
}
+
void WorldObject::GetRandomPoint(const Position &pos, float distance, float &rand_x, float &rand_y, float &rand_z) const
{
if(!distance)
@@ -1213,53 +1405,65 @@ void WorldObject::GetRandomPoint(const Position &pos, float distance, float &ran
pos.GetPosition(rand_x, rand_y, rand_z);
return;
}
+
// angle to face `obj` to `this`
float angle = rand_norm()*2*M_PI;
float new_dist = rand_norm()*distance;
+
rand_x = pos.m_positionX + new_dist * cos(angle);
rand_y = pos.m_positionY + new_dist * sin(angle);
rand_z = pos.m_positionZ;
+
Trinity::NormalizeMapCoord(rand_x);
Trinity::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 = GetBaseMap()->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 Position::IsPositionValid() const
{
return Trinity::IsValidMapCoord(m_positionX,m_positionY,m_positionZ,m_orientation);
}
+
void WorldObject::MonsterSay(const char* text, uint32 language, uint64 TargetGuid)
{
WorldPacket data(SMSG_MESSAGECHAT, 200);
BuildMonsterChat(&data,CHAT_MSG_MONSTER_SAY,text,language,GetName(),TargetGuid);
SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),true);
}
+
void WorldObject::MonsterYell(const char* text, uint32 language, uint64 TargetGuid)
{
WorldPacket data(SMSG_MESSAGECHAT, 200);
BuildMonsterChat(&data,CHAT_MSG_MONSTER_YELL,text,language,GetName(),TargetGuid);
SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),true);
}
+
void WorldObject::MonsterTextEmote(const char* text, uint64 TargetGuid, bool IsBossEmote)
{
WorldPacket data(SMSG_MESSAGECHAT, 200);
BuildMonsterChat(&data,IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE,text,LANG_UNIVERSAL,GetName(),TargetGuid);
SendMessageToSetInRange(&data,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),true);
}
+
void WorldObject::MonsterWhisper(const char* text, uint64 receiver, bool IsBossWhisper)
{
Player *player = objmgr.GetPlayer(receiver);
if(!player || !player->GetSession())
return;
+
WorldPacket data(SMSG_MESSAGECHAT, 200);
BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetName(),receiver);
+
player->GetSession()->SendPacket(&data);
}
+
void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf)
{
WorldPacket data(SMSG_PLAY_SOUND, 4);
@@ -1269,6 +1473,7 @@ void WorldObject::SendPlaySound(uint32 Sound, bool OnlySelf)
else
SendMessageToSet( &data, true ); // ToSelf ignored in this case
}
+
void Object::ForceValuesUpdateAtIndex(uint32 i)
{
m_uint32Values_mirror[i] = GetUInt32Value(i) + 1; // makes server think the field changed
@@ -1281,6 +1486,7 @@ void Object::ForceValuesUpdateAtIndex(uint32 i)
}
}
}
+
namespace Trinity
{
class MonsterChatBuilder
@@ -1291,9 +1497,11 @@ namespace Trinity
void operator()(WorldPacket& data, int32 loc_idx)
{
char const* text = objmgr.GetMangosString(i_textId,loc_idx);
+
// TODO: i_object.GetName() also must be localized?
i_object.BuildMonsterChat(&data,i_msgtype,text,i_language,i_object.GetNameForLocaleIdx(loc_idx),i_targetGUID);
}
+
private:
WorldObject const& i_object;
ChatMsg i_msgtype;
@@ -1302,12 +1510,15 @@ namespace Trinity
uint64 i_targetGUID;
};
} // namespace Trinity
+
void WorldObject::MonsterSay(int32 textId, uint32 language, uint64 TargetGuid)
{
CellPair p = Trinity::ComputeCellPair(GetPositionX(), GetPositionY());
+
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::MonsterChatBuilder say_build(*this, CHAT_MSG_MONSTER_SAY, textId,language,TargetGuid);
MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build);
MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> > say_worker(this,sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY),say_do);
@@ -1315,12 +1526,15 @@ void WorldObject::MonsterSay(int32 textId, uint32 language, uint64 TargetGuid)
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *GetMap(), *this, sWorld.getConfig(CONFIG_LISTEN_RANGE_SAY));
}
+
void WorldObject::MonsterYell(int32 textId, uint32 language, uint64 TargetGuid)
{
CellPair p = Trinity::ComputeCellPair(GetPositionX(), GetPositionY());
+
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::MonsterChatBuilder say_build(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid);
MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build);
MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> > say_worker(this,sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL),say_do);
@@ -1328,22 +1542,28 @@ void WorldObject::MonsterYell(int32 textId, uint32 language, uint64 TargetGuid)
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *GetMap(), *this, sWorld.getConfig(CONFIG_LISTEN_RANGE_YELL));
}
+
void WorldObject::MonsterYellToZone(int32 textId, uint32 language, uint64 TargetGuid)
{
MaNGOS::MonsterChatBuilder say_build(*this, CHAT_MSG_MONSTER_YELL, textId,language,TargetGuid);
MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build);
+
uint32 zoneid = GetZoneId();
+
Map::PlayerList const& pList = GetMap()->GetPlayers();
for(Map::PlayerList::const_iterator itr = pList.begin(); itr != pList.end(); ++itr)
if(itr->getSource()->GetZoneId()==zoneid)
say_do(itr->getSource());
}
+
void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossEmote)
{
CellPair p = Trinity::ComputeCellPair(GetPositionX(), GetPositionY());
+
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
MaNGOS::MonsterChatBuilder say_build(*this, IsBossEmote ? CHAT_MSG_RAID_BOSS_EMOTE : CHAT_MSG_MONSTER_EMOTE, textId,LANG_UNIVERSAL,TargetGuid);
MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> say_do(say_build);
MaNGOS::PlayerDistWorker<MaNGOS::LocalizedPacketDo<MaNGOS::MonsterChatBuilder> > say_worker(this,sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE),say_do);
@@ -1351,17 +1571,22 @@ void WorldObject::MonsterTextEmote(int32 textId, uint64 TargetGuid, bool IsBossE
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, message, *GetMap(), *this, sWorld.getConfig(CONFIG_LISTEN_RANGE_TEXTEMOTE));
}
+
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.GetTrinityString(textId,loc_idx);
+
WorldPacket data(SMSG_MESSAGECHAT, 200);
BuildMonsterChat(&data,IsBossWhisper ? CHAT_MSG_RAID_BOSS_WHISPER : CHAT_MSG_MONSTER_WHISPER,text,LANG_UNIVERSAL,GetNameForLocaleIdx(loc_idx),receiver);
+
player->GetSession()->SendPacket(&data);
}
+
void WorldObject::BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 targetGuid) const
{
*data << (uint8)msgtype;
@@ -1380,28 +1605,33 @@ void WorldObject::BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const*
*data << text;
*data << (uint8)0; // ChatTag
}
+
void Unit::BuildHeartBeatMsg(WorldPacket *data) const
{
data->Initialize(MSG_MOVE_HEARTBEAT, 32);
data->append(GetPackGUID());
BuildMovementPacket(data);
}
+
void WorldObject::SendMessageToSet(WorldPacket *data, bool /*fake*/)
{
Trinity::MessageDistDeliverer notifier(this, data, GetMap()->GetVisibilityDistance());
VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), notifier);
}
+
void WorldObject::SendMessageToSetInRange(WorldPacket *data, float dist, bool /*bToSelf*/)
{
Trinity::MessageDistDeliverer notifier(this, data, dist);
VisitNearbyWorldObject(dist, notifier);
}
+
void WorldObject::SendObjectDeSpawnAnim(uint64 guid)
{
WorldPacket data(SMSG_GAMEOBJECT_DESPAWN_ANIM, 8);
data << uint64(guid);
SendMessageToSet(&data, true);
}
+
void WorldObject::SetMap(Map * map)
{
ASSERT(map);
@@ -1419,6 +1649,7 @@ void WorldObject::SetMap(Map * map)
if(m_isWorldObject)
m_currMap->AddWorldObject(this);
}
+
void WorldObject::ResetMap()
{
ASSERT(m_currMap);
@@ -1430,22 +1661,27 @@ void WorldObject::ResetMap()
//m_mapId = 0;
//m_InstanceId = 0;
}
+
Map const* WorldObject::GetBaseMap() const
{
ASSERT(m_currMap);
return m_currMap->GetParent();
}
+
void WorldObject::AddObjectToRemoveList()
{
assert(m_uint32Values);
+
Map* map = FindMap();
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);
}
+
TempSummon *Map::SummonCreature(uint32 entry, const Position &pos, SummonPropertiesEntry const *properties, uint32 duration, Unit *summoner, uint32 vehId)
{
uint32 mask = UNIT_MASK_SUMMON;
@@ -1477,6 +1713,7 @@ TempSummon *Map::SummonCreature(uint32 entry, const Position &pos, SummonPropert
break;
}
}
+
uint32 phase = PHASEMASK_NORMAL, team = 0;
if(summoner)
{
@@ -1484,6 +1721,7 @@ TempSummon *Map::SummonCreature(uint32 entry, const Position &pos, SummonPropert
if(summoner->GetTypeId() == TYPEID_PLAYER)
team = ((Player*)summoner)->GetTeam();
}
+
TempSummon *summon = NULL;
switch(mask)
{
@@ -1494,18 +1732,24 @@ TempSummon *Map::SummonCreature(uint32 entry, const Position &pos, SummonPropert
case UNIT_MASK_MINION: summon = new Minion (properties, summoner); break;
default: return NULL;
}
+
if(!summon->Create(objmgr.GenerateLowGuid(HIGHGUID_UNIT), this, phase, entry, vehId, team, pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation()))
{
delete summon;
return NULL;
}
+
summon->SetHomePosition(pos);
+
summon->InitStats(duration);
Add((Creature*)summon);
summon->InitSummon();
+
//ObjectAccessor::UpdateObjectVisibility(summon);
+
return summon;
}
+
void WorldObject::SetZoneScript()
{
if(Map *map = FindMap())
@@ -1516,6 +1760,7 @@ void WorldObject::SetZoneScript()
m_zoneScript = sOutdoorPvPMgr.GetZoneScript(GetZoneId());
}
}
+
TempSummon* WorldObject::SummonCreature(uint32 entry, const Position &pos, TempSummonType spwtype, uint32 duration, uint32 vehId) const
{
if(Map *map = FindMap())
@@ -1526,11 +1771,14 @@ TempSummon* WorldObject::SummonCreature(uint32 entry, const Position &pos, TempS
return summon;
}
}
+
return NULL;
}
+
Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 duration)
{
Pet* pet = new Pet(this, petType);
+
if(petType == SUMMON_PET && pet->LoadPetFromDB(this, entry))
{
// Remove Demonic Sacrifice auras (known pet)
@@ -1545,16 +1793,20 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
else
++itr;
}
+
if(duration > 0)
pet->SetDuration(duration);
+
return NULL;
}
+
// petentry==0 for hunter "call pet" (current pet summoned if any)
if(!entry)
{
delete pet;
return NULL;
}
+
pet->Relocate(x, y, z, ang);
if(!pet->IsPositionValid())
{
@@ -1562,6 +1814,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
delete pet;
return NULL;
}
+
Map *map = GetMap();
uint32 pet_number = objmgr.GeneratePetNumber();
if(!pet->Create(objmgr.GenerateLowGuid(HIGHGUID_PET), map, GetPhaseMask(), entry, pet_number))
@@ -1570,13 +1823,17 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
delete pet;
return NULL;
}
+
pet->SetCreatorGUID(GetGUID());
pet->SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, getFaction());
+
pet->setPowerType(POWER_MANA);
pet->SetUInt32Value(UNIT_NPC_FLAGS , 0);
pet->SetUInt32Value(UNIT_FIELD_BYTES_1,0);
pet->InitStatsForLevel(getLevel());
+
SetMinion(pet, true);
+
switch(petType)
{
case SUMMON_PET:
@@ -1590,7 +1847,9 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
pet->SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
break;
}
+
map->Add((Creature*)pet);
+
switch(petType)
{
case SUMMON_PET:
@@ -1600,6 +1859,7 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
PetSpellInitialize();
break;
}
+
if(petType == SUMMON_PET)
{
// Remove Demonic Sacrifice auras (known pet)
@@ -1615,15 +1875,20 @@ Pet* Player::SummonPet(uint32 entry, float x, float y, float z, float ang, PetTy
++itr;
}
}
+
if(duration > 0)
pet->SetDuration(duration);
+
//ObjectAccessor::UpdateObjectVisibility(pet);
+
return pet;
}
+
GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime)
{
if(!IsInWorld())
return NULL;
+
GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry);
if(!goinfo)
{
@@ -1643,24 +1908,29 @@ GameObject* WorldObject::SummonGameObject(uint32 entry, float x, float y, float
else
go->SetSpawnedByDefault(false);
map->Add(go);
+
return go;
}
+
Creature* WorldObject::SummonTrigger(float x, float y, float z, float ang, uint32 duration, CreatureAI* (*GetAI)(Creature*))
{
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Creature* summon = SummonCreature(WORLD_TRIGGER, x, y, z, ang, summonType, duration);
if(!summon)
return NULL;
+
//summon->SetName(GetName());
if(GetTypeId()==TYPEID_PLAYER || GetTypeId()==TYPEID_UNIT)
{
summon->setFaction(((Unit*)this)->getFaction());
summon->SetLevel(((Unit*)this)->getLevel());
}
+
if(GetAI)
summon->AIM_Initialize(GetAI(summon));
return summon;
}
+
Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive)
{
Creature *creature = NULL;
@@ -1669,6 +1939,7 @@ Creature* WorldObject::FindNearestCreature(uint32 entry, float range, bool alive
VisitNearbyObject(range, searcher);
return creature;
}
+
GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range)
{
GameObject *go = NULL;
@@ -1677,30 +1948,37 @@ GameObject* WorldObject::FindNearestGameObject(uint32 entry, float range)
VisitNearbyGridObject(range, searcher);
return go;
}
+
void WorldObject::GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange)
{
CellPair pair(Trinity::ComputeCellPair(this->GetPositionX(), this->GetPositionY()));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllGameObjectsWithEntryInRange check(this, uiEntry, fMaxSearchRange);
Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange> searcher(this, lList, check);
TypeContainerVisitor<Trinity::GameObjectListSearcher<Trinity::AllGameObjectsWithEntryInRange>, GridTypeMapContainer> visitor(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, visitor, *(this->GetMap()));
}
+
void WorldObject::GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange)
{
CellPair pair(Trinity::ComputeCellPair(this->GetPositionX(), this->GetPositionY()));
Cell cell(pair);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
Trinity::AllCreaturesOfEntryInRange check(this, uiEntry, fMaxSearchRange);
Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange> searcher(this, lList, check);
TypeContainerVisitor<Trinity::CreatureListSearcher<Trinity::AllCreaturesOfEntryInRange>, GridTypeMapContainer> visitor(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, pair);
cell_lock->Visit(cell_lock, visitor, *(this->GetMap()));
}
+
/*
namespace MaNGOS
{
@@ -1709,45 +1987,58 @@ namespace MaNGOS
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_STUNNED | UNIT_STAT_DISTRACTED) ||
!c->GetMotionMaster()->GetDestination(x,y,z) )
{
x = c->GetPositionX();
y = c->GetPositionY();
}
+
add(c,x,y);
}
+
template<class T>
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
{
// u is too nearest/far away to i_object
if(!i_object.IsInRange2d(x,y,i_selector.m_dist - i_selector.m_size,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;
+
// dist include size of u
float dist2d = i_object.GetDistance2d(x,y);
i_selector.AddUsedPos(u->GetObjectSize(),angle,dist2d + i_object.GetObjectSize());
@@ -1760,19 +2051,24 @@ namespace MaNGOS
};
} // 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);
+
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
}
+
void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y, float &z, float searcher_size, float distance2d, float absAngle ) const
{
GetNearPoint2D(x,y,distance2d+searcher_size,absAngle);
z = GetPositionZ();
UpdateGroundPositionZ(x,y,z);
+
/*
// if detection disabled, return first point
if(!sWorld.getConfig(CONFIG_DETECT_POS_COLLISION))
@@ -1780,64 +2076,83 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y,
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<MaNGOS::NearUsedPosDo> worker(this,u_do);
+
TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::NearUsedPosDo>, GridTypeMapContainer > grid_obj_worker(worker);
TypeContainerVisitor<MaNGOS::WorldObjectWorker<MaNGOS::NearUsedPosDo>, WorldTypeMapContainer > world_obj_worker(worker);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_obj_worker, *GetMap(), *this, distance2d);
cell_lock->Visit(cell_lock, world_obj_worker, *GetMap(), *this, distance2d);
}
+
// 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() )
{
@@ -1846,27 +2161,34 @@ void WorldObject::GetNearPoint(WorldObject const* searcher, float &x, float &y,
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
*/
}
+
void WorldObject::MovePosition(Position &pos, float dist, float angle)
{
angle += m_orientation;
@@ -1877,12 +2199,15 @@ void WorldObject::MovePosition(Position &pos, float dist, float angle)
UpdateGroundPositionZ(pos.m_positionX, pos.m_positionY, pos.m_positionZ);
pos.m_orientation = m_orientation;
}
+
void WorldObject::SetPhaseMask(uint32 newPhaseMask, bool update)
{
m_phaseMask = newPhaseMask;
+
if(update && IsInWorld())
ObjectAccessor::UpdateObjectVisibility(this);
}
+
void WorldObject::PlayDistanceSound( uint32 sound_id, Player* target /*= NULL*/ )
{
WorldPacket data(SMSG_PLAY_OBJECT_SOUND,4+8);
@@ -1893,6 +2218,7 @@ void WorldObject::PlayDistanceSound( uint32 sound_id, Player* target /*= NULL*/
else
SendMessageToSet( &data, true );
}
+
void WorldObject::PlayDirectSound( uint32 sound_id, Player* target /*= NULL*/ )
{
WorldPacket data(SMSG_PLAY_SOUND, 4);
@@ -1902,10 +2228,12 @@ void WorldObject::PlayDirectSound( uint32 sound_id, Player* target /*= NULL*/ )
else
SendMessageToSet( &data, true );
}
+
void WorldObject::DestroyForNearbyPlayers()
{
if(!IsInWorld())
return;
+
std::list<Unit*> targets;
Trinity::AnyUnitInObjectRangeCheck check(this, GetMap()->GetVisibilityDistance());
Trinity::UnitListSearcher<Trinity::AnyUnitInObjectRangeCheck> searcher(this, targets, check);
@@ -1915,13 +2243,17 @@ void WorldObject::DestroyForNearbyPlayers()
Player *plr = dynamic_cast<Player*>(*iter);
if(!plr)
continue;
+
if(plr == this)
continue;
+
if(!plr->HaveAtClient(this))
continue;
+
if(isType(TYPEMASK_UNIT) && ((Unit*)this)->GetCharmerGUID() == plr->GetGUID()) // TODO: this is for puppet
continue;
- DestroyForPlayer(plr);
- plr->m_clientGUIDs.erase(GetGUID());
+
+ DestroyForPlayer(plr);
+ plr->m_clientGUIDs.erase(GetGUID());
}
}
diff --git a/src/game/Object.h b/src/game/Object.h
index 96d3252dc5d..9845ba10dce 100644
--- a/src/game/Object.h
+++ b/src/game/Object.h
@@ -17,8 +17,10 @@
* 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 "UpdateFields.h"
#include "UpdateData.h"
@@ -26,8 +28,10 @@
#include "ObjectDefines.h"
#include "GridDefines.h"
#include "Map.h"
+
#include <set>
#include <string>
+
#define CONTACT_DISTANCE 0.5f
#define INTERACTION_DISTANCE 5.0f
#define ATTACK_DISTANCE 5.0f
@@ -35,11 +39,13 @@
#define DEFAULT_VISIBILITY_DISTANCE 90.0f // default visible distance, 90 yards on continents
#define DEFAULT_VISIBILITY_INSTANCE 120.0f // default visible distance in instances, 120 yards
#define DEFAULT_VISIBILITY_BGARENAS 180.0f // default visible distance in BG/Arenas, 180 yards
+
#define DEFAULT_WORLD_OBJECT_SIZE 0.388999998569489f // player size, also currently used (correctly?) for any non Unit world objects
#define DEFAULT_COMBAT_REACH 1.5f
#define MIN_MELEE_REACH 2.0f
#define NOMINAL_MELEE_RANGE 5.0f
#define MELEE_RANGE (NOMINAL_MELEE_RANGE - MIN_MELEE_REACH * 2) //center to center for players
+
enum TypeMask
{
TYPEMASK_OBJECT = 0x0001,
@@ -52,6 +58,7 @@ enum TypeMask
TYPEMASK_CORPSE = 0x0080,
TYPEMASK_SEER = TYPEMASK_UNIT | TYPEMASK_DYNAMICOBJECT
};
+
enum TypeID
{
TYPEID_OBJECT = 0,
@@ -63,8 +70,11 @@ enum TypeID
TYPEID_DYNAMICOBJECT = 6,
TYPEID_CORPSE = 7
};
+
#define NUM_CLIENT_OBJECT_TYPES 8
+
uint32 GuidHigh2TypeId(uint32 guid_hi);
+
enum TempSummonType
{
TEMPSUMMON_TIMED_OR_DEAD_DESPAWN = 1, // despawns after a specified time OR when the creature disappears
@@ -76,11 +86,13 @@ enum TempSummonType
TEMPSUMMON_DEAD_DESPAWN = 7, // despawns when the creature disappears
TEMPSUMMON_MANUAL_DESPAWN = 8 // despawns when UnSummon() is called
};
+
enum PhaseMasks
{
PHASEMASK_NORMAL = 0x00000001,
PHASEMASK_ANYWHERE = 0xFFFFFFFF
};
+
class WorldPacket;
class UpdateData;
class ByteBuffer;
@@ -95,18 +107,24 @@ class Vehicle;
class CreatureAI;
class ZoneScript;
class Unit;
+
typedef UNORDERED_MAP<Player*, UpdateData> UpdateDataMapType;
+
class TRINITY_DLL_SPEC Object
{
public:
virtual ~Object ( );
+
const bool IsInWorld() const { return m_inWorld; }
virtual void AddToWorld()
{
if(m_inWorld)
return;
+
assert(m_uint32Values);
+
m_inWorld = true;
+
// synchronize values mirror with values array (changes will send in updatecreate opcode any way
ClearUpdateMask(true);
}
@@ -114,10 +132,13 @@ class TRINITY_DLL_SPEC Object
{
if(!m_inWorld)
return;
+
m_inWorld = false;
+
// if we remove from world then sending changes not required
ClearUpdateMask(true);
}
+
const uint64& GetGUID() const { return GetUInt64Value(0); }
uint32 GetGUIDLow() const { return GUID_LOPART(GetUInt64Value(0)); }
uint32 GetGUIDMid() const { return GUID_ENPART(GetUInt64Value(0)); }
@@ -125,47 +146,58 @@ class TRINITY_DLL_SPEC Object
const ByteBuffer& GetPackGUID() const { return m_PackGUID; }
uint32 GetEntry() const { return GetUInt32Value(OBJECT_FIELD_ENTRY); }
void SetEntry(uint32 entry) { SetUInt32Value(OBJECT_FIELD_ENTRY, entry); }
+
TypeID 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, bool anim = false ) 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);
}
+
uint16 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 UpdateUInt32Value( uint16 index, uint32 value );
@@ -176,20 +208,25 @@ class TRINITY_DLL_SPEC Object
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);
+
bool AddUInt64Value( uint16 index, const uint64 &value );
bool RemoveUInt64Value( uint16 index, const uint64 &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))
@@ -197,13 +234,16 @@ class TRINITY_DLL_SPEC Object
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 SetByteFlag( uint16 index, uint8 offset, uint8 newFlag );
void RemoveByteFlag( uint16 index, uint8 offset, uint8 newFlag );
+
void ToggleFlag( uint16 index, uint8 offset, uint8 flag )
{
if(HasByteFlag(index, offset, flag))
@@ -211,28 +251,33 @@ class TRINITY_DLL_SPEC Object
else
SetByteFlag(index, offset, flag);
}
+
bool HasByteFlag( uint16 index, uint8 offset, uint8 flag ) const
{
ASSERT( index < m_valuesCount || PrintIndexError( index , false ) );
ASSERT( offset < 4 );
return (((uint8*)&m_uint32Values[index])[offset] & 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))
@@ -240,57 +285,80 @@ class TRINITY_DLL_SPEC Object
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; }
+
virtual bool hasQuest(uint32 /* quest_id */) const { return false; }
virtual bool hasInvolvedQuest(uint32 /* quest_id */) const { return false; }
+
// FG: some hacky helpers
void ForceValuesUpdateAtIndex(uint32);
+
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, uint16 flags) const;
void _BuildValuesUpdate(uint8 updatetype, ByteBuffer *data, UpdateMask *updateMask, Player *target ) const;
+
uint16 m_objectType;
+
TypeID m_objectTypeId;
uint16 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
};
+
struct TRINITY_DLL_SPEC Position
{
float m_positionX;
float m_positionY;
float m_positionZ;
float m_orientation;
+
void Relocate(float x, float y)
{ m_positionX = x; m_positionY = y;}
void Relocate(float x, float y, float z)
@@ -303,10 +371,12 @@ struct TRINITY_DLL_SPEC Position
{ m_positionX = pos->m_positionX; m_positionY = pos->m_positionY; m_positionZ = pos->m_positionZ; m_orientation = pos->m_orientation; }
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; }
float GetOrientation() const { return m_orientation; }
+
void GetPosition(float &x, float &y) const
{ x = m_positionX; y = m_positionY; }
void GetPosition(float &x, float &y, float &z) const
@@ -315,7 +385,9 @@ struct TRINITY_DLL_SPEC Position
{ x = m_positionX; y = m_positionY; z = m_positionZ; o = m_orientation; }
void GetPosition(Position *pos) const
{ pos->Relocate(m_positionX, m_positionY, m_positionZ, m_orientation); }
+
bool IsPositionValid() const;
+
float GetExactDist2dSq(float x, float y) const
{ float dx = m_positionX - x; float dy = m_positionY - y; return dx*dx + dy*dy; }
float GetExactDist2d(const float x, const float y) const
@@ -332,10 +404,12 @@ struct TRINITY_DLL_SPEC Position
{ float dx = m_positionX - pos->m_positionX; float dy = m_positionY - pos->m_positionY; float dz = m_positionZ - pos->m_positionZ; return dx*dx + dy*dy + dz*dz; }
float GetExactDist(const Position *pos) const
{ return sqrt(GetExactDistSq(pos)); }
+
float GetAngle(const Position *pos) const;
float GetAngle(float x, float y) const;
float GetRelativeAngle(const Position *pos) const { return GetAngle(pos) - m_orientation; }
void GetSinCos(float x, float y, float &vsin, float &vcos) const;
+
bool IsInDist2d(float x, float y, float dist) const
{ return GetExactDist2dSq(x, y) < dist * dist; }
bool IsInDist2d(const Position *pos, float dist) const
@@ -347,24 +421,32 @@ struct TRINITY_DLL_SPEC Position
bool HasInArc(float arcangle, const Position *pos) const;
bool HasInLine(const Unit *target, float distance, float width) const;
};
+
#define MAPID_INVALID 0xFFFFFFFF
+
class WorldLocation : public Position
{
public:
explicit WorldLocation(uint32 _mapid = MAPID_INVALID, float _x = 0, float _y = 0, float _z = 0, float _o = 0)
: m_mapId(_mapid) { Relocate(_x, _y, _z, _o); }
WorldLocation(const WorldLocation &loc) { WorldRelocate(loc); }
+
void WorldRelocate(const WorldLocation &loc)
{ m_mapId = loc.GetMapId(); Relocate(loc); }
uint32 GetMapId() const { return m_mapId; }
+
uint32 m_mapId;
};
+
class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
{
public:
virtual ~WorldObject();
+
virtual void Update ( uint32 /*time_diff*/ ) { }
+
void _Create( uint32 guidlow, HighGuid guidhigh, uint32 phaseMask);
+
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
@@ -383,16 +465,19 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
GetPosition(&pos);
MovePosition(pos, radius * rand_norm(), rand_norm() * 2 * M_PI);
}
+
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_COMBATREACH ) ? m_floatValues[UNIT_FIELD_COMBATREACH] : DEFAULT_WORLD_OBJECT_SIZE;
}
void UpdateGroundPositionZ(float x, float y, float &z) const;
+
void GetRandomPoint(const Position &srcPos, float distance, float &rand_x, float &rand_y, float &rand_z) const;
void GetRandomPoint(const Position &srcPos, float distance, Position &pos) const
{
@@ -400,18 +485,25 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
GetRandomPoint(srcPos, distance, x, y, z);
pos.Relocate(x, y, z, GetOrientation());
}
+
uint32 GetInstanceId() const { return m_InstanceId; }
+
virtual void SetPhaseMask(uint32 newPhaseMask, bool update);
uint32 GetPhaseMask() const { return m_phaseMask; }
bool InSamePhase(WorldObject const* obj) const { return InSamePhase(obj->GetPhaseMask()); }
bool InSamePhase(uint32 phasemask) const { return (GetPhaseMask() & phasemask); }
+
uint32 GetZoneId() const;
uint32 GetAreaId() const;
void GetZoneAndAreaId(uint32& zoneid, uint32& areaid) const;
+
InstanceData* GetInstanceData();
+
const char* GetName() const { return m_name.c_str(); }
void SetName(const std::string& newname) { m_name=newname; }
+
virtual const char* GetNameForLocaleIdx(int32 /*locale_idx*/) const { return GetName(); }
+
float GetDistance(const WorldObject *obj) const
{ return GetExactDist(obj) + GetObjectSize() + obj->GetObjectSize(); }
float GetDistance(const Position &pos) const
@@ -423,6 +515,7 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
float GetDistance2d(float x, float y) const
{ return GetExactDist2d(x, y) + GetObjectSize(); }
float GetDistanceZ(const WorldObject* obj) const;
+
bool IsInMap(const WorldObject* obj) const
{
return IsInWorld() && obj->IsInWorld() && (GetMap() == obj->GetMap()) && InSamePhase(obj);
@@ -451,10 +544,14 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
bool IsInRange(WorldObject const* obj, float minRange, float maxRange, bool is3D = true) const;
bool IsInRange2d(float x, float y, float minRange, float maxRange) const;
bool IsInRange3d(float x, float y, float z, float minRange, float maxRange) const;
+
bool IsInBetween(const WorldObject *obj1, const WorldObject *obj2, float size = 0) const;
+
virtual void CleanupsBeforeDelete(); // used in destructor or explicitly before mass creature delete to remove cross-references to already deleted units
+
virtual void SendMessageToSet(WorldPacket *data, bool self);
virtual void SendMessageToSetInRange(WorldPacket *data, float dist, bool self);
+
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);
@@ -465,26 +562,36 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
void MonsterWhisper(int32 textId, uint64 receiver, bool IsBossWhisper = false);
void MonsterYellToZone(int32 textId, uint32 language, uint64 TargetGuid);
void BuildMonsterChat(WorldPacket *data, uint8 msgtype, char const* text, uint32 language, char const* name, uint64 TargetGuid) const;
+
void PlayDistanceSound(uint32 sound_id, Player* target = NULL);
void PlayDirectSound(uint32 sound_id, Player* target = NULL);
+
void SendObjectDeSpawnAnim(uint64 guid);
+
virtual void SaveRespawnTime() {}
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;
+
// Low Level Packets
void SendPlaySound(uint32 Sound, bool OnlySelf);
+
virtual void SetMap(Map * map);
virtual void ResetMap();
Map * GetMap() const { ASSERT(m_currMap); return m_currMap; }
Map * FindMap() const { return m_currMap; }
//used to check all object's GetMap() calls when object is not in world!
+
//this function should be removed in nearest time...
Map const* GetBaseMap() const;
+
void SetZoneScript();
ZoneScript * GetZoneScript() const { return m_zoneScript; }
+
TempSummon* SummonCreature(uint32 id, const Position &pos, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0, uint32 vehId = 0) const;
TempSummon* SummonCreature(uint32 id, float x, float y, float z, float ang = 0, TempSummonType spwtype = TEMPSUMMON_MANUAL_DESPAWN, uint32 despwtime = 0)
{
@@ -498,17 +605,22 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
}
GameObject* SummonGameObject(uint32 entry, float x, float y, float z, float ang, float rotation0, float rotation1, float rotation2, float rotation3, uint32 respawnTime);
Creature* SummonTrigger(float x, float y, float z, float ang, uint32 dur, CreatureAI* (*GetAI)(Creature*) = NULL);
+
Creature* FindNearestCreature(uint32 entry, float range, bool alive = true);
GameObject* FindNearestGameObject(uint32 entry, float range);
+
void GetGameObjectListWithEntryInGrid(std::list<GameObject*>& lList, uint32 uiEntry, float fMaxSearchRange);
void GetCreatureListWithEntryInGrid(std::list<Creature*>& lList, uint32 uiEntry, float fMaxSearchRange);
+
void DestroyForNearbyPlayers();
+
bool isActiveObject() const { return m_isActive; }
void setActive(bool isActiveObject);
void SetWorldObject(bool apply);
template<class NOTIFIER> void VisitNearbyObject(const float &radius, NOTIFIER &notifier) const { GetMap()->VisitAll(GetPositionX(), GetPositionY(), radius, notifier); }
template<class NOTIFIER> void VisitNearbyGridObject(const float &radius, NOTIFIER &notifier) const { GetMap()->VisitGrid(GetPositionX(), GetPositionY(), radius, notifier); }
template<class NOTIFIER> void VisitNearbyWorldObject(const float &radius, NOTIFIER &notifier) const { GetMap()->VisitWorld(GetPositionX(), GetPositionY(), radius, notifier); }
+
#ifdef MAP_BASED_RAND_GEN
int32 irand(int32 min, int32 max) const { return int32 (GetMap()->mtRand.randInt(max - min)) + min; }
uint32 urand(uint32 min, uint32 max) const { return GetMap()->mtRand.randInt(max - min) + min;}
@@ -516,19 +628,23 @@ class TRINITY_DLL_SPEC WorldObject : public Object, public WorldLocation
double rand_norm() const { return GetMap()->mtRand.randExc();}
double rand_chance() const { return GetMap()->mtRand.randExc(100.0);}
#endif
+
bool m_isWorldObject;
protected:
explicit WorldObject();
std::string m_name;
bool m_isActive;
ZoneScript *m_zoneScript;
+
//these functions are used mostly for Relocate() and Corpse/Player specific stuff...
//use them ONLY in LoadFromDB()/Create() funcs and nowhere else!
//mapId/instanceId should be set in SetMap() function!
void SetLocationMapId(uint32 _mapId) { m_mapId = _mapId; }
void SetLocationInstanceId(uint32 _instanceId) { m_InstanceId = _instanceId; }
+
private:
Map * m_currMap; //current object's Map location
+
//uint32 m_mapId; // object at map with map_id
uint32 m_InstanceId; // in map copy with instance id
uint32 m_phaseMask; // in area phase state
diff --git a/src/game/ObjectAccessor.cpp b/src/game/ObjectAccessor.cpp
index 2603431d513..7a12b4784e4 100644
--- a/src/game/ObjectAccessor.cpp
+++ b/src/game/ObjectAccessor.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -37,36 +38,46 @@
#include "ObjectDefines.h"
#include "MapInstanced.h"
#include "World.h"
+
#include <cmath>
+
#define CLASS_LOCK MaNGOS::ClassLevelLockable<ObjectAccessor, ACE_Thread_Mutex>
INSTANTIATE_SINGLETON_2(ObjectAccessor, CLASS_LOCK);
INSTANTIATE_CLASS_MUTEX(ObjectAccessor, ACE_Thread_Mutex);
+
ObjectAccessor::ObjectAccessor() {}
ObjectAccessor::~ObjectAccessor()
{
for(Player2CorpsesMapType::const_iterator itr = i_player2corpse.begin(); itr != i_player2corpse.end(); ++itr)
delete itr->second;
}
+
Creature*
ObjectAccessor::GetCreatureOrPetOrVehicle(WorldObject const &u, uint64 guid)
{
if(IS_PLAYER_GUID(guid))
return NULL;
+
if(IS_PET_GUID(guid))
return GetPet(guid);
+
return u.IsInWorld() ? u.GetMap()->GetCreature(guid) : NULL;
}
+
/*
Unit*
ObjectAccessor::GetUnit(WorldObject const &u, uint64 guid)
{
if(!guid)
return NULL;
+
if(IS_PLAYER_GUID(guid))
return FindPlayer(guid);
+
return GetCreatureOrPetOrVehicle(u, guid);
}
*/
+
Corpse*
ObjectAccessor::GetCorpse(WorldObject const &u, uint64 guid)
{
@@ -79,6 +90,7 @@ ObjectAccessor::GetCorpse(WorldObject const &u, uint64 guid)
return NULL;
return ret;
}
+
WorldObject* ObjectAccessor::GetWorldObject(WorldObject const &p, uint64 guid)
{
switch(GUID_HIPART(guid))
@@ -94,8 +106,10 @@ WorldObject* ObjectAccessor::GetWorldObject(WorldObject const &p, uint64 guid)
case HIGHGUID_MO_TRANSPORT: return NULL;
default: break;
}
+
return NULL;
}
+
Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const &p, uint64 guid, uint32 typemask)
{
switch(GUID_HIPART(guid))
@@ -130,16 +144,20 @@ Object* ObjectAccessor::GetObjectByTypeMask(WorldObject const &p, uint64 guid, u
case HIGHGUID_MO_TRANSPORT:
break;
}
+
return NULL;
}
+
Player*
ObjectAccessor::FindPlayer(uint64 guid)
{
Player * plr = GetObjectInWorld(guid, (Player*)NULL);
if(!plr || !plr->IsInWorld())
return NULL;
+
return plr;
}
+
Player*
ObjectAccessor::FindPlayerByName(const char *name)
{
@@ -151,6 +169,7 @@ ObjectAccessor::FindPlayerByName(const char *name)
return iter->second;
return NULL;
}
+
void
ObjectAccessor::SaveAllPlayers()
{
@@ -160,21 +179,25 @@ ObjectAccessor::SaveAllPlayers()
for(; itr != m.end(); ++itr)
itr->second->SaveToDB();
}
+
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::_buildUpdateObject(Object *obj, UpdateDataMapType &update_players)
{
@@ -186,18 +209,22 @@ ObjectAccessor::_buildUpdateObject(Object *obj, UpdateDataMapType &update_player
else
_buildChangeObjectForPlayer((WorldObject*)obj, update_players);
}
+
void
ObjectAccessor::_buildPacket(Player *pl, Object *obj, UpdateDataMapType &update_players)
{
UpdateDataMapType::iterator iter = update_players.find(pl);
+
if( iter == update_players.end() )
{
std::pair<UpdateDataMapType::iterator, bool> 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)
{
@@ -212,50 +239,66 @@ ObjectAccessor::_buildChangeObjectForPlayer(WorldObject *obj, UpdateDataMapType
//we must build packets for all visible players
cell_lock->Visit(cell_lock, player_notifier, map, *obj, map.GetVisibilityDistance());
}
+
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);
+
if(corpse->FindMap())
corpse->FindMap()->Remove(corpse, false);
else
corpse->RemoveFromWorld();
+
Guard guard(i_corpseGuard);
Player2CorpsesMapType::iterator iter = i_player2corpse.find(corpse->GetOwnerGUID());
if( iter == i_player2corpse.end() ) // i do not know when it happens but it happens
return;
+
// build mapid*cellid -> guid_set map
CellPair cell_pair = Trinity::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());
+
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 = Trinity::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)
{
@@ -277,6 +320,7 @@ ObjectAccessor::AddCorpsesToGrid(GridPair const& gridpair,GridType& grid,Map* ma
}
}
}
+
Corpse*
ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid, bool insignia)
{
@@ -288,18 +332,24 @@ ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid, bool insignia)
//sLog.outError("Try remove corpse that not in map for GUID %ul", player_guid);
return NULL;
}
+
DEBUG_LOG("Deleting Corpse and spawning bones.");
+
Map *map = corpse->FindMap();
+
// remove corpse from player_guid -> corpse map
RemoveCorpse(corpse);
+
// done in removecorpse
// remove resurrectable 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
// ignore bones creating option in case insignia
@@ -310,28 +360,36 @@ ObjectAccessor::ConvertCorpseForPlayer(uint64 player_guid, bool insignia)
// Create bones, don't change Corpse
bones = new Corpse;
bones->Create(corpse->GetGUIDLow(), map);
+
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->SetPhaseMask(corpse->GetPhaseMask(), false);
+
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)
{
@@ -347,6 +405,7 @@ ObjectAccessor::Update(uint32 diff)
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)
{
@@ -355,6 +414,7 @@ ObjectAccessor::Update(uint32 diff)
packet.clear(); // clean the string
}
}
+
void
ObjectAccessor::WorldObjectChangeAccumulator::Visit(PlayerMapType &m)
{
@@ -369,6 +429,7 @@ ObjectAccessor::WorldObjectChangeAccumulator::Visit(PlayerMapType &m)
}
}
}
+
void
ObjectAccessor::WorldObjectChangeAccumulator::Visit(CreatureMapType &m)
{
@@ -382,6 +443,7 @@ ObjectAccessor::WorldObjectChangeAccumulator::Visit(CreatureMapType &m)
}
}
}
+
void
ObjectAccessor::WorldObjectChangeAccumulator::Visit(DynamicObjectMapType &m)
{
@@ -397,6 +459,7 @@ ObjectAccessor::WorldObjectChangeAccumulator::Visit(DynamicObjectMapType &m)
}
}
}
+
void
ObjectAccessor::WorldObjectChangeAccumulator::BuildPacket(Player* plr)
{
@@ -407,39 +470,50 @@ ObjectAccessor::WorldObjectChangeAccumulator::BuildPacket(Player* plr)
plr_list.insert(plr->GetGUID());
}
}
+
void
ObjectAccessor::UpdateObjectVisibility(WorldObject *obj)
{
CellPair p = Trinity::ComputeCellPair(obj->GetPositionX(), obj->GetPositionY());
Cell cell(p);
+
obj->GetMap()->UpdateObjectVisibility(obj, cell, p);
}
+
/*void ObjectAccessor::UpdateVisibilityForPlayer( Player* player )
{
WorldObject const* viewPoint = player->GetViewPoint();
Map* m = player->GetMap();
+
CellPair p = Trinity::ComputeCellPair(player->GetPositionX(), player->GetPositionY());
Cell cell(p);
+
m->UpdatePlayerVisibility(player, cell, p);
if (player!=viewPoint)
{
CellPair pView(Trinity::ComputeCellPair(viewPoint->GetPositionX(), viewPoint->GetPositionY()));
Cell cellView(pView);
+
m->UpdateObjectsVisibilityFor(player, cellView, pView);
}
else
m->UpdateObjectsVisibilityFor(player, cell, p);
}*/
+
/// Define the static member of HashMapHolder
+
template <class T> UNORDERED_MAP< uint64, T* > HashMapHolder<T>::m_objectMap;
template <class T> ACE_Thread_Mutex HashMapHolder<T>::i_lock;
+
/// Global definitions for the hashmap storage
+
template class HashMapHolder<Player>;
template class HashMapHolder<Pet>;
template class HashMapHolder<GameObject>;
template class HashMapHolder<DynamicObject>;
template class HashMapHolder<Creature>;
template class HashMapHolder<Corpse>;
+
template Player* ObjectAccessor::GetObjectInWorld<Player>(uint32 mapid, float x, float y, uint64 guid, Player* /*fake*/);
template Pet* ObjectAccessor::GetObjectInWorld<Pet>(uint32 mapid, float x, float y, uint64 guid, Pet* /*fake*/);
template Creature* ObjectAccessor::GetObjectInWorld<Creature>(uint32 mapid, float x, float y, uint64 guid, Creature* /*fake*/);
diff --git a/src/game/ObjectAccessor.h b/src/game/ObjectAccessor.h
index fb465e7d2ba..655b44b98f2 100644
--- a/src/game/ObjectAccessor.h
+++ b/src/game/ObjectAccessor.h
@@ -17,18 +17,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_OBJECTACCESSOR_H
#define TRINITY_OBJECTACCESSOR_H
+
#include "Platform/Define.h"
#include "Policies/Singleton.h"
#include <ace/Thread_Mutex.h>
#include "Utilities/UnorderedMap.h"
#include "Policies/ThreadingModel.h"
+
#include "UpdateData.h"
+
#include "GridDefines.h"
#include "Object.h"
#include "Player.h"
+
#include <set>
+
class Creature;
class Corpse;
class Unit;
@@ -36,97 +42,124 @@ class GameObject;
class DynamicObject;
class WorldObject;
class Map;
+
template <class T>
class HashMapHolder
{
public:
+
typedef UNORDERED_MAP< uint64, T* > MapType;
typedef ACE_Thread_Mutex LockType;
typedef MaNGOS::GeneralLock<LockType > Guard;
+
static void Insert(T* o) { m_objectMap[o->GetGUID()] = o; }
+
static void Remove(T* o)
{
Guard guard(i_lock);
m_objectMap.erase(o->GetGUID());
}
+
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 instanceable only static
HashMapHolder() {}
+
static LockType i_lock;
static MapType m_objectMap;
};
+
class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor, MaNGOS::ClassLevelLockable<ObjectAccessor, ACE_Thread_Mutex> >
{
+
friend class Trinity::OperatorNew<ObjectAccessor>;
ObjectAccessor();
~ObjectAccessor();
ObjectAccessor(const ObjectAccessor &);
ObjectAccessor& operator=(const ObjectAccessor &);
+
public:
typedef UNORDERED_MAP<uint64, Corpse* > Player2CorpsesMapType;
typedef UNORDERED_MAP<Player*, UpdateData>::value_type UpdateDataValueType;
+
template<class T> static T* GetObjectInWorld(uint64 guid, T* /*fake*/)
{
return HashMapHolder<T>::Find(guid);
}
+
static Unit* GetObjectInWorld(uint64 guid, Unit* /*fake*/)
{
if(!guid)
return NULL;
+
if (IS_PLAYER_GUID(guid))
{
Unit * u = (Unit*)HashMapHolder<Player>::Find(guid);
if(!u || !u->IsInWorld())
return NULL;
+
return u;
}
+
if(IS_PET_GUID(guid))
return (Unit*)HashMapHolder<Pet>::Find(guid);
+
return (Unit*)HashMapHolder<Creature>::Find(guid);
}
+
static Unit* GetUnitInOrOutOfWorld(uint64 guid, Unit* /*fake*/)
{
if(!guid)
return NULL;
+
if (IS_PLAYER_GUID(guid))
{
Unit * u = (Unit*)HashMapHolder<Player>::Find(guid);
if(!u)
return NULL;
+
return u;
}
// Other object types than player are unloaded while out of world
return GetObjectInWorld(guid, ((Unit*)NULL));
}
+
template<class T> static T* GetObjectInWorld(uint32 mapid, float x, float y, uint64 guid, T* /*fake*/)
{
T* obj = HashMapHolder<T>::Find(guid);
if(!obj || obj->GetMapId() != mapid) return NULL;
+
CellPair p = Trinity::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 = Trinity::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 (GUID: %u TypeId: %u) has invalid coordinates X:%f Y:%f grid cell [%u:%u]", obj->GetGUIDLow(), obj->GetTypeId(), 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 WorldObject* GetWorldObject(WorldObject const &, uint64);
static Object* GetObjectByTypeMask(WorldObject const &, uint64, uint32 typemask);
static Creature* GetCreatureOrPetOrVehicle(WorldObject const &, uint64);
@@ -137,52 +170,67 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor,
static Corpse* GetCorpse(WorldObject const &u, uint64 guid);
static Pet* GetPet(uint64 guid);
static Player* FindPlayer(uint64);
+
Player* FindPlayerByName(const char *name) ;
+
HashMapHolder<Player>::MapType& GetPlayers()
{
return HashMapHolder<Player>::GetContainer();
}
+
HashMapHolder<Creature>::MapType& GetCreatures()
{
return HashMapHolder<Creature>::GetContainer();
}
+
HashMapHolder<GameObject>::MapType& GetGameObjects()
{
return HashMapHolder<GameObject>::GetContainer();
}
+
template<class T> void AddObject(T *object)
{
HashMapHolder<T>::Insert(object);
}
+
template<class T> void RemoveObject(T *object)
{
HashMapHolder<T>::Remove(object);
}
+
void RemoveObject(Player *pl)
{
HashMapHolder<Player>::Remove(pl);
+
Guard guard(i_updateGuard);
i_objects.erase((Object *)pl);
}
+
void SaveAllPlayers();
+
void AddUpdateObject(Object *obj)
{
Guard guard(i_updateGuard);
i_objects.insert(obj);
}
+
void RemoveUpdateObject(Object *obj)
{
Guard guard(i_updateGuard);
i_objects.erase( 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 insignia = false);
+
static void UpdateObject(Object* obj, Player* exceptPlayer);
static void _buildUpdateObject(Object* obj, UpdateDataMapType &);
+
static void UpdateObjectVisibility(WorldObject* obj);
//static void UpdateVisibilityForPlayer(Player* player);
private:
@@ -198,10 +246,13 @@ class MANGOS_DLL_DECL ObjectAccessor : public MaNGOS::Singleton<ObjectAccessor,
void BuildPacket(Player* plr);
template<class SKIP> void Visit(GridRefManager<SKIP> &) {}
};
+
friend struct WorldObjectChangeAccumulator;
Player2CorpsesMapType i_player2corpse;
+
typedef ACE_Thread_Mutex LockType;
typedef MaNGOS::GeneralLock<LockType > Guard;
+
static void _buildChangeObjectForPlayer(WorldObject *, UpdateDataMapType &);
static void _buildPacket(Player *, Object *, UpdateDataMapType &);
void _update(void);
diff --git a/src/game/ObjectDefines.h b/src/game/ObjectDefines.h
index be6bd7ce369..6a2eb7906eb 100644
--- a/src/game/ObjectDefines.h
+++ b/src/game/ObjectDefines.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_OBJECTDEFINES_H
#define TRINITY_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) & UI64LIT(0x00000000FFFFFFFF))
#define PAIR64_LOPART(x) (uint32)(uint64(x) & UI64LIT(0x00000000FFFFFFFF))
+
#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
@@ -41,7 +46,9 @@ enum HighGuid
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_VEHICLE_GUID(Guid) ( GUID_HIPART(Guid) == HIGHGUID_VEHICLE )
@@ -56,16 +63,20 @@ enum HighGuid
#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) & UI64LIT(0x0000000000FFFFFF))
#define _GUID_LOPART_2(x) (uint32)(uint64(x) & UI64LIT(0x00000000FFFFFFFF))
#define _GUID_LOPART_3(x) (uint32)(uint64(x) & UI64LIT(0x0000000000FFFFFF))
+
inline bool IsGuidHaveEnPart(uint64 const& guid)
{
switch(GUID_HIPART(guid))
@@ -85,8 +96,10 @@ inline bool IsGuidHaveEnPart(uint64 const& guid)
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))
diff --git a/src/game/ObjectGridLoader.cpp b/src/game/ObjectGridLoader.cpp
index 32b0a08e409..6a11fe44f86 100644
--- a/src/game/ObjectGridLoader.cpp
+++ b/src/game/ObjectGridLoader.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -28,20 +29,25 @@
#include "World.h"
#include "CellImpl.h"
#include "CreatureAI.h"
+
class TRINITY_DLL_DECL ObjectGridRespawnMover
{
public:
ObjectGridRespawnMover() {}
+
void Move(GridType &grid);
+
template<class T> void Visit(GridRefManager<T> &) {}
void Visit(CreatureMapType &m);
};
+
void
ObjectGridRespawnMover::Move(GridType &grid)
{
TypeContainerVisitor<ObjectGridRespawnMover, GridTypeMapContainer > mover(*this);
grid.Visit(mover);
}
+
void
ObjectGridRespawnMover::Visit(CreatureMapType &m)
{
@@ -52,12 +58,16 @@ ObjectGridRespawnMover::Visit(CreatureMapType &m)
{
Creature * c = iter->getSource();
++iter;
+
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 = Trinity::ComputeCellPair(resp_x, resp_y);
Cell resp_cell(resp_val);
+
if(cur_cell.DiffGrid(resp_cell))
{
c->GetMap()->CreatureRespawnRelocation(c);
@@ -65,6 +75,7 @@ ObjectGridRespawnMover::Visit(CreatureMapType &m)
}
}
}
+
// for loading world object at grid loading (Corpses)
class ObjectWorldLoader
{
@@ -72,8 +83,11 @@ class ObjectWorldLoader
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<class T> void Visit(GridRefManager<T>&) { }
+
private:
Cell i_cell;
NGridType &i_grid;
@@ -81,16 +95,20 @@ class ObjectWorldLoader
public:
uint32 i_corpses;
};
+
template<class T> 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 <class T>
void AddObjectHelper(CellPair &cell, GridRefManager<T> &m, uint32 &count, Map* map, T *obj)
{
@@ -99,8 +117,10 @@ void AddObjectHelper(CellPair &cell, GridRefManager<T> &m, uint32 &count, Map* m
obj->AddToWorld();
if(obj->isActiveObject())
map->AddToActive(obj);
+
++count;
}
+
template <class T>
void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> &m, uint32 &count, Map* map)
{
@@ -114,29 +134,37 @@ void LoadHelper(CellGuidSet const& guid_set, CellPair &cell, GridRefManager<T> &
delete obj;
continue;
}
+
AddObjectHelper(cell, m, count, map, obj);
}
}
+
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;
+
// TODO: this is a hack
// corpse's map should be reset when the map is unloaded
// but it may still exist when the grid is unloaded but map is not
// in that case map == currMap
obj->SetMap(map);
+
AddObjectHelper(cell, m, count, map, obj);
}
}
+
void
ObjectGridLoader::Visit(GameObjectMapType &m)
{
@@ -144,9 +172,12 @@ ObjectGridLoader::Visit(GameObjectMapType &m)
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)
{
@@ -154,9 +185,12 @@ ObjectGridLoader::Visit(CreatureMapType &m)
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)
{
@@ -164,10 +198,12 @@ ObjectWorldLoader::Visit(CorpseMapType &m)
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)
{
@@ -175,6 +211,7 @@ ObjectGridLoader::Load(GridType &grid)
TypeContainerVisitor<ObjectGridLoader, GridTypeMapContainer > loader(*this);
grid.Visit(loader);
}
+
{
ObjectWorldLoader wloader(*this);
TypeContainerVisitor<ObjectWorldLoader, WorldTypeMapContainer > loader(wloader);
@@ -182,6 +219,7 @@ ObjectGridLoader::Load(GridType &grid)
i_corpses = wloader.i_corpses;
}
}
+
void ObjectGridLoader::LoadN(void)
{
i_gameObjects = 0; i_creatures = 0; i_corpses = 0;
@@ -198,6 +236,7 @@ void ObjectGridLoader::LoadN(void)
}
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)
@@ -209,12 +248,14 @@ void ObjectGridUnloader::MoveToRespawnN()
}
}
}
+
void
ObjectGridUnloader::Unload(GridType &grid)
{
TypeContainerVisitor<ObjectGridUnloader, GridTypeMapContainer > unloader(*this);
grid.Visit(unloader);
}
+
template<class T>
void
ObjectGridUnloader::Visit(GridRefManager<T> &m)
@@ -229,12 +270,14 @@ ObjectGridUnloader::Visit(GridRefManager<T> &m)
delete obj;
}
}
+
void
ObjectGridStoper::Stop(GridType &grid)
{
TypeContainerVisitor<ObjectGridStoper, GridTypeMapContainer > stoper(*this);
grid.Visit(stoper);
}
+
void
ObjectGridStoper::Visit(CreatureMapType &m)
{
@@ -250,18 +293,21 @@ ObjectGridStoper::Visit(CreatureMapType &m)
}
}
}
+
void
ObjectGridCleaner::Stop(GridType &grid)
{
TypeContainerVisitor<ObjectGridCleaner, GridTypeMapContainer > stoper(*this);
grid.Visit(stoper);
}
+
void
ObjectGridCleaner::Visit(CreatureMapType &m)
{
for(CreatureMapType::iterator iter=m.begin(); iter != m.end(); ++iter)
iter->getSource()->CleanupsBeforeDelete();
}
+
template<class T>
void
ObjectGridCleaner::Visit(GridRefManager<T> &m)
@@ -269,6 +315,7 @@ ObjectGridCleaner::Visit(GridRefManager<T> &m)
for(typename GridRefManager<T>::iterator iter = m.begin(); iter != m.end(); ++iter)
iter->getSource()->RemoveFromWorld();
}
+
template void ObjectGridUnloader::Visit(CreatureMapType &);
template void ObjectGridUnloader::Visit(GameObjectMapType &);
template void ObjectGridUnloader::Visit(DynamicObjectMapType &);
diff --git a/src/game/ObjectGridLoader.h b/src/game/ObjectGridLoader.h
index f4d6eea1967..768f1f994db 100644
--- a/src/game/ObjectGridLoader.h
+++ b/src/game/ObjectGridLoader.h
@@ -17,27 +17,36 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_OBJECTGRIDLOADER_H
#define TRINITY_OBJECTGRIDLOADER_H
+
#include "Utilities/TypeList.h"
#include "Platform/Define.h"
#include "GameSystem/GridLoader.h"
#include "GridDefines.h"
#include "Cell.h"
+
class ObjectWorldLoader;
+
class TRINITY_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;
@@ -46,10 +55,12 @@ class TRINITY_DLL_DECL ObjectGridLoader
uint32 i_creatures;
uint32 i_corpses;
};
+
class TRINITY_DLL_DECL ObjectGridUnloader
{
public:
ObjectGridUnloader(NGridType &grid) : i_grid(grid) {}
+
void MoveToRespawnN();
void UnloadN()
{
@@ -62,15 +73,18 @@ class TRINITY_DLL_DECL ObjectGridUnloader
}
}
}
+
void Unload(GridType &grid);
template<class T> void Visit(GridRefManager<T> &m);
private:
NGridType &i_grid;
};
+
class TRINITY_DLL_DECL ObjectGridStoper
{
public:
ObjectGridStoper(NGridType &grid) : i_grid(grid) {}
+
void StopN()
{
for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x)
@@ -82,16 +96,20 @@ class TRINITY_DLL_DECL ObjectGridStoper
}
}
}
+
void Stop(GridType &grid);
void Visit(CreatureMapType &m);
+
template<class NONACTIVE> void Visit(GridRefManager<NONACTIVE> &) {}
private:
NGridType &i_grid;
};
+
class TRINITY_DLL_DECL ObjectGridCleaner
{
public:
ObjectGridCleaner(NGridType &grid) : i_grid(grid) {}
+
void CleanN()
{
for(unsigned int x=0; x < MAX_NUMBER_OF_CELLS; ++x)
@@ -103,12 +121,14 @@ class TRINITY_DLL_DECL ObjectGridCleaner
}
}
}
+
void Stop(GridType &grid);
void Visit(CreatureMapType &m);
template<class T> void Visit(GridRefManager<T> &);
private:
NGridType &i_grid;
};
+
typedef GridLoader<Player, AllWorldObjectTypes, AllGridObjectTypes> GridLoaderType;
#endif
diff --git a/src/game/ObjectMgr.cpp b/src/game/ObjectMgr.cpp
index 3c6a361ff9e..588722213cb 100644
--- a/src/game/ObjectMgr.cpp
+++ b/src/game/ObjectMgr.cpp
@@ -17,11 +17,13 @@
* 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 "Database/SQLStorageImpl.h"
#include "Policies/SingletonImp.h"
+
#include "Log.h"
#include "MapManager.h"
#include "ObjectMgr.h"
@@ -43,28 +45,37 @@
#include "Util.h"
#include "WaypointManager.h"
#include "InstanceData.h" //for condition_instance_data
+
INSTANTIATE_SINGLETON_1(ObjectMgr);
+
ScriptMapMap sQuestEndScripts;
ScriptMapMap sQuestStartScripts;
ScriptMapMap sSpellScripts;
ScriptMapMap sGameObjectScripts;
ScriptMapMap sEventScripts;
ScriptMapMap sWaypointScripts;
+
bool normalizePlayerName(std::string& name)
{
if(name.empty())
return false;
+
wchar_t wstr_buf[MAX_INTERNAL_PLAYER_NAME+1];
size_t wstr_len = MAX_INTERNAL_PLAYER_NAME;
+
if(!Utf8toWStr(name,&wstr_buf[0],wstr_len))
return false;
+
wstr_buf[0] = wcharToUpper(wstr_buf[0]);
for(size_t i = 1; i < wstr_len; ++i)
wstr_buf[i] = wcharToLower(wstr_buf[i]);
+
if(!WStrToUtf8(wstr_buf,wstr_len,name))
return false;
+
return true;
}
+
LanguageDesc lang_description[LANGUAGES_COUNT] =
{
{ LANG_ADDON, 0, 0 },
@@ -87,6 +98,7 @@ LanguageDesc lang_description[LANGUAGES_COUNT] =
{ LANG_GNOMISH_BINARY, 0, 0 },
{ LANG_GOBLIN_BINARY, 0, 0 }
};
+
LanguageDesc const* GetLanguageDescByID(uint32 lang)
{
for(uint8 i = 0; i < LANGUAGES_COUNT; ++i)
@@ -94,8 +106,10 @@ LanguageDesc const* GetLanguageDescByID(uint32 lang)
if(uint32(lang_description[i].lang_id) == lang)
return &lang_description[i];
}
+
return NULL;
}
+
bool SpellClickInfo::IsFitToRequirements(Player const* player, Creature const * clickNpc) const
{
if(questStart)
@@ -104,24 +118,29 @@ bool SpellClickInfo::IsFitToRequirements(Player const* player, Creature const *
if (!player || ((!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart)))
return false;
}
+
if(questEnd)
{
// not in expected forbidden quest state
if(!player || player->GetQuestRewardStatus(questEnd))
return false;
}
+
if (auraRequired)
if (!player->HasAura(auraRequired))
return false;
+
if (auraForbidden)
if (player->HasAura(auraForbidden))
return false;
+
Unit const * summoner = NULL;
// Check summoners for party
if (clickNpc->isSummon())
summoner = ((TempSummon*)clickNpc)->GetSummoner();
if (!summoner)
summoner = clickNpc;
+
switch (userType)
{
case SPELL_CLICK_USER_FRIEND:
@@ -137,8 +156,10 @@ bool SpellClickInfo::IsFitToRequirements(Player const* player, Creature const *
return false;
break;
}
+
return true;
}
+
ObjectMgr::ObjectMgr()
{
m_hiCharGuid = 1;
@@ -156,36 +177,48 @@ ObjectMgr::ObjectMgr()
m_guildId = 1;
m_arenaTeamId = 1;
m_auctionid = 1;
+
// 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;
+
for(PetLevelInfoMap::iterator i = petInfo.begin( ); i != petInfo.end( ); ++i )
delete[] i->second;
+
// 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 (GuildMap::iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
delete itr->second;
mGuildMap.clear();
+
for (CachePlayerInfoMap::iterator itr = m_mPlayerInfoMap.begin(); itr != m_mPlayerInfoMap.end(); ++itr)
delete itr->second;
+
for (ArenaTeamMap::iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
delete itr->second;
+
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
itr->second.Clear();
+
for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
itr->second.Clear();
}
+
void ObjectMgr::LoadPlayerInfoInCache()
{
QueryResult *result = CharacterDatabase.PQuery("SELECT guid, name, data, class FROM characters");
@@ -194,6 +227,7 @@ void ObjectMgr::LoadPlayerInfoInCache()
sLog.outError( "Loading Player Cache failed.");
return;
}
+
PCachePlayerInfo pPPlayerInfo = NULL;
Field *fields = NULL;
Tokens tdata;
@@ -203,25 +237,33 @@ void ObjectMgr::LoadPlayerInfoInCache()
bar.step();
fields = result->Fetch();
pPPlayerInfo = new CachePlayerInfo();
+
pPPlayerInfo->sPlayerName = fields[1].GetString();
+
tdata.clear();
tdata = StrSplit(fields[2].GetCppString(), " ");
+
pPPlayerInfo->unLevel = Player::GetUInt32ValueFromArray(tdata,UNIT_FIELD_LEVEL);
pPPlayerInfo->unfield = Player::GetUInt32ValueFromArray(tdata,UNIT_FIELD_BYTES_0);
+
pPPlayerInfo->unArenaInfoId0 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 0 * 6);
pPPlayerInfo->unArenaInfoId1 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 * 6);
pPPlayerInfo->unArenaInfoId2 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 2 * 6);
+
pPPlayerInfo->unArenaInfoSlot0 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 0 * 6 + 5);
pPPlayerInfo->unArenaInfoSlot1 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 1 * 6 + 5);
pPPlayerInfo->unArenaInfoSlot2 = Player::GetUInt32ValueFromArray(tdata,PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + 2 * 6 + 5);
+
pPPlayerInfo->unClass = (uint32)fields[3].GetUInt32();
m_mPlayerInfoMap[fields[0].GetUInt32()] = pPPlayerInfo;
}
while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded info about %d players", m_mPlayerInfoMap.size());
}
+
PCachePlayerInfo ObjectMgr::GetPlayerInfoFromCache(uint32 unPlayerGuid) const
{
//Now m_mPlayerInfoMap is using only for search, but when dinamic inserting/removing
@@ -231,101 +273,132 @@ PCachePlayerInfo ObjectMgr::GetPlayerInfoFromCache(uint32 unPlayerGuid) const
CachePlayerInfoMap::const_iterator ipos = m_mPlayerInfoMap.find(unPlayerGuid);
return ipos == m_mPlayerInfoMap.end() ? NULL : ipos->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(uint32 GuildId) const
{
GuildMap::const_iterator itr = mGuildMap.find(GuildId);
if (itr != mGuildMap.end())
return itr->second;
+
return NULL;
}
+
Guild * ObjectMgr::GetGuildByName(const std::string& guildname) const
{
for(GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
if (itr->second->GetName() == guildname)
return itr->second;
+
return NULL;
}
+
std::string ObjectMgr::GetGuildNameById(uint32 GuildId) const
{
GuildMap::const_iterator itr = mGuildMap.find(GuildId);
if (itr != mGuildMap.end())
return itr->second->GetName();
+
return "";
}
+
Guild* ObjectMgr::GetGuildByLeader(const uint64 &guid) const
{
for(GuildMap::const_iterator itr = mGuildMap.begin(); itr != mGuildMap.end(); ++itr)
if (itr->second->GetLeader() == guid)
return itr->second;
+
return NULL;
}
+
void ObjectMgr::AddGuild(Guild* guild)
{
mGuildMap[guild->GetId()] = guild;
}
+
void ObjectMgr::RemoveGuild(uint32 Id)
{
mGuildMap.erase(Id);
}
+
ArenaTeam* ObjectMgr::GetArenaTeamById(uint32 arenateamid) const
{
ArenaTeamMap::const_iterator itr = mArenaTeamMap.find(arenateamid);
if (itr != mArenaTeamMap.end())
return itr->second;
+
return NULL;
}
+
ArenaTeam* ObjectMgr::GetArenaTeamByName(const std::string& arenateamname) const
{
for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
if (itr->second->GetName() == arenateamname)
return itr->second;
+
return NULL;
}
+
ArenaTeam* ObjectMgr::GetArenaTeamByCaptain(uint64 const& guid) const
{
for(ArenaTeamMap::const_iterator itr = mArenaTeamMap.begin(); itr != mArenaTeamMap.end(); ++itr)
if (itr->second->GetCaptain() == guid)
return itr->second;
+
return NULL;
}
+
void ObjectMgr::AddArenaTeam(ArenaTeam* arenaTeam)
{
mArenaTeamMap[arenaTeam->GetId()] = arenaTeam;
}
+
void ObjectMgr::RemoveArenaTeam(uint32 Id)
{
mArenaTeamMap.erase(Id);
}
+
CreatureInfo const* ObjectMgr::GetCreatureTemplate(uint32 id)
{
return sCreatureStorage.LookupEntry<CreatureInfo>(id);
}
+
void ObjectMgr::LoadCreatureLocales()
{
mCreatureLocaleMap.clear(); // need for reload case
+
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(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
@@ -336,6 +409,7 @@ void ObjectMgr::LoadCreatureLocales()
{
if(data.Name.size() <= idx)
data.Name.resize(idx+1);
+
data.Name[idx] = str;
}
}
@@ -347,39 +421,52 @@ void ObjectMgr::LoadCreatureLocales()
{
if(data.SubName.size() <= idx)
data.SubName.resize(idx+1);
+
data.SubName[idx] = str;
}
}
}
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu creature locale strings", (unsigned long)mCreatureLocaleMap.size() );
}
+
void ObjectMgr::LoadNpcOptionLocales()
{
mNpcOptionLocaleMap.clear(); // need for reload case
+
QueryResult *result = WorldDatabase.Query("SELECT entry,"
"option_text_loc1,box_text_loc1,option_text_loc2,box_text_loc2,"
"option_text_loc3,box_text_loc3,option_text_loc4,box_text_loc4,"
"option_text_loc5,box_text_loc5,option_text_loc6,box_text_loc6,"
"option_text_loc7,box_text_loc7,option_text_loc8,box_text_loc8 "
"FROM locales_npc_option");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded 0 npc_option locale strings. DB table `locales_npc_option` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 entry = fields[0].GetUInt32();
+
NpcOptionLocale& data = mNpcOptionLocaleMap[entry];
+
for(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
@@ -390,6 +477,7 @@ void ObjectMgr::LoadNpcOptionLocales()
{
if(data.OptionText.size() <= idx)
data.OptionText.resize(idx+1);
+
data.OptionText[idx] = str;
}
}
@@ -401,52 +489,70 @@ void ObjectMgr::LoadNpcOptionLocales()
{
if(data.BoxText.size() <= idx)
data.BoxText.resize(idx+1);
+
data.BoxText[idx] = str;
}
}
}
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu npc_option locale strings", (unsigned long)mNpcOptionLocaleMap.size() );
}
+
void ObjectMgr::LoadPointOfInterestLocales()
{
mPointOfInterestLocaleMap.clear(); // need for reload case
+
QueryResult *result = WorldDatabase.Query("SELECT entry,icon_name_loc1,icon_name_loc2,icon_name_loc3,icon_name_loc4,icon_name_loc5,icon_name_loc6,icon_name_loc7,icon_name_loc8 FROM locales_points_of_interest");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded 0 points_of_interest locale strings. DB table `locales_points_of_interest` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 entry = fields[0].GetUInt32();
+
PointOfInterestLocale& data = mPointOfInterestLocaleMap[entry];
+
for(uint8 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.IconName.size() <= idx)
data.IconName.resize(idx+1);
+
data.IconName[idx] = str;
}
}
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu points_of_interest locale strings", (unsigned long)mPointOfInterestLocaleMap.size() );
}
+
struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader>
{
template<class D>
@@ -455,20 +561,25 @@ struct SQLCreatureLoader : public SQLStorageLoaderBase<SQLCreatureLoader>
dst = D(objmgr.GetScriptId(src));
}
};
+
void ObjectMgr::LoadCreatureTemplates()
{
SQLCreatureLoader loader;
loader.Load(sCreatureStorage);
+
sLog.outString( ">> Loaded %u creature definitions", sCreatureStorage.RecordCount );
sLog.outString();
+
std::set<uint32> heroicEntries; // already loaded heroic value in creatures
std::set<uint32> hasHeroicEntries; // already loaded creatures with heroic entry values
+
// check data correctness
for(uint32 i = 1; i < sCreatureStorage.MaxEntry; ++i)
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
if (!cInfo)
continue;
+
if (cInfo->HeroicEntry)
{
CreatureInfo const* heroicInfo = GetCreatureTemplate(cInfo->HeroicEntry);
@@ -477,72 +588,88 @@ void ObjectMgr::LoadCreatureTemplates()
sLog.outErrorDb("Creature (Entry: %u) have `heroic_entry`=%u but creature entry %u not exist.", i, 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->unit_class != heroicInfo->unit_class)
{
sLog.outErrorDb("Creature (Entry: %u, class %u) has different `unit_class` in heroic mode (Entry: %u, class %u).",i, cInfo->unit_class, cInfo->HeroicEntry, heroicInfo->unit_class);
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->trainer_class != heroicInfo->trainer_class)
{
sLog.outErrorDb("Creature (Entry: %u) has different `trainer_class` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
continue;
}
+
if (cInfo->trainer_race != heroicInfo->trainer_race)
{
sLog.outErrorDb("Creature (Entry: %u) has different `trainer_race` in heroic mode (Entry: %u).",i,cInfo->HeroicEntry);
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;
}
+
if (heroicInfo->AIName && *heroicInfo->AIName)
{
sLog.outErrorDb("Heroic mode creature (Entry: %u) has `AIName`, but in any case will used normal mode creature (Entry: %u) AIName.",cInfo->HeroicEntry,i);
continue;
}
+
if (heroicInfo->ScriptID)
{
sLog.outErrorDb("Heroic mode creature (Entry: %u) has `ScriptName`, but in any case will used normal mode creature (Entry: %u) ScriptName.",cInfo->HeroicEntry,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);
+
// used later for scale
CreatureDisplayInfoEntry const* displayScaleEntry = NULL;
+
if (cInfo->Modelid1)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid1);
@@ -553,10 +680,12 @@ void ObjectMgr::LoadCreatureTemplates()
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
+
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid1);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid1 (%u)", cInfo->Entry, cInfo->Modelid1);
}
+
if (cInfo->Modelid2)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid2);
@@ -567,10 +696,12 @@ void ObjectMgr::LoadCreatureTemplates()
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
+
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid2);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid2 (%u)", cInfo->Entry, cInfo->Modelid2);
}
+
if (cInfo->Modelid3)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid3);
@@ -581,10 +712,12 @@ void ObjectMgr::LoadCreatureTemplates()
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
+
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid3);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid3 (%u)", cInfo->Entry, cInfo->Modelid3);
}
+
if (cInfo->Modelid4)
{
CreatureDisplayInfoEntry const* displayEntry = sCreatureDisplayInfoStore.LookupEntry(cInfo->Modelid4);
@@ -595,12 +728,15 @@ void ObjectMgr::LoadCreatureTemplates()
}
else if(!displayScaleEntry)
displayScaleEntry = displayEntry;
+
CreatureModelInfo const* minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(cInfo->Modelid4);
if (!minfo)
sLog.outErrorDb("Creature (Entry: %u) not has model data for Modelid4 (%u)", cInfo->Entry, cInfo->Modelid4);
}
+
if (!displayScaleEntry)
sLog.outErrorDb("Creature (Entry: %u) not has any existed display id in Modelid1/Modelid2/Modelid3/Modelid4", cInfo->Entry);
+
for(int k = 0; k < MAX_KILL_CREDIT; ++k)
{
if(cInfo->KillCredit[k])
@@ -612,46 +748,57 @@ void ObjectMgr::LoadCreatureTemplates()
}
}
}
+
if (cInfo->unit_class && ((1 << (cInfo->unit_class-1)) & CLASSMASK_ALL_CREATURES) == 0)
sLog.outErrorDb("Creature (Entry: %u) has invalid unit_class(%u) for creature_template", cInfo->Entry, cInfo->unit_class);
+
if(cInfo->dmgschool >= MAX_SPELL_SCHOOL)
{
sLog.outErrorDb("Creature (Entry: %u) has invalid spell school value (%u) in `dmgschool`",cInfo->Entry,cInfo->dmgschool);
const_cast<CreatureInfo*>(cInfo)->dmgschool = SPELL_SCHOOL_NORMAL;
}
+
if(cInfo->baseattacktime == 0)
const_cast<CreatureInfo*>(cInfo)->baseattacktime = BASE_ATTACK_TIME;
+
if(cInfo->rangeattacktime == 0)
const_cast<CreatureInfo*>(cInfo)->rangeattacktime = BASE_ATTACK_TIME;
+
if(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK)
{
sLog.outErrorDb("Creature (Entry: %u) has dynamic flag UNIT_NPC_FLAG_SPELLCLICK (%u) set, it expect to be set by code base at `npc_spellclick_spells` content.",cInfo->Entry,UNIT_NPC_FLAG_SPELLCLICK);
const_cast<CreatureInfo*>(cInfo)->npcflag &= ~UNIT_NPC_FLAG_SPELLCLICK;
}
+
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->type && !sCreatureTypeStore.LookupEntry(cInfo->type))
{
sLog.outErrorDb("Creature (Entry: %u) has invalid creature type (%u) in `type`",cInfo->Entry,cInfo->type);
const_cast<CreatureInfo*>(cInfo)->type = CREATURE_TYPE_HUMANOID;
}
+
// must exist or used hidden but used in data horse case
if(cInfo->family && !sCreatureFamilyStore.LookupEntry(cInfo->family) && cInfo->family != CREATURE_FAMILY_HORSE_CUSTOM )
{
sLog.outErrorDb("Creature (Entry: %u) has invalid creature family (%u) in `family`",cInfo->Entry,cInfo->family);
const_cast<CreatureInfo*>(cInfo)->family = 0;
}
+
if(cInfo->InhabitType <= 0 || cInfo->InhabitType > INHABIT_ANYWHERE)
{
sLog.outErrorDb("Creature (Entry: %u) has wrong value (%u) in `InhabitType`, creature will not correctly walk/swim/fly",cInfo->Entry,cInfo->InhabitType);
const_cast<CreatureInfo*>(cInfo)->InhabitType = INHABIT_ANYWHERE;
}
+
if(cInfo->PetSpellDataId)
{
CreatureSpellDataEntry const* spellDataId = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
if(!spellDataId)
sLog.outErrorDb("Creature (Entry: %u) has non-existing PetSpellDataId (%u)", cInfo->Entry, cInfo->PetSpellDataId);
}
+
for(uint8 j = 0; j < CREATURE_MAX_SPELLS; ++j)
{
if(cInfo->spells[j] && !sSpellStore.LookupEntry(cInfo->spells[j]))
@@ -660,11 +807,13 @@ void ObjectMgr::LoadCreatureTemplates()
const_cast<CreatureInfo*>(cInfo)->spells[j] = 0;
}
}
+
if(cInfo->MovementType >= MAX_DB_MOTION_TYPE)
{
sLog.outErrorDb("Creature (Entry: %u) has wrong movement generator type (%u), ignore and set to IDLE.",cInfo->Entry,cInfo->MovementType);
const_cast<CreatureInfo*>(cInfo)->MovementType = IDLE_MOTION_TYPE;
}
+
if(cInfo->equipmentId > 0) // 0 no equipment
{
if(!GetEquipmentInfo(cInfo->equipmentId))
@@ -673,6 +822,7 @@ void ObjectMgr::LoadCreatureTemplates()
const_cast<CreatureInfo*>(cInfo)->equipmentId = 0;
}
}
+
/// if not set custom creature scale then load scale from CreatureDisplayInfo.dbc
if(cInfo->scale <= 0.0f)
{
@@ -681,9 +831,11 @@ void ObjectMgr::LoadCreatureTemplates()
else
const_cast<CreatureInfo*>(cInfo)->scale = 1.0f;
}
+
const_cast<CreatureInfo*>(cInfo)->dmg_multiplier *= Creature::_GetDamageMod(cInfo->rank);
}
}
+
void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr)
{
// Now add the auras, format "spellid effectindex spellid effectindex..."
@@ -703,8 +855,10 @@ void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const*
}
if (p!=s)
val.push_back(atoi(s));
+
// free char* loaded memory
delete[] (char*)reinterpret_cast<char const*>(addon->auras);
+
// wrong list
if (val.size()%2)
{
@@ -713,14 +867,17 @@ void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const*
return;
}
}
+
// empty list
if(val.empty())
{
addon->auras = NULL;
return;
}
+
// replace by new structures array
const_cast<CreatureDataAddonAura*&>(addon->auras) = new CreatureDataAddonAura[val.size()/2+1];
+
uint32 i=0;
for(uint32 j = 0; j < val.size()/2;++j)
{
@@ -738,29 +895,36 @@ void ObjectMgr::ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const*
sLog.outErrorDb("Creature (%s: %u) has wrong spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.spell_id,table);
continue;
}
+
if (!AdditionalSpellInfo->Effect[cAura.effect_idx] || !AdditionalSpellInfo->EffectApplyAuraName[cAura.effect_idx])
{
sLog.outErrorDb("Creature (%s: %u) has not aura effect %u of spell %u defined in `auras` field in `%s`.",guidEntryStr,addon->guidOrEntry,cAura.effect_idx,cAura.spell_id,table);
continue;
}
+
++i;
}
+
// fill terminator element (after last added)
CreatureDataAddonAura& endAura = const_cast<CreatureDataAddonAura&>(addon->auras[i]);
endAura.spell_id = 0;
endAura.effect_idx = 0;
}
+
void ObjectMgr::LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment)
{
creatureaddons.Load();
+
sLog.outString(">> Loaded %u %s", creatureaddons.RecordCount, comment);
sLog.outString();
+
// check data correctness and convert 'auras'
for(uint32 i = 1; i < creatureaddons.MaxEntry; ++i)
{
CreatureDataAddon const* addon = creatureaddons.LookupEntry<CreatureDataAddon>(i);
if(!addon)
continue;
+
if (addon->mount)
{
if (!sCreatureDisplayInfoStore.LookupEntry(addon->mount))
@@ -769,54 +933,69 @@ void ObjectMgr::LoadCreatureAddons(SQLStorage& creatureaddons, char const* entry
const_cast<CreatureDataAddon*>(addon)->mount = 0;
}
}
+
if (!sEmotesStore.LookupEntry(addon->emote))
sLog.outErrorDb("Creature (%s %u) have invalid emote (%u) defined in `%s`.", entryName, addon->guidOrEntry, addon->emote, creatureaddons.GetTableName());
+
/*if (addon->move_flags & (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4))
{
sLog.outErrorDb("Creature (%s %u) movement flags mask defined in `%s` include forbidden flags (" I32FMT ") that can crash client, cleanup at load.", entryName, addon->guidOrEntry, creatureaddons.GetTableName(), (MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4));
const_cast<CreatureDataAddon*>(addon)->move_flags &= ~(MONSTER_MOVE_UNK1|MONSTER_MOVE_UNK4);
}*/
+
ConvertCreatureAddonAuras(const_cast<CreatureDataAddon*>(addon), creatureaddons.GetTableName(), entryName);
}
}
+
void ObjectMgr::LoadCreatureAddons()
{
LoadCreatureAddons(sCreatureInfoAddonStorage,"Entry","creature template addons");
+
// check entry ids
for(uint32 i = 1; i < sCreatureInfoAddonStorage.MaxEntry; ++i)
if(CreatureDataAddon const* addon = sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(i))
if(!sCreatureStorage.LookupEntry<CreatureInfo>(addon->guidOrEntry))
sLog.outErrorDb("Creature (Entry: %u) does not exist but has a record in `%s`",addon->guidOrEntry, sCreatureInfoAddonStorage.GetTableName());
+
LoadCreatureAddons(sCreatureDataAddonStorage,"GUID","creature addons");
+
// check entry ids
for(uint32 i = 1; i < sCreatureDataAddonStorage.MaxEntry; ++i)
if(CreatureDataAddon const* addon = sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(i))
if(mCreatureDataMap.find(addon->guidOrEntry)==mCreatureDataMap.end())
sLog.outErrorDb("Creature (GUID: %u) does not exist but has a record in `creature_addon`",addon->guidOrEntry);
}
+
EquipmentInfo const* ObjectMgr::GetEquipmentInfo(uint32 entry)
{
return sEquipmentStorage.LookupEntry<EquipmentInfo>(entry);
}
+
void ObjectMgr::LoadEquipmentTemplates()
{
sEquipmentStorage.Load();
+
for(uint32 i=0; i< sEquipmentStorage.MaxEntry; ++i)
{
EquipmentInfo const* eqInfo = sEquipmentStorage.LookupEntry<EquipmentInfo>(i);
+
if(!eqInfo)
continue;
+
for(uint8 j=0; j<3; j++)
{
if(!eqInfo->equipentry[j])
continue;
+
ItemEntry const *dbcitem = sItemStore.LookupEntry(eqInfo->equipentry[j]);
+
if(!dbcitem)
{
sLog.outErrorDb("Unknown item (entry=%u) in creature_equip_template.equipentry%u for entry = %u, forced to 0.", eqInfo->equipentry[j], j+1, i);
const_cast<EquipmentInfo*>(eqInfo)->equipentry[j] = 0;
continue;
}
+
if(dbcitem->InventoryType != INVTYPE_WEAPON &&
dbcitem->InventoryType != INVTYPE_SHIELD &&
dbcitem->InventoryType != INVTYPE_RANGED &&
@@ -835,20 +1014,24 @@ void ObjectMgr::LoadEquipmentTemplates()
sLog.outString( ">> Loaded %u equipment template", sEquipmentStorage.RecordCount );
sLog.outString();
}
+
CreatureModelInfo const* ObjectMgr::GetCreatureModelInfo(uint32 modelid)
{
return sCreatureModelStorage.LookupEntry<CreatureModelInfo>(modelid);
}
+
uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const CreatureData *data /*= NULL*/)
{
// Load creature model (display id)
uint32 display_id = 0;
+
if (!data || data->displayid == 0)
{
display_id = cinfo->GetRandomValidModelId();
}
else
return data->displayid;
+
/*if(!team)
{
switch(cinfo->Entry)
@@ -866,13 +1049,16 @@ uint32 ObjectMgr::ChooseDisplayId(uint32 team, const CreatureInfo *cinfo, const
return cinfo->GetRandomValidModelId();
}
}*/
+
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)
{
@@ -888,36 +1074,44 @@ CreatureModelInfo const* ObjectMgr::GetCreatureModelRandomGender(uint32 display_
else
return minfo;
}
+
void ObjectMgr::LoadCreatureModelInfo()
{
sCreatureModelStorage.Load();
+
// post processing
for(uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i)
{
CreatureModelInfo const *minfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
if (!minfo)
continue;
+
if (!sCreatureDisplayInfoStore.LookupEntry(minfo->modelid))
sLog.outErrorDb("Table `creature_model_info` has model for not existed display id (%u).", minfo->modelid);
+
if (minfo->gender > GENDER_NONE)
{
sLog.outErrorDb("Table `creature_model_info` has wrong gender (%u) for display id (%u).", uint32(minfo->gender), minfo->modelid);
const_cast<CreatureModelInfo*>(minfo)->gender = GENDER_MALE;
}
+
if (minfo->modelid_other_gender && !sCreatureDisplayInfoStore.LookupEntry(minfo->modelid_other_gender))
{
sLog.outErrorDb("Table `creature_model_info` has not existed alt.gender model (%u) for existed display id (%u).", minfo->modelid_other_gender, minfo->modelid);
const_cast<CreatureModelInfo*>(minfo)->modelid_other_gender = 0;
}
}
+
sLog.outString( ">> Loaded %u creature model based info", sCreatureModelStorage.RecordCount );
sLog.outString();
+
// check if combat_reach is valid
for(uint32 i = 1; i < sCreatureModelStorage.MaxEntry; ++i)
{
CreatureModelInfo const* mInfo = sCreatureModelStorage.LookupEntry<CreatureModelInfo>(i);
if(!mInfo)
continue;
+
if(mInfo->combat_reach < 0.1f)
{
//sLog.outErrorDb("Creature model (Entry: %u) has invalid combat reach (%f), setting it to 0.5", mInfo->modelid, mInfo->combat_reach);
@@ -925,66 +1119,86 @@ void ObjectMgr::LoadCreatureModelInfo()
}
}
}
+
bool ObjectMgr::CheckCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid) const
{
const CreatureData* const slave = GetCreatureData(guid);
const CreatureData* const master = GetCreatureData(linkedGuid);
+
if(!slave || !master) // they must have a corresponding entry in db
{
sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' which doesn't exist",guid,linkedGuid);
return false;
}
+
const MapEntry* const map = sMapStore.LookupEntry(master->mapid);
+
if(master->mapid != slave->mapid // link only to same map
&& (!map || map->Instanceable())) // or to unistanced world
{
sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' on an unpermitted map",guid,linkedGuid);
return false;
}
+
if(!(master->spawnMask & slave->spawnMask) // they must have a possibility to meet (normal/heroic difficulty)
&& (!map || map->Instanceable()))
{
sLog.outError("LinkedRespawn: Creature '%u' linking to '%u' with not corresponding spawnMask",guid,linkedGuid);
return false;
}
+
return true;
}
+
void ObjectMgr::LoadCreatureLinkedRespawn()
{
mCreatureLinkedRespawnMap.clear();
QueryResult *result = WorldDatabase.Query("SELECT guid, linkedGuid FROM creature_linked_respawn ORDER BY guid ASC");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString("");
sLog.outErrorDb(">> Loaded 0 linked respawns. DB table `creature_linked_respawn` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 guid = fields[0].GetUInt32();
uint32 linkedGuid = fields[1].GetUInt32();
+
if(CheckCreatureLinkedRespawn(guid,linkedGuid))
mCreatureLinkedRespawnMap[guid] = linkedGuid;
+
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u linked respawns", mCreatureLinkedRespawnMap.size() );
}
+
bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid)
{
if(!guid)
return false;
+
if(!linkedGuid) // we're removing the linking
{
mCreatureLinkedRespawnMap.erase(guid);
WorldDatabase.DirectPExecute("DELETE FROM creature_linked_respawn WHERE guid = '%u'",guid);
return true;
}
+
if(CheckCreatureLinkedRespawn(guid,linkedGuid)) // we add/change linking
{
mCreatureLinkedRespawnMap[guid] = linkedGuid;
@@ -993,6 +1207,7 @@ bool ObjectMgr::SetCreatureLinkedRespawn(uint32 guid, uint32 linkedGuid)
}
return false;
}
+
void ObjectMgr::LoadCreatures()
{
uint32 count = 0;
@@ -1004,36 +1219,47 @@ void ObjectMgr::LoadCreatures()
"curhealth, curmana, DeathState, MovementType, spawnMask, phaseMask, event, pool_entry "
"FROM creature LEFT OUTER JOIN game_event_creature ON creature.guid = game_event_creature.guid "
"LEFT OUTER JOIN pool_creature ON creature.guid = pool_creature.guid");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 creature. DB table `creature` is empty.");
return;
}
+
// build single time for check creature data
std::set<uint32> heroicCreatures;
for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i)
if(CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i))
if(cInfo->HeroicEntry)
heroicCreatures.insert(cInfo->HeroicEntry);
+
//TODO: remove this
//gameeventmgr.mGameEventCreatureGuids.resize(52*2-1);
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 guid = fields[ 0].GetUInt32();
uint32 entry = fields[ 1].GetUInt32();
+
CreatureInfo const* cInfo = GetCreatureTemplate(entry);
if(!cInfo)
{
sLog.outErrorDb("Table `creature` has creature (GUID: %u) with non existing creature entry %u, skipped.", guid, entry);
continue;
}
+
CreatureData& data = mCreatureDataMap[guid];
+
data.id = entry;
data.mapid = fields[ 2].GetUInt32();
data.displayid = fields[ 3].GetUInt32();
@@ -1053,11 +1279,13 @@ void ObjectMgr::LoadCreatures()
data.phaseMask = fields[17].GetUInt16();
int16 gameEvent = fields[18].GetInt16();
int16 PoolId = fields[19].GetInt16();
+
if(heroicCreatures.find(data.id)!=heroicCreatures.end())
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u) that listed as heroic template (entry: %u) in `creature_template_substitution`, skipped.",guid,data.id );
continue;
}
+
// I do not know why but in db most display id are not zero
/*if(data.displayid == 11686 || data.displayid == 24719)
{
@@ -1067,6 +1295,7 @@ void ObjectMgr::LoadCreatures()
|| data.displayid == cInfo->DisplayID_H || data.displayid == cInfo->DisplayID_H2)
data.displayid = 0;
*/
+
if(data.equipmentId > 0) // -1 no equipment, 0 use default
{
if(!GetEquipmentInfo(data.equipmentId))
@@ -1075,22 +1304,26 @@ void ObjectMgr::LoadCreatures()
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(cInfo->flags_extra & CREATURE_FLAG_EXTRA_INSTANCE_BIND)
{
MapEntry const* map = sMapStore.LookupEntry(data.mapid);
if(!map || !map->IsDungeon())
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `creature_template`.`flags_extra` including CREATURE_FLAG_EXTRA_INSTANCE_BIND but creature are not in instance.",guid,data.id);
}
+
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 );
@@ -1114,11 +1347,13 @@ void ObjectMgr::LoadCreatures()
data.spawndist = 0.0f;
}
}
+
if(data.phaseMask==0)
{
sLog.outErrorDb("Table `creature` have creature (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id );
data.phaseMask = 1;
}
+
//if(entry == 32307 || entry == 32308)
/*if(entry == 30739 || entry == 30740)
{
@@ -1132,14 +1367,20 @@ void ObjectMgr::LoadCreatures()
gameeventmgr.mGameEventCreatureGuids[51+51].push_back(guid);
gameeventmgr.mGameEventCreatureGuids[51+50].push_back(guid2);
}*/
+
if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system
AddCreatureToGrid(guid, &data);
+
++count;
+
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu creatures", (unsigned long)mCreatureDataMap.size() );
}
+
void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
{
uint8 mask = data->spawnMask;
@@ -1149,11 +1390,13 @@ void ObjectMgr::AddCreatureToGrid(uint32 guid, CreatureData const* data)
{
CellPair cell_pair = Trinity::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;
@@ -1163,19 +1406,23 @@ void ObjectMgr::RemoveCreatureFromGrid(uint32 guid, CreatureData const* data)
{
CellPair cell_pair = Trinity::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);
}
}
}
+
uint32 ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay, float rotation0, float rotation1, float rotation2, float rotation3)
{
GameObjectInfo const* goinfo = GetGameObjectInfo(entry);
if (!goinfo)
return 0;
+
Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(mapId));
if(!map)
return 0;
+
uint32 guid = GenerateLowGuid(HIGHGUID_GAMEOBJECT);
GameObjectData& data = NewGOData(guid);
data.id = entry;
@@ -1195,7 +1442,9 @@ uint32 ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, float y, float
data.phaseMask = PHASEMASK_NORMAL;
data.artKit = goinfo->type == GAMEOBJECT_TYPE_CAPTURE_POINT ? 21 : 0;
data.dbData = false;
+
AddGameobjectToGrid(guid, &data);
+
// Spawn if necessary (loaded grids only)
// We use spawn coords to spawn
if(!map->Instanceable() && !map->IsRemovalGrid(x, y))
@@ -1209,14 +1458,18 @@ uint32 ObjectMgr::AddGOData(uint32 entry, uint32 mapId, float x, float y, float
}
map->Add(go);
}
+
sLog.outDebug("AddGOData: dbguid %u entry %u map %u x %f y %f z %f o %f", guid, entry, mapId, x, y, z, o);
+
return guid;
}
+
uint32 ObjectMgr::AddCreData(uint32 entry, uint32 team, uint32 mapId, float x, float y, float z, float o, uint32 spawntimedelay)
{
CreatureInfo const *cInfo = GetCreatureTemplate(entry);
if(!cInfo)
return 0;
+
uint32 guid = GenerateLowGuid(HIGHGUID_UNIT);
CreatureData& data = NewOrExistCreatureData(guid);
data.id = entry;
@@ -1237,7 +1490,9 @@ uint32 ObjectMgr::AddCreData(uint32 entry, uint32 team, uint32 mapId, float x, f
data.spawnMask = 1;
data.phaseMask = PHASEMASK_NORMAL;
data.dbData = false;
+
AddCreatureToGrid(guid, &data);
+
// Spawn if necessary (loaded grids only)
if(Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(mapId)))
{
@@ -1254,44 +1509,57 @@ uint32 ObjectMgr::AddCreData(uint32 entry, uint32 team, uint32 mapId, float x, f
map->Add(creature);
}
}
- return guid;
+
+ return 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 16 17
"rotation0, rotation1, rotation2, rotation3, spawntimesecs, animprogress, state, spawnMask, phaseMask, event, pool_entry "
"FROM gameobject LEFT OUTER JOIN game_event_gameobject ON gameobject.guid = game_event_gameobject.guid "
"LEFT OUTER JOIN pool_gameobject ON gameobject.guid = pool_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();
uint32 entry = fields[ 1].GetUInt32();
+
GameObjectInfo const* gInfo = GetGameObjectInfo(entry);
if (!gInfo)
{
sLog.outErrorDb("Table `gameobject` has gameobject (GUID: %u) with non existing gameobject entry %u, skipped.", guid, entry);
continue;
}
+
if (gInfo->displayId && !sGameObjectDisplayInfoStore.LookupEntry(gInfo->displayId))
{
sLog.outErrorDb("Gameobject (GUID: %u Entry %u GoType: %u) have invalid displayId (%u), not loaded.",guid, entry, gInfo->type, gInfo->displayId);
continue;
}
+
GameObjectData& data = mGameObjectDataMap[guid];
+
data.id = entry;
data.mapid = fields[ 2].GetUInt32();
data.posX = fields[ 3].GetFloat();
@@ -1303,12 +1571,15 @@ void ObjectMgr::LoadGameobjects()
data.rotation2 = fields[ 9].GetFloat();
data.rotation3 = fields[10].GetFloat();
data.spawntimesecs = fields[11].GetInt32();
+
if (data.spawntimesecs==0 && gInfo->IsDespawnAtAction())
{
sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `spawntimesecs` (0) value, but gameobejct marked as despawnable at action.",guid,data.id);
}
+
data.animprogress = fields[12].GetUInt32();
data.artKit = 0;
+
uint32 go_state = fields[13].GetUInt32();
if (go_state >= MAX_GO_STATE)
{
@@ -1316,38 +1587,48 @@ void ObjectMgr::LoadGameobjects()
continue;
}
data.go_state = GOState(go_state);
+
data.spawnMask = fields[14].GetUInt8();
data.phaseMask = fields[15].GetUInt16();
int16 gameEvent = fields[16].GetInt16();
int16 PoolId = fields[17].GetInt16();
+
if (data.rotation2 < -1.0f || data.rotation2 > 1.0f)
{
sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation2 (%f) value, skip",guid,data.id,data.rotation2 );
continue;
}
+
if (data.rotation3 < -1.0f || data.rotation3 > 1.0f)
{
sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid rotation3 (%f) value, skip",guid,data.id,data.rotation3 );
continue;
}
+
if (!MapManager::IsValidMapCoord(data.mapid,data.posX,data.posY,data.posZ,data.orientation))
{
sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with invalid coordinates, skip",guid,data.id );
continue;
}
+
if (data.phaseMask==0)
{
sLog.outErrorDb("Table `gameobject` have gameobject (GUID: %u Entry: %u) with `phaseMask`=0 (not visible for anyone), set to 1.",guid,data.id );
data.phaseMask = 1;
}
+
if (gameEvent==0 && PoolId==0) // if not this is to be managed by GameEvent System or Pool system
AddGameobjectToGrid(guid, &data);
++count;
+
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu gameobjects", (unsigned long)mGameObjectDataMap.size());
}
+
void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
{
uint8 mask = data->spawnMask;
@@ -1357,11 +1638,13 @@ void ObjectMgr::AddGameobjectToGrid(uint32 guid, GameObjectData const* data)
{
CellPair cell_pair = Trinity::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;
@@ -1371,81 +1654,113 @@ void ObjectMgr::RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data
{
CellPair cell_pair = Trinity::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()
{
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 %lu creature respawn times", (unsigned long)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 %lu gameobject respawn times", (unsigned long)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
@@ -1454,32 +1769,40 @@ bool ObjectMgr::GetPlayerNameByGUID(const uint64 &guid, std::string &name) const
name = player->GetName();
return true;
}
+
PCachePlayerInfo pInfo = GetPlayerInfoFromCache(GUID_LOPART(guid));
if(pInfo)
{
name = pInfo->sPlayerName.c_str();
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));
@@ -1489,8 +1812,10 @@ uint32 ObjectMgr::GetPlayerAccountIdByGUID(const uint64 &guid) const
delete result;
return acc;
}
+
return 0;
}
+
uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
{
QueryResult *result = CharacterDatabase.PQuery("SELECT account FROM characters WHERE name = '%s'", name.c_str());
@@ -1500,27 +1825,38 @@ uint32 ObjectMgr::GetPlayerAccountIdByPlayerName(const std::string& name) const
delete result;
return acc;
}
+
return 0;
}
+
void ObjectMgr::LoadItemLocales()
{
mItemLocaleMap.clear(); // need for reload case
+
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(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+2*(i-1)].GetCppString();
@@ -1531,9 +1867,11 @@ void ObjectMgr::LoadItemLocales()
{
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())
{
@@ -1542,15 +1880,19 @@ void ObjectMgr::LoadItemLocales()
{
if(data.Description.size() <= idx)
data.Description.resize(idx+1);
+
data.Description[idx] = str;
}
}
}
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu Item locale strings", (unsigned long)mItemLocaleMap.size() );
}
+
struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader>
{
template<class D>
@@ -1559,12 +1901,14 @@ struct SQLItemLoader : public SQLStorageLoaderBase<SQLItemLoader>
dst = D(objmgr.GetScriptId(src));
}
};
+
void ObjectMgr::LoadItemPrototypes()
{
SQLItemLoader loader;
loader.Load(sItemStorage);
sLog.outString( ">> Loaded %u item prototypes", sItemStorage.RecordCount );
sLog.outString();
+
// check data correctness
for(uint32 i = 1; i < sItemStorage.MaxEntry; ++i)
{
@@ -1578,6 +1922,7 @@ void ObjectMgr::LoadItemPrototypes()
*/
continue;
}
+
if(dbcitem)
{
if(proto->Class != dbcitem->Class)
@@ -1593,21 +1938,25 @@ void ObjectMgr::LoadItemPrototypes()
// It safe let use Subclass from DB
}
*/
+
if(proto->Unk0 != dbcitem->Unk0)
{
sLog.outErrorDb("Item (Entry: %u) not correct %i Unk0, must be %i (still using DB value).",i,proto->Unk0,dbcitem->Unk0);
// It safe let use Unk0 from DB
}
+
if(proto->Material != dbcitem->Material)
{
sLog.outErrorDb("Item (Entry: %u) not correct %i material, must be %i (still using DB value).",i,proto->Material,dbcitem->Material);
// It safe let use Material from DB
}
+
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);
@@ -1623,36 +1972,43 @@ void ObjectMgr::LoadItemPrototypes()
{
sLog.outErrorDb("Item (Entry: %u) not correct (not listed in list of existed items).",i);
}
+
if(proto->Class >= MAX_ITEM_CLASS)
{
sLog.outErrorDb("Item (Entry: %u) has wrong Class value (%u)",i,proto->Class);
const_cast<ItemPrototype*>(proto)->Class = ITEM_CLASS_MISC;
}
+
if(proto->SubClass >= MaxItemSubclassValues[proto->Class])
{
sLog.outErrorDb("Item (Entry: %u) has wrong Subclass value (%u) for class %u",i,proto->SubClass,proto->Class);
const_cast<ItemPrototype*>(proto)->SubClass = 0;// exist for all item classes
}
+
if(proto->Quality >= MAX_ITEM_QUALITY)
{
sLog.outErrorDb("Item (Entry: %u) has wrong Quality value (%u)",i,proto->Quality);
const_cast<ItemPrototype*>(proto)->Quality = ITEM_QUALITY_NORMAL;
}
+
if(proto->BuyCount <= 0)
{
sLog.outErrorDb("Item (Entry: %u) has wrong BuyCount value (%u), set to default(1).",i,proto->BuyCount);
const_cast<ItemPrototype*>(proto)->BuyCount = 1;
}
+
if(proto->InventoryType >= MAX_INVTYPE)
{
sLog.outErrorDb("Item (Entry: %u) has wrong InventoryType value (%u)",i,proto->InventoryType);
const_cast<ItemPrototype*>(proto)->InventoryType = INVTYPE_NON_EQUIP;
}
+
if(proto->RequiredSkill >= MAX_SKILL_TYPE)
{
sLog.outErrorDb("Item (Entry: %u) has wrong RequiredSkill value (%u)",i,proto->RequiredSkill);
const_cast<ItemPrototype*>(proto)->RequiredSkill = 0;
}
+
{
// can be used in equip slot, as page read use in inventory, or spell casting at use
bool req = proto->InventoryType!=INVTYPE_NON_EQUIP || proto->PageText;
@@ -1667,21 +2023,26 @@ void ObjectMgr::LoadItemPrototypes()
}
}
}
+
if(req)
{
if(!(proto->AllowableClass & CLASSMASK_ALL_PLAYABLE))
sLog.outErrorDb("Item (Entry: %u) not have in `AllowableClass` any playable classes (%u) and can't be equipped or use.",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 or use.",i,proto->AllowableRace);
}
}
+
if(proto->RequiredSpell && !sSpellStore.LookupEntry(proto->RequiredSpell))
{
sLog.outErrorDb("Item (Entry: %u) have wrong (non-existed) spell in RequiredSpell (%u)",i,proto->RequiredSpell);
const_cast<ItemPrototype*>(proto)->RequiredSpell = 0;
}
+
if(proto->RequiredReputationRank >= MAX_REPUTATION_RANK)
sLog.outErrorDb("Item (Entry: %u) has wrong reputation rank in RequiredReputationRank (%u), item can't be used.",i,proto->RequiredReputationRank);
+
if(proto->RequiredReputationFaction)
{
if(!sFactionStore.LookupEntry(proto->RequiredReputationFaction))
@@ -1689,14 +2050,17 @@ void ObjectMgr::LoadItemPrototypes()
sLog.outErrorDb("Item (Entry: %u) has wrong (not existing) faction in RequiredReputationFaction (%u)",i,proto->RequiredReputationFaction);
const_cast<ItemPrototype*>(proto)->RequiredReputationFaction = 0;
}
+
if(proto->RequiredReputationRank == MIN_REPUTATION_RANK)
sLog.outErrorDb("Item (Entry: %u) has min. reputation rank in RequiredReputationRank (0) but RequiredReputationFaction > 0, faction setting is useless.",i);
}
+
if(proto->MaxCount < -1)
{
sLog.outErrorDb("Item (Entry: %u) has too large negative in maxcount (%i), replace by value (-1) no storing limits.",i,proto->MaxCount);
const_cast<ItemPrototype*>(proto)->MaxCount = -1;
}
+
if(proto->Stackable == 0)
{
sLog.outErrorDb("Item (Entry: %u) has wrong value in stackable (%i), replace by default 1.",i,proto->Stackable);
@@ -1712,16 +2076,19 @@ void ObjectMgr::LoadItemPrototypes()
sLog.outErrorDb("Item (Entry: %u) has too large value in stackable (%u), replace by hardcoded upper limit (1000).",i,proto->Stackable);
const_cast<ItemPrototype*>(proto)->Stackable = 1000;
}
+
if(proto->ContainerSlots > MAX_BAG_SIZE)
{
sLog.outErrorDb("Item (Entry: %u) has too large value in ContainerSlots (%u), replace by hardcoded limit (%u).",i,proto->ContainerSlots,MAX_BAG_SIZE);
const_cast<ItemPrototype*>(proto)->ContainerSlots = MAX_BAG_SIZE;
}
+
if(proto->StatsCount > MAX_ITEM_PROTO_STATS)
{
sLog.outErrorDb("Item (Entry: %u) has too large value in statscount (%u), replace by hardcoded limit (%u).",i,proto->StatsCount,MAX_ITEM_PROTO_STATS);
const_cast<ItemPrototype*>(proto)->StatsCount = MAX_ITEM_PROTO_STATS;
}
+
for (int j = 0; j < MAX_ITEM_PROTO_STATS; ++j)
{
// for ItemStatValue != 0
@@ -1730,6 +2097,7 @@ void ObjectMgr::LoadItemPrototypes()
sLog.outErrorDb("Item (Entry: %u) has wrong stat_type%d (%u)",i,j+1,proto->ItemStat[j].ItemStatType);
const_cast<ItemPrototype*>(proto)->ItemStat[j].ItemStatType = 0;
}
+
switch(proto->ItemStat[j].ItemStatType)
{
case ITEM_MOD_SPELL_HEALING_DONE:
@@ -1740,6 +2108,7 @@ void ObjectMgr::LoadItemPrototypes()
break;
}
}
+
for (int j = 0; j < MAX_ITEM_PROTO_DAMAGES; ++j)
{
if(proto->Damage[j].DamageType >= MAX_SPELL_SCHOOL)
@@ -1748,6 +2117,7 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->Damage[j].DamageType = 0;
}
}
+
// special format
if((proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN) || (proto->Spells[0].SpellId == SPELL_ID_GENERIC_LEARN_PET))
{
@@ -1760,6 +2130,7 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->Spells[1].SpellId = 0;
const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
}
+
// spell_2 have learning spell
if(proto->Spells[1].SpellTrigger != ITEM_SPELLTRIGGER_LEARN_SPELL_ID)
{
@@ -1793,6 +2164,7 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->Spells[1].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
}
}
+
// spell_3*,spell_4*,spell_5* is empty
for (int j = 2; j < MAX_ITEM_PROTO_SPELLS; ++j)
{
@@ -1820,6 +2192,7 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->Spells[j].SpellId = 0;
const_cast<ItemPrototype*>(proto)->Spells[j].SpellTrigger = ITEM_SPELLTRIGGER_ON_USE;
}
+
if(proto->Spells[j].SpellId)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(proto->Spells[j].SpellId);
@@ -1837,36 +2210,46 @@ void ObjectMgr::LoadItemPrototypes()
}
}
}
+
if(proto->Bonding >= MAX_BIND_TYPE)
sLog.outErrorDb("Item (Entry: %u) has wrong Bonding value (%u)",i,proto->Bonding);
+
if(proto->PageText && !sPageTextStore.LookupEntry<PageText>(proto->PageText))
sLog.outErrorDb("Item (Entry: %u) has non existing first page (Id:%u)", i,proto->PageText);
+
if(proto->LockID && !sLockStore.LookupEntry(proto->LockID))
sLog.outErrorDb("Item (Entry: %u) has wrong LockID (%u)",i,proto->LockID);
+
if(proto->Sheath >= MAX_SHEATHETYPE)
{
sLog.outErrorDb("Item (Entry: %u) has wrong Sheath (%u)",i,proto->Sheath);
const_cast<ItemPrototype*>(proto)->Sheath = SHEATHETYPE_NONE;
}
+
if(proto->RandomProperty && !sItemRandomPropertiesStore.LookupEntry(GetItemEnchantMod(proto->RandomProperty)))
{
sLog.outErrorDb("Item (Entry: %u) has unknown (wrong or not listed in `item_enchantment_template`) RandomProperty (%u)",i,proto->RandomProperty);
const_cast<ItemPrototype*>(proto)->RandomProperty = 0;
}
+
if(proto->RandomSuffix && !sItemRandomSuffixStore.LookupEntry(GetItemEnchantMod(proto->RandomSuffix)))
{
sLog.outErrorDb("Item (Entry: %u) has wrong RandomSuffix (%u)",i,proto->RandomSuffix);
const_cast<ItemPrototype*>(proto)->RandomSuffix = 0;
}
+
if(proto->ItemSet && !sItemSetStore.LookupEntry(proto->ItemSet))
{
sLog.outErrorDb("Item (Entry: %u) have wrong ItemSet (%u)",i,proto->ItemSet);
const_cast<ItemPrototype*>(proto)->ItemSet = 0;
}
+
if(proto->Area && !GetAreaEntryByAreaID(proto->Area))
sLog.outErrorDb("Item (Entry: %u) has wrong Area (%u)",i,proto->Area);
+
if(proto->Map && !sMapStore.LookupEntry(proto->Map))
sLog.outErrorDb("Item (Entry: %u) has wrong Map (%u)",i,proto->Map);
+
if(proto->BagFamily)
{
// check bits
@@ -1875,6 +2258,7 @@ void ObjectMgr::LoadItemPrototypes()
uint32 mask = 1 << j;
if((proto->BagFamily & mask)==0)
continue;
+
ItemBagFamilyEntry const* bf = sItemBagFamilyStore.LookupEntry(j+1);
if(!bf)
{
@@ -1882,6 +2266,7 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->BagFamily &= ~mask;
continue;
}
+
if(BAG_FAMILY_MASK_CURRENCY_TOKENS & mask)
{
CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(proto->ItemId);
@@ -1893,8 +2278,10 @@ void ObjectMgr::LoadItemPrototypes()
}
}
}
+
if(proto->TotemCategory && !sTotemCategoryStore.LookupEntry(proto->TotemCategory))
sLog.outErrorDb("Item (Entry: %u) has wrong TotemCategory (%u)",i,proto->TotemCategory);
+
for (int j = 0; j < MAX_ITEM_PROTO_SOCKETS; ++j)
{
if(proto->Socket[j].Color && (proto->Socket[j].Color & SOCKET_COLOR_ALL) != proto->Socket[j].Color)
@@ -1903,18 +2290,22 @@ void ObjectMgr::LoadItemPrototypes()
const_cast<ItemPrototype*>(proto)->Socket[j].Color = 0;
}
}
+
if(proto->GemProperties && !sGemPropertiesStore.LookupEntry(proto->GemProperties))
sLog.outErrorDb("Item (Entry: %u) has wrong GemProperties (%u)",i,proto->GemProperties);
+
if(proto->FoodType >= MAX_PET_DIET)
{
sLog.outErrorDb("Item (Entry: %u) has wrong FoodType value (%u)",i,proto->FoodType);
const_cast<ItemPrototype*>(proto)->FoodType = 0;
}
+
if(proto->ItemLimitCategory && !sItemLimitCategoryStore.LookupEntry(proto->ItemLimitCategory))
{
sLog.outErrorDb("Item (Entry: %u) has wrong LimitCategory value (%u)",i,proto->ItemLimitCategory);
const_cast<ItemPrototype*>(proto)->ItemLimitCategory = 0;
}
+
if(proto->HolidayId && !sHolidaysStore.LookupEntry(proto->HolidayId))
{
sLog.outErrorDb("Item (Entry: %u) has wrong HolidayId value (%u)", i, proto->HolidayId);
@@ -1922,34 +2313,47 @@ void ObjectMgr::LoadItemPrototypes()
}
}
}
+
void ObjectMgr::LoadItemRequiredTarget()
{
m_ItemRequiredTarget.clear(); // needed for reload case
+
uint32 count = 0;
+
QueryResult *result = WorldDatabase.Query("SELECT entry,type,targetEntry FROM item_required_target");
+
if (!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 ItemRequiredTarget. DB table `item_required_target` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 uiItemId = fields[0].GetUInt32();
uint32 uiType = fields[1].GetUInt32();
uint32 uiTargetEntry = fields[2].GetUInt32();
+
ItemPrototype const* pItemProto = sItemStorage.LookupEntry<ItemPrototype>(uiItemId);
+
if (!pItemProto)
{
sLog.outErrorDb("Table `item_required_target`: Entry %u listed for TargetEntry %u does not exist in `item_template`.",uiItemId,uiTargetEntry);
continue;
}
+
bool bIsItemSpellValid = false;
+
for(uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
if (SpellEntry const* pSpellInfo = sSpellStore.LookupEntry(pItemProto->Spells[i].SpellId))
@@ -1960,6 +2364,7 @@ void ObjectMgr::LoadItemRequiredTarget()
SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(pSpellInfo->Id);
if (bounds.first != bounds.second)
break;
+
for (int j = 0; j < 3; ++j)
{
if (pSpellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_TARGET_ENEMY ||
@@ -1976,58 +2381,74 @@ void ObjectMgr::LoadItemRequiredTarget()
}
}
}
+
if (!bIsItemSpellValid)
{
sLog.outErrorDb("Table `item_required_target`: Spell used by item %u does not have implicit target TARGET_CHAIN_DAMAGE(6), TARGET_DUELVSPLAYER(25), already listed in `spell_script_target` or doesn't have item spelltrigger.",uiItemId);
continue;
}
+
if (!uiType || uiType > MAX_ITEM_REQ_TARGET_TYPE)
{
sLog.outErrorDb("Table `item_required_target`: Type %u for TargetEntry %u is incorrect.",uiType,uiTargetEntry);
continue;
}
+
if (!uiTargetEntry)
{
sLog.outErrorDb("Table `item_required_target`: TargetEntry == 0 for Type (%u).",uiType);
continue;
}
+
if (!sCreatureStorage.LookupEntry<CreatureInfo>(uiTargetEntry))
{
sLog.outErrorDb("Table `item_required_target`: creature template entry %u does not exist.",uiTargetEntry);
continue;
}
+
m_ItemRequiredTarget.insert(ItemRequiredTargetMap::value_type(uiItemId,ItemRequiredTarget(ItemRequiredTargetType(uiType),uiTargetEntry)));
+
++count;
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u Item required targets", count);
}
+
void ObjectMgr::LoadPetLevelInfo()
{
// Loading levels data
{
// 0 1 2 3 4 5 6 7 8 9
QueryResult *result = WorldDatabase.Query("SELECT creature_entry, level, hp, mana, str, agi, sta, inte, spi, armor FROM pet_levelstats");
+
uint32 count = 0;
+
if (!result)
{
barGoLink bar( 1 );
+
sLog.outString();
sLog.outString( ">> Loaded %u level pet stats definitions", count );
sLog.outErrorDb( "Error loading `pet_levelstats` table or empty table.");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
Field* fields = result->Fetch();
+
uint32 creature_id = fields[0].GetUInt32();
if(!sCreatureStorage.LookupEntry<CreatureInfo>(creature_id))
{
sLog.outErrorDb("Wrong creature id %u in `pet_levelstats` table, ignoring.",creature_id);
continue;
}
+
uint32 current_level = fields[1].GetUInt32();
if(current_level > sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
@@ -2045,36 +2466,47 @@ void ObjectMgr::LoadPetLevelInfo()
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)
{
@@ -2086,34 +2518,44 @@ void ObjectMgr::LoadPetLevelInfo()
}
}
}
+
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();
@@ -2121,118 +2563,152 @@ void ObjectMgr::LoadPlayerInfo()
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 );
+
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u custom player create items", count );
}
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 custom player create items", count );
}
}
+
// Load playercreate spells
{
+
QueryResult *result = NULL;
if(sWorld.getConfig(CONFIG_START_ALL_SPELLS))
result = WorldDatabase.Query("SELECT race, class, Spell, Active FROM playercreateinfo_spell_custom");
else
result = WorldDatabase.Query("SELECT race, class, Spell FROM playercreateinfo_spell");
+
uint32 count = 0;
+
if (!result)
{
barGoLink bar( 1 );
+
sLog.outString();
sLog.outString( ">> Loaded %u player create spells", count );
sLog.outErrorDb( "Error loading player starting spells or empty table.");
@@ -2240,21 +2716,25 @@ void ObjectMgr::LoadPlayerInfo()
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;
}
+
if(!current_race || !current_class)
{
uint32 min_race = current_race ? current_race : 1;
@@ -2267,23 +2747,30 @@ void ObjectMgr::LoadPlayerInfo()
}
else
playerInfo[current_race][current_class].spell.push_back(fields[2].GetUInt32());
+
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
QueryResult *result = WorldDatabase.Query("SELECT race, class, button, action, type 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.");
@@ -2291,55 +2778,70 @@ void ObjectMgr::LoadPlayerInfo()
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.push_back(PlayerCreateInfoAction(fields[2].GetUInt8(),fields[3].GetUInt32(),fields[4].GetUInt8()));
+
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))
{
@@ -2352,33 +2854,44 @@ void ObjectMgr::LoadPlayerInfo()
}
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)
{
@@ -2389,35 +2902,44 @@ void ObjectMgr::LoadPlayerInfo()
}
}
}
+
// 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))
{
@@ -2430,49 +2952,64 @@ void ObjectMgr::LoadPlayerInfo()
}
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;
+
// skip expansion classes if not playing with expansion
if (sWorld.getConfig(CONFIG_EXPANSION) < 2 && class_ == CLASS_DEATH_KNIGHT)
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)
{
@@ -2484,28 +3021,37 @@ void ObjectMgr::LoadPlayerInfo()
}
}
}
+
// Loading xp per level data
{
mPlayerXPperLevel.resize(sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
for (uint32 level = 0; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
mPlayerXPperLevel[level] = 0;
+
// 0 1
QueryResult *result = WorldDatabase.Query("SELECT lvl, xp_for_next_level FROM player_xp_for_level");
+
uint32 count = 0;
+
if (!result)
{
barGoLink bar( 1 );
+
sLog.outString();
sLog.outString( ">> Loaded %u xp for level definitions", count );
sLog.outErrorDb( "Error loading `player_xp_for_level` table or empty table.");
exit(1);
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
Field* fields = result->Fetch();
+
uint32 current_level = fields[0].GetUInt32();
uint32 current_xp = fields[1].GetUInt32();
+
if(current_level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
{
if(current_level > STRONG_MAX_LEVEL) // hardcoded level maximum
@@ -2523,10 +3069,13 @@ void ObjectMgr::LoadPlayerInfo()
++count;
}
while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u xp for level definitions", count );
}
+
// fill level gaps
for (uint32 level = 1; level < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL); ++level)
{
@@ -2537,31 +3086,40 @@ void ObjectMgr::LoadPlayerInfo()
}
}
}
+
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];
+
// if conversion from uint32 to uint8 causes unexpected behaviour, change lvl to uint32
for(uint8 lvl = sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL)-1; lvl < level; ++lvl)
{
@@ -2632,25 +3190,35 @@ void ObjectMgr::BuildPlayerLevelInfo(uint8 race, uint8 _class, uint8 level, Play
}
}
}
+
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()))
{
@@ -2659,33 +3227,47 @@ void ObjectMgr::LoadGuilds()
continue;
}
AddGuild(newguild);
+
}while( result->NextRow() );
+
delete result;
+
//delete unused LogGuid records in guild_eventlog and guild_bank_eventlog table
//you can comment these lines if you don't plan to change CONFIG_GUILD_EVENT_LOG_COUNT and CONFIG_GUILD_BANK_EVENT_LOG_COUNT
CharacterDatabase.PQuery("DELETE FROM guild_eventlog WHERE LogGuid > '%u'", sWorld.getConfig(CONFIG_GUILD_EVENT_LOG_COUNT));
CharacterDatabase.PQuery("DELETE FROM guild_bank_eventlog WHERE LogGuid > '%u'", sWorld.getConfig(CONFIG_GUILD_BANK_EVENT_LOG_COUNT));
+
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()))
{
@@ -2694,10 +3276,13 @@ void ObjectMgr::LoadArenaTeams()
}
AddArenaTeam(newarenateam);
}while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u arenateam definitions", count );
}
+
void ObjectMgr::LoadGroups()
{
// -- loading groups --
@@ -2706,21 +3291,27 @@ void ObjectMgr::LoadGroups()
uint32 count = 0;
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
QueryResult *result = CharacterDatabase.Query("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))
{
@@ -2730,9 +3321,12 @@ void ObjectMgr::LoadGroups()
}
AddGroup(group);
}while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u group definitions", count );
+
// -- loading members --
count = 0;
group = NULL;
@@ -2763,6 +3357,7 @@ void ObjectMgr::LoadGroups()
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());
@@ -2771,6 +3366,7 @@ void ObjectMgr::LoadGroups()
}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();)
@@ -2784,6 +3380,7 @@ void ObjectMgr::LoadGroups()
else
++itr;
}
+
// -- loading instances --
count = 0;
group = NULL;
@@ -2795,6 +3392,7 @@ void ObjectMgr::LoadGroups()
"(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 bar2( 1 );
@@ -2818,29 +3416,36 @@ void ObjectMgr::LoadGroups()
continue;
}
}
+
MapEntry const* mapEntry = sMapStore.LookupEntry(fields[1].GetUInt32());
if(!mapEntry || !mapEntry->IsDungeon())
{
sLog.outErrorDb("Incorrect entry in group_instance table : no dungeon map %d", fields[1].GetUInt32());
continue;
}
+
InstanceSave *save = sInstanceSaveManager.AddInstanceSave(mapEntry->MapID, fields[2].GetUInt32(), fields[4].GetUInt8(), (time_t)fields[5].GetUInt64(), (fields[6].GetUInt32() == 0), true);
group->BindToInstance(save, fields[3].GetBool(), true);
}while( result->NextRow() );
delete result;
}
+
sLog.outString();
sLog.outString( ">> Loaded %u group-instance binds total", count );
+
sLog.outString();
sLog.outString( ">> Loaded %u group members total", count );
}
+
void ObjectMgr::LoadQuests()
{
// For reload case
for(QuestMap::const_iterator itr=mQuestTemplates.begin(); itr != mQuestTemplates.end(); ++itr)
delete itr->second;
mQuestTemplates.clear();
+
mExclusiveQuestGroups.clear();
+
// 0 1 2 3 4 5 6 7 8
QueryResult *result = WorldDatabase.Query("SELECT entry, Method, ZoneOrSort, SkillOrClass, MinLevel, QuestLevel, Type, RequiredRaces, RequiredSkillValue,"
// 9 10 11 12 13 14 15 16
@@ -2880,11 +3485,13 @@ void ObjectMgr::LoadQuests()
{
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
@@ -2893,25 +3500,32 @@ void ObjectMgr::LoadQuests()
{
bar.step();
Field *fields = result->Fetch();
+
Quest * newQuest = new Quest(fields);
mQuestTemplates[newQuest->GetQuestId()] = newQuest;
} while( result->NextRow() );
+
delete result;
+
// Post processing
for (QuestMap::iterator iter = mQuestTemplates.begin(); iter != mQuestTemplates.end(); ++iter)
{
Quest * qinfo = iter->second;
+
// additional quest integrity checks (GO, creature_template and item_template must be loaded already)
+
if( qinfo->GetQuestMethod() >= 3 )
{
sLog.outErrorDb("Quest %u has `Method` = %u, expected values are 0, 1 or 2.",qinfo->GetQuestId(),qinfo->GetQuestMethod());
}
+
if (qinfo->QuestFlags & ~QUEST_TRINITY_FLAGS_DB_ALLOWED)
{
sLog.outErrorDb("Quest %u has `SpecialFlags` = %u > max allowed value. Correct `SpecialFlags` to value <= %u",
qinfo->GetQuestId(),qinfo->QuestFlags,QUEST_TRINITY_FLAGS_DB_ALLOWED >> 16);
qinfo->QuestFlags &= QUEST_TRINITY_FLAGS_DB_ALLOWED;
}
+
if(qinfo->QuestFlags & (QUEST_FLAGS_DAILY | QUEST_FLAGS_WEEKLY))
{
if(!(qinfo->QuestFlags & QUEST_TRINITY_FLAGS_REPEATABLE))
@@ -2920,6 +3534,7 @@ void ObjectMgr::LoadQuests()
qinfo->QuestFlags |= QUEST_TRINITY_FLAGS_REPEATABLE;
}
}
+
if(qinfo->QuestFlags & QUEST_FLAGS_AUTO_REWARDED)
{
// at auto-reward can be rewarded only RewChoiceItemId[0]
@@ -2933,6 +3548,7 @@ void ObjectMgr::LoadQuests()
}
}
}
+
// client quest log visual (area case)
if( qinfo->ZoneOrSort > 0 )
{
@@ -2975,6 +3591,7 @@ void ObjectMgr::LoadQuests()
}
}
}
+
// SkillOrClass (class case)
if( qinfo->SkillOrClass < 0 )
{
@@ -2993,6 +3610,7 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(),qinfo->SkillOrClass,qinfo->SkillOrClass);
}
}
+
if( qinfo->RequiredSkillValue )
{
if( qinfo->RequiredSkillValue > sWorld.GetConfigMaxSkillValue() )
@@ -3001,6 +3619,7 @@ void ObjectMgr::LoadQuests()
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.",
@@ -3009,54 +3628,63 @@ void ObjectMgr::LoadQuests()
}
}
// 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 > ReputationMgr::Reputation_Cap)
{
sLog.outErrorDb("Quest %u has `RequiredMinRepValue` = %d but max reputation is %u, quest can't be done.",
qinfo->GetQuestId(),qinfo->RequiredMinRepValue,ReputationMgr::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.",
@@ -3064,6 +3692,7 @@ void ObjectMgr::LoadQuests()
qinfo->CharTitleId = 0;
// quest can't reward this title
}
+
if(qinfo->SrcItemId)
{
if(!sItemStorage.LookupEntry<ItemPrototype>(qinfo->SrcItemId))
@@ -3085,6 +3714,7 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(),qinfo->SrcItemCount);
qinfo->SrcItemCount=0; // no quest work changes in fact
}
+
if(qinfo->SrcSpell)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(qinfo->SrcSpell);
@@ -3101,6 +3731,7 @@ void ObjectMgr::LoadQuests()
qinfo->SrcSpell = 0; // quest can't be done for this requirement
}
}
+
for(uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
{
uint32 id = qinfo->ReqItemId[j];
@@ -3112,7 +3743,9 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(),j+1,id,j+1);
// no changes, quest can't be done for this requirement
}
+
qinfo->SetFlag(QUEST_TRINITY_FLAGS_DELIVER);
+
if(!sItemStorage.LookupEntry<ItemPrototype>(id))
{
sLog.outErrorDb("Quest %u has `ReqItemId%d` = %u but item with entry %u does not exist, quest can't be done.",
@@ -3127,6 +3760,7 @@ void ObjectMgr::LoadQuests()
qinfo->ReqItemCount[j] = 0; // prevent incorrect work of quest
}
}
+
for(uint8 j = 0; j < QUEST_SOURCE_ITEM_IDS_COUNT; ++j )
{
uint32 id = qinfo->ReqSourceId[j];
@@ -3149,6 +3783,7 @@ void ObjectMgr::LoadQuests()
}
}
}
+
for(uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
{
uint32 id = qinfo->ReqSpell[j];
@@ -3161,6 +3796,7 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(),j+1,id,id);
continue;
}
+
if(!qinfo->ReqCreatureOrGOId[j])
{
bool found = false;
@@ -3173,11 +3809,13 @@ void ObjectMgr::LoadQuests()
break;
}
}
+
if(found)
{
if(!qinfo->HasFlag(QUEST_TRINITY_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_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Quest flags or ReqCreatureOrGOId%d must be fixed, quest modified to enable objective.",spellInfo->Id,qinfo->QuestId,j+1,j+1);
+
// this will prevent quest completing without objective
const_cast<Quest*>(qinfo)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
}
@@ -3191,6 +3829,7 @@ void ObjectMgr::LoadQuests()
}
}
}
+
for(uint8 j = 0; j < QUEST_OBJECTIVES_COUNT; ++j )
{
int32 id = qinfo->ReqCreatureOrGOId[j];
@@ -3200,16 +3839,20 @@ void ObjectMgr::LoadQuests()
qinfo->GetQuestId(),j+1,id,uint32(-id));
qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
}
+
if(id > 0 && !sCreatureStorage.LookupEntry<CreatureInfo>(id))
{
sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %i but creature with entry %u does not exist, quest can't be done.",
qinfo->GetQuestId(),j+1,id,uint32(id));
qinfo->ReqCreatureOrGOId[j] = 0; // quest can't be done for this requirement
}
+
if(id)
{
// In fact SpeakTo and Kill are quite same: either you can speak to mob:SpeakTo or you can't:Kill/Cast
+
qinfo->SetFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO);
+
if(!qinfo->ReqCreatureOrGOCount[j])
{
sLog.outErrorDb("Quest %u has `ReqCreatureOrGOId%d` = %u but `ReqCreatureOrGOCount%d` = 0, quest can't be done.",
@@ -3224,6 +3867,7 @@ void ObjectMgr::LoadQuests()
// no changes, quest ignore this data
}
}
+
for(uint8 j = 0; j < QUEST_REWARD_CHOICES_COUNT; ++j )
{
uint32 id = qinfo->RewChoiceItemId[j];
@@ -3235,6 +3879,7 @@ void ObjectMgr::LoadQuests()
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.",
@@ -3249,6 +3894,7 @@ void ObjectMgr::LoadQuests()
// no changes, quest ignore this data
}
}
+
for(uint8 j = 0; j < QUEST_REWARDS_COUNT; ++j )
{
uint32 id = qinfo->RewItemId[j];
@@ -3260,6 +3906,7 @@ void ObjectMgr::LoadQuests()
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.",
@@ -3274,6 +3921,7 @@ void ObjectMgr::LoadQuests()
// no changes, quest ignore this data
}
}
+
for(uint8 j = 0; j < QUEST_REPUTATIONS_COUNT; ++j)
{
if(qinfo->RewRepFaction[j])
@@ -3284,6 +3932,7 @@ void ObjectMgr::LoadQuests()
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.",
@@ -3298,21 +3947,25 @@ void ObjectMgr::LoadQuests()
// 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 will not have a spell reward.",
qinfo->GetQuestId(),qinfo->RewSpell,qinfo->RewSpell);
qinfo->RewSpell = 0; // no spell reward will display for this quest
}
+
else if(GetTalentSpellCost(qinfo->RewSpell))
{
sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
@@ -3320,21 +3973,25 @@ void ObjectMgr::LoadQuests()
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 will not have a spell reward.",
qinfo->GetQuestId(),qinfo->RewSpellCast,qinfo->RewSpellCast);
qinfo->RewSpellCast = 0; // no spell will be casted on player
}
+
else if(GetTalentSpellCost(qinfo->RewSpellCast))
{
sLog.outErrorDb("Quest %u has `RewSpell` = %u but spell %u is talent, quest will not have a spell reward.",
@@ -3342,6 +3999,7 @@ void ObjectMgr::LoadQuests()
qinfo->RewSpellCast = 0; // no spell will be casted on player
}
}
+
if(qinfo->RewMailTemplateId)
{
if(!sMailTemplateStore.LookupEntry(qinfo->RewMailTemplateId))
@@ -3352,6 +4010,7 @@ void ObjectMgr::LoadQuests()
qinfo->RewMailDelaySecs = 0; // no mail will send to player
}
}
+
if(qinfo->NextQuestInChain)
{
QuestMap::iterator qNextItr = mQuestTemplates.find(qinfo->NextQuestInChain);
@@ -3364,6 +4023,7 @@ void ObjectMgr::LoadQuests()
else
qNextItr->second->prevChainQuests.push_back(qinfo->GetQuestId());
}
+
// fill additional data stores
if(qinfo->PrevQuestId)
{
@@ -3376,6 +4036,7 @@ void ObjectMgr::LoadQuests()
qinfo->prevQuests.push_back(qinfo->PrevQuestId);
}
}
+
if(qinfo->NextQuestId)
{
QuestMap::iterator qNextItr = mQuestTemplates.find(abs(qinfo->GetNextQuestId()));
@@ -3389,40 +4050,51 @@ void ObjectMgr::LoadQuests()
qNextItr->second->prevQuests.push_back(signedQuestId);
}
}
+
if(qinfo->ExclusiveGroup)
mExclusiveQuestGroups.insert(std::pair<int32, uint32>(qinfo->ExclusiveGroup, qinfo->GetQuestId()));
if(qinfo->LimitTime)
qinfo->SetFlag(QUEST_TRINITY_FLAGS_TIMED);
}
+
// check QUEST_TRINITY_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(uint8 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 (outdated spells)
if(!quest)
continue;
+
if(!quest->HasFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
{
sLog.outErrorDb("Spell (id: %u) have SPELL_EFFECT_QUEST_COMPLETE for quest %u , but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Quest flags must be fixed, quest modified to enable objective.",spellInfo->Id,quest_id);
+
// this will prevent quest completing without objective
const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
}
}
}
+
sLog.outString();
sLog.outString( ">> Loaded %lu quests definitions", (unsigned long)mQuestTemplates.size() );
}
+
void ObjectMgr::LoadQuestLocales()
{
mQuestLocaleMap.clear(); // need for reload case
+
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,"
@@ -3434,21 +4106,29 @@ void ObjectMgr::LoadQuestLocales()
"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(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[1+10*(i-1)].GetCppString();
@@ -3459,6 +4139,7 @@ void ObjectMgr::LoadQuestLocales()
{
if(data.Title.size() <= idx)
data.Title.resize(idx+1);
+
data.Title[idx] = str;
}
}
@@ -3470,6 +4151,7 @@ void ObjectMgr::LoadQuestLocales()
{
if(data.Details.size() <= idx)
data.Details.resize(idx+1);
+
data.Details[idx] = str;
}
}
@@ -3481,6 +4163,7 @@ void ObjectMgr::LoadQuestLocales()
{
if(data.Objectives.size() <= idx)
data.Objectives.resize(idx+1);
+
data.Objectives[idx] = str;
}
}
@@ -3492,6 +4175,7 @@ void ObjectMgr::LoadQuestLocales()
{
if(data.OfferRewardText.size() <= idx)
data.OfferRewardText.resize(idx+1);
+
data.OfferRewardText[idx] = str;
}
}
@@ -3503,6 +4187,7 @@ void ObjectMgr::LoadQuestLocales()
{
if(data.RequestItemsText.size() <= idx)
data.RequestItemsText.resize(idx+1);
+
data.RequestItemsText[idx] = str;
}
}
@@ -3514,6 +4199,7 @@ void ObjectMgr::LoadQuestLocales()
{
if(data.EndText.size() <= idx)
data.EndText.resize(idx+1);
+
data.EndText[idx] = str;
}
}
@@ -3527,36 +4213,49 @@ void ObjectMgr::LoadQuestLocales()
{
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 %lu Quest locale strings", (unsigned long)mQuestLocaleMap.size() );
}
+
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,dataint, 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();
@@ -3569,6 +4268,7 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
tmp.y = fields[7].GetFloat();
tmp.z = fields[8].GetFloat();
tmp.o = fields[9].GetFloat();
+
// generic command args check
switch(tmp.command)
{
@@ -3589,9 +4289,11 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
sLog.outErrorDb("Table `%s` has out of range text id (dataint = %i expected %u-%u) in SCRIPT_COMMAND_TALK for script id %u",tablename,tmp.dataint,MIN_DB_SCRIPT_STRING_ID,MAX_DB_SCRIPT_STRING_ID,tmp.id);
continue;
}
+
// if(!objmgr.GetMangosStringLocale(tmp.dataint)) will checked after db_script_string loading
break;
}
+
case SCRIPT_COMMAND_EMOTE:
{
if(!sEmotesStore.LookupEntry(tmp.datalong))
@@ -3601,6 +4303,7 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
}
break;
}
+
case SCRIPT_COMMAND_TELEPORT_TO:
{
if(!sMapStore.LookupEntry(tmp.datalong))
@@ -3608,6 +4311,7 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
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(!Trinity::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);
@@ -3615,6 +4319,7 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
}
break;
}
+
case SCRIPT_COMMAND_TEMP_SUMMON_CREATURE:
{
if(!Trinity::IsValidMapCoord(tmp.x,tmp.y,tmp.z,tmp.o))
@@ -3622,6 +4327,7 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
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);
@@ -3629,6 +4335,7 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
}
break;
}
+
case SCRIPT_COMMAND_RESPAWN_GAMEOBJECT:
{
GameObjectData const* data = GetGOData(tmp.datalong);
@@ -3637,12 +4344,14 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
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 ||
@@ -3663,17 +4372,20 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
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:
@@ -3684,33 +4396,41 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
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_TRINITY_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_TRINITY_FLAGS_EXPLORATION_OR_EVENT in quest flags. Script command or quest flags wrong. Quest modified to require objective.",tablename,tmp.datalong,tmp.id);
+
// this will prevent quest completing without objective
const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
+
// continue; - quest objective requirement 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 %f or 0 for disable distance check",
tablename,tmp.datalong2,tmp.id,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 %f or 0 for disable distance check",
tablename,tmp.datalong2,tmp.id,INTERACTION_DISTANCE);
continue;
}
+
break;
}
+
case SCRIPT_COMMAND_REMOVE_AURA:
{
if(!sSpellStore.LookupEntry(tmp.datalong))
@@ -3744,21 +4464,27 @@ void ObjectMgr::LoadScripts(ScriptMapMap& scripts, char const* tablename)
break;
}
}
+
if (scripts.find(tmp.id) == scripts.end())
{
ScriptMap emptyMap;
scripts[tmp.id] = emptyMap;
}
scripts[tmp.id].insert(std::pair<uint32, ScriptInfo>(tmp.delay, tmp));
+
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u script definitions", count );
}
+
void ObjectMgr::LoadGameObjectScripts()
{
LoadScripts(sGameObjectScripts, "gameobject_scripts");
+
// check ids
for(ScriptMapMap::const_iterator itr = sGameObjectScripts.begin(); itr != sGameObjectScripts.end(); ++itr)
{
@@ -3766,9 +4492,11 @@ void ObjectMgr::LoadGameObjectScripts()
sLog.outErrorDb("Table `gameobject_scripts` has not existing gameobject (GUID: %u) as script id",itr->first);
}
}
+
void ObjectMgr::LoadQuestEndScripts()
{
LoadScripts(sQuestEndScripts, "quest_end_scripts");
+
// check ids
for(ScriptMapMap::const_iterator itr = sQuestEndScripts.begin(); itr != sQuestEndScripts.end(); ++itr)
{
@@ -3776,9 +4504,11 @@ void ObjectMgr::LoadQuestEndScripts()
sLog.outErrorDb("Table `quest_end_scripts` has not existing quest (Id: %u) as script id",itr->first);
}
}
+
void ObjectMgr::LoadQuestStartScripts()
{
LoadScripts(sQuestStartScripts,"quest_start_scripts");
+
// check ids
for(ScriptMapMap::const_iterator itr = sQuestStartScripts.begin(); itr != sQuestStartScripts.end(); ++itr)
{
@@ -3786,18 +4516,22 @@ void ObjectMgr::LoadQuestStartScripts()
sLog.outErrorDb("Table `quest_start_scripts` has not existing quest (Id: %u) as script id",itr->first);
}
}
+
void ObjectMgr::LoadSpellScripts()
{
LoadScripts(sSpellScripts, "spell_scripts");
+
// check ids
for(ScriptMapMap::const_iterator itr = sSpellScripts.begin(); itr != sSpellScripts.end(); ++itr)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(itr->first);
+
if(!spellInfo)
{
sLog.outErrorDb("Table `spell_scripts` has not existing spell (Id: %u) as script id",itr->first);
continue;
}
+
//check for correct spellEffect
bool found = false;
for(uint8 i=0; i<3; ++i)
@@ -3805,19 +4539,23 @@ void ObjectMgr::LoadSpellScripts()
// skip empty effects
if( !spellInfo->Effect[i] )
continue;
+
if( spellInfo->Effect[i] == SPELL_EFFECT_SCRIPT_EFFECT )
{
found = true;
break;
}
}
+
if(!found)
sLog.outErrorDb("Table `spell_scripts` has unsupported spell (Id: %u) without SPELL_EFFECT_SCRIPT_EFFECT (%u) spell effect",itr->first,SPELL_EFFECT_SCRIPT_EFFECT);
}
}
+
void ObjectMgr::LoadEventScripts()
{
LoadScripts(sEventScripts, "event_scripts");
+
std::set<uint32> evt_scripts;
// Load all possible script entries from gameobjects
for(uint32 i = 1; i < sGOStorage.MaxEntry; ++i)
@@ -3868,10 +4606,12 @@ void ObjectMgr::LoadEventScripts()
itr->first, SPELL_EFFECT_SEND_EVENT);
}
}
+
//Load WP Scripts
void ObjectMgr::LoadWaypointScripts()
{
LoadScripts(sWaypointScripts, "waypoint_scripts");
+
for(ScriptMapMap::const_iterator itr = sWaypointScripts.begin(); itr != sWaypointScripts.end(); ++itr)
{
QueryResult *query = WorldDatabase.PQuery("SELECT * FROM waypoint_scripts WHERE id = %u", itr->first);
@@ -3879,48 +4619,65 @@ void ObjectMgr::LoadWaypointScripts()
sLog.outErrorDb("There is no waypoint which links to the waypoint script %u", itr->first);
}
}
+
void ObjectMgr::LoadItemTexts()
{
QueryResult *result = CharacterDatabase.Query("SELECT id, text FROM item_text");
+
uint32 count = 0;
+
if( !result )
{
barGoLink bar( 1 );
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u item pages", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
+
Field* fields;
do
{
bar.step();
+
fields = result->Fetch();
+
mItemTexts[ fields[0].GetUInt32() ] = fields[1].GetCppString();
+
++count;
+
} while ( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u item texts", count );
}
+
void ObjectMgr::LoadPageTexts()
{
sPageTextStore.Free(); // for reload case
+
sPageTextStore.Load();
sLog.outString( ">> Loaded %u page texts", sPageTextStore.RecordCount );
sLog.outString();
+
for(uint32 i = 1; i < sPageTextStore.MaxEntry; ++i)
{
// check data correctness
PageText const* page = sPageTextStore.LookupEntry<PageText>(i);
if(!page)
continue;
+
if(page->Next_Page && !sPageTextStore.LookupEntry<PageText>(page->Next_Page))
{
sLog.outErrorDb("Page text (Id: %u) has not existing next page (Id:%u)", i,page->Next_Page);
continue;
}
+
// detect circular reference
std::set<uint32> checkedPages;
for(PageText const* pageItr = page; pageItr; pageItr = sPageTextStore.LookupEntry<PageText>(pageItr->Next_Page))
@@ -3943,43 +4700,59 @@ void ObjectMgr::LoadPageTexts()
}
}
}
+
void ObjectMgr::LoadPageTextLocales()
{
mPageTextLocaleMap.clear(); // need for reload case
+
QueryResult *result = WorldDatabase.Query("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(uint8 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 %lu PageText locale strings", (unsigned long)mPageTextLocaleMap.size() );
}
+
struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader>
{
template<class D>
@@ -3988,10 +4761,12 @@ struct SQLInstanceLoader : public SQLStorageLoaderBase<SQLInstanceLoader>
dst = D(objmgr.GetScriptId(src));
}
};
+
void ObjectMgr::LoadInstanceTemplate()
{
SQLInstanceLoader loader;
loader.Load(sInstanceTemplate);
+
for(uint32 i = 0; i < sInstanceTemplate.MaxEntry; i++)
{
InstanceTemplate* temp = (InstanceTemplate*)GetInstanceTemplate(i);
@@ -4004,6 +4779,7 @@ void ObjectMgr::LoadInstanceTemplate()
}
else if(!entry->HasResetTime())
continue;
+
//FIXME: now exist heroic instance, normal/heroic raid instances
// entry->resetTimeHeroic store reset time for both heroic mode instance (raid and non-raid)
// entry->resetTimeRaid store reset time for normal raid only
@@ -4022,12 +4798,15 @@ void ObjectMgr::LoadInstanceTemplate()
temp->reset_delay = entry->resetTimeRaid / DAY;
}
}
+
// the reset_delay must be at least 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();
}
+
GossipText const *ObjectMgr::GetGossipText(uint32 Text_ID) const
{
GossipTextMap::const_iterator itr = mGossipText.find(Text_ID);
@@ -4035,39 +4814,52 @@ GossipText const *ObjectMgr::GetGossipText(uint32 Text_ID) const
return &itr->second;
return NULL;
}
+
void ObjectMgr::LoadGossipText()
{
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();
+
uint32 Text_ID = fields[cic++].GetUInt32();
if(!Text_ID)
{
sLog.outErrorDb("Table `npc_text` has record wit reserved id 0, ignore.");
continue;
}
+
GossipText& gText = mGossipText[Text_ID];
+
for (int i=0; i< 8; i++)
{
gText.Options[i].Text_0 = fields[cic++].GetCppString();
gText.Options[i].Text_1 = fields[cic++].GetCppString();
+
gText.Options[i].Language = fields[cic++].GetUInt32();
gText.Options[i].Probability = fields[cic++].GetFloat();
+
for(uint8 j=0; j < 3; ++j)
{
gText.Options[i].Emotes[j]._Delay = fields[cic++].GetUInt32();
@@ -4075,13 +4867,16 @@ void ObjectMgr::LoadGossipText()
}
}
} while( result->NextRow() );
+
sLog.outString();
sLog.outString( ">> Loaded %u npc texts", count );
delete result;
}
+
void ObjectMgr::LoadNpcTextLocales()
{
mNpcTextLocaleMap.clear(); // need for reload case
+
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,"
@@ -4092,21 +4887,29 @@ void ObjectMgr::LoadNpcTextLocales()
"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 NpcText 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(uint8 i=1; i<MAX_LOCALE; ++i)
{
for(uint8 j=0; j<8; ++j)
@@ -4119,6 +4922,7 @@ void ObjectMgr::LoadNpcTextLocales()
{
if(data.Text_0[j].size() <= idx)
data.Text_0[j].resize(idx+1);
+
data.Text_0[j][idx] = str0;
}
}
@@ -4130,16 +4934,20 @@ void ObjectMgr::LoadNpcTextLocales()
{
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 %lu NpcText locale strings", (unsigned long)mNpcTextLocaleMap.size() );
}
+
//not very fast function but it is called only once a day, or on starting-up
void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
{
@@ -4158,16 +4966,20 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
sLog.outString(">> Only expired mails (need to be return or delete) or DB table `mail` is empty.");
return; // any mails need to be returned or deleted
}
+
//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 ( "
+
barGoLink bar( result->GetRowCount() );
uint32 count = 0;
Field *fields;
+
do
{
bar.step();
+
fields = result->Fetch();
Mail *m = new Mail;
m->messageID = fields[0].GetUInt32();
@@ -4181,6 +4993,7 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
m->COD = fields[7].GetUInt32();
m->checked = fields[8].GetUInt32();
m->mailTemplateId = fields[9].GetInt16();
+
Player *pl = 0;
if (serverUp)
pl = GetPlayer((uint64)m->receiver);
@@ -4199,11 +5012,14 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
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
@@ -4221,8 +5037,10 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
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);
@@ -4230,108 +5048,150 @@ void ObjectMgr::ReturnOrDeleteOldMails(bool serverUp)
++count;
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u mails", count );
}
+
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_TRINITY_FLAGS_EXPLORATION_OR_EVENT))
{
sLog.outErrorDb("Table `areatrigger_involvedrelation` has record (id: %u) for not quest %u, but quest not have flag QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT. Trigger or quest flags must be fixed, quest modified to require objective.",trigger_ID,quest_ID);
+
// this will prevent quest completing without objective
const_cast<Quest*>(quest)->SetFlag(QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT);
+
// continue; - quest modified to required objective 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();
const char *scriptName = fields[1].GetString();
+
AreaTriggerEntry const* atEntry = sAreaTriggerStore.LookupEntry(Trigger_ID);
if(!atEntry)
{
@@ -4340,25 +5200,32 @@ void ObjectMgr::LoadAreaTriggerScripts()
}
mAreaTriggerScripts[Trigger_ID] = GetScriptId(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, uint32 team )
{
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 || !node->MountCreatureID[team == ALLIANCE ? 1 : 0] && node->MountCreatureID[0] != 32981) // dk flight
continue;
+
uint8 field = (uint8)((i - 1) / 32);
uint32 submask = 1<<((i-1)%32);
+
// skip not taxi network nodes
if((sTaxiNodesMask[field] & submask)==0)
continue;
+
float dist2 = (node->x - x)*(node->x - x)+(node->y - y)*(node->y - y)+(node->z - z)*(node->z - z);
if(found)
{
@@ -4375,8 +5242,10 @@ uint32 ObjectMgr::GetNearestTaxiNode( float x, float y, float z, uint32 mapid, u
id = i;
}
}
+
return id;
}
+
void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost)
{
TaxiPathSetBySource::iterator src_i = sTaxiPathSetBySource.find(source);
@@ -4386,7 +5255,9 @@ void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, ui
cost = 0;
return;
}
+
TaxiPathSetForSource& pathSet = src_i->second;
+
TaxiPathSetForSource::iterator dest_i = pathSet.find(destination);
if(dest_i==pathSet.end())
{
@@ -4394,13 +5265,16 @@ void ObjectMgr::GetTaxiPath( uint32 source, uint32 destination, uint32 &path, ui
cost = 0;
return;
}
+
cost = dest_i->second.price;
path = dest_i->second.ID;
}
+
uint32 ObjectMgr::GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_alt_team /* = false */)
{
uint32 mount_entry = 0;
uint32 mount_id = 0;
+
// select mount creature id
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(id);
if(node)
@@ -4409,6 +5283,7 @@ uint32 ObjectMgr::GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_al
mount_entry = node->MountCreatureID[1];
else
mount_entry = node->MountCreatureID[0];
+
// Fix for Alliance not being able to use Acherus taxi
// only one mount type for both sides
if (mount_entry == 0 && allowed_alt_team)
@@ -4417,6 +5292,7 @@ uint32 ObjectMgr::GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_al
// have a valid mount ID to choose
mount_entry = (team == ALLIANCE) ? node->MountCreatureID[0] : node->MountCreatureID[1];
}
+
CreatureInfo const *mount_info = GetCreatureTemplate(mount_entry);
if (mount_info)
{
@@ -4427,32 +5303,43 @@ uint32 ObjectMgr::GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_al
}
}
}
+
CreatureModelInfo const *minfo = objmgr.GetCreatureModelRandomGender(mount_id);
if (minfo)
mount_id = minfo->modelid;
+
return mount_id;
}
+
void ObjectMgr::GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds)
{
if(path >= sTaxiPathNodesByPath.size())
return;
+
TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
+
pathnodes.Resize(nodeList.size());
mapIds.resize(nodeList.size());
+
for(size_t i = 0; i < nodeList.size(); ++i)
{
pathnodes[ i ].x = nodeList[i].x;
pathnodes[ i ].y = nodeList[i].y;
pathnodes[ i ].z = nodeList[i].z;
+
mapIds[i] = nodeList[i].mapid;
}
}
+
void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
{
if(path >= sTaxiPathNodesByPath.size())
return;
+
TaxiPathNodeList& nodeList = sTaxiPathNodesByPath[path];
+
pathnodes.Resize(nodeList.size());
+
for(size_t i = 0; i < nodeList.size(); ++i)
{
pathnodes[ i ].mapid = nodeList[i].mapid;
@@ -4463,61 +5350,79 @@ void ObjectMgr::GetTransportPathNodes( uint32 path, TransportPath &pathnodes )
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(!AddGraveYardLink(safeLocId,zoneId,team,false))
sLog.outErrorDb("Table `game_graveyard_zone` has a duplicate record for Graveyard (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,z);
+
// Simulate std. algorithm:
// found some graveyard associated to (ghost_zone,ghost_map)
//
@@ -4529,35 +5434,44 @@ WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float
GraveYardMap::const_iterator graveUp = mGraveYardMap.upper_bound(zoneId);
MapEntry const* map = sMapStore.LookupEntry(MapId);
// not need to check validity of map object; MapId _MUST_ be valid here
+
if(graveLow==graveUp && !map->IsBattleArena())
{
sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
return NULL;
}
+
// at corpse map
bool foundNear = false;
float distNear;
WorldSafeLocsEntry const* entryNear = NULL;
+
// at entrance map for corpse map
bool foundEntr = false;
float distEntr;
WorldSafeLocsEntry const* entryEntr = NULL;
+
// some where other
WorldSafeLocsEntry const* entryFar = NULL;
+
MapEntry const* mapEntry = sMapStore.LookupEntry(MapId);
+
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;
}
+
// skip enemy faction graveyard
// team == 0 case can be at call from .neargrave
if(data.team != 0 && team != 0 && data.team != team)
continue;
+
// find now nearest graveyard at other map
if(MapId != entry->map_id)
{
@@ -4571,6 +5485,7 @@ WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float
entryFar = entry;
continue;
}
+
// at entrance map calculate distance (2D);
float dist2 = (entry->x - mapEntry->entrance_x)*(entry->x - mapEntry->entrance_x)
+(entry->y - mapEntry->entrance_y)*(entry->y - mapEntry->entrance_y);
@@ -4609,40 +5524,52 @@ WorldSafeLocsEntry const *ObjectMgr::GetClosestGraveYard(float x, float y, float
}
}
}
+
if(entryNear)
return entryNear;
+
if(entryEntr)
return entryEntr;
+
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::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool inDB)
{
GraveYardMap::iterator graveLow = mGraveYardMap.lower_bound(zoneId);
@@ -4652,107 +5579,150 @@ void ObjectMgr::RemoveGraveYardLink(uint32 id, uint32 zoneId, uint32 team, bool
//sLog.outErrorDb("Table `game_graveyard_zone` incomplete: Zone %u Team %u does not have a linked graveyard.",zoneId,team);
return;
}
+
bool found = false;
+
GraveYardMap::iterator itr;
+
for(itr = graveLow; itr != graveUp; ++itr)
{
GraveYardData & data = itr->second;
+
// skip not matching safezone id
if(data.safeLocId != id)
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;
+
found = true;
break;
}
+
// no match, return
if(!found)
return;
+
// remove from links
mGraveYardMap.erase(itr);
+
// remove link from DB
if(inDB)
{
WorldDatabase.PExecute("DELETE FROM game_graveyard_zone WHERE id = '%u' AND ghost_zone = '%u' AND faction = '%u'",id,zoneId,team);
}
+
return;
}
+
void ObjectMgr::LoadAreaTriggerTeleports()
{
mAreaTriggers.clear(); // need for reload case
+
uint32 count = 0;
+
// 0 1 2 3 4 5 6
QueryResult *result = WorldDatabase.Query("SELECT id, access_id, 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.access_id = fields[1].GetUInt32();
at.target_mapId = fields[2].GetUInt32();
at.target_X = fields[3].GetFloat();
at.target_Y = fields[4].GetFloat();
at.target_Z = fields[5].GetFloat();
at.target_Orientation = fields[6].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;
}
+
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 );
}
+
void ObjectMgr::LoadAccessRequirements()
{
mAccessRequirements.clear(); // need for reload case
+
uint32 count = 0;
+
// 0 1 2 3 4 5 6 7 8 9 10 11
QueryResult *result = WorldDatabase.Query("SELECT id, level_min, level_max, item, item2, heroic_key, heroic_key2, quest_done, quest_failed_text, heroic_quest_done, heroic_quest_failed_text, heroic_level_min FROM access_requirement");
if( !result )
{
+
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u access requirement definitions", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
Field *fields = result->Fetch();
+
bar.step();
+
++count;
+
uint32 requiremt_ID = fields[0].GetUInt32();
+
AccessRequirement ar;
+
ar.levelMin = fields[1].GetUInt8();
ar.levelMax = fields[2].GetUInt8();
ar.heroicLevelMin = fields[11].GetUInt8();
@@ -4764,6 +5734,7 @@ void ObjectMgr::LoadAccessRequirements()
ar.questFailedText = fields[8].GetCppString();
ar.heroicQuest = fields[9].GetUInt32();
ar.heroicQuestFailedText = fields[10].GetCppString();
+
if(ar.item)
{
ItemPrototype const *pProto = GetItemPrototype(ar.item);
@@ -4773,6 +5744,7 @@ void ObjectMgr::LoadAccessRequirements()
ar.item = 0;
}
}
+
if(ar.item2)
{
ItemPrototype const *pProto = GetItemPrototype(ar.item2);
@@ -4782,6 +5754,7 @@ void ObjectMgr::LoadAccessRequirements()
ar.item2 = 0;
}
}
+
if(ar.heroicKey)
{
ItemPrototype const *pProto = GetItemPrototype(ar.heroicKey);
@@ -4791,6 +5764,7 @@ void ObjectMgr::LoadAccessRequirements()
ar.heroicKey = 0;
}
}
+
if(ar.heroicKey2)
{
ItemPrototype const *pProto = GetItemPrototype(ar.heroicKey2);
@@ -4800,6 +5774,7 @@ void ObjectMgr::LoadAccessRequirements()
ar.heroicKey2 = 0;
}
}
+
if(ar.heroicQuest)
{
QuestMap::iterator qReqItr = mQuestTemplates.find(ar.heroicQuest);
@@ -4809,6 +5784,7 @@ void ObjectMgr::LoadAccessRequirements()
ar.heroicQuest = 0;
}
}
+
if(ar.quest)
{
QuestMap::iterator qReqItr = mQuestTemplates.find(ar.quest);
@@ -4818,12 +5794,17 @@ void ObjectMgr::LoadAccessRequirements()
ar.quest = 0;
}
}
+
mAccessRequirements[requiremt_ID] = ar;
+
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u access requirement definitions", count );
}
+
/*
* Searches for the areatrigger which teleports players out of the given map
*/
@@ -4842,6 +5823,7 @@ AreaTrigger const* ObjectMgr::GetGoBackTrigger(uint32 Map) const
}
return NULL;
}
+
/**
* Searches for the areatrigger which teleports players to the given map
*/
@@ -4858,6 +5840,7 @@ AreaTrigger const* ObjectMgr::GetMapEntranceTrigger(uint32 Map) const
}
return NULL;
}
+
void ObjectMgr::SetHighestGuids()
{
QueryResult *result = CharacterDatabase.Query( "SELECT MAX(guid) FROM characters" );
@@ -4866,65 +5849,76 @@ void ObjectMgr::SetHighestGuids()
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(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;
}
+
result = CharacterDatabase.Query( "SELECT MAX(id) FROM mail" );
if( result )
{
m_mailid = (*result)[0].GetUInt32()+1;
delete result;
}
+
result = CharacterDatabase.Query( "SELECT MAX(id) FROM item_text" );
if( result )
{
m_ItemTextId = (*result)[0].GetUInt32()+1;
delete result;
}
+
result = CharacterDatabase.Query( "SELECT MAX(guid) FROM corpse" );
if( result )
{
m_hiCorpseGuid = (*result)[0].GetUInt32()+1;
delete result;
}
+
result = CharacterDatabase.Query("SELECT MAX(arenateamid) FROM arena_team");
if (result)
{
m_arenaTeamId = (*result)[0].GetUInt32()+1;
delete result;
}
+
result = CharacterDatabase.Query("SELECT MAX(setguid) FROM character_equipmentsets");
if (result)
{
m_equipmentSetGuid = (*result)[0].GetUInt64()+1;
delete result;
}
+
result = CharacterDatabase.Query( "SELECT MAX(guildid) FROM guild" );
if (result)
{
@@ -4932,6 +5926,7 @@ void ObjectMgr::SetHighestGuids()
delete result;
}
}
+
uint32 ObjectMgr::GenerateArenaTeamId()
{
if(m_arenaTeamId>=0xFFFFFFFE)
@@ -4941,6 +5936,7 @@ uint32 ObjectMgr::GenerateArenaTeamId()
}
return m_arenaTeamId++;
}
+
uint32 ObjectMgr::GenerateAuctionID()
{
if(m_auctionid>=0xFFFFFFFE)
@@ -4950,6 +5946,7 @@ uint32 ObjectMgr::GenerateAuctionID()
}
return m_auctionid++;
}
+
uint64 ObjectMgr::GenerateEquipmentSetGuid()
{
if(m_equipmentSetGuid>=0xFFFFFFFFFFFFFFFEll)
@@ -4959,6 +5956,7 @@ uint64 ObjectMgr::GenerateEquipmentSetGuid()
}
return m_equipmentSetGuid++;
}
+
uint32 ObjectMgr::GenerateGuildId()
{
if(m_guildId>=0xFFFFFFFE)
@@ -4968,6 +5966,7 @@ uint32 ObjectMgr::GenerateGuildId()
}
return m_guildId++;
}
+
uint32 ObjectMgr::GenerateMailID()
{
if(m_mailid>=0xFFFFFFFE)
@@ -4977,6 +5976,7 @@ uint32 ObjectMgr::GenerateMailID()
}
return m_mailid++;
}
+
uint32 ObjectMgr::GenerateItemTextID()
{
if(m_ItemTextId>=0xFFFFFFFE)
@@ -4986,6 +5986,7 @@ uint32 ObjectMgr::GenerateItemTextID()
}
return m_ItemTextId++;
}
+
uint32 ObjectMgr::CreateItemText(std::string text)
{
uint32 newItemTextId = GenerateItemTextID();
@@ -4999,6 +6000,7 @@ uint32 ObjectMgr::CreateItemText(std::string 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)
@@ -5062,31 +6064,42 @@ uint32 ObjectMgr::GenerateLowGuid(HighGuid guidhigh)
default:
ASSERT(0);
}
+
ASSERT(0);
return 0;
}
+
void ObjectMgr::LoadGameObjectLocales()
{
mGameObjectLocaleMap.clear(); // need for reload case
+
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(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[i].GetCppString();
@@ -5097,10 +6110,12 @@ void ObjectMgr::LoadGameObjectLocales()
{
if(data.Name.size() <= idx)
data.Name.resize(idx+1);
+
data.Name[idx] = str;
}
}
}
+
for(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[i+(MAX_LOCALE-1)].GetCppString();
@@ -5111,15 +6126,20 @@ void ObjectMgr::LoadGameObjectLocales()
{
if(data.CastBarCaption.size() <= idx)
data.CastBarCaption.resize(idx+1);
+
data.CastBarCaption[idx] = str;
}
}
}
+
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %lu gameobject locale strings", (unsigned long)mGameObjectLocaleMap.size() );
}
+
struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader>
{
template<class D>
@@ -5128,13 +6148,16 @@ struct SQLGameObjectLoader : public SQLStorageLoaderBase<SQLGameObjectLoader>
dst = D(objmgr.GetScriptId(src));
}
};
+
inline void CheckGOLockId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
{
if (sLockStore.LookupEntry(dataN))
return;
+
sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but lock (Id: %u) not found.",
goInfo->id,goInfo->type,N,goInfo->door.lockId,goInfo->door.lockId);
}
+
inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
{
if (GameObjectInfo const* trapInfo = sGOStorage.LookupEntry<GameObjectInfo>(dataN))
@@ -5149,49 +6172,62 @@ inline void CheckGOLinkedTrapId(GameObjectInfo const* goInfo,uint32 dataN,uint32
goInfo->id,goInfo->type,N,dataN,dataN);
*/
}
+
inline void CheckGOSpellId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
{
if (sSpellStore.LookupEntry(dataN))
return;
+
sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but Spell (Entry %u) not exist.",
goInfo->id,goInfo->type,N,dataN,dataN);
}
+
inline void CheckAndFixGOChairHeightId(GameObjectInfo const* goInfo,uint32 const& dataN,uint32 N)
{
if (dataN <= (UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR) )
return;
+
sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but correct chair height in range 0..%i.",
goInfo->id,goInfo->type,N,dataN,UNIT_STAND_STATE_SIT_HIGH_CHAIR-UNIT_STAND_STATE_SIT_LOW_CHAIR);
+
// prevent client and server unexpected work
const_cast<uint32&>(dataN) = 0;
}
+
inline void CheckGONoDamageImmuneId(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
{
// 0/1 correct values
if (dataN <= 1)
return;
+
sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) noDamageImmune field value.",
goInfo->id,goInfo->type,N,dataN);
}
+
inline void CheckGOConsumable(GameObjectInfo const* goInfo,uint32 dataN,uint32 N)
{
// 0/1 correct values
if (dataN <= 1)
return;
+
sLog.outErrorDb("Gameobject (Entry: %u GoType: %u) have data%d=%u but expected boolean (0/1) consumable field value.",
goInfo->id,goInfo->type,N,dataN);
}
+
void ObjectMgr::LoadGameobjectInfo()
{
SQLGameObjectLoader loader;
loader.Load(sGOStorage);
+
// some checks
for(uint32 id = 1; id < sGOStorage.MaxEntry; id++)
{
GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(id);
if (!goInfo)
continue;
+
// some GO types have unused go template, check goInfo->displayId at GO spawn data loading or ignore
+
switch(goInfo->type)
{
case GAMEOBJECT_TYPE_DOOR: //0
@@ -5219,7 +6255,9 @@ void ObjectMgr::LoadGameobjectInfo()
{
if (goInfo->chest.lockId)
CheckGOLockId(goInfo,goInfo->chest.lockId,0);
+
CheckGOConsumable(goInfo,goInfo->chest.consumable,3);
+
if (goInfo->chest.linkedTrapId) // linked trap
CheckGOLinkedTrapId(goInfo,goInfo->chest.linkedTrapId,7);
break;
@@ -5245,6 +6283,7 @@ void ObjectMgr::LoadGameobjectInfo()
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
CheckGOLinkedTrapId(goInfo,goInfo->spellFocus.linkedTrapId,2);
break;
@@ -5253,7 +6292,9 @@ void ObjectMgr::LoadGameobjectInfo()
{
if (goInfo->goober.lockId)
CheckGOLockId(goInfo,goInfo->goober.lockId,0);
+
CheckGOConsumable(goInfo,goInfo->goober.consumable,3);
+
if (goInfo->goober.pageId) // pageId
{
if (!sPageTextStore.LookupEntry<PageText>(goInfo->goober.pageId))
@@ -5330,25 +6371,33 @@ void ObjectMgr::LoadGameobjectInfo()
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();
@@ -5356,36 +6405,47 @@ void ObjectMgr::LoadExplorationBaseXP()
++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;
}
+
uint32 ObjectMgr::GetXPForLevel(uint32 level)
{
if (level < mPlayerXPperLevel.size())
return mPlayerXPperLevel[level];
return 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();
@@ -5398,9 +6458,11 @@ void ObjectMgr::LoadPetNames()
}
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");
@@ -5410,15 +6472,19 @@ void ObjectMgr::LoadPetNumber()
m_hiPetNumber = fields[0].GetUInt32()+1;
delete result;
}
+
barGoLink bar( 1 );
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded the max pet number: %d", m_hiPetNumber-1);
}
+
std::string ObjectMgr::GeneratePetName(uint32 entry)
{
std::vector<std::string> & list0 = PetHalfName0[entry];
std::vector<std::string> & list1 = PetHalfName1[entry];
+
if(list0.empty() || list1.empty())
{
CreatureInfo const *cinfo = GetCreatureTemplate(entry);
@@ -5427,67 +6493,90 @@ std::string ObjectMgr::GeneratePetName(uint32 entry)
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.Query("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();
@@ -5498,11 +6587,13 @@ void ObjectMgr::LoadReputationOnKill()
repOnKill.reputation_max_cap2 = fields[7].GetUInt32();
repOnKill.repvalue2 = fields[8].GetInt32();
repOnKill.team_dependent = fields[9].GetUInt8();
+
if(!GetCreatureTemplate(creature_id))
{
sLog.outErrorDb("Table `creature_onkill_reputation` have data for not existed creature entry (%u), skipped",creature_id);
continue;
}
+
if(repOnKill.repfaction1)
{
FactionEntry const *factionEntry1 = sFactionStore.LookupEntry(repOnKill.repfaction1);
@@ -5512,6 +6603,7 @@ void ObjectMgr::LoadReputationOnKill()
continue;
}
}
+
if(repOnKill.repfaction2)
{
FactionEntry const *factionEntry2 = sFactionStore.LookupEntry(repOnKill.repfaction2);
@@ -5521,32 +6613,45 @@ void ObjectMgr::LoadReputationOnKill()
continue;
}
}
+
mRepOnKill[creature_id] = repOnKill;
+
++count;
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u creature award reputation definitions", count);
}
+
void ObjectMgr::LoadPointsOfInterest()
{
uint32 count = 0;
+
// 0 1 2 3 4 5
QueryResult *result = WorldDatabase.Query("SELECT entry, x, y, icon, flags, data, icon_name FROM points_of_interest");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 Points of Interest definitions. DB table `points_of_interest` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 point_id = fields[0].GetUInt32();
+
PointOfInterest POI;
POI.x = fields[1].GetFloat();
POI.y = fields[2].GetFloat();
@@ -5554,37 +6659,50 @@ void ObjectMgr::LoadPointsOfInterest()
POI.flags = fields[4].GetUInt32();
POI.data = fields[5].GetUInt32();
POI.icon_name = fields[6].GetCppString();
+
if(!MaNGOS::IsValidMapCoord(POI.x,POI.y))
{
sLog.outErrorDb("Table `points_of_interest` (Entry: %u) have invalid coordinates (X: %f Y: %f), ignored.",point_id,POI.x,POI.y);
continue;
}
+
mPointsOfInterest[point_id] = POI;
+
++count;
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u Points of Interest definitions", count);
}
+
void ObjectMgr::LoadNPCSpellClickSpells()
{
uint32 count = 0;
+
mSpellClickInfoMap.clear();
// 0 1 2 3 4 5 6 7 8
QueryResult *result = WorldDatabase.Query("SELECT npc_entry, spell_id, quest_start, quest_start_active, quest_end, cast_flags, aura_required, aura_forbidden, user_type FROM npc_spellclick_spells");
+
if(!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded 0 spellclick spells. DB table `npc_spellclick_spells` is empty.");
return;
}
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 npc_entry = fields[0].GetUInt32();
CreatureInfo const* cInfo = GetCreatureTemplate(npc_entry);
if (!cInfo)
@@ -5592,8 +6710,10 @@ void ObjectMgr::LoadNPCSpellClickSpells()
sLog.outErrorDb("Table npc_spellclick_spells references unknown creature_template %u. Skipping entry.", npc_entry);
continue;
}
+
if(!(cInfo->npcflag & UNIT_NPC_FLAG_SPELLCLICK))
const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK;
+
uint32 spellid = fields[1].GetUInt32();
SpellEntry const *spellinfo = sSpellStore.LookupEntry(spellid);
if (!spellinfo)
@@ -5601,6 +6721,7 @@ void ObjectMgr::LoadNPCSpellClickSpells()
sLog.outErrorDb("Table npc_spellclick_spells references unknown spellid %u. Skipping entry.", spellid);
continue;
}
+
uint32 auraRequired = fields[6].GetUInt32();
if (auraRequired)
{
@@ -5611,6 +6732,7 @@ void ObjectMgr::LoadNPCSpellClickSpells()
continue;
}
}
+
uint32 auraForbidden = fields[7].GetUInt32();
if (auraForbidden)
{
@@ -5621,7 +6743,9 @@ void ObjectMgr::LoadNPCSpellClickSpells()
continue;
}
}
+
uint32 quest_start = fields[2].GetUInt32();
+
// quest might be 0 to enable spellclick independent of any quest
if (quest_start)
{
@@ -5631,7 +6755,9 @@ void ObjectMgr::LoadNPCSpellClickSpells()
continue;
}
}
+
bool quest_start_active = fields[3].GetBool();
+
uint32 quest_end = fields[4].GetUInt32();
// quest might be 0 to enable spellclick active infinity after start quest
if (quest_end)
@@ -5642,9 +6768,11 @@ void ObjectMgr::LoadNPCSpellClickSpells()
continue;
}
}
+
uint8 userType = fields[8].GetUInt8();
if (userType >= SPELL_CLICK_USER_MAX)
sLog.outErrorDb("Table npc_spellclick_spells references unknown user type %u. Skipping entry.", uint32(userType));
+
uint8 castFlags = fields[5].GetUInt8();
SpellClickInfo info;
info.spellId = spellid;
@@ -5656,61 +6784,82 @@ void ObjectMgr::LoadNPCSpellClickSpells()
info.auraForbidden = auraForbidden;
info.userType = SpellClickUserTypes(userType);
mSpellClickInfoMap.insert(SpellClickInfoMap::value_type(npc_entry, info));
+
// mark creature template as spell clickable
const_cast<CreatureInfo*>(cInfo)->npcflag |= UNIT_NPC_FLAG_SPELLCLICK;
+
++count;
} while (result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u spellclick 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(uint8 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;
@@ -5718,14 +6867,17 @@ void ObjectMgr::SaveCreatureRespawnTime(uint32 loguid, uint32 instance, time_t t
if(t)
WorldDatabase.PExecute("INSERT INTO creature_respawn VALUES ( '%u', '" UI64FMTD "', '%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;
@@ -5733,81 +6885,107 @@ void ObjectMgr::SaveGORespawnTime(uint32 loguid, uint32 instance, time_t t)
if(t)
WorldDatabase.PExecute("INSERT INTO gameobject_respawn VALUES ( '%u', '" UI64FMTD "', '%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);
@@ -5817,9 +6995,11 @@ void ObjectMgr::LoadGameobjectQuestRelations()
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);
@@ -5829,9 +7009,11 @@ void ObjectMgr::LoadGameobjectInvolvedRelations()
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);
@@ -5841,9 +7023,11 @@ void ObjectMgr::LoadCreatureQuestRelations()
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);
@@ -5853,48 +7037,64 @@ void ObjectMgr::LoadCreatureInvolvedRelations()
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.Query("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();
+
std::wstring wstr;
if(!Utf8toWStr (name,wstr))
{
sLog.outError("Table `reserved_name` have invalid name: %s", name.c_str() );
continue;
}
+
wstrToLower(wstr);
+
m_ReservedNames.insert(wstr);
++count;
} while ( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u reserved player names", count );
}
+
bool ObjectMgr::IsReservedName( const std::string& name ) const
{
std::wstring wstr;
if(!Utf8toWStr (name,wstr))
return false;
+
wstrToLower(wstr);
+
return m_ReservedNames.find(wstr) != m_ReservedNames.end();
}
+
enum LanguageType
{
LT_BASIC_LATIN = 0x0000,
@@ -5903,6 +7103,7 @@ enum LanguageType
LT_EAST_ASIA = 0x0004,
LT_ANY = 0xFFFF
};
+
static LanguageType GetRealmLanguageType(bool create)
{
switch(sWorld.getConfig(CONFIG_REALM_ZONE))
@@ -5930,6 +7131,7 @@ static LanguageType GetRealmLanguageType(bool create)
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
@@ -5942,6 +7144,7 @@ bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bo
return true;
return false;
}
+
if(strictMask & 0x2) // realm zone specific
{
LanguageType lt = GetRealmLanguageType(create);
@@ -5955,84 +7158,111 @@ bool isValidString(std::wstring wstr, uint32 strictMask, bool numericOrSpace, bo
if(isEastAsianString(wstr,numericOrSpace))
return true;
}
+
if(strictMask & 0x1) // basic Latin
{
if(isBasicLatinString(wstr,numericOrSpace))
return true;
}
+
return false;
}
+
uint8 ObjectMgr::CheckPlayerName( const std::string& name, bool create )
{
std::wstring wname;
if(!Utf8toWStr(name,wname))
return CHAR_NAME_INVALID_CHARACTER;
+
if(wname.size() > MAX_PLAYER_NAME)
return CHAR_NAME_TOO_LONG;
+
uint32 minName = sWorld.getConfig(CONFIG_MIN_PLAYER_NAME);
if(wname.size() < minName)
return CHAR_NAME_TOO_SHORT;
+
uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PLAYER_NAMES);
if(!isValidString(wname,strictMask,false,create))
return CHAR_NAME_MIXED_LANGUAGES;
+
return CHAR_NAME_SUCCESS;
}
+
bool ObjectMgr::IsValidCharterName( const std::string& name )
{
std::wstring wname;
if(!Utf8toWStr(name,wname))
return false;
+
if(wname.size() > MAX_CHARTER_NAME)
return false;
+
uint32 minName = sWorld.getConfig(CONFIG_MIN_CHARTER_NAME);
if(wname.size() < minName)
return false;
+
uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_CHARTER_NAMES);
+
return isValidString(wname,strictMask,true);
}
+
PetNameInvalidReason ObjectMgr::CheckPetName( const std::string& name )
{
std::wstring wname;
if(!Utf8toWStr(name,wname))
return PET_NAME_INVALID;
+
if(wname.size() > MAX_PET_NAME)
return PET_NAME_TOO_LONG;
+
uint32 minName = sWorld.getConfig(CONFIG_MIN_PET_NAME);
if(wname.size() < minName)
return PET_NAME_TOO_SHORT;
+
uint32 strictMask = sWorld.getConfig(CONFIG_STRICT_PET_NAMES);
if(!isValidString(wname,strictMask,false))
return PET_NAME_MIXED_LANGUAGES;
+
return PET_NAME_SUCCESS;
}
+
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::LoadGameObjectForQuests()
{
mGameObjectForQuestSet.clear(); // need for reload case
+
if( !sGOStorage.MaxEntry )
{
barGoLink bar( 1 );
@@ -6041,8 +7271,10 @@ void ObjectMgr::LoadGameObjectForQuests()
sLog.outString( ">> Loaded 0 GameObjects for quests" );
return;
}
+
barGoLink bar( sGOStorage.MaxEntry - 1 );
uint32 count = 0;
+
// collect GO entries for GO that must activated
for(uint32 go_entry = 1; go_entry < sGOStorage.MaxEntry; ++go_entry)
{
@@ -6050,12 +7282,14 @@ void ObjectMgr::LoadGameObjectForQuests()
GameObjectInfo const* goInfo = sGOStorage.LookupEntry<GameObjectInfo>(go_entry);
if(!goInfo)
continue;
+
switch(goInfo->type)
{
// scan GO chest with loot including quest items
case GAMEOBJECT_TYPE_CHEST:
{
uint32 loot_id = goInfo->GetLootId();
+
// find quest loot for GO
if(LootTemplates_Gameobject.HaveQuestLootFor(loot_id))
{
@@ -6077,9 +7311,11 @@ void ObjectMgr::LoadGameObjectForQuests()
break;
}
}
+
sLog.outString();
sLog.outString( ">> Loaded %u GameObjects for quests", count );
}
+
bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value)
{
int32 start_value = min_value;
@@ -6092,6 +7328,7 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi
sLog.outErrorDb("Table '%s' attempt loaded with invalid range (%d - %d), strings not loaded.",table,min_value,max_value);
return false;
}
+
// real range (max+1,min+1) exaple: (-10,-1000) -> -999...-10+1
std::swap(start_value,end_value);
++start_value;
@@ -6105,6 +7342,7 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi
return false;
}
}
+
// cleanup affected map part for reloading case
for(TrinityStringLocaleMap::iterator itr = mTrinityStringLocaleMap.begin(); itr != mTrinityStringLocaleMap.end();)
{
@@ -6113,11 +7351,15 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi
else
++itr;
}
+
QueryResult *result = db.PQuery("SELECT entry,content_default,content_loc1,content_loc2,content_loc3,content_loc4,content_loc5,content_loc6,content_loc7,content_loc8 FROM %s",table);
+
if (!result)
{
barGoLink bar(1);
+
bar.step();
+
sLog.outString();
if (min_value == MIN_TRINITY_STRING_ID) // error only in case internal strings
sLog.outErrorDb(">> Loaded 0 trinity strings. DB table `%s` is empty. Cannot continue.",table);
@@ -6125,13 +7367,18 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi
sLog.outString(">> Loaded 0 string templates. DB table `%s` is empty.",table);
return false;
}
+
uint32 count = 0;
+
barGoLink bar(result->GetRowCount());
+
do
{
Field *fields = result->Fetch();
bar.step();
+
int32 entry = fields[0].GetInt32();
+
if (entry==0)
{
sLog.outErrorDb("Table `%s` contain reserved entry 0, ignored.",table);
@@ -6142,16 +7389,21 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi
sLog.outErrorDb("Table `%s` contain entry %i out of allowed range (%d - %d), ignored.",table,entry,min_value,max_value);
continue;
}
+
TrinityStringLocale& data = mTrinityStringLocaleMap[entry];
+
if (data.Content.size() > 0)
{
sLog.outErrorDb("Table `%s` contain data for already loaded entry %i (from another table?), ignored.",table,entry);
continue;
}
+
data.Content.resize(1);
++count;
+
// 0 -> default, idx in to idx+1
data.Content[0] = fields[1].GetCppString();
+
for(uint8 i = 1; i < MAX_LOCALE; ++i)
{
std::string str = fields[i+1].GetCppString();
@@ -6163,19 +7415,24 @@ bool ObjectMgr::LoadTrinityStrings(DatabaseType& db, char const* table, int32 mi
// 0 -> default, idx in to idx+1
if (data.Content.size() <= idx+1)
data.Content.resize(idx+2);
+
data.Content[idx+1] = str;
}
}
}
} while (result->NextRow());
+
delete result;
+
sLog.outString();
if (min_value == MIN_TRINITY_STRING_ID)
sLog.outString( ">> Loaded %u Trinity strings from table %s", count,table);
else
sLog.outString( ">> Loaded %u string templates from %s", count,table);
+
return true;
}
+
const char *ObjectMgr::GetTrinityString(int32 entry, int locale_idx) const
{
// locale_idx==-1 -> default, locale_idx >= 0 in to idx+1
@@ -6187,28 +7444,35 @@ const char *ObjectMgr::GetTrinityString(int32 entry, int locale_idx) const
else
return msl->Content[0].c_str();
}
+
if(entry > 0)
sLog.outErrorDb("Entry %i not found in `trinity_string` table.",entry);
else
sLog.outErrorDb("Trinity string entry %i not found in DB.",entry);
return "<error>";
}
+
void ObjectMgr::LoadSpellDisabledEntrys()
{
m_DisabledPlayerSpells.clear(); // need for reload case
m_DisabledCreatureSpells.clear();
m_DisabledPetSpells.clear();
QueryResult *result = WorldDatabase.Query("SELECT entry, disable_mask FROM spell_disabled");
+
uint32 total_count = 0;
+
if( !result )
{
barGoLink bar( 1 );
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u disabled spells", total_count );
return;
}
+
barGoLink bar( result->GetRowCount() );
+
Field* fields;
do
{
@@ -6229,44 +7493,59 @@ void ObjectMgr::LoadSpellDisabledEntrys()
m_DisabledPetSpells.insert(spellid);
++total_count;
} while ( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u disabled spells from `spell_disabled`", total_count);
}
+
void ObjectMgr::LoadFishingBaseSkillLevel()
{
mFishingBaseForArea.clear(); // for reload 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 )
@@ -6277,14 +7556,18 @@ uint16 ObjectMgr::GetConditionId( ConditionType condition, uint32 value1, uint32
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(uint8 i =0; i < MAX_DECLINED_NAME_CASES; ++i)
@@ -6292,11 +7575,13 @@ bool ObjectMgr::CheckDeclinedNames( std::wstring mainpart, DeclinedName const& n
std::wstring wname;
if(!Utf8toWStr(names.name[i],wname))
return false;
+
if(mainpart!=GetMainPartOfName(wname,i+1))
return false;
}
return true;
}
+
uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id)
{
AreaTriggerScriptMap::const_iterator i = mAreaTriggerScripts.find(trigger_id);
@@ -6304,11 +7589,13 @@ uint32 ObjectMgr::GetAreaTriggerScriptId(uint32 trigger_id)
return i->second;
return 0;
}
+
// 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:
@@ -6359,6 +7646,7 @@ bool PlayerCondition::Meets(Player const * player) const
return false;
}
}
+
// Verification of condition values validity
bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 value2)
{
@@ -6367,6 +7655,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val
sLog.outErrorDb("Condition has bad type of %u, skipped ", condition );
return false;
}
+
switch (condition)
{
case CONDITION_AURA:
@@ -6505,6 +7794,7 @@ bool PlayerCondition::IsValid(ConditionType condition, uint32 value1, uint32 val
}
return true;
}
+
SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
{
switch(pSkill->categoryId)
@@ -6536,59 +7826,79 @@ SkillRangeType GetSkillRangeType(SkillLineEntry const *pSkill, bool racial)
return SKILL_RANGE_NONE;
}
}
+
void ObjectMgr::LoadGameTele()
{
m_GameTeleMap.clear(); // for reload case
+
uint32 count = 0;
QueryResult *result = WorldDatabase.Query("SELECT id, position_x, position_y, position_z, orientation, map, name FROM game_tele");
+
if( !result )
{
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded `game_tele`, table is empty!");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
bar.step();
+
Field *fields = result->Fetch();
+
uint32 id = fields[0].GetUInt32();
+
GameTele gt;
+
gt.position_x = fields[1].GetFloat();
gt.position_y = fields[2].GetFloat();
gt.position_z = fields[3].GetFloat();
gt.orientation = fields[4].GetFloat();
gt.mapId = fields[5].GetUInt32();
gt.name = fields[6].GetCppString();
+
if(!MapManager::IsValidMapCoord(gt.mapId,gt.position_x,gt.position_y,gt.position_z,gt.orientation))
{
sLog.outErrorDb("Wrong position for id %u (name: %s) in `game_tele` table, ignoring.",id,gt.name.c_str());
continue;
}
+
if(!Utf8toWStr(gt.name,gt.wnameLow))
{
sLog.outErrorDb("Wrong UTF8 name for id %u in `game_tele` table, ignoring.",id);
continue;
}
+
wstrToLower( gt.wnameLow );
+
m_GameTeleMap[id] = gt;
+
++count;
}
while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u GameTeleports", count );
}
+
GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
{
// explicit name case
std::wstring wname;
if(!Utf8toWStr(name,wname))
return false;
+
// converting string that we try to find to lower case
wstrToLower( wname );
+
// Alternative first GameTele what contains wnameLow as substring in case no GameTele location found
const GameTele* alt = NULL;
for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
@@ -6598,8 +7908,10 @@ GameTele const* ObjectMgr::GetGameTele(const std::string& name) const
else if (alt == NULL && itr->second.wnameLow.find(wname) != std::wstring::npos)
alt = &itr->second;
}
+
return alt;
}
+
bool ObjectMgr::AddGameTele(GameTele& tele)
{
// find max id
@@ -6607,23 +7919,31 @@ bool ObjectMgr::AddGameTele(GameTele& tele)
for(GameTeleMap::const_iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
if(itr->first > new_id)
new_id = itr->first;
+
// use next
++new_id;
+
if(!Utf8toWStr(tele.name,tele.wnameLow))
return false;
+
wstrToLower( tele.wnameLow );
+
m_GameTeleMap[new_id] = tele;
+
return WorldDatabase.PExecuteLog("INSERT INTO game_tele (id,position_x,position_y,position_z,orientation,map,name) VALUES (%u,%f,%f,%f,%f,%d,'%s')",
new_id,tele.position_x,tele.position_y,tele.position_z,tele.orientation,tele.mapId,tele.name.c_str());
}
+
bool ObjectMgr::DeleteGameTele(const std::string& name)
{
// explicit name case
std::wstring wname;
if(!Utf8toWStr(name,wname))
return false;
+
// converting string that we try to find to lower case
wstrToLower( wname );
+
for(GameTeleMap::iterator itr = m_GameTeleMap.begin(); itr != m_GameTeleMap.end(); ++itr)
{
if(itr->second.wnameLow == wname)
@@ -6633,39 +7953,54 @@ bool ObjectMgr::DeleteGameTele(const std::string& name)
return true;
}
}
+
return false;
}
+
void ObjectMgr::LoadTrainerSpell()
{
// For reload case
for (CacheTrainerSpellMap::iterator itr = m_mCacheTrainerSpellMap.begin(); itr != m_mCacheTrainerSpellMap.end(); ++itr)
itr->second.Clear();
m_mCacheTrainerSpellMap.clear();
+
std::set<uint32> skip_trainers;
+
QueryResult *result = WorldDatabase.Query("SELECT entry, spell,spellcost,reqskill,reqskillvalue,reqlevel FROM npc_trainer");
+
if( !result )
{
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded `npc_trainer`, table is empty!");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
std::set<uint32> talentIds;
+
uint32 count = 0;
do
{
bar.step();
+
Field* fields = result->Fetch();
+
uint32 entry = fields[0].GetUInt32();
uint32 spell = fields[1].GetUInt32();
+
CreatureInfo const* cInfo = GetCreatureTemplate(entry);
+
if(!cInfo)
{
sLog.outErrorDb("Table `npc_trainer` have entry for not existed creature template (Entry: %u), ignore", entry);
continue;
}
+
if(!(cInfo->npcflag & UNIT_NPC_FLAG_TRAINER))
{
if(skip_trainers.count(entry) == 0)
@@ -6675,17 +8010,20 @@ void ObjectMgr::LoadTrainerSpell()
}
continue;
}
+
SpellEntry const *spellinfo = sSpellStore.LookupEntry(spell);
if(!spellinfo)
{
sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u ) has non existing spell %u, ignore", entry,spell);
continue;
}
+
if(!SpellMgr::IsSpellValid(spellinfo))
{
sLog.outErrorDb("Table `npc_trainer` for Trainer (Entry: %u) has broken learning spell %u, ignore", entry, spell);
continue;
}
+
if(GetTalentSpellCost(spell))
{
if(talentIds.count(spell)==0)
@@ -6695,15 +8033,19 @@ void ObjectMgr::LoadTrainerSpell()
}
continue;
}
+
TrainerSpellData& data = m_mCacheTrainerSpellMap[entry];
+
TrainerSpell& trainerSpell = data.spellList[spell];
trainerSpell.spell = spell;
trainerSpell.spellCost = fields[2].GetUInt32();
trainerSpell.reqSkill = fields[3].GetUInt32();
trainerSpell.reqSkillValue = fields[4].GetUInt32();
trainerSpell.reqLevel = fields[5].GetUInt32();
+
if(!trainerSpell.reqLevel)
trainerSpell.reqLevel = spellinfo->spellLevel;
+
// calculate learned spell for profession case when stored cast-spell
trainerSpell.learnedSpell = spell;
for(uint8 i = 0; i <3; ++i)
@@ -6716,72 +8058,99 @@ void ObjectMgr::LoadTrainerSpell()
break;
}
}
+
if(SpellMgr::IsProfessionSpell(trainerSpell.learnedSpell))
data.trainerType = 2;
+
++count;
+
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %d Trainers", count );
}
+
void ObjectMgr::LoadVendors()
{
// For reload case
for (CacheVendorItemMap::iterator itr = m_mCacheVendorItemMap.begin(); itr != m_mCacheVendorItemMap.end(); ++itr)
itr->second.Clear();
m_mCacheVendorItemMap.clear();
+
std::set<uint32> skip_vendors;
+
QueryResult *result = WorldDatabase.Query("SELECT entry, item, maxcount, incrtime, ExtendedCost FROM npc_vendor");
if( !result )
{
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded `npc_vendor`, table is empty!");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
uint32 count = 0;
do
{
bar.step();
Field* fields = result->Fetch();
+
uint32 entry = fields[0].GetUInt32();
uint32 item_id = fields[1].GetUInt32();
uint32 maxcount = fields[2].GetUInt32();
uint32 incrtime = fields[3].GetUInt32();
uint32 ExtendedCost = fields[4].GetUInt32();
+
if(!IsVendorItemValid(entry,item_id,maxcount,incrtime,ExtendedCost,NULL,&skip_vendors))
continue;
+
VendorItemData& vList = m_mCacheVendorItemMap[entry];
+
vList.AddItem(item_id,maxcount,incrtime,ExtendedCost);
++count;
+
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %d Vendors ", count );
}
+
void ObjectMgr::LoadNpcTextId()
{
+
m_mCacheNpcTextIdMap.clear();
+
QueryResult* result = WorldDatabase.Query("SELECT npc_guid, textid FROM npc_gossip");
if( !result )
{
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded `npc_gossip`, table is empty!");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
uint32 count = 0;
uint32 guid,textid;
do
{
bar.step();
+
Field* fields = result->Fetch();
+
guid = fields[0].GetUInt32();
textid = fields[1].GetUInt32();
+
if (!GetCreatureData(guid))
{
sLog.outErrorDb("Table `npc_gossip` have not existed creature (GUID: %u) entry, ignore. ",guid);
@@ -6792,34 +8161,47 @@ void ObjectMgr::LoadNpcTextId()
sLog.outErrorDb("Table `npc_gossip` for creature (GUID: %u) have wrong Textid (%u), ignore. ", guid, textid);
continue;
}
+
m_mCacheNpcTextIdMap[guid] = textid ;
++count;
+
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %d NpcTextId ", count );
}
+
void ObjectMgr::LoadNpcOptions()
{
m_mCacheNpcOptionList.clear(); // For reload case
+
QueryResult *result = WorldDatabase.Query(
// 0 1 2 3 4 5 6 7 8
"SELECT id,gossip_id,npcflag,icon,action,box_money,coded,option_text,box_text "
"FROM npc_option");
+
if( !result )
{
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outErrorDb(">> Loaded `npc_option`, table is empty!");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
uint32 count = 0;
+
do
{
bar.step();
+
Field* fields = result->Fetch();
+
GossipOption go;
go.Id = fields[0].GetUInt32();
go.GossipId = fields[1].GetUInt32();
@@ -6830,30 +8212,40 @@ void ObjectMgr::LoadNpcOptions()
go.Coded = fields[6].GetUInt8()!=0;
go.OptionText = fields[7].GetCppString();
go.BoxText = fields[8].GetCppString();
+
m_mCacheNpcOptionList.push_back(go);
+
++count;
+
} while (result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %d npc_option entries", count );
}
+
void ObjectMgr::AddVendorItem( uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 extendedcost, bool savetodb)
{
VendorItemData& vList = m_mCacheVendorItemMap[entry];
vList.AddItem(item,maxcount,incrtime,extendedcost);
+
if(savetodb) WorldDatabase.PExecuteLog("INSERT INTO npc_vendor (entry,item,maxcount,incrtime,extendedcost) VALUES('%u','%u','%u','%u','%u')",entry, item, maxcount,incrtime,extendedcost);
}
+
bool ObjectMgr::RemoveVendorItem( uint32 entry,uint32 item, bool savetodb)
{
CacheVendorItemMap::iterator iter = m_mCacheVendorItemMap.find(entry);
if(iter == m_mCacheVendorItemMap.end())
return false;
+
if(!iter->second.FindItem(item))
return false;
+
iter->second.RemoveItem(item);
if(savetodb) WorldDatabase.PExecuteLog("DELETE FROM npc_vendor WHERE entry='%u' AND item='%u'",entry, item);
return true;
}
+
bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, Player* pl, std::set<uint32>* skip_vendors, uint32 ORnpcflag ) const
{
CreatureInfo const* cInfo = GetCreatureTemplate(vendor_entry);
@@ -6865,6 +8257,7 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
sLog.outErrorDb("Table `(game_event_)npc_vendor` have data for not existed creature template (Entry: %u), ignore", vendor_entry);
return false;
}
+
if(!((cInfo->npcflag | ORnpcflag) & UNIT_NPC_FLAG_VENDOR))
{
if(!skip_vendors || skip_vendors->count(vendor_entry)==0)
@@ -6873,11 +8266,13 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
ChatHandler(pl).SendSysMessage(LANG_COMMAND_VENDORSELECTION);
else
sLog.outErrorDb("Table `(game_event_)npc_vendor` have data for not creature template (Entry: %u) without vendor flag, ignore", vendor_entry);
+
if(skip_vendors)
skip_vendors->insert(vendor_entry);
}
return false;
}
+
if(!GetItemPrototype(item_id))
{
if(pl)
@@ -6886,6 +8281,7 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
sLog.outErrorDb("Table `(game_event_)npc_vendor` for Vendor (Entry: %u) have in item list non-existed item (%u), ignore",vendor_entry,item_id);
return false;
}
+
if(ExtendedCost && !sItemExtendedCostStore.LookupEntry(ExtendedCost))
{
if(pl)
@@ -6894,6 +8290,7 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
sLog.outErrorDb("Table `(game_event_)npc_vendor` have Item (Entry: %u) with wrong ExtendedCost (%u) for vendor (%u), ignore",item_id,ExtendedCost,vendor_entry);
return false;
}
+
if(maxcount > 0 && incrtime == 0)
{
if(pl)
@@ -6910,9 +8307,11 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
sLog.outErrorDb( "Table `(game_event_)npc_vendor` has `maxcount`=0 for item %u of vendor (Entry: %u) but `incrtime`<>0, ignore", item_id, vendor_entry);
return false;
}
+
VendorItemData const* vItems = GetNpcVendorItemList(vendor_entry);
if(!vItems)
return true; // later checks for non-empty lists
+
if(vItems->FindItem(item_id))
{
if(pl)
@@ -6921,6 +8320,7 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
sLog.outErrorDb( "Table `(game_event_)npc_vendor` has duplicate items %u for vendor (Entry: %u), ignore", item_id, vendor_entry);
return false;
}
+
if(vItems->GetItemCount() >= MAX_VENDOR_ITEMS)
{
if(pl)
@@ -6929,8 +8329,10 @@ bool ObjectMgr::IsVendorItemValid( uint32 vendor_entry, uint32 item_id, uint32 m
sLog.outErrorDb( "Table `npc_vendor` has too many items (%u >= %i) for vendor (Entry: %u), ignore", vItems->GetItemCount(), MAX_VENDOR_ITEMS, vendor_entry);
return false;
}
+
return true;
}
+
void ObjectMgr::LoadScriptNames()
{
m_scriptNames.push_back("");
@@ -6944,6 +8346,7 @@ void ObjectMgr::LoadScriptNames()
"SELECT DISTINCT(ScriptName) FROM areatrigger_scripts WHERE ScriptName <> '' "
"UNION "
"SELECT DISTINCT(script) FROM instance_template WHERE script <> ''");
+
if( !result )
{
barGoLink bar( 1 );
@@ -6952,10 +8355,13 @@ void ObjectMgr::LoadScriptNames()
sLog.outErrorDb(">> Loaded empty set of Script Names!");
return;
}
+
barGoLink bar( result->GetRowCount() );
+
//OnEvent Changes
m_scriptNames.push_back("scripted_on_events");
uint32 count = 1;
+
do
{
bar.step();
@@ -6963,10 +8369,12 @@ void ObjectMgr::LoadScriptNames()
++count;
} while (result->NextRow());
delete result;
+
std::sort(m_scriptNames.begin(), m_scriptNames.end());
sLog.outString();
sLog.outString( ">> Loaded %d Script Names", count );
}
+
uint32 ObjectMgr::GetScriptId(const char *name)
{
// use binary search to find the script name in the sorted vector
@@ -6977,6 +8385,7 @@ uint32 ObjectMgr::GetScriptId(const char *name)
if(itr == m_scriptNames.end() || *itr != name) return 0;
return itr - m_scriptNames.begin();
}
+
void ObjectMgr::CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids)
{
for(ScriptMapMap::const_iterator itrMM = scripts.begin(); itrMM != scripts.end(); ++itrMM)
@@ -6989,6 +8398,7 @@ void ObjectMgr::CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids)
{
if(!GetTrinityStringLocale (itrM->second.dataint))
sLog.outErrorDb( "Table `db_script_string` not has string id %u used db script (ID: %u)", itrM->second.dataint, itrMM->first);
+
if(ids.count(itrM->second.dataint))
ids.erase(itrM->second.dataint);
}
@@ -6996,27 +8406,35 @@ void ObjectMgr::CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids)
}
}
}
+
void ObjectMgr::LoadDbScriptStrings()
{
LoadTrinityStrings(WorldDatabase,"db_script_string",MIN_DB_SCRIPT_STRING_ID,MAX_DB_SCRIPT_STRING_ID);
+
std::set<int32> ids;
+
for(int32 i = MIN_DB_SCRIPT_STRING_ID; i < MAX_DB_SCRIPT_STRING_ID; ++i)
if(GetTrinityStringLocale(i))
ids.insert(i);
+
CheckScripts(sQuestEndScripts,ids);
CheckScripts(sQuestStartScripts,ids);
CheckScripts(sSpellScripts,ids);
CheckScripts(sGameObjectScripts,ids);
CheckScripts(sEventScripts,ids);
+
CheckScripts(sWaypointScripts,ids);
+
for(std::set<int32>::const_iterator itr = ids.begin(); itr != ids.end(); ++itr)
sLog.outErrorDb( "Table `db_script_string` has unused string id %u", *itr);
}
+
// Functions for scripting access
uint32 GetAreaTriggerScriptId(uint32 trigger_id)
{
return objmgr.GetAreaTriggerScriptId(trigger_id);
}
+
bool LoadTrinityStrings(DatabaseType& db, char const* table,int32 start_value, int32 end_value)
{
// MAX_DB_SCRIPT_STRING_ID is max allowed negative value for scripts (scrpts can use only more deep negative values
@@ -7026,27 +8444,35 @@ bool LoadTrinityStrings(DatabaseType& db, char const* table,int32 start_value, i
sLog.outErrorDb("Table '%s' attempt loaded with reserved by mangos range (%d - %d), strings not loaded.",table,start_value,end_value+1);
return false;
}
+
return objmgr.LoadTrinityStrings(db,table,start_value,end_value);
}
+
uint32 TRINITY_DLL_SPEC GetScriptId(const char *name)
{
return objmgr.GetScriptId(name);
}
+
ObjectMgr::ScriptNameMap & GetScriptNames()
{
return objmgr.GetScriptNames();
}
+
GameObjectInfo const *GetGameObjectInfo(uint32 id)
{
return objmgr.GetGameObjectInfo(id);
}
+
CreatureInfo const *GetCreatureInfo(uint32 id)
{
return objmgr.GetCreatureTemplate(id);
}
+
void ObjectMgr::LoadTransportEvents()
{
+
QueryResult *result = WorldDatabase.Query("SELECT entry, waypoint_id, event_id FROM transport_events");
+
if( !result )
{
barGoLink bar1( 1 );
@@ -7054,43 +8480,57 @@ void ObjectMgr::LoadTransportEvents()
sLog.outString( "\n>> Transport events table is empty \n" );
return;
}
+
barGoLink bar1( result->GetRowCount() );
+
do
{
bar1.step();
+
Field *fields = result->Fetch();
+
//Load event values
uint32 entry = fields[0].GetUInt32();
uint32 waypoint_id = fields[1].GetUInt32();
uint32 event_id = fields[2].GetUInt32();
+
uint32 event_count = (entry*100)+waypoint_id;
TransportEventMap[event_count] = event_id;
}
while(result->NextRow());
+
sLog.outString( "\n>> Loaded %u transport events \n", result->GetRowCount() );
+
delete result;
}
+
CreatureInfo const* GetCreatureTemplateStore(uint32 entry)
{
return sCreatureStorage.LookupEntry<CreatureInfo>(entry);
}
+
Quest const* GetQuestTemplateStore(uint32 entry)
{
return objmgr.GetQuestTemplate(entry);
}
+
uint64 ObjectMgr::GenerateGMTicketId()
{
return ++m_GMticketid;
}
+
void ObjectMgr::LoadGMTickets()
{
m_GMTicketList.clear();
+
QueryResult *result = CharacterDatabase.Query( "SELECT guid, playerGuid, name, message, createtime, map, posX, posY, posZ, timestamp, closed, assignedto, comment FROM gm_tickets" );
+
if(!result)
{
sLog.outString(" \n>> GM Tickets table is empty, no tickets were loaded.\n" );
return;
}
+
uint16 count = 0;
barGoLink bar ((*result).GetRowCount());
GM_Ticket *ticket;
@@ -7113,22 +8553,29 @@ void ObjectMgr::LoadGMTickets()
ticket->comment = fields[12].GetCppString();
++count;
bar.step();
+
m_GMTicketList.push_back(ticket);
+
} while( result->NextRow() );
+
result = CharacterDatabase.PQuery("SELECT MAX(guid) from gm_tickets");
- m_GMticketid = (*result)[0].GetUInt64();
+ m_GMticketid = (*result)[0].GetUInt64();
+
sLog.outString(">>> %u GM Tickets loaded from the database.", count);
delete result;
}
+
void ObjectMgr::AddOrUpdateGMTicket(GM_Ticket &ticket, bool create)
{
if(create)
m_GMTicketList.push_back(&ticket);
- _AddOrUpdateGMTicket(ticket);
+
+ _AddOrUpdateGMTicket(ticket);
}
+
void ObjectMgr::_AddOrUpdateGMTicket(GM_Ticket &ticket)
{
- std::string msg(ticket.message), name(ticket.name), comment(ticket.comment);
+ std::string msg(ticket.message), name(ticket.name), comment(ticket.comment);
CharacterDatabase.escape_string(msg);
CharacterDatabase.escape_string(name);
CharacterDatabase.escape_string(comment);
@@ -7151,6 +8598,7 @@ void ObjectMgr::_AddOrUpdateGMTicket(GM_Ticket &ticket)
CharacterDatabase.Execute(ss.str().c_str());
CharacterDatabase.CommitTransaction();
}
+
void ObjectMgr::RemoveGMTicket(GM_Ticket *ticket, int64 source, bool permanently)
{
for(GmTicketList::iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i)
@@ -7167,28 +8615,35 @@ void ObjectMgr::RemoveGMTicket(GM_Ticket *ticket, int64 source, bool permanently
_AddOrUpdateGMTicket(*(*i));
}
}
+
void ObjectMgr::RemoveGMTicket(uint64 ticketGuid, int64 source, bool permanently)
{
GM_Ticket *ticket = GetGMTicket(ticketGuid);
assert( ticket );
RemoveGMTicket(ticket, source, permanently);
}
+
bool ObjectMgr::CheckDB() const
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(28511);
if(!cInfo || cInfo->spells[4] != 51890)
return false;
+
cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(30068);
if(!cInfo || cInfo->faction_A != 21)
return false;
+
cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(28768); // Dark Rider of Acherus
if(!cInfo || !cInfo->ScriptID)
return false;
+
cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(32627); // Wintergrasp Siege Engine
if(!cInfo || !cInfo->spells[0] || cInfo->VehicleId != 117)
return false;
+
cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(33114); // Flame Leviathan Seat
if(!cInfo || cInfo->VehicleId != 341)
return false;
+
return true;
}
diff --git a/src/game/ObjectMgr.h b/src/game/ObjectMgr.h
index 716f4390134..b0e1bce8367 100644
--- a/src/game/ObjectMgr.h
+++ b/src/game/ObjectMgr.h
@@ -17,8 +17,10 @@
* 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"
@@ -38,9 +40,11 @@
#include "ObjectDefines.h"
#include "Policies/Singleton.h"
#include "Database/SQLStorage.h"
+
#include <string>
#include <map>
#include <limits>
+
extern SQLStorage sCreatureStorage;
extern SQLStorage sCreatureDataAddonStorage;
extern SQLStorage sCreatureInfoAddonStorage;
@@ -50,12 +54,14 @@ extern SQLStorage sGOStorage;
extern SQLStorage sPageTextStore;
extern SQLStorage sItemStorage;
extern SQLStorage sInstanceTemplate;
+
class Group;
class Guild;
class ArenaTeam;
class Path;
class TransportPath;
class Item;
+
struct GameTele
{
float position_x;
@@ -66,8 +72,10 @@ struct GameTele
std::string name;
std::wstring wnameLow;
};
+
typedef UNORDERED_MAP<uint32, GameTele > GameTeleMap;
typedef std::list<GossipOption> CacheNpcOptionList;
+
struct ScriptInfo
{
uint32 id;
@@ -81,6 +89,7 @@ struct ScriptInfo
float z;
float o;
};
+
typedef std::multimap<uint32, ScriptInfo> ScriptMap;
typedef std::map<uint32, ScriptMap > ScriptMapMap;
extern ScriptMapMap sQuestEndScripts;
@@ -89,6 +98,7 @@ extern ScriptMapMap sSpellScripts;
extern ScriptMapMap sGameObjectScripts;
extern ScriptMapMap sEventScripts;
extern ScriptMapMap sWaypointScripts;
+
struct SpellClickInfo
{
uint32 spellId;
@@ -99,11 +109,14 @@ struct SpellClickInfo
uint32 auraRequired;
uint32 auraForbidden;
SpellClickUserTypes userType;
+
// helpers
bool IsFitToRequirements(Player const* player, Creature const * clickNpc) const;
};
+
typedef std::multimap<uint32, SpellClickInfo> SpellClickInfoMap;
typedef std::pair<SpellClickInfoMap::const_iterator,SpellClickInfoMap::const_iterator> SpellClickInfoMapBounds;
+
struct AreaTrigger
{
uint32 access_id;
@@ -113,6 +126,7 @@ struct AreaTrigger
float target_Z;
float target_Orientation;
};
+
typedef std::set<uint32> CellGuidSet;
typedef std::map<uint32/*player guid*/,uint32/*instance*/> CellCorpseSet;
struct CellObjectGuids
@@ -123,8 +137,10 @@ struct CellObjectGuids
};
typedef UNORDERED_MAP<uint32/*cell_id*/,CellObjectGuids> CellObjectGuidsMap;
typedef UNORDERED_MAP<uint32/*(mapid,spawnMode) pair*/,CellObjectGuidsMap> MapObjectGuids;
+
typedef UNORDERED_MAP<uint64/*(instance,guid) pair*/,time_t> RespawnTimes;
+
// mangos string ranges
#define MIN_TRINITY_STRING_ID 1 // 'mangos_string'
#define MAX_TRINITY_STRING_ID 2000000000
@@ -132,10 +148,12 @@ typedef UNORDERED_MAP<uint64/*(instance,guid) pair*/,time_t> RespawnTimes;
#define MAX_DB_SCRIPT_STRING_ID 2000010000
#define MIN_CREATURE_AI_TEXT_STRING_ID (-1) // 'creature_ai_texts'
#define MAX_CREATURE_AI_TEXT_STRING_ID (-1000000)
+
struct TrinityStringLocale
{
std::vector<std::string> Content; // 0 -> default, i -> i-1 locale index
};
+
typedef std::map<uint32,uint32> CreatureLinkedRespawnMap;
typedef UNORDERED_MAP<uint32,CreatureData> CreatureDataMap;
typedef UNORDERED_MAP<uint32,GameObjectData> GameObjectDataMap;
@@ -148,17 +166,21 @@ typedef UNORDERED_MAP<uint32,PageTextLocale> PageTextLocaleMap;
typedef UNORDERED_MAP<int32,TrinityStringLocale> TrinityStringLocaleMap;
typedef UNORDERED_MAP<uint32,NpcOptionLocale> NpcOptionLocaleMap;
typedef UNORDERED_MAP<uint32,PointOfInterestLocale> PointOfInterestLocaleMap;
+
typedef std::multimap<uint32,uint32> QuestRelations;
typedef std::multimap<uint32,ItemRequiredTarget> ItemRequiredTargetMap;
typedef std::pair<ItemRequiredTargetMap::const_iterator, ItemRequiredTargetMap::const_iterator> ItemRequiredTargetMapBounds;
+
struct PetLevelInfo
{
PetLevelInfo() : health(0), mana(0) { for(uint8 i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
+
uint16 stats[MAX_STATS];
uint16 health;
uint16 mana;
uint16 armor;
};
+
struct ReputationOnKillEntry
{
uint32 repfaction1;
@@ -171,6 +193,7 @@ struct ReputationOnKillEntry
int32 repvalue2;
bool team_dependent;
};
+
struct PointOfInterest
{
uint32 entry;
@@ -181,6 +204,7 @@ struct PointOfInterest
uint32 data;
std::string icon_name;
};
+
#define WEATHER_SEASONS 4
struct WeatherSeasonChances
{
@@ -188,16 +212,19 @@ struct WeatherSeasonChances
uint32 snowChance;
uint32 stormChance;
};
+
struct WeatherZoneChances
{
WeatherSeasonChances data[WEATHER_SEASONS];
};
+
struct GraveYardData
{
uint32 safeLocId;
uint32 team;
};
typedef std::multimap<uint32,GraveYardData> GraveYardMap;
+
enum ConditionType
{ // value1 value2 for the Condition enumed
CONDITION_NONE = 0, // 0 0
@@ -215,7 +242,9 @@ enum ConditionType
CONDITION_ACTIVE_EVENT = 12, // event_id
CONDITION_INSTANCE_DATA = 13, // entry data
};
+
#define MAX_CONDITION 14 // maximum value in ConditionType enum
+
//Player's info
typedef struct _tagCachePlayerInfo
{
@@ -232,13 +261,16 @@ typedef struct _tagCachePlayerInfo
uint32 unArenaInfoSlot2;
}CachePlayerInfo, *PCachePlayerInfo;
typedef UNORDERED_MAP<uint32, PCachePlayerInfo> CachePlayerInfoMap;
+
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
@@ -247,11 +279,14 @@ struct PlayerCondition
return (lc.condition == condition && lc.value1 == value1 && lc.value2 == value2);
}
};
+
// NPC gossip text id
typedef UNORDERED_MAP<uint32, uint32> CacheNpcTextIdMap;
typedef std::list<GossipOption> CacheNpcOptionList;
+
typedef UNORDERED_MAP<uint32, VendorItemData> CacheVendorItemMap;
typedef UNORDERED_MAP<uint32, TrainerSpellData> CacheTrainerSpellMap;
+
enum SkillRangeType
{
SKILL_RANGE_LANGUAGE, // 300..300
@@ -260,6 +295,7 @@ enum SkillRangeType
SKILL_RANGE_RANK, // 1..skill for known rank
SKILL_RANGE_NONE, // 0..0 always
};
+
struct GM_Ticket
{
uint64 guid;
@@ -278,53 +314,78 @@ struct GM_Ticket
};
typedef std::list<GM_Ticket*> GmTicketList;
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 )
#define MAX_PET_NAME 12 // max allowed by client name length
#define MAX_CHARTER_NAME 24 // max allowed by client name length
+
bool normalizePlayerName(std::string& name);
+
struct TRINITY_DLL_SPEC LanguageDesc
{
Language lang_id;
uint32 spell_id;
uint32 skill_id;
};
+
extern LanguageDesc lang_description[LANGUAGES_COUNT];
TRINITY_DLL_SPEC LanguageDesc const* GetLanguageDescByID(uint32 lang);
+
class PlayerDumpReader;
+
class ObjectMgr
{
friend class PlayerDumpReader;
+
public:
ObjectMgr();
~ObjectMgr();
+
typedef UNORDERED_MAP<uint32, Item*> ItemMap;
+
typedef std::set< Group * > GroupSet;
+
typedef UNORDERED_MAP<uint32, Guild *> GuildMap;
+
typedef UNORDERED_MAP<uint32, ArenaTeam*> ArenaTeamMap;
+
typedef UNORDERED_MAP<uint32, Quest*> QuestMap;
+
typedef UNORDERED_MAP<uint32, AreaTrigger> AreaTriggerMap;
+
typedef UNORDERED_MAP<uint32, uint32> AreaTriggerScriptMap;
+
typedef UNORDERED_MAP<uint32, AccessRequirement> AccessRequirementMap;
+
typedef UNORDERED_MAP<uint32, ReputationOnKillEntry> RepOnKillMap;
typedef UNORDERED_MAP<uint32, PointOfInterest> PointOfInterestMap;
+
typedef UNORDERED_MAP<uint32, WeatherZoneChances> WeatherZoneMap;
+
typedef std::vector<std::string> ScriptNameMap;
+
UNORDERED_MAP<uint32, uint32> TransportEventMap;
+
Player* GetPlayer(const char* name) const { return ObjectAccessor::Instance().FindPlayerByName(name);}
Player* GetPlayer(uint64 guid) const { return ObjectAccessor::FindPlayer(guid); }
+
static GameObjectInfo const *GetGameObjectInfo(uint32 id) { return sGOStorage.LookupEntry<GameObjectInfo>(id); }
+
void LoadGameobjectInfo();
void AddGameobjectInfo(GameObjectInfo *goinfo);
+
Group * GetGroupByLeader(const uint64 &guid) const;
void AddGroup(Group* group) { mGroupSet.insert( group ); }
void RemoveGroup(Group* group) { mGroupSet.erase( group ); }
+
Guild* GetGuildByLeader(uint64 const&guid) const;
Guild* GetGuildById(uint32 GuildId) const;
Guild* GetGuildByName(const std::string& guildname) const;
std::string GetGuildNameById(uint32 GuildId) const;
void AddGuild(Guild* guild);
void RemoveGuild(uint32 Id);
+
ArenaTeam* GetArenaTeamById(uint32 arenateamid) const;
ArenaTeam* GetArenaTeamByName(const std::string& arenateamname) const;
ArenaTeam* GetArenaTeamByCaptain(uint64 const& guid) const;
@@ -332,6 +393,7 @@ class ObjectMgr
void RemoveArenaTeam(uint32 Id);
ArenaTeamMap::iterator GetArenaTeamMapBegin() { return mArenaTeamMap.begin(); }
ArenaTeamMap::iterator GetArenaTeamMapEnd() { return mArenaTeamMap.end(); }
+
static CreatureInfo const *GetCreatureTemplate( uint32 id );
CreatureModelInfo const *GetCreatureModelInfo( uint32 modelid );
CreatureModelInfo const* GetCreatureModelRandomGender(uint32 display_id);
@@ -341,22 +403,28 @@ class ObjectMgr
{
return sCreatureDataAddonStorage.LookupEntry<CreatureDataAddon>(lowguid);
}
+
static CreatureDataAddon const *GetCreatureTemplateAddon( uint32 entry )
{
return sCreatureInfoAddonStorage.LookupEntry<CreatureDataAddon>(entry);
}
+
static ItemPrototype const* GetItemPrototype(uint32 id) { return sItemStorage.LookupEntry<ItemPrototype>(id); }
+
static InstanceTemplate const* GetInstanceTemplate(uint32 map)
{
return sInstanceTemplate.LookupEntry<InstanceTemplate>(map);
}
+
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;
@@ -366,22 +434,26 @@ class ObjectMgr
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 GetPlayerAccountIdByPlayerName(const std::string& name) const;
+
uint32 GetNearestTaxiNode( float x, float y, float z, uint32 mapid, uint32 team );
void GetTaxiPath( uint32 source, uint32 destination, uint32 &path, uint32 &cost);
uint32 GetTaxiMountDisplayId( uint32 id, uint32 team, bool allowed_alt_team = false);
void GetTaxiPathNodes( uint32 path, Path &pathnodes, std::vector<uint32>& mapIds );
void GetTransportPathNodes( uint32 path, TransportPath &pathnodes );
+
Quest const* GetQuestTemplate(uint32 quest_id) const
{
QuestMap::const_iterator itr = mQuestTemplates.find(quest_id);
return itr != mQuestTemplates.end() ? itr->second : NULL;
}
QuestMap const& GetQuestTemplates() const { return mQuestTemplates; }
+
uint32 GetQuestForAreaTrigger(uint32 Trigger_ID) const
{
QuestAreaTriggerMap::const_iterator itr = mQuestAreaTriggerMap.find(Trigger_ID);
@@ -393,16 +465,20 @@ class ObjectMgr
{
return mTavernAreaTriggerSet.find(Trigger_ID) != mTavernAreaTriggerSet.end();
}
+
bool IsGameObjectForQuests(uint32 entry) const
{
return mGameObjectForQuestSet.find(entry) != mGameObjectForQuestSet.end();
}
+
GossipText const* GetGossipText(uint32 Text_ID) const;
+
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 RemoveGraveYardLink(uint32 id, uint32 zone, uint32 team, bool inDB = false);
void LoadGraveyardZones();
GraveYardData const* FindGraveYardData(uint32 id, uint32 zone);
+
AreaTrigger const* GetAreaTrigger(uint32 trigger) const
{
AreaTriggerMap::const_iterator itr = mAreaTriggers.find( trigger );
@@ -410,6 +486,7 @@ class ObjectMgr
return &itr->second;
return NULL;
}
+
AccessRequirement const* GetAccessRequirement(uint32 requirement) const
{
AccessRequirementMap::const_iterator itr = mAccessRequirements.find( requirement );
@@ -417,9 +494,12 @@ class ObjectMgr
return &itr->second;
return NULL;
}
+
AreaTrigger const* GetGoBackTrigger(uint32 Map) const;
AreaTrigger const* GetMapEntranceTrigger(uint32 Map) const;
+
uint32 GetAreaTriggerScriptId(uint32 trigger_id);
+
ReputationOnKillEntry const* GetReputationOnKilEntry(uint32 id) const
{
RepOnKillMap::const_iterator itr = mRepOnKill.find(id);
@@ -427,6 +507,7 @@ class ObjectMgr
return &itr->second;
return NULL;
}
+
PointOfInterest const* GetPointOfInterest(uint32 id) const
{
PointOfInterestMap::const_iterator itr = mPointsOfInterest.find(id);
@@ -434,6 +515,7 @@ class ObjectMgr
return &itr->second;
return NULL;
}
+
void LoadGuilds();
void LoadArenaTeams();
void LoadGroups();
@@ -449,17 +531,21 @@ class ObjectMgr
void LoadGameobjectInvolvedRelations();
void LoadCreatureQuestRelations();
void LoadCreatureInvolvedRelations();
+
QuestRelations mGOQuestRelations;
QuestRelations mGOQuestInvolvedRelations;
QuestRelations mCreatureQuestRelations;
QuestRelations mCreatureQuestInvolvedRelations;
+
void LoadGameObjectScripts();
void LoadQuestEndScripts();
void LoadQuestStartScripts();
void LoadEventScripts();
void LoadSpellScripts();
void LoadWaypointScripts();
+
void LoadTransportEvents();
+
bool LoadTrinityStrings(DatabaseType& db, char const* table, int32 min_value, int32 max_value);
bool LoadTrinityStrings() { return LoadTrinityStrings(WorldDatabase,"trinity_string",MIN_TRINITY_STRING_ID,MAX_TRINITY_STRING_ID); }
void LoadDbScriptStrings();
@@ -485,15 +571,19 @@ class ObjectMgr
void LoadNpcOptionLocales();
void LoadPointOfInterestLocales();
void LoadInstanceTemplate();
+
void LoadGossipText();
+
void LoadAreaTriggerTeleports();
void LoadAccessRequirements();
void LoadQuestAreaTriggers();
void LoadAreaTriggerScripts();
void LoadTavernAreaTriggers();
void LoadGameObjectForQuests();
+
void LoadItemTexts();
void LoadPageTexts();
+
void LoadPlayerInfo();
void LoadPetLevelInfo();
void LoadExplorationBaseXP();
@@ -501,25 +591,33 @@ class ObjectMgr
void LoadPetNumber();
void LoadCorpses();
void LoadFishingBaseSkillLevel();
+
void LoadReputationOnKill();
void LoadPointsOfInterest();
+
void LoadNPCSpellClickSpells();
+
void LoadWeatherZoneChances();
void LoadGameTele();
+
void LoadNpcOptions();
void LoadNpcTextId();
void LoadVendors();
void LoadTrainerSpell();
void LoadGMTickets();
+
std::string GeneratePetName(uint32 entry);
uint32 GetBaseXP(uint32 level);
uint32 GetXPForLevel(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 GenerateArenaTeamId();
@@ -529,9 +627,11 @@ class ObjectMgr
uint32 GenerateItemTextID();
uint32 GenerateMailID();
uint32 GeneratePetNumber();
+
void LoadPlayerInfoInCache();
PCachePlayerInfo GetPlayerInfoFromCache(uint32 unPlayerGuid) const;
CachePlayerInfoMap m_mPlayerInfoMap;
+
uint32 CreateItemText(std::string text);
void AddItemText(uint32 itemTextId, std::string text) { mItemTexts[itemTextId] = text; }
std::string GetItemText( uint32 id )
@@ -542,8 +642,10 @@ class ObjectMgr
else
return "There is no info for this item";
}
+
typedef std::multimap<int32, uint32> ExclusiveQuestGroups;
ExclusiveQuestGroups mExclusiveQuestGroups;
+
WeatherZoneChances const* GetWeatherChances(uint32 zone_id) const
{
WeatherZoneMap::const_iterator itr = mWeatherZoneMap.find(zone_id);
@@ -552,10 +654,12 @@ class ObjectMgr
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);
@@ -618,6 +722,7 @@ class ObjectMgr
if(itr==mPointOfInterestLocaleMap.end()) return NULL;
return &itr->second;
}
+
GameObjectData const* GetGOData(uint32 guid) const
{
GameObjectDataMap::const_iterator itr = mGameObjectDataMap.find(guid);
@@ -626,6 +731,7 @@ class ObjectMgr
}
GameObjectData& NewGOData(uint32 guid) { return mGameObjectDataMap[guid]; }
void DeleteGOData(uint32 guid);
+
TrinityStringLocale const* GetTrinityStringLocale(int32 entry) const
{
TrinityStringLocaleMap::const_iterator itr = mTrinityStringLocaleMap.find(entry);
@@ -636,13 +742,16 @@ class ObjectMgr
const char *GetTrinityStringForDBCLocale(int32 entry) const { return GetTrinityString(entry,DBCLocaleIndex); }
int32 GetDBCLocaleIndex() const { return 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);
@@ -650,27 +759,35 @@ class ObjectMgr
void RemoveGameobjectFromGrid(uint32 guid, GameObjectData const* data);
uint32 AddGOData(uint32 entry, uint32 map, float x, float y, float z, float o, uint32 spawntimedelay = 0, float rotation0 = 0, float rotation1 = 0, float rotation2 = 0, float rotation3 = 0);
uint32 AddCreData(uint32 entry, uint32 team, uint32 map, float x, float y, float z, float o, uint32 spawntimedelay = 0);
+
// reserved names
void LoadReservedPlayersNames();
bool IsReservedName(const std::string& name) const;
+
// name with valid structure and symbols
static uint8 CheckPlayerName( const std::string& name, bool create = false );
static PetNameInvalidReason CheckPetName( const std::string& name );
static bool IsValidCharterName( const std::string& name );
+
static bool CheckDeclinedNames(std::wstring mainpart, DeclinedName const& names);
+
void LoadSpellDisabledEntrys();
bool IsPlayerSpellDisabled(uint32 spellid) { return (m_DisabledPlayerSpells.count(spellid) != 0); }
bool IsCreatureSpellDisabled(uint32 spellid) { return (m_DisabledCreatureSpells.count(spellid) != 0); }
bool IsPetSpellDisabled(uint32 spellid) { return (m_DisabledPetSpells.count(spellid) != 0); }
+
int GetIndexForLocale(LocaleConstant loc);
LocaleConstant GetLocaleForIndex(int i);
+
uint16 GetConditionId(ConditionType condition, uint32 value1, uint32 value2);
bool IsPlayerMeetToCondition(Player const* player, uint16 condition_id) const
{
if(condition_id >= mConditions.size())
return false;
+
return mConditions[condition_id].Meets(player);
}
+
GameTele const* GetGameTele(uint32 id) const
{
GameTeleMap::const_iterator itr = m_GameTeleMap.find(id);
@@ -681,49 +798,62 @@ class ObjectMgr
GameTeleMap const& GetGameTeleMap() const { return m_GameTeleMap; }
bool AddGameTele(GameTele& data);
bool DeleteGameTele(const std::string& name);
+
CacheNpcOptionList const& GetNpcOptions() const { return m_mCacheNpcOptionList; }
+
uint32 GetNpcGossip(uint32 entry) const
{
CacheNpcTextIdMap::const_iterator iter = m_mCacheNpcTextIdMap.find(entry);
if(iter == m_mCacheNpcTextIdMap.end())
return 0;
+
return iter->second;
}
+
TrainerSpellData const* GetNpcTrainerSpells(uint32 entry) const
{
CacheTrainerSpellMap::const_iterator iter = m_mCacheTrainerSpellMap.find(entry);
if(iter == m_mCacheTrainerSpellMap.end())
return NULL;
+
return &iter->second;
}
+
VendorItemData const* GetNpcVendorItemList(uint32 entry) const
{
CacheVendorItemMap::const_iterator iter = m_mCacheVendorItemMap.find(entry);
if(iter == m_mCacheVendorItemMap.end())
return NULL;
+
return &iter->second;
}
void AddVendorItem(uint32 entry,uint32 item, uint32 maxcount, uint32 incrtime, uint32 ExtendedCost, bool savetodb = true); // for event
bool RemoveVendorItem(uint32 entry,uint32 item, bool savetodb = true); // for event
bool IsVendorItemValid( uint32 vendor_entry, uint32 item, uint32 maxcount, uint32 ptime, uint32 ExtendedCost, Player* pl = NULL, std::set<uint32>* skip_vendors = NULL, uint32 ORnpcflag = 0 ) const;
+
void LoadScriptNames();
ScriptNameMap &GetScriptNames() { return m_scriptNames; }
const char * GetScriptName(uint32 id) { return id < m_scriptNames.size() ? m_scriptNames[id].c_str() : ""; }
uint32 GetScriptId(const char *name);
+
int GetOrNewIndexForLocale(LocaleConstant loc);
+
SpellClickInfoMapBounds GetSpellClickInfoMapBounds(uint32 creature_id) const
{
return SpellClickInfoMapBounds(mSpellClickInfoMap.lower_bound(creature_id),mSpellClickInfoMap.upper_bound(creature_id));
}
+
ItemRequiredTargetMapBounds GetItemRequiredTargetMapBounds(uint32 uiItemEntry) const
{
return ItemRequiredTargetMapBounds(m_ItemRequiredTarget.lower_bound(uiItemEntry),m_ItemRequiredTarget.upper_bound(uiItemEntry));
}
+
GM_Ticket *GetGMTicket(uint64 ticketGuid)
{
for(GmTicketList::const_iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i)
if((*i) && (*i)->guid == ticketGuid)
return (*i);
+
return NULL;
}
GM_Ticket *GetGMTicketByPlayer(uint64 playerGuid)
@@ -731,18 +861,23 @@ class ObjectMgr
for(GmTicketList::const_iterator i = m_GMTicketList.begin(); i != m_GMTicketList.end(); ++i)
if((*i) && (*i)->playerGuid == playerGuid && (*i)->closed == 0)
return (*i);
- return NULL;
+
+ return NULL;
}
+
void AddOrUpdateGMTicket(GM_Ticket &ticket, bool create = false);
void _AddOrUpdateGMTicket(GM_Ticket &ticket);
void RemoveGMTicket(uint64 ticketGuid, int64 source = -1, bool permanently = false);
void RemoveGMTicket(GM_Ticket *ticket, int64 source = -1, bool permanently = false);
GmTicketList m_GMTicketList;
uint64 GenerateGMTicketId();
+
bool CheckDB() const;
+
// for wintergrasp only
GraveYardMap mGraveYardMap;
protected:
+
// first free id for selected id type
uint32 m_arenaTeamId;
uint32 m_auctionid;
@@ -752,6 +887,7 @@ class ObjectMgr
uint32 m_mailid;
uint32 m_hiPetNumber;
uint64 m_GMticketid;
+
// first free low guid for seelcted guid type
uint32 m_hiCharGuid;
uint32 m_hiCreatureGuid;
@@ -761,16 +897,21 @@ class ObjectMgr
uint32 m_hiGoGuid;
uint32 m_hiDoGuid;
uint32 m_hiCorpseGuid;
+
QuestMap mQuestTemplates;
+
typedef UNORDERED_MAP<uint32, GossipText> GossipTextMap;
typedef UNORDERED_MAP<uint32, uint32> QuestAreaTriggerMap;
typedef UNORDERED_MAP<uint32, std::string> ItemTextMap;
typedef std::set<uint32> TavernAreaTriggerSet;
typedef std::set<uint32> GameObjectForQuestSet;
+
GroupSet mGroupSet;
GuildMap mGuildMap;
ArenaTeamMap mArenaTeamMap;
+
ItemTextMap mItemTexts;
+
QuestAreaTriggerMap mQuestAreaTriggerMap;
TavernAreaTriggerSet mTavernAreaTriggerSet;
GameObjectForQuestSet mGameObjectForQuestSet;
@@ -778,44 +919,65 @@ class ObjectMgr
AreaTriggerMap mAreaTriggers;
AreaTriggerScriptMap mAreaTriggerScripts;
AccessRequirementMap mAccessRequirements;
+
RepOnKillMap mRepOnKill;
+
PointOfInterestMap mPointsOfInterest;
+
WeatherZoneMap mWeatherZoneMap;
+
//character reserved names
typedef std::set<std::wstring> ReservedNamesMap;
ReservedNamesMap m_ReservedNames;
+
std::set<uint32> m_DisabledPlayerSpells;
std::set<uint32> m_DisabledCreatureSpells;
std::set<uint32> m_DisabledPetSpells;
+
// GraveYardMap mGraveYardMap;
+
GameTeleMap m_GameTeleMap;
+
ScriptNameMap m_scriptNames;
+
SpellClickInfoMap mSpellClickInfoMap;
+
ItemRequiredTargetMap m_ItemRequiredTarget;
+
typedef std::vector<LocaleConstant> LocalForIndex;
LocalForIndex m_LocalForIndex;
+
int DBCLocaleIndex;
+
private:
void LoadScripts(ScriptMapMap& scripts, char const* tablename);
void CheckScripts(ScriptMapMap const& scripts,std::set<int32>& ids);
void LoadCreatureAddons(SQLStorage& creatureaddons, char const* entryName, char const* comment);
void ConvertCreatureAddonAuras(CreatureDataAddon* addon, char const* table, char const* guidEntryStr);
void LoadQuestRelationsHelper(QuestRelations& map,char const* table);
+
typedef std::map<uint32,PetLevelInfo*> PetLevelInfoMap;
// PetLevelInfoMap[creature_id][level]
PetLevelInfoMap petInfo; // [creature_id][level]
+
PlayerClassInfo playerClassInfo[MAX_CLASSES];
+
void BuildPlayerLevelInfo(uint8 race, uint8 class_, uint8 level, PlayerLevelInfo* plinfo) const;
PlayerInfo playerInfo[MAX_RACES][MAX_CLASSES];
+
typedef std::vector<uint32> PlayerXPperLevel; // [level]
PlayerXPperLevel mPlayerXPperLevel;
+
typedef std::map<uint32,uint32> BaseXPMap; // [area level][base xp]
BaseXPMap mBaseXPTable;
+
typedef std::map<uint32,int32> FishingBaseSkillMap; // [areaId][base skill level]
FishingBaseSkillMap mFishingBaseForArea;
+
typedef std::map<uint32,std::vector<std::string> > HalfNameMap;
HalfNameMap PetHalfName0;
HalfNameMap PetHalfName1;
+
MapObjectGuids mMapObjectGuids;
CreatureDataMap mCreatureDataMap;
CreatureLinkedRespawnMap mCreatureLinkedRespawnMap;
@@ -831,15 +993,19 @@ class ObjectMgr
PointOfInterestLocaleMap mPointOfInterestLocaleMap;
RespawnTimes mCreatureRespawnTimes;
RespawnTimes mGORespawnTimes;
+
// Storage for Conditions. First element (index 0) is reserved for zero-condition (nothing required)
typedef std::vector<PlayerCondition> ConditionStore;
ConditionStore mConditions;
+
CacheNpcOptionList m_mCacheNpcOptionList;
CacheNpcTextIdMap m_mCacheNpcTextIdMap;
CacheVendorItemMap m_mCacheVendorItemMap;
CacheTrainerSpellMap m_mCacheTrainerSpellMap;
};
+
#define objmgr Trinity::Singleton<ObjectMgr>::Instance()
+
// scripting access functions
TRINITY_DLL_SPEC bool LoadTrinityStrings(DatabaseType& db, char const* table,int32 start_value = MAX_CREATURE_AI_TEXT_STRING_ID, int32 end_value = std::numeric_limits<int32>::min());
TRINITY_DLL_SPEC uint32 GetAreaTriggerScriptId(uint32 trigger_id);
@@ -849,4 +1015,5 @@ TRINITY_DLL_SPEC GameObjectInfo const *GetGameObjectInfo(uint32 id);
TRINITY_DLL_SPEC CreatureInfo const *GetCreatureInfo(uint32 id);
TRINITY_DLL_SPEC CreatureInfo const* GetCreatureTemplateStore(uint32 entry);
TRINITY_DLL_SPEC Quest const* GetQuestTemplateStore(uint32 entry);
+
#endif
diff --git a/src/game/Opcodes.cpp b/src/game/Opcodes.cpp
index d4547f4ff36..ba1deaeff7c 100644
--- a/src/game/Opcodes.cpp
+++ b/src/game/Opcodes.cpp
@@ -17,11 +17,14 @@
* 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] =
{
diff --git a/src/game/Opcodes.h b/src/game/Opcodes.h
index e805eea3752..c3371f866b6 100644
--- a/src/game/Opcodes.h
+++ b/src/game/Opcodes.h
@@ -17,17 +17,22 @@
* 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"
+
// Note: this include need for be sure have full definition of class WorldSession
// if this class definition not complite then VS for x64 release use different size for
// struct OpcodeHandler in this header and Opcode.cpp and get totally wrong data from
// table opcodeTable in source when Opcode.h included but WorldSession.h not included
#include "WorldSession.h"
+
/// List of Opcodes
enum Opcodes
{
@@ -1262,6 +1267,7 @@ enum Opcodes
SMSG_EQUIPMENT_SET_USE_RESULT = 0x4CC, // SMSG, UseEquipmentSetResult?
NUM_MSG_TYPES = 0x4CD
};
+
/// Player state
enum SessionStatus
{
@@ -1271,14 +1277,18 @@ enum SessionStatus
STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT, ///< _player!= NULL or _player==NULL && m_playerRecentlyLogout, m_GUID store last _player guid)
STATUS_NEVER ///< Opcode not accepted from client (deprecated or server side only)
};
+
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)
{
diff --git a/src/game/OutdoorPvP.cpp b/src/game/OutdoorPvP.cpp
index 6bb0c94336c..75b794c0452 100644
--- a/src/game/OutdoorPvP.cpp
+++ b/src/game/OutdoorPvP.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvP.h"
#include "OutdoorPvPImpl.h"
#include "OutdoorPvPMgr.h"
@@ -29,12 +30,14 @@
#include "GridNotifiers.h"
#include "GridNotifiersImpl.h"
#include "CellImpl.h"
+
OPvPCapturePoint::OPvPCapturePoint(OutdoorPvP * pvp)
: m_PvP(pvp), m_value(0), m_maxValue(0), m_team(TEAM_NEUTRAL),
m_State(OBJECTIVESTATE_NEUTRAL), m_OldState(OBJECTIVESTATE_NEUTRAL), m_capturePointGUID(0), m_neutralValuePct(0),
m_maxSpeed(0), m_capturePoint(NULL)
{
}
+
bool OPvPCapturePoint::HandlePlayerEnter(Player * plr)
{
if(m_capturePoint)
@@ -45,16 +48,19 @@ bool OPvPCapturePoint::HandlePlayerEnter(Player * plr)
}
return m_activePlayers[plr->GetTeamId()].insert(plr).second;
}
+
void OPvPCapturePoint::HandlePlayerLeave(Player * plr)
{
if(m_capturePoint)
plr->SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldState1, 0);
m_activePlayers[plr->GetTeamId()].erase(plr);
}
+
void OPvPCapturePoint::SendChangePhase()
{
if(!m_capturePoint)
return;
+
// send this too, sometimes the slider disappears, dunno why :(
SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldState1, 1);
// send these updates to only the ones in this objective
@@ -62,6 +68,7 @@ void OPvPCapturePoint::SendChangePhase()
// send this too, sometimes it resets :S
SendUpdateWorldState(m_capturePoint->GetGOInfo()->capturePoint.worldstate3, m_neutralValuePct);
}
+
void OPvPCapturePoint::AddGO(uint32 type, uint32 guid, uint32 entry)
{
if(!entry)
@@ -74,6 +81,7 @@ void OPvPCapturePoint::AddGO(uint32 type, uint32 guid, uint32 entry)
m_Objects[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_GAMEOBJECT);
m_ObjectTypes[m_Objects[type]]=type;
}
+
void OPvPCapturePoint::AddCre(uint32 type, uint32 guid, uint32 entry)
{
if(!entry)
@@ -86,6 +94,7 @@ void OPvPCapturePoint::AddCre(uint32 type, uint32 guid, uint32 entry)
m_Creatures[type] = MAKE_NEW_GUID(guid, entry, HIGHGUID_UNIT);
m_CreatureTypes[m_Creatures[type]] = type;
}
+
bool OPvPCapturePoint::AddObject(uint32 type, uint32 entry, uint32 map, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3)
{
if(uint32 guid = objmgr.AddGOData(entry, map, x, y, z, o, 0, rotation0, rotation1, rotation2, rotation3))
@@ -93,8 +102,10 @@ bool OPvPCapturePoint::AddObject(uint32 type, uint32 entry, uint32 map, float x,
AddGO(type, guid, entry);
return true;
}
+
return false;
}
+
bool OPvPCapturePoint::AddCreature(uint32 type, uint32 entry, uint32 team, uint32 map, float x, float y, float z, float o, uint32 spawntimedelay)
{
if(uint32 guid = objmgr.AddCreData(entry, team, map, x, y, z, o, spawntimedelay))
@@ -102,11 +113,14 @@ bool OPvPCapturePoint::AddCreature(uint32 type, uint32 entry, uint32 team, uint3
AddCre(type, guid, entry);
return true;
}
+
return false;
}
+
bool OPvPCapturePoint::SetCapturePointData(uint32 entry, uint32 map, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3)
{
sLog.outDebug("Creating capture point %u", entry);
+
// check info existence
GameObjectInfo const* goinfo = objmgr.GetGameObjectInfo(entry);
if(!goinfo || goinfo->type != GAMEOBJECT_TYPE_CAPTURE_POINT)
@@ -114,16 +128,20 @@ bool OPvPCapturePoint::SetCapturePointData(uint32 entry, uint32 map, float x, fl
sLog.outError("OutdoorPvP: GO %u is not capture point!", entry);
return false;
}
+
m_capturePointGUID = objmgr.AddGOData(entry, map, x, y, z, o, 0, rotation0, rotation1, rotation2, rotation3);
if(!m_capturePointGUID)
return false;
+
// get the needed values from goinfo
m_maxValue = goinfo->capturePoint.maxTime;
m_maxSpeed = m_maxValue / (goinfo->capturePoint.minTime ? goinfo->capturePoint.minTime : 60);
m_neutralValuePct = goinfo->capturePoint.neutralPercent;
m_minValue = m_maxValue * goinfo->capturePoint.neutralPercent / 100;
+
return true;
}
+
bool OPvPCapturePoint::DelCreature(uint32 type)
{
if(!m_Creatures[type])
@@ -131,6 +149,7 @@ bool OPvPCapturePoint::DelCreature(uint32 type)
sLog.outDebug("opvp creature type %u was already deleted",type);
return false;
}
+
Creature *cr = HashMapHolder<Creature>::Find(m_Creatures[type]);
if(!cr)
{
@@ -156,10 +175,12 @@ bool OPvPCapturePoint::DelCreature(uint32 type)
m_Creatures[type] = 0;
return true;
}
+
bool OPvPCapturePoint::DelObject(uint32 type)
{
if(!m_Objects[type])
return false;
+
GameObject *obj = HashMapHolder<GameObject>::Find(m_Objects[type]);
if(!obj)
{
@@ -174,17 +195,21 @@ bool OPvPCapturePoint::DelObject(uint32 type)
m_Objects[type] = 0;
return true;
}
+
bool OPvPCapturePoint::DelCapturePoint()
{
objmgr.DeleteGOData(m_capturePointGUID);
m_capturePointGUID = 0;
+
if(m_capturePoint)
{
m_capturePoint->SetRespawnTime(0); // not save respawn time
m_capturePoint->Delete();
}
+
return true;
}
+
void OPvPCapturePoint::DeleteSpawns()
{
for(std::map<uint32,uint64>::iterator i = m_Objects.begin(); i != m_Objects.end(); ++i)
@@ -193,22 +218,27 @@ void OPvPCapturePoint::DeleteSpawns()
DelCreature(i->first);
DelCapturePoint();
}
+
void OutdoorPvP::DeleteSpawns()
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
itr->second->DeleteSpawns();
}
+
OutdoorPvP::OutdoorPvP() : m_sendUpdate(true)
{
}
+
OutdoorPvP::~OutdoorPvP()
{
DeleteSpawns();
}
+
void OutdoorPvP::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
m_players[plr->GetTeamId()].insert(plr);
}
+
void OutdoorPvP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// inform the objectives of the leaving
@@ -220,6 +250,7 @@ void OutdoorPvP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
m_players[plr->GetTeamId()].erase(plr);
sLog.outDebug("Player %s left an outdoorpvp zone", plr->GetName());
}
+
bool OutdoorPvP::Update(uint32 diff)
{
bool objective_changed = false;
@@ -230,11 +261,14 @@ bool OutdoorPvP::Update(uint32 diff)
}
return objective_changed;
}
+
bool OPvPCapturePoint::Update(uint32 diff)
{
if(!m_capturePoint)
return false;
+
float radius = m_capturePoint->GetGOInfo()->capturePoint.radius;
+
for(uint32 team = 0; team < 2; ++team)
{
for(PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end();)
@@ -245,10 +279,12 @@ bool OPvPCapturePoint::Update(uint32 diff)
HandlePlayerLeave(player);
}
}
+
std::list<Player*> players;
Trinity::AnyPlayerInObjectRangeCheck checker(m_capturePoint, radius);
Trinity::PlayerListSearcher<Trinity::AnyPlayerInObjectRangeCheck> searcher(m_capturePoint, players, checker);
m_capturePoint->VisitNearbyWorldObject(radius, searcher);
+
for(std::list<Player*>::iterator itr = players.begin(); itr != players.end(); ++itr)
{
if((*itr)->IsOutdoorPvPActive())
@@ -257,19 +293,24 @@ bool OPvPCapturePoint::Update(uint32 diff)
HandlePlayerEnter(*itr);
}
}
+
// get the difference of numbers
float fact_diff = ((float)m_activePlayers[0].size() - (float)m_activePlayers[1].size()) * diff / OUTDOORPVP_OBJECTIVE_UPDATE_INTERVAL;
if(!fact_diff)
return false;
+
uint32 Challenger = 0;
float maxDiff = m_maxSpeed * diff;
+
if(fact_diff < 0)
{
// horde is in majority, but it's already horde-controlled -> no change
if(m_State == OBJECTIVESTATE_HORDE && m_value <= -m_maxValue)
return false;
+
if(fact_diff < -maxDiff)
fact_diff = -maxDiff;
+
Challenger = HORDE;
}
else
@@ -277,14 +318,20 @@ bool OPvPCapturePoint::Update(uint32 diff)
// ally is in majority, but it's already ally-controlled -> no change
if(m_State == OBJECTIVESTATE_ALLIANCE && m_value >= m_maxValue)
return false;
+
if(fact_diff > maxDiff)
fact_diff = maxDiff;
+
Challenger = ALLIANCE;
}
+
float oldValue = m_value;
TeamId oldTeam = m_team;
+
m_OldState = m_State;
+
m_value += fact_diff;
+
if(m_value < -m_minValue) // red
{
if(m_value < -m_maxValue)
@@ -318,8 +365,10 @@ bool OPvPCapturePoint::Update(uint32 diff)
m_State = OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE;
m_team = TEAM_NEUTRAL;
}
+
if(m_value != oldValue)
SendChangePhase();
+
if(m_OldState != m_State)
{
//sLog.outError("%u->%u", m_OldState, m_State);
@@ -328,8 +377,10 @@ bool OPvPCapturePoint::Update(uint32 diff)
ChangeState();
return true;
}
+
return false;
}
+
void OutdoorPvP::SendUpdateWorldState(uint32 field, uint32 value)
{
if(m_sendUpdate)
@@ -337,6 +388,7 @@ void OutdoorPvP::SendUpdateWorldState(uint32 field, uint32 value)
for(PlayerSet::iterator itr = m_players[i].begin(); itr != m_players[i].end(); ++itr)
(*itr)->SendUpdateWorldState(field, value);
}
+
void OPvPCapturePoint::SendUpdateWorldState(uint32 field, uint32 value)
{
for(uint32 team = 0; team < 2; ++team)
@@ -348,6 +400,7 @@ void OPvPCapturePoint::SendUpdateWorldState(uint32 field, uint32 value)
}
}
}
+
void OPvPCapturePoint::SendObjectiveComplete(uint32 id,uint64 guid)
{
uint32 team;
@@ -362,10 +415,12 @@ void OPvPCapturePoint::SendObjectiveComplete(uint32 id,uint64 guid)
default:
return;
}
+
// send to all players present in the area
for(PlayerSet::iterator itr = m_activePlayers[team].begin(); itr != m_activePlayers[team].end(); ++itr)
(*itr)->KilledMonsterCredit(id, guid);
}
+
void OutdoorPvP::HandleKill(Player *killer, Unit * killed)
{
if(Group * pGroup = killer->GetGroup())
@@ -373,11 +428,14 @@ void OutdoorPvP::HandleKill(Player *killer, Unit * killed)
for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player *pGroupGuy = itr->getSource();
+
if(!pGroupGuy)
continue;
+
// skip if too far away
if(!pGroupGuy->IsAtGroupRewardDistance(killed))
continue;
+
// creature kills must be notified, even if not inside objective / not outdoor pvp active
// player kills only count if active and inside objective
if(( pGroupGuy->IsOutdoorPvPActive() && IsInsideObjective(pGroupGuy) ) || killed->GetTypeId() == TYPEID_UNIT)
@@ -395,70 +453,88 @@ void OutdoorPvP::HandleKill(Player *killer, Unit * killed)
}
}
}
+
bool OutdoorPvP::IsInsideObjective(Player *plr) const
{
for(OPvPCapturePointMap::const_iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if(itr->second->IsInsideObjective(plr))
return true;
+
return false;
}
+
bool OPvPCapturePoint::IsInsideObjective(Player *plr) const
{
return m_activePlayers[plr->GetTeamId()].find(plr) != m_activePlayers[plr->GetTeamId()].end();
}
+
bool OutdoorPvP::HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if(itr->second->HandleCustomSpell(plr,spellId,go))
return true;
+
return false;
}
+
bool OPvPCapturePoint::HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go)
{
if(!plr->IsOutdoorPvPActive())
return false;
return false;
}
+
bool OutdoorPvP::HandleOpenGo(Player *plr, uint64 guid)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if(itr->second->HandleOpenGo(plr,guid) >= 0)
return true;
+
return false;
}
+
bool OutdoorPvP::HandleGossipOption(Player * plr, uint64 guid, uint32 id)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if(itr->second->HandleGossipOption(plr, guid, id))
return true;
+
return false;
}
+
bool OutdoorPvP::CanTalkTo(Player * plr, Creature * c, GossipOption &gso)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if(itr->second->CanTalkTo(plr, c, gso))
return true;
+
return false;
}
+
bool OutdoorPvP::HandleDropFlag(Player * plr, uint32 id)
{
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if(itr->second->HandleDropFlag(plr, id))
return true;
+
return false;
}
+
bool OPvPCapturePoint::HandleGossipOption(Player * plr, uint64 guid, uint32 id)
{
return false;
}
+
bool OPvPCapturePoint::CanTalkTo(Player * plr, Creature * c, GossipOption &gso)
{
return false;
}
+
bool OPvPCapturePoint::HandleDropFlag(Player * plr, uint32 id)
{
return false;
}
+
int32 OPvPCapturePoint::HandleOpenGo(Player *plr, uint64 guid)
{
std::map<uint64,uint32>::iterator itr = m_ObjectTypes.find(guid);
@@ -468,10 +544,12 @@ int32 OPvPCapturePoint::HandleOpenGo(Player *plr, uint64 guid)
}
return -1;
}
+
bool OutdoorPvP::HandleAreaTrigger(Player *plr, uint32 trigger)
{
return false;
}
+
void OutdoorPvP::BroadcastPacket(WorldPacket &data) const
{
// This is faster than sWorld.SendZoneMessage
@@ -479,14 +557,17 @@ void OutdoorPvP::BroadcastPacket(WorldPacket &data) const
for(PlayerSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
(*itr)->GetSession()->SendPacket(&data);
}
+
void OutdoorPvP::RegisterZone(uint32 zoneId)
{
sOutdoorPvPMgr.AddZone(zoneId, this);
}
+
bool OutdoorPvP::HasPlayer(Player *plr) const
{
return m_players[plr->GetTeamId()].find(plr) != m_players[plr->GetTeamId()].end();
}
+
void OutdoorPvP::TeamCastSpell(TeamId team, int32 spellId)
{
if(spellId > 0)
@@ -496,15 +577,18 @@ void OutdoorPvP::TeamCastSpell(TeamId team, int32 spellId)
for(PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
(*itr)->RemoveAura((uint32)-spellId); // by stack?
}
+
void OutdoorPvP::TeamApplyBuff(TeamId team, uint32 spellId, uint32 spellId2)
{
TeamCastSpell(team, spellId);
TeamCastSpell(OTHER_TEAM(team), spellId2 ? -(int32)spellId2 : -(int32)spellId);
}
+
void OutdoorPvP::OnGameObjectCreate(GameObject *go, bool add)
{
if(go->GetGoType() != GAMEOBJECT_TYPE_CAPTURE_POINT)
return;
+
if(OPvPCapturePoint *cp = GetCapturePoint(go->GetDBTableGUIDLow()))
cp->m_capturePoint = add ? go : NULL;
}
diff --git a/src/game/OutdoorPvP.h b/src/game/OutdoorPvP.h
index 487b923a4d6..e48356633c3 100644
--- a/src/game/OutdoorPvP.h
+++ b/src/game/OutdoorPvP.h
@@ -15,12 +15,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_H_
#define OUTDOOR_PVP_H_
+
#include "Util.h"
#include "SharedDefines.h"
#include "ZoneScript.h"
+
class GameObject;
+
enum OutdoorPvPTypes
{
OUTDOOR_PVP_HP = 1,
@@ -31,7 +35,9 @@ enum OutdoorPvPTypes
OUTDOOR_PVP_EP,
OPVP_WINTERGRASP,
};
+
const uint8 CapturePointArtKit[3] = {2, 1, 21};
+
enum ObjectiveStates
{
OBJECTIVESTATE_NEUTRAL = 0,
@@ -42,6 +48,7 @@ enum ObjectiveStates
OBJECTIVESTATE_ALLIANCE_HORDE_CHALLENGE,
OBJECTIVESTATE_HORDE_ALLIANCE_CHALLENGE,
};
+
// struct for go spawning
struct go_type{
uint32 entry;
@@ -55,6 +62,7 @@ struct go_type{
float rot2;
float rot3;
};
+
// struct for creature spawning
struct creature_type{
uint32 entry;
@@ -65,6 +73,7 @@ struct creature_type{
float z;
float o;
};
+
// some class predefs
class Player;
class GameObject;
@@ -72,45 +81,63 @@ class WorldPacket;
class Creature;
class Unit;
struct GossipOption;
+
typedef std::set<Player*> PlayerSet;
+
class OutdoorPvP;
class OPvPCapturePoint
{
public:
OPvPCapturePoint(OutdoorPvP * pvp);
+
virtual void FillInitialWorldStates(WorldPacket & data) {}
+
// send world state update to all players present
void SendUpdateWorldState(uint32 field, uint32 value);
// send kill notify to players in the controlling faction
void SendObjectiveComplete(uint32 id, uint64 guid);
+
// used when player is activated/inactivated in the area
virtual bool HandlePlayerEnter(Player * plr);
virtual void HandlePlayerLeave(Player * plr);
//virtual void HandlePlayerActivityChanged(Player * plr);
+
// checks if player is in range of a capture credit marker
bool IsInsideObjective(Player * plr) const;
+
virtual bool HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go);
virtual int32 HandleOpenGo(Player *plr, uint64 guid);
+
// returns true if the state of the objective has changed, in this case, the OutdoorPvP must send a world state ui update.
virtual bool Update(uint32 diff);
virtual void ChangeState() = 0;
virtual void ChangeTeam(TeamId oldTeam) {}
virtual void SendChangePhase();
+
virtual bool HandleGossipOption(Player *plr, uint64 guid, uint32 gossipid);
+
virtual bool CanTalkTo(Player * plr, Creature * c, GossipOption &gso);
+
virtual bool HandleDropFlag(Player * plr, uint32 spellId);
+
virtual void DeleteSpawns();
+
uint32 m_capturePointGUID;
GameObject *m_capturePoint;
+
void AddGO(uint32 type, uint32 guid, uint32 entry = 0);
void AddCre(uint32 type, uint32 guid, uint32 entry = 0);
bool SetCapturePointData(uint32 entry, uint32 map, float x, float y, float z, float o = 0, float rotation0 = 0, float rotation1 = 0, float rotation2 = 0, float rotation3 = 0);
+
protected:
+
bool AddObject(uint32 type, uint32 entry, uint32 map, float x, float y, float z, float o, float rotation0, float rotation1, float rotation2, float rotation3);
bool AddCreature(uint32 type, uint32 entry, uint32 teamval, uint32 map, float x, float y, float z, float o, uint32 spawntimedelay = 0);
+
bool DelCreature(uint32 type);
bool DelObject(uint32 type);
bool DelCapturePoint();
+
protected:
// active players in the area of the objective, 0 - alliance, 1 - horde
PlayerSet m_activePlayers[2];
@@ -127,8 +154,10 @@ protected:
ObjectiveStates m_State;
// neutral value on capture bar
uint32 m_neutralValuePct;
+
// pointer to the OutdoorPvP this objective belongs to
OutdoorPvP* m_PvP;
+
// map to store the various gameobjects and creatures spawned by the objective
// type , guid
std::map<uint32,uint64> m_Objects;
@@ -136,6 +165,7 @@ protected:
std::map<uint64,uint32> m_ObjectTypes;
std::map<uint64,uint32> m_CreatureTypes;
};
+
// base class for specific outdoor pvp handlers
class OutdoorPvP : public ZoneScript
{
@@ -147,7 +177,9 @@ public:
~OutdoorPvP();
// deletes all gos/creatures spawned by the pvp
void DeleteSpawns();
+
typedef std::map<uint32/*lowguid*/, OPvPCapturePoint*> OPvPCapturePointMap;
+
virtual void FillInitialWorldStates(WorldPacket & data) {}
// called when a player triggers an areatrigger
virtual bool HandleAreaTrigger(Player * plr, uint32 trigger);
@@ -155,41 +187,60 @@ public:
virtual bool HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go);
// called on go use
virtual bool HandleOpenGo(Player *plr, uint64 guid);
+
// setup stuff
virtual bool SetupOutdoorPvP() {return true;}
+
void OnGameObjectCreate(GameObject *go, bool add);
void OnCreatureCreate(Creature *, bool add) {}
+
// send world state update to all players present
void SendUpdateWorldState(uint32 field, uint32 value);
+
// called by OutdoorPvPMgr, updates the objectives and if needed, sends new worldstateui information
virtual bool Update(uint32 diff);
+
// handle npc/player kill
virtual void HandleKill(Player * killer, Unit * killed);
virtual void HandleKillImpl(Player * killer, Unit * killed) {}
+
// checks if player is in range of a capture credit marker
bool IsInsideObjective(Player * plr) const;
+
// awards rewards for player kill
virtual void AwardKillBonus(Player * plr) {}
+
uint32 GetTypeId() {return m_TypeId;}
+
virtual bool HandleDropFlag(Player * plr, uint32 spellId);
+
virtual bool HandleGossipOption(Player *plr, uint64 guid, uint32 gossipid);
+
virtual bool CanTalkTo(Player * plr, Creature * c, GossipOption &gso);
+
void TeamApplyBuff(TeamId team, uint32 spellId, uint32 spellId2 = 0);
protected:
// the map of the objectives belonging to this outdoorpvp
OPvPCapturePointMap m_capturePoints;
+
PlayerSet m_players[2];
uint32 m_TypeId;
+
bool m_sendUpdate;
+
// world state stuff
virtual void SendRemoveWorldStates(Player * plr) {}
+
void BroadcastPacket(WorldPacket & data) const;
+
virtual void HandlePlayerEnterZone(Player * plr, uint32 zone);
virtual void HandlePlayerLeaveZone(Player * plr, uint32 zone);
+
void AddCapturePoint(OPvPCapturePoint* cp)
{
m_capturePoints[cp->m_capturePointGUID] = cp;
}
+
OPvPCapturePoint * GetCapturePoint(uint32 lowguid) const
{
OutdoorPvP::OPvPCapturePointMap::const_iterator itr = m_capturePoints.find(lowguid);
@@ -197,9 +248,11 @@ protected:
return itr->second;
return NULL;
}
+
void RegisterZone(uint32 zoneid);
bool HasPlayer(Player *plr) const;
void TeamCastSpell(TeamId team, int32 spellId);
};
+
#endif /*OUTDOOR_PVP_H_*/
diff --git a/src/game/OutdoorPvPEP.cpp b/src/game/OutdoorPvPEP.cpp
index b73dc83268c..c108c733f04 100644
--- a/src/game/OutdoorPvPEP.cpp
+++ b/src/game/OutdoorPvPEP.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPEP.h"
#include "WorldPacket.h"
#include "Player.h"
@@ -26,12 +27,14 @@
#include "Language.h"
#include "World.h"
#include "GossipDef.h"
+
OPvPCapturePointEP_EWT::OPvPCapturePointEP_EWT(OutdoorPvP *pvp)
: OPvPCapturePoint(pvp), m_TowerState(EP_TS_N), m_UnitsSummonedSide(0)
{
SetCapturePointData(EPCapturePoints[EP_EWT].entry,EPCapturePoints[EP_EWT].map,EPCapturePoints[EP_EWT].x,EPCapturePoints[EP_EWT].y,EPCapturePoints[EP_EWT].z,EPCapturePoints[EP_EWT].o,EPCapturePoints[EP_EWT].rot0,EPCapturePoints[EP_EWT].rot1,EPCapturePoints[EP_EWT].rot2,EPCapturePoints[EP_EWT].rot3);
AddObject(EP_EWT_FLAGS,EPTowerFlags[EP_EWT].entry,EPTowerFlags[EP_EWT].map,EPTowerFlags[EP_EWT].x,EPTowerFlags[EP_EWT].y,EPTowerFlags[EP_EWT].z,EPTowerFlags[EP_EWT].o,EPTowerFlags[EP_EWT].rot0,EPTowerFlags[EP_EWT].rot1,EPTowerFlags[EP_EWT].rot2,EPTowerFlags[EP_EWT].rot3);
}
+
void OPvPCapturePointEP_EWT::ChangeState()
{
if(fabs(m_value) == m_maxValue) // state won't change, only phase when maxed out!
@@ -47,7 +50,9 @@ void OPvPCapturePointEP_EWT::ChangeState()
sWorld.SendZoneText(EP_GraveYardZone,objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_EP_LOOSE_EWT_H));
((OutdoorPvPEP*)m_PvP)->EP_Controls[EP_EWT] = 0;
}
+
uint32 artkit = 21;
+
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
@@ -82,6 +87,7 @@ void OPvPCapturePointEP_EWT::ChangeState()
m_TowerState = EP_TS_N_H;
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
GameObject* flag2 = HashMapHolder<GameObject>::Find(m_Objects[EP_EWT_FLAGS]);
if(flag)
@@ -95,12 +101,15 @@ void OPvPCapturePointEP_EWT::ChangeState()
flag2->SendUpdateObjectToAllExcept(NULL);
}
+
UpdateTowerState();
+
// complete quest objective
if(m_TowerState == EP_TS_A || m_TowerState == EP_TS_H)
SendObjectiveComplete(EP_EWT_CM, 0);
}
}
+
void OPvPCapturePointEP_EWT::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
@@ -111,6 +120,7 @@ void OPvPCapturePointEP_EWT::SendChangePhase()
// send this too, sometimes it resets :S
SendUpdateWorldState(EP_UI_TOWER_SLIDER_N, m_neutralValuePct);
}
+
void OPvPCapturePointEP_EWT::FillInitialWorldStates(WorldPacket &data)
{
data << EP_EWT_A << uint32(bool(m_TowerState & EP_TS_A));
@@ -121,6 +131,7 @@ void OPvPCapturePointEP_EWT::FillInitialWorldStates(WorldPacket &data)
data << EP_EWT_N_H << uint32(bool(m_TowerState & EP_TS_N_H));
data << EP_EWT_N << uint32(bool(m_TowerState & EP_TS_N));
}
+
void OPvPCapturePointEP_EWT::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(EP_EWT_A , bool(m_TowerState & EP_TS_A));
@@ -131,6 +142,7 @@ void OPvPCapturePointEP_EWT::UpdateTowerState()
m_PvP->SendUpdateWorldState(EP_EWT_N_H , bool(m_TowerState & EP_TS_N_H));
m_PvP->SendUpdateWorldState(EP_EWT_N , bool(m_TowerState & EP_TS_N));
}
+
bool OPvPCapturePointEP_EWT::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -143,11 +155,13 @@ bool OPvPCapturePointEP_EWT::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointEP_EWT::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
void OPvPCapturePointEP_EWT::SummonSupportUnitAtNorthpassTower(uint32 team)
{
if(m_UnitsSummonedSide != team)
@@ -158,6 +172,7 @@ void OPvPCapturePointEP_EWT::SummonSupportUnitAtNorthpassTower(uint32 team)
ct=EP_EWT_Summons_A;
else
ct=EP_EWT_Summons_H;
+
for(int i = 0; i < EP_EWT_NUM_CREATURES; ++i)
{
DelCreature(i);
@@ -165,6 +180,7 @@ void OPvPCapturePointEP_EWT::SummonSupportUnitAtNorthpassTower(uint32 team)
}
}
}
+
// NPT
OPvPCapturePointEP_NPT::OPvPCapturePointEP_NPT(OutdoorPvP *pvp)
: OPvPCapturePoint(pvp), m_TowerState(EP_TS_N), m_SummonedGOSide(0)
@@ -172,6 +188,7 @@ OPvPCapturePointEP_NPT::OPvPCapturePointEP_NPT(OutdoorPvP *pvp)
SetCapturePointData(EPCapturePoints[EP_NPT].entry,EPCapturePoints[EP_NPT].map,EPCapturePoints[EP_NPT].x,EPCapturePoints[EP_NPT].y,EPCapturePoints[EP_NPT].z,EPCapturePoints[EP_NPT].o,EPCapturePoints[EP_NPT].rot0,EPCapturePoints[EP_NPT].rot1,EPCapturePoints[EP_NPT].rot2,EPCapturePoints[EP_NPT].rot3);
AddObject(EP_NPT_FLAGS,EPTowerFlags[EP_NPT].entry,EPTowerFlags[EP_NPT].map,EPTowerFlags[EP_NPT].x,EPTowerFlags[EP_NPT].y,EPTowerFlags[EP_NPT].z,EPTowerFlags[EP_NPT].o,EPTowerFlags[EP_NPT].rot0,EPTowerFlags[EP_NPT].rot1,EPTowerFlags[EP_NPT].rot2,EPTowerFlags[EP_NPT].rot3);
}
+
void OPvPCapturePointEP_NPT::ChangeState()
{
if(fabs(m_value) == m_maxValue) // state won't change, only phase when maxed out!
@@ -187,7 +204,9 @@ void OPvPCapturePointEP_NPT::ChangeState()
sWorld.SendZoneText(EP_GraveYardZone,objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_EP_LOOSE_NPT_H));
((OutdoorPvPEP*)m_PvP)->EP_Controls[EP_NPT] = 0;
}
+
uint32 artkit = 21;
+
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
@@ -224,6 +243,7 @@ void OPvPCapturePointEP_NPT::ChangeState()
m_TowerState = EP_TS_N_H;
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
GameObject* flag2 = HashMapHolder<GameObject>::Find(m_Objects[EP_NPT_FLAGS]);
if(flag)
@@ -236,12 +256,15 @@ void OPvPCapturePointEP_NPT::ChangeState()
flag2->SetGoArtKit(artkit);
flag2->SendUpdateObjectToAllExcept(NULL);
}
+
UpdateTowerState();
+
// complete quest objective
if(m_TowerState == EP_TS_A || m_TowerState == EP_TS_H)
SendObjectiveComplete(EP_NPT_CM, 0);
}
}
+
void OPvPCapturePointEP_NPT::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
@@ -252,6 +275,7 @@ void OPvPCapturePointEP_NPT::SendChangePhase()
// send this too, sometimes it resets :S
SendUpdateWorldState(EP_UI_TOWER_SLIDER_N, m_neutralValuePct);
}
+
void OPvPCapturePointEP_NPT::FillInitialWorldStates(WorldPacket &data)
{
data << EP_NPT_A << uint32(bool(m_TowerState & EP_TS_A));
@@ -262,6 +286,7 @@ void OPvPCapturePointEP_NPT::FillInitialWorldStates(WorldPacket &data)
data << EP_NPT_N_H << uint32(bool(m_TowerState & EP_TS_N_H));
data << EP_NPT_N << uint32(bool(m_TowerState & EP_TS_N));
}
+
void OPvPCapturePointEP_NPT::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(EP_NPT_A , bool(m_TowerState & EP_TS_A));
@@ -272,6 +297,7 @@ void OPvPCapturePointEP_NPT::UpdateTowerState()
m_PvP->SendUpdateWorldState(EP_NPT_N_H , bool(m_TowerState & EP_TS_N_H));
m_PvP->SendUpdateWorldState(EP_NPT_N , bool(m_TowerState & EP_TS_N));
}
+
bool OPvPCapturePointEP_NPT::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -284,11 +310,13 @@ bool OPvPCapturePointEP_NPT::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointEP_NPT::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
void OPvPCapturePointEP_NPT::SummonGO(uint32 team)
{
if(m_SummonedGOSide != team)
@@ -301,6 +329,7 @@ void OPvPCapturePointEP_NPT::SummonGO(uint32 team)
go->SetUInt32Value(GAMEOBJECT_FACTION,(team == ALLIANCE ? 84 : 83));
}
}
+
// CGT
OPvPCapturePointEP_CGT::OPvPCapturePointEP_CGT(OutdoorPvP *pvp)
: OPvPCapturePoint(pvp), m_TowerState(EP_TS_N), m_GraveyardSide(0)
@@ -308,6 +337,7 @@ OPvPCapturePointEP_CGT::OPvPCapturePointEP_CGT(OutdoorPvP *pvp)
SetCapturePointData(EPCapturePoints[EP_CGT].entry,EPCapturePoints[EP_CGT].map,EPCapturePoints[EP_CGT].x,EPCapturePoints[EP_CGT].y,EPCapturePoints[EP_CGT].z,EPCapturePoints[EP_CGT].o,EPCapturePoints[EP_CGT].rot0,EPCapturePoints[EP_CGT].rot1,EPCapturePoints[EP_CGT].rot2,EPCapturePoints[EP_CGT].rot3);
AddObject(EP_CGT_FLAGS,EPTowerFlags[EP_CGT].entry,EPTowerFlags[EP_CGT].map,EPTowerFlags[EP_CGT].x,EPTowerFlags[EP_CGT].y,EPTowerFlags[EP_CGT].z,EPTowerFlags[EP_CGT].o,EPTowerFlags[EP_CGT].rot0,EPTowerFlags[EP_CGT].rot1,EPTowerFlags[EP_CGT].rot2,EPTowerFlags[EP_CGT].rot3);
}
+
void OPvPCapturePointEP_CGT::ChangeState()
{
if(fabs(m_value) == m_maxValue) // state won't change, only phase when maxed out!
@@ -323,7 +353,9 @@ void OPvPCapturePointEP_CGT::ChangeState()
sWorld.SendZoneText(EP_GraveYardZone,objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_EP_LOOSE_CGT_H));
((OutdoorPvPEP*)m_PvP)->EP_Controls[EP_CGT] = 0;
}
+
uint32 artkit = 21;
+
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
@@ -358,6 +390,7 @@ void OPvPCapturePointEP_CGT::ChangeState()
m_TowerState = EP_TS_N_H;
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
GameObject* flag2 = HashMapHolder<GameObject>::Find(m_Objects[EP_CGT_FLAGS]);
if(flag)
@@ -370,12 +403,15 @@ void OPvPCapturePointEP_CGT::ChangeState()
flag2->SetGoArtKit(artkit);
flag2->SendUpdateObjectToAllExcept(NULL);
}
+
UpdateTowerState();
+
// complete quest objective
if(m_TowerState == EP_TS_A || m_TowerState == EP_TS_H)
SendObjectiveComplete(EP_CGT_CM, 0);
}
}
+
void OPvPCapturePointEP_CGT::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
@@ -386,6 +422,7 @@ void OPvPCapturePointEP_CGT::SendChangePhase()
// send this too, sometimes it resets :S
SendUpdateWorldState(EP_UI_TOWER_SLIDER_N, m_neutralValuePct);
}
+
void OPvPCapturePointEP_CGT::FillInitialWorldStates(WorldPacket &data)
{
data << EP_CGT_A << uint32(bool(m_TowerState & EP_TS_A));
@@ -396,6 +433,7 @@ void OPvPCapturePointEP_CGT::FillInitialWorldStates(WorldPacket &data)
data << EP_CGT_N_H << uint32(bool(m_TowerState & EP_TS_N_H));
data << EP_CGT_N << uint32(bool(m_TowerState & EP_TS_N));
}
+
void OPvPCapturePointEP_CGT::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(EP_CGT_A , bool(m_TowerState & EP_TS_A));
@@ -406,6 +444,7 @@ void OPvPCapturePointEP_CGT::UpdateTowerState()
m_PvP->SendUpdateWorldState(EP_CGT_N_H , bool(m_TowerState & EP_TS_N_H));
m_PvP->SendUpdateWorldState(EP_CGT_N , bool(m_TowerState & EP_TS_N));
}
+
bool OPvPCapturePointEP_CGT::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -418,11 +457,13 @@ bool OPvPCapturePointEP_CGT::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointEP_CGT::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
void OPvPCapturePointEP_CGT::LinkGraveYard(uint32 team)
{
if(m_GraveyardSide != team)
@@ -432,6 +473,7 @@ void OPvPCapturePointEP_CGT::LinkGraveYard(uint32 team)
objmgr.AddGraveYardLink(EP_GraveYardId,EP_GraveYardZone,team,false);
}
}
+
// PWT
OPvPCapturePointEP_PWT::OPvPCapturePointEP_PWT(OutdoorPvP *pvp)
: OPvPCapturePoint(pvp), m_TowerState(EP_TS_N), m_FlightMasterSpawned(0)
@@ -439,6 +481,7 @@ OPvPCapturePointEP_PWT::OPvPCapturePointEP_PWT(OutdoorPvP *pvp)
SetCapturePointData(EPCapturePoints[EP_PWT].entry,EPCapturePoints[EP_PWT].map,EPCapturePoints[EP_PWT].x,EPCapturePoints[EP_PWT].y,EPCapturePoints[EP_PWT].z,EPCapturePoints[EP_PWT].o,EPCapturePoints[EP_PWT].rot0,EPCapturePoints[EP_PWT].rot1,EPCapturePoints[EP_PWT].rot2,EPCapturePoints[EP_PWT].rot3);
AddObject(EP_PWT_FLAGS,EPTowerFlags[EP_PWT].entry,EPTowerFlags[EP_PWT].map,EPTowerFlags[EP_PWT].x,EPTowerFlags[EP_PWT].y,EPTowerFlags[EP_PWT].z,EPTowerFlags[EP_PWT].o,EPTowerFlags[EP_PWT].rot0,EPTowerFlags[EP_PWT].rot1,EPTowerFlags[EP_PWT].rot2,EPTowerFlags[EP_PWT].rot3);
}
+
void OPvPCapturePointEP_PWT::ChangeState()
{
if(fabs(m_value) == m_maxValue) // state won't change, only phase when maxed out!
@@ -454,7 +497,9 @@ void OPvPCapturePointEP_PWT::ChangeState()
sWorld.SendZoneText(EP_GraveYardZone,objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_EP_LOOSE_PWT_H));
((OutdoorPvPEP*)m_PvP)->EP_Controls[EP_PWT] = 0;
}
+
uint32 artkit = 21;
+
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
@@ -491,6 +536,7 @@ void OPvPCapturePointEP_PWT::ChangeState()
m_TowerState = EP_TS_N_H;
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
GameObject* flag2 = HashMapHolder<GameObject>::Find(m_Objects[EP_PWT_FLAGS]);
if(flag)
@@ -503,12 +549,15 @@ void OPvPCapturePointEP_PWT::ChangeState()
flag2->SetGoArtKit(artkit);
flag2->SendUpdateObjectToAllExcept(NULL);
}
+
UpdateTowerState();
+
// complete quest objective
if(m_TowerState == EP_TS_A || m_TowerState == EP_TS_H)
SendObjectiveComplete(EP_PWT_CM, 0);
}
}
+
void OPvPCapturePointEP_PWT::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
@@ -519,6 +568,7 @@ void OPvPCapturePointEP_PWT::SendChangePhase()
// send this too, sometimes it resets :S
SendUpdateWorldState(EP_UI_TOWER_SLIDER_N, m_neutralValuePct);
}
+
void OPvPCapturePointEP_PWT::FillInitialWorldStates(WorldPacket &data)
{
data << EP_PWT_A << uint32(bool(m_TowerState & EP_TS_A));
@@ -529,6 +579,7 @@ void OPvPCapturePointEP_PWT::FillInitialWorldStates(WorldPacket &data)
data << EP_PWT_N_H << uint32(bool(m_TowerState & EP_TS_N_H));
data << EP_PWT_N << uint32(bool(m_TowerState & EP_TS_N));
}
+
void OPvPCapturePointEP_PWT::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(EP_PWT_A , bool(m_TowerState & EP_TS_A));
@@ -539,6 +590,7 @@ void OPvPCapturePointEP_PWT::UpdateTowerState()
m_PvP->SendUpdateWorldState(EP_PWT_N_H , bool(m_TowerState & EP_TS_N_H));
m_PvP->SendUpdateWorldState(EP_PWT_N , bool(m_TowerState & EP_TS_N));
}
+
bool OPvPCapturePointEP_PWT::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -551,11 +603,13 @@ bool OPvPCapturePointEP_PWT::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointEP_PWT::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
void OPvPCapturePointEP_PWT::SummonFlightMaster(uint32 team)
{
if(m_FlightMasterSpawned != team)
@@ -574,6 +628,7 @@ void OPvPCapturePointEP_PWT::SummonFlightMaster(uint32 team)
gso.Icon = 0;
gso.NpcFlag = 0;
c->addGossipOption(gso);
+
gso.Action = GOSSIP_OPTION_OUTDOORPVP;
gso.GossipId = 0;
gso.OptionText.assign(objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_EP_FLIGHT_EWT));
@@ -581,6 +636,7 @@ void OPvPCapturePointEP_PWT::SummonFlightMaster(uint32 team)
gso.Icon = 0;
gso.NpcFlag = 0;
c->addGossipOption(gso);
+
gso.Action = GOSSIP_OPTION_OUTDOORPVP;
gso.GossipId = 0;
gso.OptionText.assign(objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_EP_FLIGHT_CGT));
@@ -591,6 +647,7 @@ void OPvPCapturePointEP_PWT::SummonFlightMaster(uint32 team)
}
}
}
+
bool OPvPCapturePointEP_PWT::CanTalkTo(Player * p, Creature * c, GossipOption &gso)
{
if( p->GetTeam() == m_FlightMasterSpawned &&
@@ -599,6 +656,7 @@ bool OPvPCapturePointEP_PWT::CanTalkTo(Player * p, Creature * c, GossipOption &g
return true;
return false;
}
+
bool OPvPCapturePointEP_PWT::HandleGossipOption(Player *plr, uint64 guid, uint32 gossipid)
{
std::map<uint64,uint32>::iterator itr = m_CreatureTypes.find(guid);
@@ -623,10 +681,12 @@ bool OPvPCapturePointEP_PWT::HandleGossipOption(Player *plr, uint64 guid, uint32
dst = EP_CGT_Taxi;
break;
}
+
std::vector<uint32> nodes;
nodes.resize(2);
nodes[0] = src;
nodes[1] = dst;
+
plr->PlayerTalkClass->CloseGossip();
plr->ActivateTaxiPathTo(nodes, cr);
// leave the opvp, seems like moveinlineofsight isn't called when entering a taxi
@@ -636,6 +696,7 @@ bool OPvPCapturePointEP_PWT::HandleGossipOption(Player *plr, uint64 guid, uint32
}
return false;
}
+
// ep
OutdoorPvPEP::OutdoorPvPEP()
{
@@ -644,16 +705,19 @@ OutdoorPvPEP::OutdoorPvPEP()
m_AllianceTowersControlled = 0;
m_HordeTowersControlled = 0;
}
+
bool OutdoorPvPEP::SetupOutdoorPvP()
{
for(int i = 0; i < EPBuffZonesNum; ++i)
RegisterZone(EPBuffZones[i]);
+
AddCapturePoint(new OPvPCapturePointEP_EWT(this));
AddCapturePoint(new OPvPCapturePointEP_PWT(this));
AddCapturePoint(new OPvPCapturePointEP_CGT(this));
AddCapturePoint(new OPvPCapturePointEP_NPT(this));
return true;
}
+
bool OutdoorPvPEP::Update(uint32 diff)
{
if(OutdoorPvP::Update(diff))
@@ -674,6 +738,7 @@ bool OutdoorPvPEP::Update(uint32 diff)
}
return false;
}
+
void OutdoorPvPEP::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
// add buffs
@@ -689,6 +754,7 @@ void OutdoorPvPEP::HandlePlayerEnterZone(Player * plr, uint32 zone)
}
OutdoorPvP::HandlePlayerEnterZone(plr,zone);
}
+
void OutdoorPvPEP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// remove buffs
@@ -704,6 +770,7 @@ void OutdoorPvPEP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
}
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
}
+
void OutdoorPvPEP::BuffTeams()
{
for(PlayerSet::iterator itr = m_players[0].begin(); itr != m_players[0].end(); ++itr)
@@ -727,6 +794,7 @@ void OutdoorPvPEP::BuffTeams()
}
}
}
+
void OutdoorPvPEP::FillInitialWorldStates(WorldPacket & data)
{
data << EP_UI_TOWER_COUNT_A << m_AllianceTowersControlled;
@@ -739,6 +807,7 @@ void OutdoorPvPEP::FillInitialWorldStates(WorldPacket & data)
itr->second->FillInitialWorldStates(data);
}
}
+
void OutdoorPvPEP::SendRemoveWorldStates(Player *plr)
{
plr->SendUpdateWorldState(EP_UI_TOWER_COUNT_A,0);
@@ -746,6 +815,7 @@ void OutdoorPvPEP::SendRemoveWorldStates(Player *plr)
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_DISPLAY,0);
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_POS,0);
plr->SendUpdateWorldState(EP_UI_TOWER_SLIDER_N,0);
+
plr->SendUpdateWorldState(EP_EWT_A,0);
plr->SendUpdateWorldState(EP_EWT_H,0);
plr->SendUpdateWorldState(EP_EWT_N,0);
@@ -753,6 +823,7 @@ void OutdoorPvPEP::SendRemoveWorldStates(Player *plr)
plr->SendUpdateWorldState(EP_EWT_H_P,0);
plr->SendUpdateWorldState(EP_EWT_N_A,0);
plr->SendUpdateWorldState(EP_EWT_N_H,0);
+
plr->SendUpdateWorldState(EP_PWT_A,0);
plr->SendUpdateWorldState(EP_PWT_H,0);
plr->SendUpdateWorldState(EP_PWT_N,0);
@@ -760,6 +831,7 @@ void OutdoorPvPEP::SendRemoveWorldStates(Player *plr)
plr->SendUpdateWorldState(EP_PWT_H_P,0);
plr->SendUpdateWorldState(EP_PWT_N_A,0);
plr->SendUpdateWorldState(EP_PWT_N_H,0);
+
plr->SendUpdateWorldState(EP_NPT_A,0);
plr->SendUpdateWorldState(EP_NPT_H,0);
plr->SendUpdateWorldState(EP_NPT_N,0);
@@ -767,6 +839,7 @@ void OutdoorPvPEP::SendRemoveWorldStates(Player *plr)
plr->SendUpdateWorldState(EP_NPT_H_P,0);
plr->SendUpdateWorldState(EP_NPT_N_A,0);
plr->SendUpdateWorldState(EP_NPT_N_H,0);
+
plr->SendUpdateWorldState(EP_CGT_A,0);
plr->SendUpdateWorldState(EP_CGT_H,0);
plr->SendUpdateWorldState(EP_CGT_N,0);
diff --git a/src/game/OutdoorPvPEP.h b/src/game/OutdoorPvPEP.h
index e074650f290..c5cff5c0411 100644
--- a/src/game/OutdoorPvPEP.h
+++ b/src/game/OutdoorPvPEP.h
@@ -15,26 +15,38 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_EP_
#define OUTDOOR_PVP_EP_
+
#include "OutdoorPvPImpl.h"
+
#include "DBCStructure.h"
+
const uint32 EP_AllianceBuffs[4] = {11413, 11414, 11415, 1386};
+
const uint32 EP_HordeBuffs[4] = {30880, 30683, 30682, 29520};
+
const uint32 EP_GraveYardZone = 139;
+
const uint32 EP_GraveYardId = 927;
+
const uint32 EPBuffZonesNum = 3;
+
const uint32 EP_EWT_CM = 17690;
const uint32 EP_CGT_CM = 17689;
const uint32 EP_NPT_CM = 17696;
const uint32 EP_PWT_CM = 17698;
+
const uint32 EPBuffZones[EPBuffZonesNum] = {139, 2017, 2057};
+
enum EP_TaxiNodes {
EP_CGT_Taxi = 87,
EP_EWT_Taxi = 86,
EP_NPT_Taxi = 85,
EP_PWT_Taxi = 84
};
+
enum EP_EastwallTowerWorldStates {
EP_EWT_A = 2354,
EP_EWT_H = 2356,
@@ -44,6 +56,7 @@ enum EP_EastwallTowerWorldStates {
EP_EWT_N_H = 2360,
EP_EWT_N = 2361
};
+
enum EP_NorthpassTowerWorldStates {
EP_NPT_N = 2352,
EP_NPT_N_A = 2362,
@@ -53,6 +66,7 @@ enum EP_NorthpassTowerWorldStates {
EP_NPT_A = 2372,
EP_NPT_H = 2373
};
+
enum EP_PlagewoodTowerWorldStates {
EP_PWT_N_A = 2366,
EP_PWT_N_H = 2353, //2367 not present! use neutral!
@@ -62,6 +76,7 @@ enum EP_PlagewoodTowerWorldStates {
EP_PWT_H = 2371,
EP_PWT_N = 2353
};
+
enum EP_CrownGuardTowerWorldStates {
EP_CGT_N_A = 2374,
EP_CGT_N_H = 2375,
@@ -71,13 +86,16 @@ enum EP_CrownGuardTowerWorldStates {
EP_CGT_H = 2379,
EP_CGT_N = 2355
};
+
enum EP_WorldStates {
EP_UI_TOWER_SLIDER_DISPLAY = 2426,
EP_UI_TOWER_SLIDER_POS = 2427,
EP_UI_TOWER_SLIDER_N = 2428,
+
EP_UI_TOWER_COUNT_A = 2327,
EP_UI_TOWER_COUNT_H = 2328
};
+
enum EP_Summons {
EP_EWT_COMMANDER = 0,
EP_EWT_SOLDIER1,
@@ -86,6 +104,7 @@ enum EP_Summons {
EP_EWT_SOLDIER4,
EP_PWT_FLIGHTMASTER,
};
+
enum EP_GoSummons {
EP_NPT_BUFF = 0,
EP_NPT_FLAGS,
@@ -93,6 +112,7 @@ enum EP_GoSummons {
EP_CGT_FLAGS,
EP_PWT_FLAGS
};
+
enum EP_Towers {
EP_EWT = 0, // plaguelands 03
EP_NPT,// plaguelands 01
@@ -100,22 +120,28 @@ enum EP_Towers {
EP_CGT,// plaguelands 02
EP_TOWER_NUM
};
+
const go_type EPCapturePoints[EP_TOWER_NUM] = {
{182097,0,2574.51,-4794.89,144.704,-1.45003,-0.097056,0.095578,-0.656229,0.742165},
{181899,0,3181.08,-4379.36,174.123,-2.03472,-0.065392,0.119494,-0.842275,0.521553},
{182098,0,2962.71,-3042.31,154.789,2.08426,-0.074807,-0.113837,0.855928,0.49883},
{182096,0,1860.85,-3731.23,196.716,-2.53214,0.033967,-0.131914,0.944741,-0.298177}
};
+
const go_type EPTowerFlags[EP_TOWER_NUM] = {
{182106,0,2569.60,-4772.93,115.399,2.72271,0,0,0.978148,0.207912},
{182106,0,3148.17,-4365.51,145.029,1.53589,0,0,0.694658,0.71934},
{182106,0,2992.63,-3022.95,125.593,3.03687,0,0,0.99863,0.052336},
{182106,0,1838.42,-3703.56,167.713,0.890118,0,0,0.430511,0.902585}
};
+
const uint32 EPTowerPlayerEnterEvents[EP_TOWER_NUM] = {10691,10699,10701,10705};
+
const uint32 EPTowerPlayerLeaveEvents[EP_TOWER_NUM] = {10692,10698,10700,10704};
+
const uint32 EP_NUM_CREATURES = 6;
const uint32 EP_EWT_NUM_CREATURES = 5;
+
// one lordaeron commander, 4 soldiers
// should be spawned at EWT and follow a path, but trans-grid pathing isn't safe, so summon them directly at NPT
const creature_type EP_EWT_Summons_A[EP_EWT_NUM_CREATURES] = {
@@ -125,6 +151,7 @@ const creature_type EP_EWT_Summons_A[EP_EWT_NUM_CREATURES] = {
{17647,469,0, 3164.65,-4350.26,138.22,2.4794},
{17647,469,0, 3169.91,-4349.68,138.37,0.7444}
};
+
const creature_type EP_EWT_Summons_H[EP_EWT_NUM_CREATURES] = {
{17995,67,0, 3167.61,-4352.09,138.20,4.5811},
{17996,67,0, 3172.74,-4352.99,139.14,4.9873},
@@ -132,6 +159,7 @@ const creature_type EP_EWT_Summons_H[EP_EWT_NUM_CREATURES] = {
{17996,67,0, 3164.65,-4350.26,138.22,2.4794},
{17996,67,0, 3169.91,-4349.68,138.37,0.7444}
};
+
enum EP_TowerStates {
EP_TS_N = 1,
EP_TS_N_A = 2,
@@ -141,11 +169,15 @@ enum EP_TowerStates {
EP_TS_A = 32,
EP_TS_H = 64
};
+
// when spawning, pay attention at setting the faction manually!
const creature_type EP_PWT_FlightMaster = {17209,0,0,2987.5,-3049.11,120.126,5.75959};
+
// after spawning, modify the faction so that only the controller will be able to use it with SetUInt32Value(GAMEOBJECT_FACTION, faction_id);
const go_type EP_NPT_LordaeronShrine = {181682,0,3167.72,-4355.91,138.785,1.69297,0,0,0.748956,0.66262};
+
class OutdoorPvPEP;
+
class OPvPCapturePointEP_EWT : public OPvPCapturePoint
{
friend class OutdoorPvPEP;
@@ -164,6 +196,7 @@ protected:
uint32 m_TowerState;
uint32 m_UnitsSummonedSide;
};
+
class OPvPCapturePointEP_NPT : public OPvPCapturePoint
{
friend class OutdoorPvPEP;
@@ -182,6 +215,7 @@ protected:
uint32 m_TowerState;
uint32 m_SummonedGOSide;
};
+
class OPvPCapturePointEP_CGT : public OPvPCapturePoint
{
friend class OutdoorPvPEP;
@@ -200,6 +234,7 @@ protected:
uint32 m_TowerState;
uint32 m_GraveyardSide;
};
+
class OPvPCapturePointEP_PWT : public OPvPCapturePoint
{
friend class OutdoorPvPEP;
@@ -220,6 +255,7 @@ protected:
uint32 m_FlightMasterSpawned;
uint32 m_TowerState;
};
+
class OutdoorPvPEP : public OutdoorPvP
{
friend class OPvPCapturePointEP_EWT;
@@ -241,5 +277,6 @@ private:
uint32 m_AllianceTowersControlled;
uint32 m_HordeTowersControlled;
};
+
#endif
diff --git a/src/game/OutdoorPvPHP.cpp b/src/game/OutdoorPvPHP.cpp
index 0b5d3bfac6c..a25cb86dc21 100644
--- a/src/game/OutdoorPvPHP.cpp
+++ b/src/game/OutdoorPvPHP.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPHP.h"
#include "OutdoorPvP.h"
#include "OutdoorPvPMgr.h"
@@ -23,10 +24,15 @@
#include "World.h"
#include "ObjectMgr.h"
#include "Language.h"
+
const uint32 HP_LANG_LOOSE_A[HP_TOWER_NUM] = {LANG_OPVP_HP_LOOSE_BROKENHILL_A,LANG_OPVP_HP_LOOSE_OVERLOOK_A,LANG_OPVP_HP_LOOSE_STADIUM_A};
+
const uint32 HP_LANG_LOOSE_H[HP_TOWER_NUM] = {LANG_OPVP_HP_LOOSE_BROKENHILL_H,LANG_OPVP_HP_LOOSE_OVERLOOK_H,LANG_OPVP_HP_LOOSE_STADIUM_H};
+
const uint32 HP_LANG_CAPTURE_A[HP_TOWER_NUM] = {LANG_OPVP_HP_CAPTURE_BROKENHILL_A,LANG_OPVP_HP_CAPTURE_OVERLOOK_A,LANG_OPVP_HP_CAPTURE_STADIUM_A};
+
const uint32 HP_LANG_CAPTURE_H[HP_TOWER_NUM] = {LANG_OPVP_HP_CAPTURE_BROKENHILL_H,LANG_OPVP_HP_CAPTURE_OVERLOOK_H,LANG_OPVP_HP_CAPTURE_STADIUM_H};
+
OPvPCapturePointHP::OPvPCapturePointHP(OutdoorPvP *pvp,OutdoorPvPHPTowerType type)
: OPvPCapturePoint(pvp), m_TowerType(type)
{
@@ -52,10 +58,12 @@ OPvPCapturePointHP::OPvPCapturePointHP(OutdoorPvP *pvp,OutdoorPvPHPTowerType typ
HPTowerFlags[type].rot2,
HPTowerFlags[type].rot3);
}
+
OutdoorPvPHP::OutdoorPvPHP()
{
m_TypeId = OUTDOOR_PVP_HP;
}
+
bool OutdoorPvPHP::SetupOutdoorPvP()
{
m_AllianceTowersControlled = 0;
@@ -63,11 +71,16 @@ bool OutdoorPvPHP::SetupOutdoorPvP()
// add the zones affected by the pvp buff
for(int i = 0; i < OutdoorPvPHPBuffZonesNum; ++i)
RegisterZone(OutdoorPvPHPBuffZones[i]);
+
AddCapturePoint(new OPvPCapturePointHP(this,HP_TOWER_BROKEN_HILL));
+
AddCapturePoint(new OPvPCapturePointHP(this,HP_TOWER_OVERLOOK));
+
AddCapturePoint(new OPvPCapturePointHP(this,HP_TOWER_STADIUM));
+
return true;
}
+
void OutdoorPvPHP::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
// add buffs
@@ -83,6 +96,7 @@ void OutdoorPvPHP::HandlePlayerEnterZone(Player * plr, uint32 zone)
}
OutdoorPvP::HandlePlayerEnterZone(plr,zone);
}
+
void OutdoorPvPHP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// remove buffs
@@ -96,6 +110,7 @@ void OutdoorPvPHP::HandlePlayerLeaveZone(Player * plr, uint32 zone)
}
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
}
+
bool OutdoorPvPHP::Update(uint32 diff)
{
bool changed = false;
@@ -115,6 +130,7 @@ bool OutdoorPvPHP::Update(uint32 diff)
}
return changed;
}
+
void OutdoorPvPHP::SendRemoveWorldStates(Player *plr)
{
plr->SendUpdateWorldState(HP_UI_TOWER_DISPLAY_A,0);
@@ -131,6 +147,7 @@ void OutdoorPvPHP::SendRemoveWorldStates(Player *plr)
plr->SendUpdateWorldState(HP_MAP_H[i],0);
}
}
+
void OutdoorPvPHP::FillInitialWorldStates(WorldPacket &data)
{
data << uint32(HP_UI_TOWER_DISPLAY_A) << uint32(1);
@@ -145,6 +162,7 @@ void OutdoorPvPHP::FillInitialWorldStates(WorldPacket &data)
itr->second->FillInitialWorldStates(data);
}
}
+
void OPvPCapturePointHP::ChangeState()
{
uint32 field = 0;
@@ -178,6 +196,7 @@ void OPvPCapturePointHP::ChangeState()
field = HP_MAP_H[m_TowerType];
break;
}
+
// send world state update
if(field)
{
@@ -224,6 +243,7 @@ void OPvPCapturePointHP::ChangeState()
artkit2 = HP_TowerArtKit_H[m_TowerType];
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
GameObject* flag2 = HashMapHolder<GameObject>::Find(m_Objects[m_TowerType]);
if(flag)
@@ -236,13 +256,16 @@ void OPvPCapturePointHP::ChangeState()
flag2->SetGoArtKit(artkit2);
flag2->SendUpdateObjectToAllExcept(NULL);
}
+
// send world state update
if(field)
m_PvP->SendUpdateWorldState(field, 1);
+
// complete quest objective
if(m_State == OBJECTIVESTATE_ALLIANCE || m_State == OBJECTIVESTATE_HORDE)
SendObjectiveComplete(HP_CREDITMARKER[m_TowerType], 0);
}
+
void OPvPCapturePointHP::SendChangePhase()
{
SendUpdateWorldState(HP_UI_TOWER_SLIDER_N, m_neutralValuePct);
@@ -252,6 +275,7 @@ void OPvPCapturePointHP::SendChangePhase()
// send this too, sometimes the slider disappears, dunno why :(
SendUpdateWorldState(HP_UI_TOWER_SLIDER_DISPLAY, 1);
}
+
void OPvPCapturePointHP::FillInitialWorldStates(WorldPacket &data)
{
switch(m_State)
@@ -278,6 +302,7 @@ void OPvPCapturePointHP::FillInitialWorldStates(WorldPacket &data)
break;
}
}
+
bool OPvPCapturePointHP::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -290,15 +315,18 @@ bool OPvPCapturePointHP::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointHP::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(HP_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
void OutdoorPvPHP::HandleKillImpl(Player *plr, Unit * killed)
{
if(killed->GetTypeId() != TYPEID_PLAYER)
return;
+
if(plr->GetTeam() == ALLIANCE && ((Player*)killed)->GetTeam() != ALLIANCE)
plr->CastSpell(plr,AlliancePlayerKillReward,true);
else if(plr->GetTeam() == HORDE && ((Player*)killed)->GetTeam() != HORDE)
diff --git a/src/game/OutdoorPvPHP.h b/src/game/OutdoorPvPHP.h
index 44d9305713a..3c9b531630b 100644
--- a/src/game/OutdoorPvPHP.h
+++ b/src/game/OutdoorPvPHP.h
@@ -15,12 +15,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_HP_
#define OUTDOOR_PVP_HP_
+
#include "OutdoorPvPImpl.h"
+
#define OutdoorPvPHPBuffZonesNum 6
// HP, citadel, ramparts, blood furnace, shattered halls, mag's lair
const uint32 OutdoorPvPHPBuffZones[OutdoorPvPHPBuffZonesNum] = { 3483, 3563, 3562, 3713, 3714, 3836 };
+
enum OutdoorPvPHPSpells
{
AlliancePlayerKillReward = 32155,
@@ -28,40 +32,56 @@ enum OutdoorPvPHPSpells
AllianceBuff = 32071,
HordeBuff = 32049
};
+
enum OutdoorPvPHPTowerType{
HP_TOWER_BROKEN_HILL = 0,
HP_TOWER_OVERLOOK = 1,
HP_TOWER_STADIUM = 2,
HP_TOWER_NUM = 3
};
+
const uint32 HP_CREDITMARKER[HP_TOWER_NUM] = {19032,19028,19029};
+
const uint32 HP_CapturePointEvent_Enter[HP_TOWER_NUM] = {11404,11396,11388};
+
const uint32 HP_CapturePointEvent_Leave[HP_TOWER_NUM] = {11403,11395,11387};
+
enum OutdoorPvPHPWorldStates{
HP_UI_TOWER_DISPLAY_A = 0x9ba,
HP_UI_TOWER_DISPLAY_H = 0x9b9,
+
HP_UI_TOWER_COUNT_H = 0x9ae,
HP_UI_TOWER_COUNT_A = 0x9ac,
+
HP_UI_TOWER_SLIDER_N = 2475,
HP_UI_TOWER_SLIDER_POS = 2474,
HP_UI_TOWER_SLIDER_DISPLAY = 2473
};
+
const uint32 HP_MAP_N[HP_TOWER_NUM] = {0x9b5,0x9b2,0x9a8};
+
const uint32 HP_MAP_A[HP_TOWER_NUM] = {0x9b3,0x9b0,0x9a7};
+
const uint32 HP_MAP_H[HP_TOWER_NUM] = {0x9b4,0x9b1,0x9a6};
+
const uint32 HP_TowerArtKit_A[HP_TOWER_NUM] = {65,62,67};
+
const uint32 HP_TowerArtKit_H[HP_TOWER_NUM] = {64,61,68};
+
const uint32 HP_TowerArtKit_N[HP_TOWER_NUM] = {66,63,69};
+
const go_type HPCapturePoints[HP_TOWER_NUM] = {
{182175,530,-471.462,3451.09,34.6432,0.174533,0,0,0.087156,0.996195}, // 0 - Broken Hill
{182174,530,-184.889,3476.93,38.205,-0.017453,0,0,0.008727,-0.999962}, // 1 - Overlook
{182173,530,-290.016,3702.42,56.6729,0.034907,0,0,0.017452,0.999848} // 2 - Stadium
};
+
const go_type HPTowerFlags[HP_TOWER_NUM] = {
{183514,530,-467.078,3528.17,64.7121,3.14159,0,0,1,0}, // 0 broken hill
{182525,530,-187.887,3459.38,60.0403,-3.12414,0,0,0.999962,-0.008727}, // 1 overlook
{183515,530,-289.610,3696.83,75.9447,3.12414,0,0,0.999962,0.008727} // 2 stadium
};
+
class OPvPCapturePointHP : public OPvPCapturePoint
{
public:
@@ -75,6 +95,7 @@ public:
private:
OutdoorPvPHPTowerType m_TowerType;
};
+
class OutdoorPvPHP : public OutdoorPvP
{
friend class OPvPCapturePointHP;
@@ -92,5 +113,6 @@ private:
uint32 m_AllianceTowersControlled;
uint32 m_HordeTowersControlled;
};
+
#endif
diff --git a/src/game/OutdoorPvPImpl.h b/src/game/OutdoorPvPImpl.h
index 2e4f2a0e268..b79b307c046 100644
--- a/src/game/OutdoorPvPImpl.h
+++ b/src/game/OutdoorPvPImpl.h
@@ -17,9 +17,12 @@
*/
#ifndef OUTDOORPVP_IMPL_H
#define OUTDOORPVP_IMPL_H
+
#include "SharedDefines.h"
#include "OutdoorPvP.h"
#include "Player.h"
#include "WorldPacket.h"
+
#define OTHER_TEAM(a) (a == TEAM_ALLIANCE ? TEAM_HORDE : TEAM_ALLIANCE)
+
#endif
diff --git a/src/game/OutdoorPvPMgr.cpp b/src/game/OutdoorPvPMgr.cpp
index 9ccce15ca2d..8a59b438d5b 100644
--- a/src/game/OutdoorPvPMgr.cpp
+++ b/src/game/OutdoorPvPMgr.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPMgr.h"
#include "OutdoorPvPHP.h"
#include "OutdoorPvPNA.h"
@@ -25,12 +26,15 @@
#include "Wintergrasp.h"
#include "Player.h"
#include "Policies/SingletonImp.h"
+
INSTANTIATE_SINGLETON_1( OutdoorPvPMgr );
+
OutdoorPvPMgr::OutdoorPvPMgr()
{
m_UpdateTimer = 0;
//sLog.outDebug("Instantiating OutdoorPvPMgr");
}
+
OutdoorPvPMgr::~OutdoorPvPMgr()
{
//sLog.outDebug("Deleting OutdoorPvPMgr");
@@ -39,6 +43,7 @@ OutdoorPvPMgr::~OutdoorPvPMgr()
(*itr)->DeleteSpawns();
}
}
+
void OutdoorPvPMgr::InitOutdoorPvP()
{
// create new opvp
@@ -55,6 +60,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
sLog.outDebug("OutdoorPvP : HP successfully initiated.");
}
+
pOP = new OutdoorPvPNA;
// respawn, init variables
if(!pOP->SetupOutdoorPvP())
@@ -68,6 +74,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
sLog.outDebug("OutdoorPvP : NA successfully initiated.");
}
+
pOP = new OutdoorPvPTF;
// respawn, init variables
if(!pOP->SetupOutdoorPvP())
@@ -80,6 +87,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
m_OutdoorPvPSet.push_back(pOP);
sLog.outDebug("OutdoorPvP : TF successfully initiated.");
}
+
pOP = new OutdoorPvPZM;
// respawn, init variables
if(!pOP->SetupOutdoorPvP())
@@ -92,6 +100,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
m_OutdoorPvPSet.push_back(pOP);
sLog.outDebug("OutdoorPvP : ZM successfully initiated.");
}
+
pOP = new OutdoorPvPSI;
// respawn, init variables
if(!pOP->SetupOutdoorPvP())
@@ -104,6 +113,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
m_OutdoorPvPSet.push_back(pOP);
sLog.outDebug("OutdoorPvP : SI successfully initiated.");
}
+
pOP = new OutdoorPvPEP;
// respawn, init variables
if(!pOP->SetupOutdoorPvP())
@@ -116,6 +126,7 @@ void OutdoorPvPMgr::InitOutdoorPvP()
m_OutdoorPvPSet.push_back(pOP);
sLog.outDebug("OutdoorPvP : EP successfully initiated.");
}
+
pOP = new OPvPWintergrasp;
// respawn, init variables
if(!pOP->SetupOutdoorPvP())
@@ -129,32 +140,40 @@ void OutdoorPvPMgr::InitOutdoorPvP()
sLog.outDebug("OutdoorPvP : Wintergrasp successfully initiated.");
}
}
+
void OutdoorPvPMgr::AddZone(uint32 zoneid, OutdoorPvP *handle)
{
m_OutdoorPvPMap[zoneid] = handle;
}
+
void OutdoorPvPMgr::HandlePlayerEnterZone(Player *plr, uint32 zoneid)
{
OutdoorPvPMap::iterator itr = m_OutdoorPvPMap.find(zoneid);
if(itr == m_OutdoorPvPMap.end())
return;
+
if(itr->second->HasPlayer(plr))
return;
+
itr->second->HandlePlayerEnterZone(plr, zoneid);
sLog.outDebug("Player %u entered outdoorpvp id %u", plr->GetGUIDLow(), itr->second->GetTypeId());
}
+
void OutdoorPvPMgr::HandlePlayerLeaveZone(Player *plr, uint32 zoneid)
{
OutdoorPvPMap::iterator itr = m_OutdoorPvPMap.find(zoneid);
if(itr == m_OutdoorPvPMap.end())
return;
+
// teleport: remove once in removefromworld, once in updatezone
if(!itr->second->HasPlayer(plr))
return;
+
itr->second->HandlePlayerLeaveZone(plr, zoneid);
sLog.outDebug("Player %u left outdoorpvp id %u",plr->GetGUIDLow(), itr->second->GetTypeId());
}
+
OutdoorPvP * OutdoorPvPMgr::GetOutdoorPvPToZoneId(uint32 zoneid)
{
OutdoorPvPMap::iterator itr = m_OutdoorPvPMap.find(zoneid);
@@ -165,6 +184,7 @@ OutdoorPvP * OutdoorPvPMgr::GetOutdoorPvPToZoneId(uint32 zoneid)
}
return itr->second;
}
+
void OutdoorPvPMgr::Update(uint32 diff)
{
m_UpdateTimer += diff;
@@ -175,6 +195,7 @@ void OutdoorPvPMgr::Update(uint32 diff)
m_UpdateTimer = 0;
}
}
+
bool OutdoorPvPMgr::HandleCustomSpell(Player *plr, uint32 spellId, GameObject * go)
{
for(OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
@@ -184,6 +205,7 @@ bool OutdoorPvPMgr::HandleCustomSpell(Player *plr, uint32 spellId, GameObject *
}
return false;
}
+
ZoneScript * OutdoorPvPMgr::GetZoneScript(uint32 zoneId)
{
OutdoorPvPMap::iterator itr = m_OutdoorPvPMap.find(zoneId);
@@ -192,6 +214,7 @@ ZoneScript * OutdoorPvPMgr::GetZoneScript(uint32 zoneId)
else
return NULL;
}
+
bool OutdoorPvPMgr::HandleOpenGo(Player *plr, uint64 guid)
{
for(OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
@@ -201,6 +224,7 @@ bool OutdoorPvPMgr::HandleOpenGo(Player *plr, uint64 guid)
}
return false;
}
+
void OutdoorPvPMgr::HandleGossipOption(Player *plr, uint64 guid, uint32 gossipid)
{
for(OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
@@ -209,6 +233,7 @@ void OutdoorPvPMgr::HandleGossipOption(Player *plr, uint64 guid, uint32 gossipid
return;
}
}
+
bool OutdoorPvPMgr::CanTalkTo(Player * plr, Creature * c, GossipOption & gso)
{
for(OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
@@ -218,6 +243,7 @@ bool OutdoorPvPMgr::CanTalkTo(Player * plr, Creature * c, GossipOption & gso)
}
return false;
}
+
void OutdoorPvPMgr::HandleDropFlag(Player *plr, uint32 spellId)
{
for(OutdoorPvPSet::iterator itr = m_OutdoorPvPSet.begin(); itr != m_OutdoorPvPSet.end(); ++itr)
diff --git a/src/game/OutdoorPvPMgr.h b/src/game/OutdoorPvPMgr.h
index 28c949c4bce..d3b1af92e97 100644
--- a/src/game/OutdoorPvPMgr.h
+++ b/src/game/OutdoorPvPMgr.h
@@ -15,16 +15,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_MGR_H_
#define OUTDOOR_PVP_MGR_H_
+
#define OUTDOORPVP_OBJECTIVE_UPDATE_INTERVAL 1000
+
#include "OutdoorPvP.h"
#include "Policies/Singleton.h"
+
class Player;
class GameObject;
class Creature;
class ZoneScript;
struct GossipOption;
+
// class to handle player enter / leave / areatrigger / GO use events
class OutdoorPvPMgr
{
@@ -33,6 +38,7 @@ public:
OutdoorPvPMgr();
// dtor
~OutdoorPvPMgr();
+
// create outdoor pvp events
void InitOutdoorPvP();
// called when a player enters an outdoor pvp area
@@ -45,12 +51,19 @@ public:
bool HandleCustomSpell(Player * plr, uint32 spellId, GameObject* go);
// handle custom go if registered
bool HandleOpenGo(Player * plr, uint64 guid);
+
ZoneScript * GetZoneScript(uint32 zoneId);
+
void AddZone(uint32 zoneid, OutdoorPvP * handle);
+
void Update(uint32 diff);
+
void HandleGossipOption(Player * player, uint64 guid, uint32 gossipid);
+
bool CanTalkTo(Player * player, Creature * creature, GossipOption & gso);
+
void HandleDropFlag(Player * plr, uint32 spellId);
+
typedef std::vector<OutdoorPvP*> OutdoorPvPSet;
typedef std::map<uint32 /* zoneid */, OutdoorPvP*> OutdoorPvPMap;
private:
@@ -63,6 +76,8 @@ private:
// update interval
uint32 m_UpdateTimer;
};
+
#define sOutdoorPvPMgr Trinity::Singleton<OutdoorPvPMgr>::Instance()
+
#endif /*OUTDOOR_PVP_MGR_H_*/
diff --git a/src/game/OutdoorPvPNA.cpp b/src/game/OutdoorPvPNA.cpp
index 56708550510..035d6ce9405 100644
--- a/src/game/OutdoorPvPNA.cpp
+++ b/src/game/OutdoorPvPNA.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPNA.h"
#include "Player.h"
#include "ObjectMgr.h"
@@ -22,10 +23,12 @@
#include "WorldPacket.h"
#include "Language.h"
#include "World.h"
+
OutdoorPvPNA::OutdoorPvPNA()
{
m_TypeId = OUTDOOR_PVP_NA;
}
+
void OutdoorPvPNA::HandleKillImpl(Player *plr, Unit * killed)
{
if(killed->GetTypeId() == TYPEID_PLAYER && plr->GetTeam() != ((Player*)killed)->GetTeam())
@@ -37,6 +40,7 @@ void OutdoorPvPNA::HandleKillImpl(Player *plr, Unit * killed)
plr->CastSpell(plr,NA_KILL_TOKEN_HORDE,true);
}
}
+
uint32 OPvPCapturePointNA::GetAliveGuardsCount()
{
uint32 cnt = 0;
@@ -78,6 +82,7 @@ uint32 OPvPCapturePointNA::GetAliveGuardsCount()
}
return cnt;
}
+
void OPvPCapturePointNA::SpawnNPCsForTeam(uint32 team)
{
const creature_type * creatures = NULL;
@@ -90,11 +95,13 @@ void OPvPCapturePointNA::SpawnNPCsForTeam(uint32 team)
for(int i = 0; i < NA_CONTROL_NPC_NUM; ++i)
AddCreature(i,creatures[i].entry,creatures[i].teamval,creatures[i].map,creatures[i].x,creatures[i].y,creatures[i].z,creatures[i].o,1000000);
}
+
void OPvPCapturePointNA::DeSpawnNPCs()
{
for(int i = 0; i < NA_CONTROL_NPC_NUM; ++i)
DelCreature(i);
}
+
void OPvPCapturePointNA::SpawnGOsForTeam(uint32 team)
{
const go_type * gos = NULL;
@@ -118,6 +125,7 @@ void OPvPCapturePointNA::SpawnGOsForTeam(uint32 team)
AddObject(i,gos[i].entry,gos[i].map,gos[i].x,gos[i].y,gos[i].z,gos[i].o,gos[i].rot0,gos[i].rot1,gos[i].rot2,gos[i].rot3);
}
}
+
void OPvPCapturePointNA::DeSpawnGOs()
{
for(int i = 0; i < NA_CONTROL_GO_NUM; ++i)
@@ -125,6 +133,7 @@ void OPvPCapturePointNA::DeSpawnGOs()
DelObject(i);
}
}
+
void OPvPCapturePointNA::FactionTakeOver(uint32 team)
{
if(m_ControllingFaction)
@@ -133,6 +142,7 @@ void OPvPCapturePointNA::FactionTakeOver(uint32 team)
sWorld.SendZoneText(NA_HALAA_GRAVEYARD_ZONE,objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_NA_LOOSE_A));
else if(m_ControllingFaction == HORDE)
sWorld.SendZoneText(NA_HALAA_GRAVEYARD_ZONE,objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_NA_LOOSE_H));
+
m_ControllingFaction = team;
if(m_ControllingFaction)
objmgr.AddGraveYardLink(NA_HALAA_GRAVEYARD,NA_HALAA_GRAVEYARD_ZONE,m_ControllingFaction,false);
@@ -172,6 +182,7 @@ void OPvPCapturePointNA::FactionTakeOver(uint32 team)
UpdateWyvernRoostWorldState(NA_ROOST_W);
UpdateWyvernRoostWorldState(NA_ROOST_E);
}
+
bool OPvPCapturePointNA::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -184,11 +195,13 @@ bool OPvPCapturePointNA::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointNA::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(NA_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
OPvPCapturePointNA::OPvPCapturePointNA(OutdoorPvP *pvp) :
OPvPCapturePoint(pvp), m_capturable(true), m_GuardsAlive(0), m_ControllingFaction(0),
m_HalaaState(HALAA_N), m_WyvernStateSouth(0), m_WyvernStateNorth(0), m_WyvernStateWest(0),
@@ -196,18 +209,22 @@ m_WyvernStateEast(0), m_RespawnTimer(NA_RESPAWN_TIME), m_GuardCheckTimer(NA_GUAR
{
SetCapturePointData(182210,530,-1572.57,7945.3,-22.475,2.05949,0,0,0.857167,0.515038);
}
+
bool OutdoorPvPNA::SetupOutdoorPvP()
{
// m_TypeId = OUTDOOR_PVP_NA; _MUST_ be set in ctor, because of spawns cleanup
// add the zones affected by the pvp buff
RegisterZone(NA_BUFF_ZONE);
+
// halaa
m_obj = new OPvPCapturePointNA(this);
if(!m_obj)
return false;
AddCapturePoint(m_obj);
+
return true;
}
+
void OutdoorPvPNA::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
// add buffs
@@ -215,16 +232,19 @@ void OutdoorPvPNA::HandlePlayerEnterZone(Player * plr, uint32 zone)
plr->CastSpell(plr,NA_CAPTURE_BUFF,true);
OutdoorPvP::HandlePlayerEnterZone(plr,zone);
}
+
void OutdoorPvPNA::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// remove buffs
plr->RemoveAurasDueToSpell(NA_CAPTURE_BUFF);
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
}
+
void OutdoorPvPNA::FillInitialWorldStates(WorldPacket &data)
{
m_obj->FillInitialWorldStates(data);
}
+
void OPvPCapturePointNA::FillInitialWorldStates(WorldPacket &data)
{
if(m_ControllingFaction == ALLIANCE)
@@ -242,33 +262,41 @@ void OPvPCapturePointNA::FillInitialWorldStates(WorldPacket &data)
data << NA_UI_HORDE_GUARDS_SHOW << uint32(0);
data << NA_UI_ALLIANCE_GUARDS_SHOW << uint32(0);
}
+
data << NA_UI_GUARDS_MAX << NA_GUARDS_MAX;
data << NA_UI_GUARDS_LEFT << uint32(m_GuardsAlive);
+
data << NA_UI_TOWER_SLIDER_DISPLAY << uint32(0);
data << NA_UI_TOWER_SLIDER_POS << uint32(50);
data << NA_UI_TOWER_SLIDER_N << uint32(100);
+
data << NA_MAP_WYVERN_NORTH_NEU_H << uint32(bool(m_WyvernStateNorth & WYVERN_NEU_HORDE));
data << NA_MAP_WYVERN_NORTH_NEU_A << uint32(bool(m_WyvernStateNorth & WYVERN_NEU_ALLIANCE));
data << NA_MAP_WYVERN_NORTH_H << uint32(bool(m_WyvernStateNorth & WYVERN_HORDE));
data << NA_MAP_WYVERN_NORTH_A << uint32(bool(m_WyvernStateNorth & WYVERN_ALLIANCE));
+
data << NA_MAP_WYVERN_SOUTH_NEU_H << uint32(bool(m_WyvernStateSouth & WYVERN_NEU_HORDE));
data << NA_MAP_WYVERN_SOUTH_NEU_A << uint32(bool(m_WyvernStateSouth & WYVERN_NEU_ALLIANCE));
data << NA_MAP_WYVERN_SOUTH_H << uint32(bool(m_WyvernStateSouth & WYVERN_HORDE));
data << NA_MAP_WYVERN_SOUTH_A << uint32(bool(m_WyvernStateSouth & WYVERN_ALLIANCE));
+
data << NA_MAP_WYVERN_WEST_NEU_H << uint32(bool(m_WyvernStateWest & WYVERN_NEU_HORDE));
data << NA_MAP_WYVERN_WEST_NEU_A << uint32(bool(m_WyvernStateWest & WYVERN_NEU_ALLIANCE));
data << NA_MAP_WYVERN_WEST_H << uint32(bool(m_WyvernStateWest & WYVERN_HORDE));
data << NA_MAP_WYVERN_WEST_A << uint32(bool(m_WyvernStateWest & WYVERN_ALLIANCE));
+
data << NA_MAP_WYVERN_EAST_NEU_H << uint32(bool(m_WyvernStateEast & WYVERN_NEU_HORDE));
data << NA_MAP_WYVERN_EAST_NEU_A << uint32(bool(m_WyvernStateEast & WYVERN_NEU_ALLIANCE));
data << NA_MAP_WYVERN_EAST_H << uint32(bool(m_WyvernStateEast & WYVERN_HORDE));
data << NA_MAP_WYVERN_EAST_A << uint32(bool(m_WyvernStateEast & WYVERN_ALLIANCE));
+
data << NA_MAP_HALAA_NEUTRAL << uint32(bool(m_HalaaState & HALAA_N));
data << NA_MAP_HALAA_NEU_A << uint32(bool(m_HalaaState & HALAA_N_A));
data << NA_MAP_HALAA_NEU_H << uint32(bool(m_HalaaState & HALAA_N_H));
data << NA_MAP_HALAA_HORDE << uint32(bool(m_HalaaState & HALAA_H));
data << NA_MAP_HALAA_ALLIANCE << uint32(bool(m_HalaaState & HALAA_A));
}
+
void OutdoorPvPNA::SendRemoveWorldStates(Player *plr)
{
plr->SendUpdateWorldState(NA_UI_HORDE_GUARDS_SHOW,0);
@@ -300,10 +328,12 @@ void OutdoorPvPNA::SendRemoveWorldStates(Player *plr)
plr->SendUpdateWorldState(NA_MAP_HALAA_HORDE,0);
plr->SendUpdateWorldState(NA_MAP_HALAA_ALLIANCE,0);
}
+
bool OutdoorPvPNA::Update(uint32 diff)
{
return m_obj->Update(diff);
}
+
bool OPvPCapturePointNA::HandleCustomSpell(Player * plr, uint32 spellId, GameObject * go)
{
std::vector<uint32> nodes;
@@ -346,31 +376,39 @@ bool OPvPCapturePointNA::HandleCustomSpell(Player * plr, uint32 spellId, GameObj
default:
break;
}
+
if(retval)
{
//Adding items
uint32 noSpaceForCount = 0;
+
// check space and find places
ItemPosCountVec dest;
+
int32 count = 10;
uint32 itemid = 24538;
// bomb id count
uint8 msg = plr->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
{
return true;
}
+
Item* item = plr->StoreNewItem( dest, itemid, true);
+
if(count > 0 && item)
{
plr->SendNewItem(item,count,true,false);
}
+
return true;
}
return false;
}
+
int32 OPvPCapturePointNA::HandleOpenGo(Player *plr, uint64 guid)
{
uint32 retval = OPvPCapturePoint::HandleOpenGo(plr, guid);
@@ -383,10 +421,12 @@ int32 OPvPCapturePointNA::HandleOpenGo(Player *plr, uint64 guid)
gos=HordeControlGOs;
else
return -1;
+
int32 del = -1;
int32 del2 = -1;
int32 add = -1;
int32 add2 = -1;
+
switch(retval)
{
case NA_DESTROYED_ROOST_S:
@@ -473,18 +513,24 @@ int32 OPvPCapturePointNA::HandleOpenGo(Player *plr, uint64 guid)
return -1;
break;
}
+
if(del>-1)
DelObject(del);
+
if(del2>-1)
DelObject(del2);
+
if(add>-1)
AddObject(add,gos[add].entry,gos[add].map,gos[add].x,gos[add].y,gos[add].z,gos[add].o,gos[add].rot0,gos[add].rot1,gos[add].rot2,gos[add].rot3);
+
if(add2>-1)
AddObject(add2,gos[add2].entry,gos[add2].map,gos[add2].x,gos[add2].y,gos[add2].z,gos[add2].o,gos[add2].rot0,gos[add2].rot1,gos[add2].rot2,gos[add2].rot3);
+
return retval;
}
return -1;
}
+
bool OPvPCapturePointNA::Update(uint32 diff)
{
// let the controlling faction advance in phase
@@ -493,6 +539,7 @@ bool OPvPCapturePointNA::Update(uint32 diff)
capturable = true;
else if(m_ControllingFaction == HORDE && m_activePlayers[0].size() < m_activePlayers[1].size())
capturable = true;
+
if(m_GuardCheckTimer < diff)
{
m_GuardCheckTimer = NA_GUARD_CHECK_TIME;
@@ -506,6 +553,7 @@ bool OPvPCapturePointNA::Update(uint32 diff)
m_PvP->SendUpdateWorldState(NA_UI_GUARDS_LEFT,m_GuardsAlive);
}
} else m_GuardCheckTimer -= diff;
+
if(m_capturable || capturable)
{
if(m_RespawnTimer < diff)
@@ -516,10 +564,12 @@ bool OPvPCapturePointNA::Update(uint32 diff)
FactionTakeOver(m_ControllingFaction);
m_RespawnTimer = NA_RESPAWN_TIME;
} else m_RespawnTimer -= diff;
+
return OPvPCapturePoint::Update(diff);
}
return false;
}
+
void OPvPCapturePointNA::ChangeState()
{
uint32 artkit = 21;
@@ -553,14 +603,17 @@ void OPvPCapturePointNA::ChangeState()
artkit = 1;
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
if(flag)
{
flag->SetGoArtKit(artkit);
flag->SendUpdateObjectToAllExcept(NULL);
}
+
UpdateHalaaWorldState();
}
+
void OPvPCapturePointNA::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
@@ -570,6 +623,7 @@ void OPvPCapturePointNA::SendChangePhase()
SendUpdateWorldState(NA_UI_TOWER_SLIDER_POS, phase);
SendUpdateWorldState(NA_UI_TOWER_SLIDER_N, m_neutralValuePct);
}
+
void OPvPCapturePointNA::UpdateHalaaWorldState()
{
m_PvP->SendUpdateWorldState( NA_MAP_HALAA_NEUTRAL ,uint32(bool(m_HalaaState & HALAA_N)));
@@ -578,6 +632,7 @@ void OPvPCapturePointNA::UpdateHalaaWorldState()
m_PvP->SendUpdateWorldState( NA_MAP_HALAA_HORDE ,uint32(bool(m_HalaaState & HALAA_H)));
m_PvP->SendUpdateWorldState( NA_MAP_HALAA_ALLIANCE ,uint32(bool(m_HalaaState & HALAA_A)));
}
+
void OPvPCapturePointNA::UpdateWyvernRoostWorldState(uint32 roost)
{
switch(roost)
diff --git a/src/game/OutdoorPvPNA.h b/src/game/OutdoorPvPNA.h
index 53d34d87f97..bfafda34340 100644
--- a/src/game/OutdoorPvPNA.h
+++ b/src/game/OutdoorPvPNA.h
@@ -15,9 +15,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_NA_
#define OUTDOOR_PVP_NA_
+
// TODO: "sometimes" set to neutral
+
#include "OutdoorPvPImpl.h"
enum OutdoorPvPNASpells
{
@@ -28,102 +31,129 @@ enum OutdoorPvPNASpells
// kill credit for pks
const uint32 NA_CREDIT_MARKER = 24867;
const uint32 NA_GUARDS_MAX = 15;
+
const uint32 NA_BUFF_ZONE = 3518;
+
const uint32 NA_HALAA_GRAVEYARD = 993;
+
const uint32 NA_HALAA_GRAVEYARD_ZONE = 3518; // need to add zone id, not area id
+
const uint32 NA_RESPAWN_TIME = 3600000; // one hour to capture after defeating all guards
+
const uint32 NA_GUARD_CHECK_TIME = 500; // every half second
+
enum OutdoorPvPNAWorldStates{
NA_UI_HORDE_GUARDS_SHOW = 2503,
NA_UI_ALLIANCE_GUARDS_SHOW = 2502,
NA_UI_GUARDS_MAX = 2493,
NA_UI_GUARDS_LEFT = 2491,
+
NA_UI_TOWER_SLIDER_DISPLAY = 2495,
NA_UI_TOWER_SLIDER_POS = 2494,
NA_UI_TOWER_SLIDER_N = 2497,
+
NA_MAP_WYVERN_NORTH_NEU_H = 2762,
NA_MAP_WYVERN_NORTH_NEU_A = 2662,
NA_MAP_WYVERN_NORTH_H = 2663,
NA_MAP_WYVERN_NORTH_A = 2664,
+
NA_MAP_WYVERN_SOUTH_NEU_H = 2760,
NA_MAP_WYVERN_SOUTH_NEU_A = 2670,
NA_MAP_WYVERN_SOUTH_H = 2668,
NA_MAP_WYVERN_SOUTH_A = 2669,
+
NA_MAP_WYVERN_WEST_NEU_H = 2761,
NA_MAP_WYVERN_WEST_NEU_A = 2667,
NA_MAP_WYVERN_WEST_H = 2665,
NA_MAP_WYVERN_WEST_A = 2666,
+
NA_MAP_WYVERN_EAST_NEU_H = 2763,
NA_MAP_WYVERN_EAST_NEU_A = 2659,
NA_MAP_WYVERN_EAST_H = 2660,
NA_MAP_WYVERN_EAST_A = 2661,
+
NA_MAP_HALAA_NEUTRAL = 2671,
NA_MAP_HALAA_NEU_A = 2676,
NA_MAP_HALAA_NEU_H = 2677,
NA_MAP_HALAA_HORDE = 2672,
NA_MAP_HALAA_ALLIANCE = 2673
};
+
const uint32 FLIGHT_NODES_NUM = 4;
+
// used to access the elements of Horde/AllyControlGOs
enum ControlGOTypes{
NA_ROOST_S = 0,
NA_ROOST_W = 1,
NA_ROOST_N = 2,
NA_ROOST_E = 3,
+
NA_BOMB_WAGON_S = 4,
NA_BOMB_WAGON_W = 5,
NA_BOMB_WAGON_N = 6,
NA_BOMB_WAGON_E = 7,
+
NA_DESTROYED_ROOST_S = 8,
NA_DESTROYED_ROOST_W = 9,
NA_DESTROYED_ROOST_N = 10,
NA_DESTROYED_ROOST_E = 11,
+
NA_CONTROL_GO_NUM = 12
};
+
const uint32 FlightPathStartNodes[FLIGHT_NODES_NUM] = {103,105,107,109};
const uint32 FlightPathEndNodes[FLIGHT_NODES_NUM] = {104,106,108,110};
+
enum FlightSpellsNA{
NA_SPELL_FLY_SOUTH = 32059,
NA_SPELL_FLY_WEST = 32068,
NA_SPELL_FLY_NORTH = 32075,
NA_SPELL_FLY_EAST = 32081
};
+
// spawned when the alliance is attacking, horde is in control
const go_type HordeControlGOs[NA_CONTROL_GO_NUM] = {
{182267,530,-1815.8,8036.51,-26.2354,-2.89725,0,0,0.992546,-0.121869}, //ALLY_ROOST_SOUTH
{182280,530,-1507.95,8132.1,-19.5585,-1.3439,0,0,0.622515,-0.782608}, //ALLY_ROOST_WEST
{182281,530,-1384.52,7779.33,-11.1663,-0.575959,0,0,0.284015,-0.95882}, //ALLY_ROOST_NORTH
{182282,530,-1650.11,7732.56,-15.4505,-2.80998,0,0,0.986286,-0.165048}, //ALLY_ROOST_EAST
+
{182222,530,-1825.4022,8039.2602,-26.08,-2.89725,0,0,0.992546,-0.121869}, //HORDE_BOMB_WAGON_SOUTH
{182272,530,-1515.37,8136.91,-20.42,-1.3439,0,0,0.622515,-0.782608}, //HORDE_BOMB_WAGON_WEST
{182273,530,-1377.95,7773.44,-10.31,-0.575959,0,0,0.284015,-0.95882}, //HORDE_BOMB_WAGON_NORTH
{182274,530,-1659.87,7733.15,-15.75,-2.80998,0,0,0.986286,-0.165048}, //HORDE_BOMB_WAGON_EAST
+
{182266,530,-1815.8,8036.51,-26.2354,-2.89725,0,0,0.992546,-0.121869}, //DESTROYED_ALLY_ROOST_SOUTH
{182275,530,-1507.95,8132.1,-19.5585,-1.3439,0,0,0.622515,-0.782608}, //DESTROYED_ALLY_ROOST_WEST
{182276,530,-1384.52,7779.33,-11.1663,-0.575959,0,0,0.284015,-0.95882}, //DESTROYED_ALLY_ROOST_NORTH
{182277,530,-1650.11,7732.56,-15.4505,-2.80998,0,0,0.986286,-0.165048} //DESTROYED_ALLY_ROOST_EAST
};
+
// spawned when the horde is attacking, alliance is in control
const go_type AllianceControlGOs[NA_CONTROL_GO_NUM] = {
{182301,530,-1815.8,8036.51,-26.2354,-2.89725,0,0,0.992546,-0.121869}, //HORDE_ROOST_SOUTH
{182302,530,-1507.95,8132.1,-19.5585,-1.3439,0,0,0.622515,-0.782608}, //HORDE_ROOST_WEST
{182303,530,-1384.52,7779.33,-11.1663,-0.575959,0,0,0.284015,-0.95882}, //HORDE_ROOST_NORTH
{182304,530,-1650.11,7732.56,-15.4505,-2.80998,0,0,0.986286,-0.165048}, //HORDE_ROOST_EAST
+
{182305,530,-1825.4022,8039.2602,-26.08,-2.89725,0,0,0.992546,-0.121869}, //ALLY_BOMB_WAGON_SOUTH
{182306,530,-1515.37,8136.91,-20.42,-1.3439,0,0,0.622515,-0.782608}, //ALLY_BOMB_WAGON_WEST
{182307,530,-1377.95,7773.44,-10.31,-0.575959,0,0,0.284015,-0.95882}, //ALLY_BOMB_WAGON_NORTH
{182308,530,-1659.87,7733.15,-15.75,-2.80998,0,0,0.986286,-0.165048}, //ALLY_BOMB_WAGON_EAST
+
{182297,530,-1815.8,8036.51,-26.2354,-2.89725,0,0,0.992546,-0.121869}, //DESTROYED_HORDE_ROOST_SOUTH
{182298,530,-1507.95,8132.1,-19.5585,-1.3439,0,0,0.622515,-0.782608}, //DESTROYED_HORDE_ROOST_WEST
{182299,530,-1384.52,7779.33,-11.1663,-0.575959,0,0,0.284015,-0.95882}, //DESTROYED_HORDE_ROOST_NORTH
{182300,530,-1650.11,7732.56,-15.4505,-2.80998,0,0,0.986286,-0.165048} //DESTROYED_HORDE_ROOST_EAST
};
+
enum ControlNPCTypes{
NA_NPC_RESEARCHER = 0,
NA_NPC_QUARTERMASTER,
NA_NPC_BLADE_MERCHANT,
NA_NPC_FOOD_MERCHANT,
NA_NPC_AMMO,
+
NA_NPC_GUARD_01,
NA_NPC_GUARD_02,
NA_NPC_GUARD_03,
@@ -139,8 +169,10 @@ enum ControlNPCTypes{
NA_NPC_GUARD_13,
NA_NPC_GUARD_14,
NA_NPC_GUARD_15,
+
NA_CONTROL_NPC_NUM
};
+
const creature_type HordeControlNPCs[NA_CONTROL_NPC_NUM] = {
{18816,67,530,-1523.92,7951.76,-17.6942,3.51172},
{18821,67,530,-1527.75,7952.46,-17.6948,3.99317},
@@ -163,6 +195,7 @@ const creature_type HordeControlNPCs[NA_CONTROL_NPC_NUM] = {
{18192,67,530,-1545.57,7935.83,-21.13,3.448},
{18192,67,530,-1550.86,7937.56,-21.7,3.801}
};
+
const creature_type AllianceControlNPCs[NA_CONTROL_NPC_NUM] = {
{18817,469,530,-1591.18,8020.39,-22.2042,4.59022},
{18822,469,530,-1588.0,8019.0,-22.2042,4.06662},
@@ -185,12 +218,14 @@ const creature_type AllianceControlNPCs[NA_CONTROL_NPC_NUM] = {
{18256,469,530,-1585.73,7994.68,-23.29,4.439},
{18256,469,530,-1595.5,7991.27,-23.53,4.738}
};
+
enum WyvernStates{
WYVERN_NEU_HORDE = 1,
WYVERN_NEU_ALLIANCE = 2,
WYVERN_HORDE = 4,
WYVERN_ALLIANCE = 8
};
+
enum HalaaStates{
HALAA_N = 1,
HALAA_N_A = 2,
@@ -198,6 +233,7 @@ enum HalaaStates{
HALAA_N_H = 8,
HALAA_H = 16
};
+
class Unit;
class Creature;
class OutdoorPvPNA;
@@ -219,12 +255,15 @@ public:
protected:
// called when a faction takes control
void FactionTakeOver(uint32 team);
+
void DeSpawnNPCs();
void DeSpawnGOs();
void SpawnNPCsForTeam(uint32 team);
void SpawnGOsForTeam(uint32 team);
+
void UpdateWyvernRoostWorldState(uint32 roost);
void UpdateHalaaWorldState();
+
private:
bool m_capturable;
uint32 m_GuardsAlive;
@@ -237,6 +276,7 @@ private:
uint32 m_RespawnTimer;
uint32 m_GuardCheckTimer;
};
+
class OutdoorPvPNA : public OutdoorPvP
{
friend class OPvPCapturePointNA;
@@ -252,5 +292,6 @@ public:
private:
OPvPCapturePointNA * m_obj;
};
+
#endif
diff --git a/src/game/OutdoorPvPSI.cpp b/src/game/OutdoorPvPSI.cpp
index 83c3f4fdbd8..e60888c7e08 100644
--- a/src/game/OutdoorPvPSI.cpp
+++ b/src/game/OutdoorPvPSI.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPSI.h"
#include "WorldPacket.h"
#include "Player.h"
@@ -24,6 +25,7 @@
#include "OutdoorPvPMgr.h"
#include "Language.h"
#include "World.h"
+
OutdoorPvPSI::OutdoorPvPSI()
{
m_TypeId = OUTDOOR_PVP_SI;
@@ -31,46 +33,54 @@ OutdoorPvPSI::OutdoorPvPSI()
m_Gathered_H = 0;
m_LastController = 0;
}
+
void OutdoorPvPSI::FillInitialWorldStates(WorldPacket &data)
{
data << SI_GATHERED_A << m_Gathered_A;
data << SI_GATHERED_H << m_Gathered_H;
data << SI_SILITHYST_MAX << SI_MAX_RESOURCES;
}
+
void OutdoorPvPSI::SendRemoveWorldStates(Player *plr)
{
plr->SendUpdateWorldState(SI_GATHERED_A,0);
plr->SendUpdateWorldState(SI_GATHERED_H,0);
plr->SendUpdateWorldState(SI_SILITHYST_MAX,0);
}
+
void OutdoorPvPSI::UpdateWorldState()
{
SendUpdateWorldState(SI_GATHERED_A,m_Gathered_A);
SendUpdateWorldState(SI_GATHERED_H,m_Gathered_H);
SendUpdateWorldState(SI_SILITHYST_MAX,SI_MAX_RESOURCES);
}
+
bool OutdoorPvPSI::SetupOutdoorPvP()
{
for(int i = 0; i < OutdoorPvPSIBuffZonesNum; ++i)
RegisterZone(OutdoorPvPSIBuffZones[i]);
return true;
}
+
bool OutdoorPvPSI::Update(uint32 diff)
{
return false;
}
+
void OutdoorPvPSI::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
if(plr->GetTeam() == m_LastController)
plr->CastSpell(plr,SI_CENARION_FAVOR,true);
OutdoorPvP::HandlePlayerEnterZone(plr,zone);
}
+
void OutdoorPvPSI::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// remove buffs
plr->RemoveAurasDueToSpell(SI_CENARION_FAVOR);
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
}
+
bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger)
{
switch(trigger)
@@ -128,6 +138,7 @@ bool OutdoorPvPSI::HandleAreaTrigger(Player *plr, uint32 trigger)
}
return false;
}
+
bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId)
{
if(spellId == SI_SILITHYST_FLAG)
@@ -151,6 +162,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId)
delete go;
return true;
}
+
if(!go->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT),SI_SILITHYST_MOUND, map, plr->GetPhaseMask(), plr->GetPositionX(),plr->GetPositionY(),plr->GetPositionZ(),plr->GetOrientation(),0,0,0,0,100,GO_STATE_READY))
{
delete go;
@@ -198,6 +210,7 @@ bool OutdoorPvPSI::HandleDropFlag(Player *plr, uint32 spellId)
}
return false;
}
+
bool OutdoorPvPSI::HandleCustomSpell(Player *plr, uint32 spellId, GameObject *go)
{
if(!go || spellId != SI_SILITHYST_FLAG_GO_SPELL)
diff --git a/src/game/OutdoorPvPSI.h b/src/game/OutdoorPvPSI.h
index 86041c29026..569e7c62057 100644
--- a/src/game/OutdoorPvPSI.h
+++ b/src/game/OutdoorPvPSI.h
@@ -15,9 +15,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_SI_
#define OUTDOOR_PVP_SI_
+
#include "OutdoorPvPImpl.h"
+
enum OutdoorPvPSISpells
{
SI_SILITHYST_FLAG_GO_SPELL = 29518,
@@ -25,19 +28,29 @@ enum OutdoorPvPSISpells
SI_TRACES_OF_SILITHYST = 29534,
SI_CENARION_FAVOR = 30754
};
+
const uint32 SI_MAX_RESOURCES = 200;
+
const uint32 OutdoorPvPSIBuffZonesNum = 3;
+
const uint32 OutdoorPvPSIBuffZones[OutdoorPvPSIBuffZonesNum] = { 1377, 3428, 3429 };
+
const uint32 SI_AREATRIGGER_H = 4168;
+
const uint32 SI_AREATRIGGER_A = 4162;
+
const uint32 SI_TURNIN_QUEST_CM_A = 17090;
+
const uint32 SI_TURNIN_QUEST_CM_H = 18199;
+
const uint32 SI_SILITHYST_MOUND = 181597;
+
enum SI_WorldStates{
SI_GATHERED_A = 2313,
SI_GATHERED_H = 2314,
SI_SILITHYST_MAX = 2317
};
+
class OutdoorPvPSI : public OutdoorPvP
{
public:
@@ -57,5 +70,6 @@ private:
uint32 m_Gathered_H;
uint32 m_LastController;
};
+
#endif
diff --git a/src/game/OutdoorPvPTF.cpp b/src/game/OutdoorPvPTF.cpp
index 6ac6067b909..5a8483a2c11 100644
--- a/src/game/OutdoorPvPTF.cpp
+++ b/src/game/OutdoorPvPTF.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPTF.h"
#include "OutdoorPvPMgr.h"
#include "WorldPacket.h"
@@ -22,54 +23,67 @@
#include "ObjectMgr.h"
#include "Language.h"
#include "World.h"
+
OutdoorPvPTF::OutdoorPvPTF()
{
m_TypeId = OUTDOOR_PVP_TF;
}
+
OPvPCapturePointTF::OPvPCapturePointTF(OutdoorPvP *pvp, OutdoorPvPTF_TowerType type)
: OPvPCapturePoint(pvp), m_TowerType(type), m_TowerState(TF_TOWERSTATE_N)
{
SetCapturePointData(TFCapturePoints[type].entry,TFCapturePoints[type].map,TFCapturePoints[type].x,TFCapturePoints[type].y,TFCapturePoints[type].z,TFCapturePoints[type].o,TFCapturePoints[type].rot0,TFCapturePoints[type].rot1,TFCapturePoints[type].rot2,TFCapturePoints[type].rot3);
}
+
void OPvPCapturePointTF::FillInitialWorldStates(WorldPacket &data)
{
data << uint32(TFTowerWorldStates[m_TowerType].n) << uint32(bool(m_TowerState & TF_TOWERSTATE_N));
data << uint32(TFTowerWorldStates[m_TowerType].h) << uint32(bool(m_TowerState & TF_TOWERSTATE_H));
data << uint32(TFTowerWorldStates[m_TowerType].a) << uint32(bool(m_TowerState & TF_TOWERSTATE_A));
}
+
void OutdoorPvPTF::FillInitialWorldStates(WorldPacket &data)
{
data << TF_UI_TOWER_SLIDER_POS << uint32(50);
data << TF_UI_TOWER_SLIDER_N << uint32(100);
data << TF_UI_TOWER_SLIDER_DISPLAY << uint32(0);
+
data << TF_UI_TOWER_COUNT_H << m_HordeTowersControlled;
data << TF_UI_TOWER_COUNT_A << m_AllianceTowersControlled;
data << TF_UI_TOWERS_CONTROLLED_DISPLAY << uint32(!m_IsLocked);
+
data << TF_UI_LOCKED_TIME_MINUTES_FIRST_DIGIT << first_digit;
data << TF_UI_LOCKED_TIME_MINUTES_SECOND_DIGIT << second_digit;
data << TF_UI_LOCKED_TIME_HOURS << hours_left;
+
data << TF_UI_LOCKED_DISPLAY_NEUTRAL << uint32(m_IsLocked && !m_HordeTowersControlled && !m_AllianceTowersControlled);
data << TF_UI_LOCKED_DISPLAY_HORDE << uint32(m_IsLocked && (m_HordeTowersControlled > m_AllianceTowersControlled));
data << TF_UI_LOCKED_DISPLAY_ALLIANCE << uint32(m_IsLocked && (m_HordeTowersControlled < m_AllianceTowersControlled));
+
for(OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
{
itr->second->FillInitialWorldStates(data);
}
}
+
void OutdoorPvPTF::SendRemoveWorldStates(Player * plr)
{
plr->SendUpdateWorldState(TF_UI_TOWER_SLIDER_POS,uint32(0));
plr->SendUpdateWorldState(TF_UI_TOWER_SLIDER_N,uint32(0));
plr->SendUpdateWorldState(TF_UI_TOWER_SLIDER_DISPLAY,uint32(0));
+
plr->SendUpdateWorldState(TF_UI_TOWER_COUNT_H,uint32(0));
plr->SendUpdateWorldState(TF_UI_TOWER_COUNT_A,uint32(0));
plr->SendUpdateWorldState(TF_UI_TOWERS_CONTROLLED_DISPLAY,uint32(0));
+
plr->SendUpdateWorldState(TF_UI_LOCKED_TIME_MINUTES_FIRST_DIGIT,uint32(0));
plr->SendUpdateWorldState(TF_UI_LOCKED_TIME_MINUTES_SECOND_DIGIT,uint32(0));
plr->SendUpdateWorldState(TF_UI_LOCKED_TIME_HOURS,uint32(0));
+
plr->SendUpdateWorldState(TF_UI_LOCKED_DISPLAY_NEUTRAL,uint32(0));
plr->SendUpdateWorldState(TF_UI_LOCKED_DISPLAY_HORDE,uint32(0));
plr->SendUpdateWorldState(TF_UI_LOCKED_DISPLAY_ALLIANCE,uint32(0));
+
for(int i = 0; i < TF_TOWER_NUM; ++i)
{
plr->SendUpdateWorldState(uint32(TFTowerWorldStates[i].n),uint32(0));
@@ -77,12 +91,14 @@ void OutdoorPvPTF::SendRemoveWorldStates(Player * plr)
plr->SendUpdateWorldState(uint32(TFTowerWorldStates[i].a),uint32(0));
}
}
+
void OPvPCapturePointTF::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(uint32(TFTowerWorldStates[m_TowerType].n),uint32(bool(m_TowerState & TF_TOWERSTATE_N)));
m_PvP->SendUpdateWorldState(uint32(TFTowerWorldStates[m_TowerType].h),uint32(bool(m_TowerState & TF_TOWERSTATE_H)));
m_PvP->SendUpdateWorldState(uint32(TFTowerWorldStates[m_TowerType].a),uint32(bool(m_TowerState & TF_TOWERSTATE_A)));
}
+
bool OPvPCapturePointTF::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -95,14 +111,17 @@ bool OPvPCapturePointTF::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointTF::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(TF_UI_TOWER_SLIDER_DISPLAY, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
bool OutdoorPvPTF::Update(uint32 diff)
{
bool changed = false;
+
if(changed = OutdoorPvP::Update(diff))
{
if(m_AllianceTowersControlled == TF_TOWER_NUM)
@@ -155,6 +174,7 @@ bool OutdoorPvPTF::Update(uint32 diff)
minutes_left -= hours_left * 60;
second_digit = minutes_left % 10;
first_digit = minutes_left / 10;
+
SendUpdateWorldState(TF_UI_LOCKED_TIME_MINUTES_FIRST_DIGIT,first_digit);
SendUpdateWorldState(TF_UI_LOCKED_TIME_MINUTES_SECOND_DIGIT,second_digit);
SendUpdateWorldState(TF_UI_LOCKED_TIME_HOURS,hours_left);
@@ -164,6 +184,7 @@ bool OutdoorPvPTF::Update(uint32 diff)
}
return changed;
}
+
void OutdoorPvPTF::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
if(plr->GetTeam() == ALLIANCE)
@@ -178,32 +199,39 @@ void OutdoorPvPTF::HandlePlayerEnterZone(Player * plr, uint32 zone)
}
OutdoorPvP::HandlePlayerEnterZone(plr,zone);
}
+
void OutdoorPvPTF::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// remove buffs
plr->RemoveAurasDueToSpell(TF_CAPTURE_BUFF);
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
}
+
bool OutdoorPvPTF::SetupOutdoorPvP()
{
m_AllianceTowersControlled = 0;
m_HordeTowersControlled = 0;
+
m_IsLocked = false;
m_LockTimer = TF_LOCK_TIME;
m_LockTimerUpdate = 0;
hours_left = 6;
second_digit = 0;
first_digit = 0;
+
// add the zones affected by the pvp buff
for(int i = 0; i < OutdoorPvPTFBuffZonesNum; ++i)
RegisterZone(OutdoorPvPTFBuffZones[i]);
+
AddCapturePoint(new OPvPCapturePointTF(this,TF_TOWER_NW));
AddCapturePoint(new OPvPCapturePointTF(this,TF_TOWER_N));
AddCapturePoint(new OPvPCapturePointTF(this,TF_TOWER_NE));
AddCapturePoint(new OPvPCapturePointTF(this,TF_TOWER_SE));
AddCapturePoint(new OPvPCapturePointTF(this,TF_TOWER_S));
+
return true;
}
+
bool OPvPCapturePointTF::Update(uint32 diff)
{
// can update even in locked state if gathers the controlling faction
@@ -213,6 +241,7 @@ bool OPvPCapturePointTF::Update(uint32 diff)
canupdate = canupdate || !((OutdoorPvPTF*)m_PvP)->m_IsLocked;
return canupdate && OPvPCapturePoint::Update(diff);
}
+
void OPvPCapturePointTF::ChangeState()
{
// if changing from controlling alliance to horde
@@ -229,7 +258,9 @@ void OPvPCapturePointTF::ChangeState()
((OutdoorPvPTF*)m_PvP)->m_HordeTowersControlled--;
sWorld.SendZoneText(OutdoorPvPTFBuffZones[0],objmgr.GetTrinityStringForDBCLocale(LANG_OPVP_TF_LOOSE_H));
}
+
uint32 artkit = 21;
+
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
@@ -254,14 +285,17 @@ void OPvPCapturePointTF::ChangeState()
m_TowerState = TF_TOWERSTATE_N;
break;
}
+
GameObject* flag = HashMapHolder<GameObject>::Find(m_capturePointGUID);
if(flag)
{
flag->SetGoArtKit(artkit);
flag->SendUpdateObjectToAllExcept(NULL);
}
+
UpdateTowerState();
}
+
void OPvPCapturePointTF::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
diff --git a/src/game/OutdoorPvPTF.h b/src/game/OutdoorPvPTF.h
index 3d567443563..3a88b7fd309 100644
--- a/src/game/OutdoorPvPTF.h
+++ b/src/game/OutdoorPvPTF.h
@@ -1,16 +1,23 @@
#ifndef OUTDOOR_PVP_TF_
#define OUTDOOR_PVP_TF_
+
#include "OutdoorPvPImpl.h"
+
const uint32 OutdoorPvPTFBuffZonesNum = 5;
+
const uint32 OutdoorPvPTFBuffZones[OutdoorPvPTFBuffZonesNum] = { 3519 /*Terokkar Forest*/, 3791 /*Sethekk Halls*/, 3789 /*Shadow Labyrinth*/, 3792 /*Mana-Tombs*/, 3790 /*Auchenai Crypts*/ };
+
// locked for 6 hours after capture
const uint32 TF_LOCK_TIME = 3600 * 6 * 1000;
// update lock timer every 1/4 minute (overkill, but this way it's sure the timer won't "jump" 2 minutes at once.)
const uint32 TF_LOCK_TIME_UPDATE = 15000;
+
// blessing of auchindoun
#define TF_CAPTURE_BUFF 33377
+
const uint32 TF_ALLY_QUEST = 11505;
const uint32 TF_HORDE_QUEST = 11506;
+
enum OutdoorPvPTF_TowerType{
TF_TOWER_NW = 0,
TF_TOWER_N,
@@ -19,6 +26,7 @@ enum OutdoorPvPTF_TowerType{
TF_TOWER_S,
TF_TOWER_NUM
};
+
const go_type TFCapturePoints[TF_TOWER_NUM] = {
{183104,530,-3081.65,5335.03,17.1853,-2.14675,0,0,0.878817,-0.477159},
{183411,530,-2939.9,4788.73,18.987,2.77507,0,0,0.983255,0.182236},
@@ -26,11 +34,13 @@ const go_type TFCapturePoints[TF_TOWER_NUM] = {
{183413,530,-3603.31,4529.15,20.9077,0.994838,0,0,0.477159,0.878817},
{183414,530,-3812.37,4899.3,17.7249,0.087266,0,0,0.043619,0.999048}
};
+
struct tf_tower_world_state{
uint32 n;
uint32 h;
uint32 a;
};
+
const tf_tower_world_state TFTowerWorldStates[TF_TOWER_NUM] = {
{0xa79,0xa7a,0xa7b},
{0xa7e,0xa7d,0xa7c},
@@ -38,15 +48,20 @@ const tf_tower_world_state TFTowerWorldStates[TF_TOWER_NUM] = {
{0xa88,0xa87,0xa86},
{0xa85,0xa84,0xa83}
};
+
const uint32 TFTowerPlayerEnterEvents[TF_TOWER_NUM] = {12226, 12497, 12486, 12499, 12501};
+
const uint32 TFTowerPlayerLeaveEvents[TF_TOWER_NUM] = {12225, 12496, 12487, 12498, 12500};
+
enum TFWorldStates{
TF_UI_TOWER_SLIDER_POS = 0xa41,
TF_UI_TOWER_SLIDER_N = 0xa40,
TF_UI_TOWER_SLIDER_DISPLAY = 0xa3f,
+
TF_UI_TOWER_COUNT_H = 0xa3e,
TF_UI_TOWER_COUNT_A = 0xa3d,
TF_UI_TOWERS_CONTROLLED_DISPLAY = 0xa3c,
+
TF_UI_LOCKED_TIME_MINUTES_FIRST_DIGIT = 0x9d0,
TF_UI_LOCKED_TIME_MINUTES_SECOND_DIGIT = 0x9ce,
TF_UI_LOCKED_TIME_HOURS = 0x9cd,
@@ -54,11 +69,13 @@ enum TFWorldStates{
TF_UI_LOCKED_DISPLAY_HORDE = 0xad0,
TF_UI_LOCKED_DISPLAY_ALLIANCE = 0xacf
};
+
enum TFTowerStates {
TF_TOWERSTATE_N = 1,
TF_TOWERSTATE_H = 2,
TF_TOWERSTATE_A = 4
};
+
class OPvPCapturePointTF : public OPvPCapturePoint
{
public:
@@ -75,6 +92,7 @@ protected:
OutdoorPvPTF_TowerType m_TowerType;
uint32 m_TowerState;
};
+
class OutdoorPvPTF : public OutdoorPvP
{
friend class OPvPCapturePointTF;
@@ -94,5 +112,6 @@ private:
uint32 m_HordeTowersControlled;
uint32 hours_left, second_digit, first_digit;
};
+
#endif
diff --git a/src/game/OutdoorPvPZM.cpp b/src/game/OutdoorPvPZM.cpp
index 4101b21ac82..550dbcccdc8 100644
--- a/src/game/OutdoorPvPZM.cpp
+++ b/src/game/OutdoorPvPZM.cpp
@@ -15,6 +15,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "OutdoorPvPZM.h"
#include "ObjectMgr.h"
#include "OutdoorPvPMgr.h"
@@ -24,11 +25,13 @@
#include "WorldPacket.h"
#include "GossipDef.h"
#include "World.h"
+
OPvPCapturePointZM_Beacon::OPvPCapturePointZM_Beacon(OutdoorPvP *pvp, ZM_BeaconType type)
: OPvPCapturePoint(pvp), m_TowerType(type), m_TowerState(ZM_TOWERSTATE_N)
{
SetCapturePointData(ZMCapturePoints[type].entry,ZMCapturePoints[type].map,ZMCapturePoints[type].x,ZMCapturePoints[type].y,ZMCapturePoints[type].z,ZMCapturePoints[type].o,ZMCapturePoints[type].rot0,ZMCapturePoints[type].rot1,ZMCapturePoints[type].rot2,ZMCapturePoints[type].rot3);
}
+
void OPvPCapturePointZM_Beacon::FillInitialWorldStates(WorldPacket &data)
{
data << uint32(ZMBeaconInfo[m_TowerType].ui_tower_n) << uint32(bool(m_TowerState & ZM_TOWERSTATE_N));
@@ -38,6 +41,7 @@ void OPvPCapturePointZM_Beacon::FillInitialWorldStates(WorldPacket &data)
data << uint32(ZMBeaconInfo[m_TowerType].ui_tower_h) << uint32(bool(m_TowerState & ZM_TOWERSTATE_H));
data << uint32(ZMBeaconInfo[m_TowerType].map_tower_h) << uint32(bool(m_TowerState & ZM_TOWERSTATE_H));
}
+
void OPvPCapturePointZM_Beacon::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(uint32(ZMBeaconInfo[m_TowerType].ui_tower_n),uint32(bool(m_TowerState & ZM_TOWERSTATE_N)));
@@ -47,6 +51,7 @@ void OPvPCapturePointZM_Beacon::UpdateTowerState()
m_PvP->SendUpdateWorldState(uint32(ZMBeaconInfo[m_TowerType].ui_tower_h),uint32(bool(m_TowerState & ZM_TOWERSTATE_H)));
m_PvP->SendUpdateWorldState(uint32(ZMBeaconInfo[m_TowerType].map_tower_h),uint32(bool(m_TowerState & ZM_TOWERSTATE_H)));
}
+
bool OPvPCapturePointZM_Beacon::HandlePlayerEnter(Player *plr)
{
if(OPvPCapturePoint::HandlePlayerEnter(plr))
@@ -59,11 +64,13 @@ bool OPvPCapturePointZM_Beacon::HandlePlayerEnter(Player *plr)
}
return false;
}
+
void OPvPCapturePointZM_Beacon::HandlePlayerLeave(Player *plr)
{
plr->SendUpdateWorldState(ZMBeaconInfo[m_TowerType].slider_disp, 0);
OPvPCapturePoint::HandlePlayerLeave(plr);
}
+
void OPvPCapturePointZM_Beacon::ChangeState()
{
// if changing from controlling alliance to horde
@@ -80,6 +87,7 @@ void OPvPCapturePointZM_Beacon::ChangeState()
((OutdoorPvPZM*)m_PvP)->m_HordeTowersControlled--;
sWorld.SendZoneText(ZM_GRAVEYARD_ZONE,objmgr.GetTrinityStringForDBCLocale(ZMBeaconLooseH[m_TowerType]));
}
+
switch(m_State)
{
case OBJECTIVESTATE_ALLIANCE:
@@ -102,8 +110,10 @@ void OPvPCapturePointZM_Beacon::ChangeState()
m_TowerState = ZM_TOWERSTATE_N;
break;
}
+
UpdateTowerState();
}
+
void OPvPCapturePointZM_Beacon::SendChangePhase()
{
// send this too, sometimes the slider disappears, dunno why :(
@@ -113,6 +123,7 @@ void OPvPCapturePointZM_Beacon::SendChangePhase()
SendUpdateWorldState(ZMBeaconInfo[m_TowerType].slider_pos, phase);
SendUpdateWorldState(ZMBeaconInfo[m_TowerType].slider_n, m_neutralValuePct);
}
+
bool OutdoorPvPZM::Update(uint32 diff)
{
bool changed = false;
@@ -127,6 +138,7 @@ bool OutdoorPvPZM::Update(uint32 diff)
}
return changed;
}
+
void OutdoorPvPZM::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
if(plr->GetTeam() == ALLIANCE)
@@ -141,6 +153,7 @@ void OutdoorPvPZM::HandlePlayerEnterZone(Player * plr, uint32 zone)
}
OutdoorPvP::HandlePlayerEnterZone(plr,zone);
}
+
void OutdoorPvPZM::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
// remove buffs
@@ -150,41 +163,51 @@ void OutdoorPvPZM::HandlePlayerLeaveZone(Player * plr, uint32 zone)
plr->RemoveAurasDueToSpell(ZM_BATTLE_STANDARD_H);
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
}
+
OutdoorPvPZM::OutdoorPvPZM()
{
m_TypeId = OUTDOOR_PVP_ZM;
m_GraveYard = NULL;
m_AllianceTowersControlled = 0;
m_HordeTowersControlled = 0;
+
}
+
bool OutdoorPvPZM::SetupOutdoorPvP()
{
m_AllianceTowersControlled = 0;
m_HordeTowersControlled = 0;
+
// add the zones affected by the pvp buff
for(int i = 0; i < OutdoorPvPZMBuffZonesNum; ++i)
RegisterZone(OutdoorPvPZMBuffZones[i]);
+
AddCapturePoint(new OPvPCapturePointZM_Beacon(this,ZM_BEACON_WEST));
AddCapturePoint(new OPvPCapturePointZM_Beacon(this,ZM_BEACON_EAST));
m_GraveYard = new OPvPCapturePointZM_GraveYard(this);
AddCapturePoint(m_GraveYard); // though the update function isn't used, the handleusego is!
+
return true;
}
+
void OutdoorPvPZM::HandleKillImpl(Player *plr, Unit * killed)
{
if(killed->GetTypeId() != TYPEID_PLAYER)
return;
+
if(plr->GetTeam() == ALLIANCE && ((Player*)killed)->GetTeam() != ALLIANCE)
plr->CastSpell(plr,ZM_AlliancePlayerKillReward,true);
else if(plr->GetTeam() == HORDE && ((Player*)killed)->GetTeam() != HORDE)
plr->CastSpell(plr,ZM_HordePlayerKillReward,true);
}
+
bool OPvPCapturePointZM_GraveYard::Update(uint32 diff)
{
bool retval = m_State != m_OldState;
m_State = m_OldState;
return retval;
}
+
int32 OPvPCapturePointZM_GraveYard::HandleOpenGo(Player *plr, uint64 guid)
{
uint32 retval = OPvPCapturePoint::HandleOpenGo(plr, guid);
@@ -220,6 +243,7 @@ int32 OPvPCapturePointZM_GraveYard::HandleOpenGo(Player *plr, uint64 guid)
}
return retval;
}
+
OPvPCapturePointZM_GraveYard::OPvPCapturePointZM_GraveYard(OutdoorPvP *pvp)
: OPvPCapturePoint(pvp)
{
@@ -232,32 +256,38 @@ OPvPCapturePointZM_GraveYard::OPvPCapturePointZM_GraveYard(OutdoorPvP *pvp)
// add neutral banner
AddObject(0,ZM_Banner_N.entry,ZM_Banner_N.map,ZM_Banner_N.x,ZM_Banner_N.y,ZM_Banner_N.z,ZM_Banner_N.o,ZM_Banner_N.rot0,ZM_Banner_N.rot1,ZM_Banner_N.rot2,ZM_Banner_N.rot3);
}
+
void OPvPCapturePointZM_GraveYard::UpdateTowerState()
{
m_PvP->SendUpdateWorldState(ZM_MAP_GRAVEYARD_N,uint32(bool(m_GraveYardState & ZM_GRAVEYARD_N)));
m_PvP->SendUpdateWorldState(ZM_MAP_GRAVEYARD_H,uint32(bool(m_GraveYardState & ZM_GRAVEYARD_H)));
m_PvP->SendUpdateWorldState(ZM_MAP_GRAVEYARD_A,uint32(bool(m_GraveYardState & ZM_GRAVEYARD_A)));
+
m_PvP->SendUpdateWorldState(ZM_MAP_ALLIANCE_FLAG_READY,uint32(m_BothControllingFaction == ALLIANCE));
m_PvP->SendUpdateWorldState(ZM_MAP_ALLIANCE_FLAG_NOT_READY,uint32(m_BothControllingFaction != ALLIANCE));
m_PvP->SendUpdateWorldState(ZM_MAP_HORDE_FLAG_READY,uint32(m_BothControllingFaction == HORDE));
m_PvP->SendUpdateWorldState(ZM_MAP_HORDE_FLAG_NOT_READY,uint32(m_BothControllingFaction != HORDE));
}
+
void OPvPCapturePointZM_GraveYard::FillInitialWorldStates(WorldPacket &data)
{
data << ZM_MAP_GRAVEYARD_N << uint32(bool(m_GraveYardState & ZM_GRAVEYARD_N));
data << ZM_MAP_GRAVEYARD_H << uint32(bool(m_GraveYardState & ZM_GRAVEYARD_H));
data << ZM_MAP_GRAVEYARD_A << uint32(bool(m_GraveYardState & ZM_GRAVEYARD_A));
+
data << ZM_MAP_ALLIANCE_FLAG_READY << uint32(m_BothControllingFaction == ALLIANCE);
data << ZM_MAP_ALLIANCE_FLAG_NOT_READY << uint32(m_BothControllingFaction != ALLIANCE);
data << ZM_MAP_HORDE_FLAG_READY << uint32(m_BothControllingFaction == HORDE);
data << ZM_MAP_HORDE_FLAG_NOT_READY << uint32(m_BothControllingFaction != HORDE);
}
+
void OPvPCapturePointZM_GraveYard::SetBeaconState(uint32 controlling_faction)
{
// nothing to do here
if(m_BothControllingFaction == controlling_faction)
return;
m_BothControllingFaction = controlling_faction;
+
switch(controlling_faction)
{
case ALLIANCE:
@@ -295,6 +325,7 @@ void OPvPCapturePointZM_GraveYard::SetBeaconState(uint32 controlling_faction)
// send worldstateupdate
UpdateTowerState();
}
+
bool OPvPCapturePointZM_GraveYard::CanTalkTo(Player * plr, Creature * c, GossipOption & gso)
{
uint64 guid = c->GetGUID();
@@ -314,6 +345,7 @@ bool OPvPCapturePointZM_GraveYard::CanTalkTo(Player * plr, Creature * c, GossipO
}
return false;
}
+
bool OPvPCapturePointZM_GraveYard::HandleGossipOption(Player *plr, uint64 guid, uint32 gossipid)
{
std::map<uint64,uint32>::iterator itr = m_CreatureTypes.find(guid);
@@ -341,6 +373,7 @@ bool OPvPCapturePointZM_GraveYard::HandleGossipOption(Player *plr, uint64 guid,
}
return false;
}
+
bool OPvPCapturePointZM_GraveYard::HandleDropFlag(Player * plr, uint32 spellId)
{
switch(spellId)
@@ -354,6 +387,7 @@ bool OPvPCapturePointZM_GraveYard::HandleDropFlag(Player * plr, uint32 spellId)
}
return false;
}
+
void OutdoorPvPZM::FillInitialWorldStates(WorldPacket &data)
{
data << ZM_WORLDSTATE_UNK_1 << uint32(1);
@@ -362,6 +396,7 @@ void OutdoorPvPZM::FillInitialWorldStates(WorldPacket &data)
itr->second->FillInitialWorldStates(data);
}
}
+
void OutdoorPvPZM::SendRemoveWorldStates(Player *plr)
{
plr->SendUpdateWorldState(ZM_UI_TOWER_SLIDER_N_W,0);
diff --git a/src/game/OutdoorPvPZM.h b/src/game/OutdoorPvPZM.h
index 374e42bc7a7..fc2c48f4eec 100644
--- a/src/game/OutdoorPvPZM.h
+++ b/src/game/OutdoorPvPZM.h
@@ -15,10 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef OUTDOOR_PVP_ZM_
#define OUTDOOR_PVP_ZM_
+
#include "OutdoorPvPImpl.h"
#include "Language.h"
+
const uint32 OutdoorPvPZMBuffZonesNum = 5;
// the buff is cast in these zones
const uint32 OutdoorPvPZMBuffZones[OutdoorPvPZMBuffZonesNum] = {3521,3607,3717,3715,3716};
@@ -26,6 +29,7 @@ const uint32 OutdoorPvPZMBuffZones[OutdoorPvPZMBuffZonesNum] = {3521,3607,3717,3
const uint32 ZM_GRAVEYARD_ZONE = 3521;
// linked when the central tower is controlled
const uint32 ZM_GRAVEYARD_ID = 969;
+
enum OutdoorPvPZMSpells
{
// cast on the players of the controlling faction
@@ -39,19 +43,23 @@ enum OutdoorPvPZMSpells
// token create spell
ZM_HordePlayerKillReward = 32158
};
+
// banners 182527, 182528, 182529, gotta check them ingame
const go_type ZM_Banner_A = { 182527,530,253.54,7083.81,36.7728,-0.017453,0,0,0.008727,-0.999962 };
const go_type ZM_Banner_H = { 182528,530,253.54,7083.81,36.7728,-0.017453,0,0,0.008727,-0.999962 };
const go_type ZM_Banner_N = { 182529,530,253.54,7083.81,36.7728,-0.017453,0,0,0.008727,-0.999962 };
+
// horde field scout spawn data
const creature_type ZM_HordeFieldScout = {18564,67,530,296.625,7818.4,42.6294,5.18363};
// alliance field scout spawn data
const creature_type ZM_AllianceFieldScout = {18581,469,530,374.395,6230.08,22.8351,0.593412};
+
enum ZMCreatureTypes{
ZM_ALLIANCE_FIELD_SCOUT = 0,
ZM_HORDE_FIELD_SCOUT,
ZM_CREATURE_NUM
};
+
struct zm_beacon {
uint32 slider_disp;
uint32 slider_n;
@@ -65,50 +73,62 @@ struct zm_beacon {
uint32 event_enter;
uint32 event_leave;
};
+
enum ZM_BeaconType{
ZM_BEACON_EAST = 0,
ZM_BEACON_WEST,
ZM_NUM_BEACONS
};
+
const zm_beacon ZMBeaconInfo[ZM_NUM_BEACONS] = {
{2533,2535,2534,2560,2559,2558,2652,2651,2650,11807,11806},
{2527,2529,2528,2557,2556,2555,2646,2645,2644,11805,11804}
};
+
const uint32 ZMBeaconCaptureA[ZM_NUM_BEACONS] = {
LANG_OPVP_ZM_CAPTURE_EAST_A,
LANG_OPVP_ZM_CAPTURE_WEST_A
};
+
const uint32 ZMBeaconCaptureH[ZM_NUM_BEACONS] = {
LANG_OPVP_ZM_CAPTURE_EAST_H,
LANG_OPVP_ZM_CAPTURE_WEST_H
};
+
const uint32 ZMBeaconLooseA[ZM_NUM_BEACONS] = {
LANG_OPVP_ZM_LOOSE_EAST_A,
LANG_OPVP_ZM_LOOSE_WEST_A
};
+
const uint32 ZMBeaconLooseH[ZM_NUM_BEACONS] = {
LANG_OPVP_ZM_LOOSE_EAST_H,
LANG_OPVP_ZM_LOOSE_WEST_H
};
+
const go_type ZMCapturePoints[ZM_NUM_BEACONS] = {
{182523,530,303.243,6841.36,40.1245,-1.58825,0,0,0.71325,-0.700909},
{182522,530,336.466,7340.26,41.4984,-1.58825,0,0,0.71325,-0.700909}
};
+
enum OutdoorPvPZMWorldStates
{
ZM_UI_TOWER_SLIDER_N_W = 2529,
ZM_UI_TOWER_SLIDER_POS_W = 2528,
ZM_UI_TOWER_SLIDER_DISPLAY_W = 2527,
+
ZM_UI_TOWER_SLIDER_N_E = 2535,
ZM_UI_TOWER_SLIDER_POS_E = 2534,
ZM_UI_TOWER_SLIDER_DISPLAY_E = 2533,
+
ZM_WORLDSTATE_UNK_1 = 2653,
+
ZM_UI_TOWER_EAST_N = 2560,
ZM_UI_TOWER_EAST_H = 2559,
ZM_UI_TOWER_EAST_A = 2558,
ZM_UI_TOWER_WEST_N = 2557,
ZM_UI_TOWER_WEST_H = 2556,
ZM_UI_TOWER_WEST_A = 2555,
+
ZM_MAP_TOWER_EAST_N = 2652,
ZM_MAP_TOWER_EAST_H = 2651,
ZM_MAP_TOWER_EAST_A = 2650,
@@ -118,16 +138,19 @@ enum OutdoorPvPZMWorldStates
ZM_MAP_TOWER_WEST_N = 2646,
ZM_MAP_TOWER_WEST_H = 2645,
ZM_MAP_TOWER_WEST_A = 2644,
+
ZM_MAP_HORDE_FLAG_READY = 2658,
ZM_MAP_HORDE_FLAG_NOT_READY = 2657,
ZM_MAP_ALLIANCE_FLAG_NOT_READY = 2656,
ZM_MAP_ALLIANCE_FLAG_READY = 2655
};
+
enum ZM_TowerStateMask{
ZM_TOWERSTATE_N = 1,
ZM_TOWERSTATE_A = 2,
ZM_TOWERSTATE_H = 4
};
+
class OutdoorPvPZM;
class OPvPCapturePointZM_Beacon : public OPvPCapturePoint
{
@@ -145,11 +168,13 @@ protected:
ZM_BeaconType m_TowerType;
uint32 m_TowerState;
};
+
enum ZM_GraveYardState{
ZM_GRAVEYARD_N = 1,
ZM_GRAVEYARD_A = 2,
ZM_GRAVEYARD_H = 4
};
+
class OPvPCapturePointZM_GraveYard : public OPvPCapturePoint
{
friend class OutdoorPvPZM;
@@ -170,6 +195,7 @@ protected:
uint32 m_BothControllingFaction;
uint64 m_FlagCarrierGUID;
};
+
class OutdoorPvPZM : public OutdoorPvP
{
friend class OPvPCapturePointZM_Beacon;
@@ -187,6 +213,7 @@ private:
uint32 m_AllianceTowersControlled;
uint32 m_HordeTowersControlled;
};
+
// todo: flag carrier death/leave/mount/activitychange should give back the gossip options
#endif
diff --git a/src/game/PassiveAI.h b/src/game/PassiveAI.h
index 4ca0ecb875f..19ea9938320 100644
--- a/src/game/PassiveAI.h
+++ b/src/game/PassiveAI.h
@@ -17,54 +17,70 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_PASSIVEAI_H
#define TRINITY_PASSIVEAI_H
+
#include "CreatureAI.h"
//#include "CreatureAIImpl.h"
+
class TRINITY_DLL_SPEC PassiveAI : public CreatureAI
{
public:
explicit PassiveAI(Creature *c);
+
void MoveInLineOfSight(Unit *) {}
void AttackStart(Unit *) {}
void UpdateAI(const uint32);
+
static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; }
};
+
class TRINITY_DLL_DECL PossessedAI : public CreatureAI
{
public:
explicit PossessedAI(Creature *c);
+
void MoveInLineOfSight(Unit *) {}
void AttackStart(Unit *target);
void UpdateAI(const uint32);
void EnterEvadeMode() {}
+
void JustDied(Unit*);
void KilledUnit(Unit* victim);
+
static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; }
};
+
class TRINITY_DLL_SPEC NullCreatureAI : public CreatureAI
{
public:
explicit NullCreatureAI(Creature *c);
+
void MoveInLineOfSight(Unit *) {}
void AttackStart(Unit *) {}
void UpdateAI(const uint32) {}
void EnterEvadeMode() {}
void OnCharmed(bool apply) {}
+
static int Permissible(const Creature *) { return PERMIT_BASE_IDLE; }
};
+
class TRINITY_DLL_DECL CritterAI : public PassiveAI
{
public:
explicit CritterAI(Creature *c) : PassiveAI(c) {}
+
void DamageTaken(Unit *done_by, uint32 & /*damage*/);
void EnterEvadeMode();
};
+
class TRINITY_DLL_SPEC TriggerAI : public NullCreatureAI
{
public:
explicit TriggerAI(Creature *c) : NullCreatureAI(c) {}
void IsSummonedBy(Unit *summoner);
};
+
#endif
diff --git a/src/game/Path.h b/src/game/Path.h
index 8024a03382b..2fd6723b01c 100644
--- a/src/game/Path.h
+++ b/src/game/Path.h
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_PATH_H
#define TRINITYCORE_PATH_H
+
#include "Common.h"
#include <vector>
+
class Path
{
public:
@@ -28,10 +31,12 @@ class Path
{
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); }
@@ -50,6 +55,7 @@ class Path
}
return len;
}
+
float GetPassedLength(uint32 curnode, float x, float y, float z)
{
float len = 0, xd, yd, zd;
@@ -60,6 +66,7 @@ class Path
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;
@@ -67,10 +74,13 @@ class Path
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<PathNode> i_nodes;
};
diff --git a/src/game/Pet.cpp b/src/game/Pet.cpp
index 29d9ffc34c8..47d406f6453 100644
--- a/src/game/Pet.cpp
+++ b/src/game/Pet.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -29,6 +30,7 @@
#include "CreatureAI.h"
#include "Unit.h"
#include "Util.h"
+
char const* petTypeSuffix[MAX_PET_TYPE] =
{
"'s Minion", // SUMMON_PET
@@ -36,7 +38,9 @@ char const* petTypeSuffix[MAX_PET_TYPE] =
"'s Guardian", // GUARDIAN_PET
"'s Companion" // MINI_PET
};
+
#define PET_XP_FACTOR 0.1f
+
Pet::Pet(Player *owner, PetType type) : Guardian(NULL, owner),
m_petType(type), m_removed(false), m_happinessTimer(7500), m_duration(0),
m_resetTalentsCost(0), m_resetTalentsTime(0), m_usedTalentCount(0), m_auraRaidUpdateMask(0), m_loading(false),
@@ -45,19 +49,24 @@ m_declinedname(NULL), m_owner(owner)
m_unitTypeMask |= UNIT_MASK_PET;
if(type == HUNTER_PET)
m_unitTypeMask |= UNIT_MASK_HUNTER_PET;
+
if (!(m_unitTypeMask & UNIT_MASK_CONTROLABLE_GUARDIAN))
{
m_unitTypeMask |= UNIT_MASK_CONTROLABLE_GUARDIAN;
InitCharmInfo();
}
+
m_name = "Pet";
m_regenTimer = 4000;
+
m_isWorldObject = true;
}
+
Pet::~Pet()
{
delete m_declinedname;
}
+
void Pet::AddToWorld()
{
///- Register the pet for guid lookup
@@ -68,6 +77,7 @@ void Pet::AddToWorld()
Unit::AddToWorld();
AIM_Initialize();
}
+
// Prevent stuck pets when zoning. Pets default to "follow" when added to world
// so we'll reset flags and let the AI handle things
if (this->GetCharmInfo() && this->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
@@ -77,7 +87,9 @@ void Pet::AddToWorld()
this->GetCharmInfo()->SetIsFollowing(false);
this->GetCharmInfo()->SetIsReturning(false);
}
+
}
+
void Pet::RemoveFromWorld()
{
///- Remove the pet from the accessor
@@ -88,11 +100,15 @@ void Pet::RemoveFromWorld()
ObjectAccessor::Instance().RemoveObject(this);
}
}
+
bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool current )
{
m_loading = true;
+
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
result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
@@ -115,9 +131,12 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
result = CharacterDatabase.PQuery("SELECT id, entry, owner, modelid, level, exp, Reactstate, slot, name, renamed, curhealth, curmana, curhappiness, abdata, savetime, resettalents_cost, resettalents_time, CreatedBySpell, PetType "
"FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u') ",
ownerid,PET_SAVE_AS_CURRENT,PET_SAVE_LAST_STABLE_SLOT);
+
if(!result)
return false;
+
Field *fields = result->Fetch();
+
// update for case of current pet "slot = 0"
petentry = fields[1].GetUInt32();
if (!petentry)
@@ -125,15 +144,19 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
delete result;
return false;
}
+
uint32 summon_spell_id = fields[17].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;
}
+
PetType pet_type = PetType(fields[18].GetUInt8());
if(pet_type==HUNTER_PET)
{
@@ -144,13 +167,16 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
return false;
}
}
+
uint32 pet_number = fields[0].GetUInt32();
+
if (current && owner->IsPetNeedBeTemporaryUnsummoned())
{
owner->SetTemporaryUnsummonedPetNumber(pet_number);
delete result;
return false;
}
+
Map *map = owner->GetMap();
uint32 guid = objmgr.GenerateLowGuid(HIGHGUID_PET);
if (!Create(guid, map, owner->GetPhaseMask(), petentry, pet_number))
@@ -158,9 +184,11 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
delete result;
return false;
}
+
float px, py, pz;
owner->GetClosePoint(px, py, pz, GetObjectSize(), PET_FOLLOW_DIST, GetFollowAngle());
Relocate(px, py, pz, owner->GetOrientation());
+
if (!IsPositionValid())
{
sLog.outError("Pet (guidlow %d, entry %d) not loaded. Suggested coordinates isn't valid (X: %f Y: %f)",
@@ -168,9 +196,11 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
delete result;
return false;
}
+
setPetType(pet_type);
setFaction(owner->getFaction());
SetUInt32Value(UNIT_CREATED_BY_SPELL, summon_spell_id);
+
CreatureInfo const *cinfo = GetCreatureInfo();
if (cinfo->type == CREATURE_TYPE_CRITTER)
{
@@ -178,16 +208,20 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
delete result;
return true;
}
+
m_charmInfo->SetPetNumber(pet_number, IsPermanentPetFor(owner));
+
SetDisplayId(fields[3].GetUInt32());
SetNativeDisplayId(fields[3].GetUInt32());
uint32 petlevel = fields[4].GetUInt32();
SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
SetName(fields[8].GetString());
+
switch (getPetType())
{
case SUMMON_PET:
petlevel=owner->getLevel();
+
SetUInt32Value(UNIT_FIELD_BYTES_0, 0x800); //class=mage
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet dismiss, cancel)
@@ -196,6 +230,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100); //class=warrior,gender=none,power=focus
SetSheath(SHEATH_STATE_MELEE);
SetByteValue(UNIT_FIELD_BYTES_2, 2, fields[9].GetBool() ? UNIT_RENAME_NOT_ALLOWED : UNIT_RENAME_ALLOWED);
+
SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
// this enables popup window (pet abandon, cancel)
SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS));
@@ -207,12 +242,16 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
sLog.outError("Pet have incorrect type (%u) for pet loading.", getPetType());
break;
}
+
SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, fields[5].GetUInt32());
SetCreatorGUID(owner->GetGUID());
+
SetReactState( ReactStates( fields[6].GetUInt8() ));
+
SetCanModifyStats(true);
InitStatsForLevel(petlevel);
+
if(getPetType() == SUMMON_PET && !current) //all (?) summon pets come with full health when called, but not when they are current
{
SetHealth(GetMaxHealth());
@@ -225,6 +264,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
SetHealth(savedhealth > GetMaxHealth() ? GetMaxHealth() : savedhealth);
SetPower(POWER_MANA, savedmana > GetMaxPower(POWER_MANA) ? GetMaxPower(POWER_MANA) : savedmana);
}
+
// set current pet as current
// 0=current
// 1..MAX_PET_STABLES in stable slot
@@ -238,6 +278,7 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
PET_SAVE_AS_CURRENT, ownerid, m_charmInfo->GetPetNumber());
CharacterDatabase.CommitTransaction();
}
+
// Send fake summon spell cast - this is needed for correct cooldown application for spells
// Example: 46584 - without this cooldown (which should be set always when pet is loaded) isn't set clientside
// TODO: pets should be summoned from real cast instead of just faking it?
@@ -252,37 +293,50 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
data << uint32(0);
SendMessageToSet(&data, true);
}
+
owner->SetMinion(this, true);
map->Add((Creature*)this);
+
m_resetTalentsCost = fields[15].GetUInt32();
m_resetTalentsTime = fields[16].GetUInt64();
InitTalentForLevel(); // set original talents points before spell loading
+
uint32 timediff = (time(NULL) - fields[14].GetUInt32());
_LoadAuras(timediff);
+
// load action bar, if data broken will fill later by default spells.
if (!is_temporary_summoned)
{
m_charmInfo->LoadPetActionBar(fields[13].GetCppString());
+
_LoadSpells();
_LoadSpellCooldowns();
LearnPetPassives();
InitLevelupSpellsForLevel();
CastPetAuras(current);
}
+
CleanupActionBar(); // remove unknown spells from action bar after load
+
delete result;
sLog.outDebug("New Pet has guid %u", GetGUIDLow());
+
owner->PetSpellInitialize();
+
if(owner->GetGroup())
owner->SetGroupUpdateFlag(GROUP_UPDATE_PET);
+
owner->SendTalentsInfoData(true);
+
if(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 *fields2 = result->Fetch();
for(uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
@@ -291,26 +345,34 @@ bool Pet::LoadPetFromDB( Player* owner, uint32 petentry, uint32 petnumber, bool
}
}
}
+
//set last used pet number (for use in BG's)
if(owner->GetTypeId() == TYPEID_PLAYER && isControlled() && !isTemporarySummoned() && (getPetType() == SUMMON_PET || getPetType() == HUNTER_PET))
((Player*)owner)->SetLastPetNumber(pet_number);
+
m_loading = false;
+
SynchronizeLevelWithOwner();
return true;
}
+
void Pet::SavePetToDB(PetSaveMode mode)
{
if (!GetEntry())
return;
+
// save only fully controlled creature
if (!isControlled())
return;
+
// not save not player pets
if(!IS_PLAYER_GUID(GetOwnerGUID()))
return;
+
Player* pOwner = (Player*)GetOwner();
if (!pOwner)
return;
+
// not save pet as current if another pet temporary unsummoned
if (mode == PET_SAVE_AS_CURRENT && pOwner->GetTemporaryUnsummonedPetNumber() &&
pOwner->GetTemporaryUnsummonedPetNumber() != m_charmInfo->GetPetNumber())
@@ -318,19 +380,24 @@ void Pet::SavePetToDB(PetSaveMode mode)
// pet will lost anyway at restore temporary unsummoned
if(getPetType()==HUNTER_PET)
return;
+
// for warlock case
mode = PET_SAVE_NOT_IN_SLOT;
}
+
uint32 curhealth = GetHealth();
uint32 curmana = GetPower(POWER_MANA);
+
// stable and not in slot saves
if(mode > PET_SAVE_AS_CURRENT)
{
RemoveAllAuras();
}
+
_SaveSpells();
_SaveSpellCooldowns();
_SaveAuras();
+
// current/stable/not_in_slot
if(mode >= PET_SAVE_AS_CURRENT)
{
@@ -340,10 +407,12 @@ void Pet::SavePetToDB(PetSaveMode mode)
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_LAST_STABLE_SLOT)
CharacterDatabase.PExecute("UPDATE character_pet SET slot = '%u' WHERE owner = '%u' AND slot = '%u'",
PET_SAVE_NOT_IN_SLOT, 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_LAST_STABLE_SLOT))
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE owner = '%u' AND (slot = '%u' OR slot > '%u')",
@@ -365,18 +434,21 @@ void Pet::SavePetToDB(PetSaveMode mode)
<< (curhealth<1?1:curhealth) << ", "
<< curmana << ", "
<< GetPower(POWER_HAPPINESS) << ", '";
+
// save only spell slots from action bar
for(uint32 i = ACTION_BAR_INDEX_PET_SPELL_START; i < ACTION_BAR_INDEX_PET_SPELL_END; ++i)
{
ss << uint32(m_charmInfo->GetActionBarEntry(i)->GetType()) << " "
<< uint32(m_charmInfo->GetActionBarEntry(i)->GetAction()) << " ";
};
+
ss << "', "
<< time(NULL) << ", "
<< uint32(m_resetTalentsCost) << ", "
<< uint64(m_resetTalentsTime) << ", "
<< GetUInt32Value(UNIT_CREATED_BY_SPELL) << ", "
<< uint32(getPetType()) << ")";
+
CharacterDatabase.Execute( ss.str().c_str() );
CharacterDatabase.CommitTransaction();
}
@@ -387,6 +459,7 @@ void Pet::SavePetToDB(PetSaveMode mode)
DeleteFromDB(m_charmInfo->GetPetNumber());
}
}
+
void Pet::DeleteFromDB(uint32 guidlow)
{
CharacterDatabase.PExecute("DELETE FROM character_pet WHERE id = '%u'", guidlow);
@@ -395,6 +468,7 @@ void Pet::DeleteFromDB(uint32 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);
@@ -405,10 +479,12 @@ void Pet::setDeathState(DeathState s) // overwrite virtual
// 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_STUNNED);
}
}
@@ -418,10 +494,12 @@ void Pet::setDeathState(DeathState s) // overwrite virtual
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:
@@ -443,6 +521,7 @@ void Pet::Update(uint32 diff)
Remove(PET_SAVE_NOT_IN_SLOT, true);
return;
}
+
if(isControlled())
{
if( owner->GetPetGUID() != GetGUID() )
@@ -452,6 +531,7 @@ void Pet::Update(uint32 diff)
return;
}
}
+
if(m_duration > 0)
{
if(m_duration > diff)
@@ -462,6 +542,7 @@ void Pet::Update(uint32 diff)
return;
}
}
+
//regenerate focus for hunter pets or energy for deathknight's ghoul
if(m_regenTimer)
{
@@ -486,10 +567,12 @@ void Pet::Update(uint32 diff)
m_regenTimer = 0;
break;
}
- }
+ }
}
+
if(getPetType() != HUNTER_PET)
break;
+
if(m_happinessTimer <= diff)
{
LooseHappiness();
@@ -497,6 +580,7 @@ void Pet::Update(uint32 diff)
}
else
m_happinessTimer -= diff;
+
break;
}
default:
@@ -504,13 +588,17 @@ void Pet::Update(uint32 diff)
}
Creature::Update(diff);
}
+
void Creature::Regenerate(Powers power)
{
uint32 curValue = GetPower(power);
uint32 maxValue = GetMaxPower(power);
+
if (curValue >= maxValue)
return;
+
float addvalue = 0.0f;
+
switch (power)
{
case POWER_FOCUS:
@@ -528,13 +616,16 @@ void Creature::Regenerate(Powers power)
default:
return;
}
+
// Apply modifiers (if any).
AuraEffectList const& ModPowerRegenPCTAuras = GetAurasByType(SPELL_AURA_MOD_POWER_REGEN_PERCENT);
for(AuraEffectList::const_iterator i = ModPowerRegenPCTAuras.begin(); i != ModPowerRegenPCTAuras.end(); ++i)
if ((*i)->GetMiscValue() == power)
addvalue *= ((*i)->GetAmount() + 100) / 100.0f;
+
ModifyPower(power, (int32)addvalue);
}
+
void Pet::LooseHappiness()
{
uint32 curValue = GetPower(POWER_HAPPINESS);
@@ -545,6 +636,7 @@ void Pet::LooseHappiness()
addvalue = int32(addvalue * 1.5);
ModifyPower(POWER_HAPPINESS, -addvalue);
}
+
HappinessState Pet::GetHappinessState()
{
if(GetPower(POWER_HAPPINESS) < HAPPINESS_LEVEL_SIZE)
@@ -554,26 +646,35 @@ HappinessState Pet::GetHappinessState()
else
return CONTENT;
}
+
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::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
{
if(itr->second.state == PETSPELL_REMOVED)
continue;
+
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;
@@ -584,48 +685,63 @@ bool Pet::CanTakeMoreActiveSpells(uint32 spellid)
}
return true;
}
+
void Pet::Remove(PetSaveMode mode, bool returnreagent)
{
m_owner->RemovePet(this,mode,returnreagent);
}
+
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;
+
GivePetLevel(level+1);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(level+1)*PET_XP_FACTOR);
+
level = getLevel();
nextLvlXP = GetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP);
}
+
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, newXP);
}
+
void Pet::GivePetLevel(uint32 level)
{
if(!level)
return;
+
InitStatsForLevel(level);
InitLevelupSpellsForLevel();
InitTalentForLevel();
}
+
bool Pet::CreateBaseAtCreature(Creature* creature)
{
if(!creature)
@@ -634,23 +750,28 @@ bool Pet::CreateBaseAtCreature(Creature* creature)
return false;
}
uint32 guid=objmgr.GenerateLowGuid(HIGHGUID_PET);
+
sLog.outDebug("Create pet");
uint32 pet_number = objmgr.GeneratePetNumber();
if(!Create(guid, creature->GetMap(), creature->GetPhaseMask(), creature->GetEntry(), pet_number))
return false;
+
Relocate(creature->GetPositionX(), creature->GetPositionY(), creature->GetPositionZ(), creature->GetOrientation());
+
if(!IsPositionValid())
{
sLog.outError("Pet (guidlow %d, entry %d) not created base at creature. Suggested coordinates isn't valid (X: %f Y: %f)",
GetGUIDLow(), GetEntry(), GetPositionX(), GetPositionY());
return false;
}
+
CreatureInfo const *cinfo = GetCreatureInfo();
if(!cinfo)
{
sLog.outError("CreateBaseAtCreature() failed, creatureInfo is missing!");
return false;
}
+
SetDisplayId(creature->GetDisplayId());
SetNativeDisplayId(creature->GetNativeDisplayId());
SetMaxPower(POWER_HAPPINESS, GetCreatePowers(POWER_HAPPINESS));
@@ -660,10 +781,12 @@ bool Pet::CreateBaseAtCreature(Creature* creature)
SetUInt32Value(UNIT_FIELD_PETEXPERIENCE, 0);
SetUInt32Value(UNIT_FIELD_PETNEXTLEVELEXP, objmgr.GetXPForLevel(creature->getLevel())*PET_XP_FACTOR);
SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_NONE);
+
if(CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family))
SetName(cFamily->Name[sWorld.GetDefaultDbcLocale()]);
else
SetName(creature->GetNameForLocaleIdx(objmgr.GetDBCLocaleIndex()));
+
if(cinfo->type == CREATURE_TYPE_BEAST)
{
SetUInt32Value(UNIT_FIELD_BYTES_0, 0x02020100);
@@ -673,12 +796,15 @@ bool Pet::CreateBaseAtCreature(Creature* creature)
}
return true;
}
+
// TODO: Move stat mods code to pet passive auras
bool Guardian::InitStatsForLevel(uint32 petlevel)
{
CreatureInfo const *cinfo = GetCreatureInfo();
assert(cinfo);
+
SetLevel(petlevel);
+
//Determine pet type
PetType petType = MAX_PET_TYPE;
if(isPet() && m_owner->GetTypeId() == TYPEID_PLAYER)
@@ -693,13 +819,19 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
else
sLog.outError("Unknown type pet %u is summoned by player class %u", GetEntry(), m_owner->getClass());
}
+
uint32 creature_ID = (petType == HUNTER_PET) ? 1 : cinfo->Entry;
+
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);
+
//scale
CreatureFamilyEntry const* cFamily = sCreatureFamilyStore.LookupEntry(cinfo->family);
if(cFamily && cFamily->minScale > 0.0f && petType==HUNTER_PET)
@@ -711,8 +843,10 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
scale = cFamily->minScale;
else
scale = cFamily->minScale + float(getLevel() - cFamily->minScaleLevel) / cFamily->maxScaleLevel * (cFamily->maxScale - cFamily->minScale);
+
SetFloatValue(OBJECT_FIELD_SCALE_X, scale);
}
+
//resistance
int32 createResistance[MAX_SPELL_SCHOOL] = {0,0,0,0,0,0,0};
if(cinfo && petType != HUNTER_PET)
@@ -726,6 +860,7 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
}
for (uint8 i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
SetModifierValue(UnitMods(UNIT_MOD_RESISTANCE_START + i), BASE_VALUE, float(createResistance[i]));
+
//health, mana, armor and resistance
PetLevelInfo const* pInfo = objmgr.GetPetLevelInfo(creature_ID, petlevel);
if(pInfo) // exist in DB
@@ -733,8 +868,10 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
SetCreateHealth(pInfo->health);
if(petType != HUNTER_PET) //hunter pet use focus
SetCreateMana(pInfo->mana);
+
if(pInfo->armor > 0)
SetModifierValue(UNIT_MOD_ARMOR, BASE_VALUE, float(pInfo->armor));
+
for(uint8 stat = 0; stat < MAX_STATS; ++stat)
SetCreateStat(Stats(stat), float(pInfo->stats[stat]));
}
@@ -743,12 +880,14 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
// 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);
}
+
m_bonusdamage = 0;
switch(petType)
{
@@ -760,8 +899,10 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
uint32 val = (fire > shadow) ? fire : shadow;
SetBonusDamage(int32 (val * 0.15f));
//bonusAP += val * 0.57;
+
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));
break;
}
@@ -855,25 +996,32 @@ bool Guardian::InitStatsForLevel(uint32 petlevel)
break;
}
}
+
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
@@ -889,47 +1037,63 @@ uint32 Pet::GetCurrentFoodBenefitLevel(uint32 itemlevel)
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); // flags (0x1, 0x2)
+
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)*IN_MILISECONDS);
+
_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();)
{
@@ -942,28 +1106,35 @@ void Pet::_SaveSpellCooldowns()
}
}
}
+
void Pet::_LoadSpells()
{
QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active FROM pet_spell WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
if(result)
{
do
{
Field *fields = result->Fetch();
+
addSpell(fields[0].GetUInt32(), ActiveStates(fields[1].GetUInt8()), PETSPELL_UNCHANGED);
}
while( result->NextRow() );
+
delete result;
}
}
+
void Pet::_SaveSpells()
{
for (PetSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end(); itr = next)
{
++next;
+
// prevent saving family passives to DB
if (itr->second.type == PETSPELL_FAMILY)
continue;
+
switch(itr->second.state)
{
case PETSPELL_REMOVED:
@@ -980,13 +1151,17 @@ void Pet::_SaveSpells()
case PETSPELL_UNCHANGED:
continue;
}
+
itr->second.state = PETSPELL_UNCHANGED;
}
}
+
void Pet::_LoadAuras(uint32 timediff)
{
sLog.outDebug("Loading auras for pet %u",GetGUIDLow());
+
QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2 ,maxduration,remaintime,remaincharges FROM pet_aura WHERE guid = '%u'",m_charmInfo->GetPetNumber());
+
if(result)
{
do
@@ -1003,19 +1178,23 @@ void Pet::_LoadAuras(uint32 timediff)
int32 maxduration = (int32)fields[7].GetUInt32();
int32 remaintime = (int32)fields[8].GetUInt32();
int32 remaincharges = (int32)fields[9].GetUInt32();
+
SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
if(!spellproto)
{
sLog.outError("Unknown aura (spellid %u), ignore.",spellid);
continue;
}
+
// negative effects should continue counting down after logout
if (remaintime != -1 && !IsPositiveSpell(spellid))
{
if(remaintime <= int32(timediff))
continue;
+
remaintime -= timediff;
}
+
// prevent wrong values of remaincharges
if(spellproto->procCharges)
{
@@ -1024,6 +1203,7 @@ void Pet::_LoadAuras(uint32 timediff)
}
else
remaincharges = 0;
+
Aura* aura = new Aura(spellproto, effmask, this, this, this);
aura->SetLoadedState(caster_guid,maxduration,remaintime,remaincharges, stackcount, &damage[0]);
if(!aura->CanBeSaved())
@@ -1035,17 +1215,21 @@ void Pet::_LoadAuras(uint32 timediff)
sLog.outDetail("Added aura spellid %u, effectmask %u", spellproto->Id, effmask);
}
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)
{
if(!itr->second->CanBeSaved())
continue;
+
int32 amounts[MAX_SPELL_EFFECTS];
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
@@ -1054,6 +1238,7 @@ void Pet::_SaveAuras()
else
amounts[i] = 0;
}
+
CharacterDatabase.PExecute("INSERT INTO pet_aura (guid,caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2,maxduration,remaintime,remaincharges) "
"VALUES ('%u', '" UI64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%u')",
m_charmInfo->GetPetNumber(), itr->second->GetCasterGUID(), itr->second->GetId(), (uint32)itr->second->GetEffectMask(),
@@ -1061,6 +1246,7 @@ void Pet::_SaveAuras()
,itr->second->GetAuraMaxDuration(), itr->second->GetAuraDuration(), (uint32)itr->second->GetAuraCharges());
}
}
+
bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpellState state /*= PETSPELL_NEW*/, PetSpellType type /*= PETSPELL_NORMAL*/)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
@@ -1074,8 +1260,10 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
}
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())
{
@@ -1088,19 +1276,24 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
{
// can be in case spell loading but learned at some previous spell loading
itr->second.state = PETSPELL_UNCHANGED;
+
if(active == ACT_ENABLED)
ToggleAutocast(spell_id, true);
else if(active == ACT_DISABLED)
ToggleAutocast(spell_id, false);
+
return false;
}
else
return false;
}
+
uint32 oldspell_id = 0;
+
PetSpell newspell;
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(IsAutocastableSpell(spell_id))
@@ -1110,6 +1303,7 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
}
else
newspell.active = active;
+
// talent: unlearn all other talent ranks (high and low)
if(TalentSpellPos const* talentPos = GetTalentSpellPos(spell_id))
{
@@ -1121,6 +1315,7 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
uint32 rankSpellId = talentInfo->RankID[i];
if(!rankSpellId || rankSpellId==spell_id)
continue;
+
// skip unknown ranks
if(!HasSpell(rankSpellId))
continue;
@@ -1133,14 +1328,17 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
for (PetSpellMap::const_iterator itr2 = m_spells.begin(); itr2 != m_spells.end(); ++itr2)
{
if(itr2->second.state == PETSPELL_REMOVED) continue;
+
if( spellmgr.IsRankSpellDueToSpell(spellInfo,itr2->first) )
{
// replace by new high rank
if(spellmgr.IsHighRankOfSpell(spell_id,itr2->first))
{
newspell.active = itr2->second.active;
+
if(newspell.active == ACT_ENABLED)
ToggleAutocast(itr2->first, false);
+
oldspell_id = itr2->first;
unlearnSpell(itr2->first,false,false);
break;
@@ -1151,13 +1349,17 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
}
}
}
+
m_spells[spell_id] = newspell;
+
if (IsPassiveSpell(spell_id))
CastSpell(this, spell_id, true);
else
m_charmInfo->AddSpellToActionBar(spell_id);
+
if(newspell.active == ACT_ENABLED)
ToggleAutocast(spell_id, true);
+
uint32 talentCost = GetTalentSpellCost(spell_id);
if (talentCost)
{
@@ -1169,11 +1371,13 @@ bool Pet::addSpell(uint32 spell_id,ActiveStates active /*= ACT_DECIDE*/, PetSpel
}
return true;
}
+
bool Pet::learnSpell(uint32 spell_id)
{
// prevent duplicated entires in spell book
if (!addSpell(spell_id))
return false;
+
if(!m_loading)
{
WorldPacket data(SMSG_PET_LEARNED_SPELL, 4);
@@ -1183,9 +1387,11 @@ bool Pet::learnSpell(uint32 spell_id)
}
return true;
}
+
void Pet::InitLevelupSpellsForLevel()
{
uint32 level = getLevel();
+
if(PetLevelupSpellSet const *levelupSpells = GetCreatureInfo()->family ? spellmgr.GetPetLevelupSpellList(GetCreatureInfo()->family) : NULL)
{
// PetLevelupSpellSet ordered by levels, process in reversed order
@@ -1199,7 +1405,9 @@ void Pet::InitLevelupSpellsForLevel()
learnSpell(itr->second); // will unlearn prev rank if any
}
}
+
int32 petSpellsId = GetCreatureInfo()->PetSpellDataId ? -(int32)GetCreatureInfo()->PetSpellDataId : GetEntry();
+
// default spells (can be not learned if pet level (as owner level decrease result for example) less first possible in normal game)
if(PetDefaultSpellsEntry const *defSpells = spellmgr.GetPetDefaultSpellsEntry(petSpellsId))
{
@@ -1208,6 +1416,7 @@ void Pet::InitLevelupSpellsForLevel()
SpellEntry const* spellEntry = sSpellStore.LookupEntry(defSpells->spellid[i]);
if(!spellEntry)
continue;
+
// will called first if level down
if(spellEntry->spellLevel > level)
unlearnSpell(spellEntry->Id,true);
@@ -1217,6 +1426,7 @@ void Pet::InitLevelupSpellsForLevel()
}
}
}
+
bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
{
if(removeSpell(spell_id,learn_prev,clear_ab))
@@ -1231,18 +1441,23 @@ bool Pet::unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
}
return false;
}
+
bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
{
PetSpellMap::iterator itr = m_spells.find(spell_id);
if (itr == m_spells.end())
return false;
+
if(itr->second.state == PETSPELL_REMOVED)
return false;
+
if(itr->second.state == PETSPELL_NEW)
m_spells.erase(itr);
else
itr->second.state = PETSPELL_REMOVED;
+
RemoveAurasDueToSpell(spell_id);
+
uint32 talentCost = GetTalentSpellCost(spell_id);
if (talentCost > 0)
{
@@ -1254,6 +1469,7 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
int32 free_points = GetMaxTalentPointsForLevel(getLevel()) - m_usedTalentCount;
SetFreeTalentPoints(free_points > 0 ? free_points : 0);
}
+
if (learn_prev)
{
if (uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id))
@@ -1261,6 +1477,7 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
else
learn_prev = false;
}
+
// if remove last rank or non-ranked then update action bar at server and client if need
if (clear_ab && !learn_prev && m_charmInfo->RemoveSpellFromActionBar(spell_id))
{
@@ -1272,9 +1489,11 @@ bool Pet::removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab)
((Player*)owner)->PetSpellInitialize();
}
}
+
return true;
}
+
void Pet::CleanupActionBar()
{
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
@@ -1287,22 +1506,28 @@ void Pet::CleanupActionBar()
ToggleAutocast(ab->GetAction(), true);
}
}
+
void Pet::InitPetCreateSpells()
{
m_charmInfo->InitPetActionBar();
m_spells.clear();
+
LearnPetPassives();
InitLevelupSpellsForLevel();
+
CastPetAuras(false);
}
+
bool Pet::resetTalents(bool no_cost)
{
Unit *owner = GetOwner();
if (!owner || owner->GetTypeId()!=TYPEID_PLAYER)
return false;
+
// not need after this call
if(((Player*)owner)->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
((Player*)owner)->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS,true);
+
CreatureInfo const * ci = GetCreatureInfo();
if(!ci)
return false;
@@ -1310,34 +1535,46 @@ bool Pet::resetTalents(bool no_cost)
CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
if(!pet_family || pet_family->petTalentType < 0)
return false;
+
Player *player = (Player *)owner;
+
uint32 level = getLevel();
uint32 talentPointsForLevel = GetMaxTalentPointsForLevel(level);
+
if (m_usedTalentCount == 0)
{
SetFreeTalentPoints(talentPointsForLevel);
return false;
}
+
uint32 cost = 0;
+
if(!no_cost)
{
cost = resetTalentsCost();
+
if (player->GetMoney() < cost)
{
player->SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
return false;
}
}
+
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;
+
// unlearn only talents for pets family talent type
if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
continue;
+
for (int j = 0; j < MAX_TALENT_RANK; j++)
{
for(PetSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end();)
@@ -1349,6 +1586,7 @@ bool Pet::resetTalents(bool no_cost)
}
// 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))
{
@@ -1361,10 +1599,13 @@ bool Pet::resetTalents(bool no_cost)
}
}
}
+
SetFreeTalentPoints(talentPointsForLevel);
+
if(!no_cost)
{
player->ModifyMoney(-(int32)cost);
+
m_resetTalentsCost = cost;
m_resetTalentsTime = time(NULL);
}
@@ -1372,65 +1613,91 @@ bool Pet::resetTalents(bool no_cost)
player->PetSpellInitialize();
return true;
}
+
void Pet::resetTalentsForAllPetsOf(Player* owner, Pet* online_pet /*= NULL*/)
{
// not need after this call
if(((Player*)owner)->HasAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS))
((Player*)owner)->RemoveAtLoginFlag(AT_LOGIN_RESET_PET_TALENTS,true);
+
// reset for online
if(online_pet)
online_pet->resetTalents(true);
+
// now need only reset for offline pets (all pets except online case)
uint32 except_petnumber = online_pet ? online_pet->GetCharmInfo()->GetPetNumber() : 0;
+
QueryResult *resultPets = CharacterDatabase.PQuery(
"SELECT id FROM character_pet WHERE owner = '%u' AND id <> '%u'",
owner->GetGUIDLow(),except_petnumber);
+
// no offline pets
if(!resultPets)
return;
+
QueryResult *result = CharacterDatabase.PQuery(
"SELECT DISTINCT pet_spell.spell FROM pet_spell, character_pet "
"WHERE character_pet.owner = '%u' AND character_pet.id = pet_spell.guid AND character_pet.id <> %u",
owner->GetGUIDLow(),except_petnumber);
+
if(!result)
{
delete resultPets;
return;
}
+
bool need_comma = false;
std::ostringstream ss;
ss << "DELETE FROM pet_spell WHERE guid IN (";
+
do
{
Field *fields = resultPets->Fetch();
+
uint32 id = fields[0].GetUInt32();
+
if(need_comma)
ss << ",";
+
ss << id;
+
need_comma = true;
}
while( resultPets->NextRow() );
+
delete resultPets;
+
ss << ") AND spell IN (";
+
bool need_execute = false;
do
{
Field *fields = result->Fetch();
+
uint32 spell = fields[0].GetUInt32();
+
if(!GetTalentSpellCost(spell))
continue;
+
if(need_execute)
ss << ",";
+
ss << spell;
+
need_execute = true;
}
while( result->NextRow() );
+
delete result;
+
if(!need_execute)
return;
+
ss << ")";
+
CharacterDatabase.Execute(ss.str().c_str());
}
+
void Pet::InitTalentForLevel()
{
uint32 level = getLevel();
@@ -1442,15 +1709,19 @@ void Pet::InitTalentForLevel()
resetTalents(true);
}
SetFreeTalentPoints(talentPointsForLevel - m_usedTalentCount);
+
Unit *owner = GetOwner();
if (!owner || owner->GetTypeId() != TYPEID_PLAYER)
return;
+
if(!m_loading)
((Player*)owner)->SendTalentsInfoData(true);
}
+
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;
@@ -1464,6 +1735,7 @@ uint32 Pet::resetTalentsCost() const
else
return (m_resetTalentsCost + 1*GOLD > 10*GOLD ? 10*GOLD : m_resetTalentsCost + 1*GOLD);
}
+
uint8 Pet::GetMaxTalentPointsForLevel(uint32 level)
{
uint8 points = (level >= 20) ? ((level - 16) / 4) : 0;
@@ -1472,21 +1744,27 @@ uint8 Pet::GetMaxTalentPointsForLevel(uint32 level)
points+=owner->GetTotalAuraModifier(SPELL_AURA_MOD_PET_TALENT_POINTS);
return points;
}
+
void Pet::ToggleAutocast(uint32 spellid, bool apply)
{
if(!IsAutocastableSpell(spellid))
return;
+
PetSpellMap::iterator itr = m_spells.find(spellid);
if(itr == m_spells.end())
return;
+
uint32 i;
+
if(apply)
{
for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; ++i)
; // just search
+
if (i == m_autospells.size())
{
m_autospells.push_back(spellid);
+
if(itr->second.active != ACT_ENABLED)
{
itr->second.active = ACT_ENABLED;
@@ -1500,6 +1778,7 @@ void Pet::ToggleAutocast(uint32 spellid, bool apply)
AutoSpellList::iterator itr2 = m_autospells.begin();
for (i = 0; i < m_autospells.size() && m_autospells[i] != spellid; ++i, itr2++)
; // just search
+
if (i < m_autospells.size())
{
m_autospells.erase(itr2);
@@ -1512,6 +1791,7 @@ void Pet::ToggleAutocast(uint32 spellid, bool apply)
}
}
}
+
bool Pet::IsPermanentPetFor(Player* owner)
{
switch(getPetType())
@@ -1532,33 +1812,43 @@ bool Pet::IsPermanentPetFor(Player* owner)
return false;
}
}
+
bool Pet::Create(uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number)
{
assert(map);
SetMap(map);
+
SetPhaseMask(phaseMask,false);
Object::_Create(guidlow, pet_number, HIGHGUID_PET);
+
m_DBTableGuid = guidlow;
m_originalEntry = Entry;
+
if(!InitEntry(Entry))
return false;
+
SetSheath(SHEATH_STATE_MELEE);
+
return true;
}
+
bool Pet::HasSpell(uint32 spell) const
{
PetSpellMap::const_iterator itr = m_spells.find(spell);
return (itr != m_spells.end() && itr->second.state != PETSPELL_REMOVED );
}
+
// 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())
{
@@ -1569,28 +1859,34 @@ void Pet::LearnPetPassives()
addSpell(*petSet, ACT_DECIDE, PETSPELL_NEW, PETSPELL_FAMILY);
}
}
+
void Pet::CastPetAuras(bool current)
{
Unit* owner = GetOwner();
if(!owner || owner->GetTypeId()!=TYPEID_PLAYER)
return;
+
if(!IsPermanentPetFor((Player*)owner))
return;
+
for(PetAuraSet::const_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)
{
uint32 auraId = aura->GetAura(GetEntry());
if(!auraId)
return;
+
if(auraId == 35696) // Demonic Knowledge
{
int32 basePoints = int32(aura->GetDamage() * (GetStat(STAT_STAMINA) + GetStat(STAT_INTELLECT)) / 100);
@@ -1599,17 +1895,21 @@ void Pet::CastPetAura(PetAura const* aura)
else
CastSpell(this, auraId, true);
}
+
void Pet::learnSpellHighRank(uint32 spellid)
{
learnSpell(spellid);
+
if(uint32 next = spellmgr.GetNextSpellInChain(spellid))
learnSpellHighRank(next);
}
+
void Pet::SynchronizeLevelWithOwner()
{
Unit* owner = GetOwner();
if (!owner || owner->GetTypeId() != TYPEID_PLAYER)
return;
+
switch(getPetType())
{
// always same level
diff --git a/src/game/Pet.h b/src/game/Pet.h
index 1355b259e5f..7f3684bbd59 100644
--- a/src/game/Pet.h
+++ b/src/game/Pet.h
@@ -17,19 +17,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_PET_H
#define TRINITYCORE_PET_H
+
#include "ObjectDefines.h"
#include "Unit.h"
#include "TemporarySummon.h"
+
enum PetType
{
SUMMON_PET = 0,
HUNTER_PET = 1,
MAX_PET_TYPE = 4,
};
+
extern char const* petTypeSuffix[MAX_PET_TYPE];
+
#define MAX_PET_STABLES 4
+
// stored in character_pet.slot
enum PetSaveMode
{
@@ -39,12 +45,14 @@ enum PetSaveMode
PET_SAVE_LAST_STABLE_SLOT = MAX_PET_STABLES, // last in DB stable slot index (including), all higher have same meaning as PET_SAVE_NOT_IN_SLOT
PET_SAVE_NOT_IN_SLOT = 100 // for avoid conflict with stable size grow will use 100
};
+
enum HappinessState
{
UNHAPPY = 1,
CONTENT = 2,
HAPPY = 3
};
+
enum PetSpellState
{
PETSPELL_UNCHANGED = 0,
@@ -52,18 +60,21 @@ enum PetSpellState
PETSPELL_NEW = 2,
PETSPELL_REMOVED = 3
};
+
enum PetSpellType
{
PETSPELL_NORMAL = 0,
PETSPELL_FAMILY = 1,
PETSPELL_TALENT = 2,
};
+
struct PetSpell
{
ActiveStates active;
PetSpellState state;
PetSpellType type;
};
+
enum ActionFeedback
{
FEEDBACK_NONE = 0,
@@ -71,15 +82,18 @@ enum ActionFeedback
FEEDBACK_NOTHING_TO_ATT = 2,
FEEDBACK_CANT_ATT_TARGET = 3
};
+
enum PetTalk
{
PET_TALK_SPECIAL_SPELL = 0,
PET_TALK_ATTACK = 1
};
+
enum PetNameInvalidReason
{
// custom, not send
PET_NAME_SUCCESS = 0,
+
PET_NAME_INVALID = 1,
PET_NAME_NO_NAME = 2,
PET_NAME_TOO_SHORT = 3,
@@ -94,26 +108,37 @@ enum PetNameInvalidReason
PET_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 15,
PET_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 16
};
+
typedef UNORDERED_MAP<uint32, PetSpell> PetSpellMap;
typedef std::vector<uint32> AutoSpellList;
+
#define HAPPINESS_LEVEL_SIZE 333000
+
#define ACTIVE_SPELLS_MAX 4
+
#define OWNER_MAX_DISTANCE 100.0f
+
#define PET_FOLLOW_DIST 1
#define PET_FOLLOW_ANGLE (M_PI/2)
+
class Player;
+
class Pet : public Guardian
{
public:
explicit Pet(Player *owner, 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 IsPermanentPetFor(Player* owner); // pet have tab in character windows and set UNIT_FIELD_PETNUMBER
+
bool Create (uint32 guidlow, Map *map, uint32 phaseMask, uint32 Entry, uint32 pet_number);
bool CreateBaseAtCreature(Creature* creature);
bool LoadPetFromDB( Player* owner,uint32 petentry = 0,uint32 petnumber = 0, bool current = false );
@@ -121,8 +146,10 @@ class Pet : public Guardian
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
{
@@ -131,6 +158,7 @@ class Pet : public Guardian
else
return m_autospells[pos];
}
+
void LooseHappiness();
HappinessState GetHappinessState();
void GivePetXP(uint32 xp);
@@ -139,6 +167,7 @@ class Pet : public Guardian
bool HaveInDiet(ItemPrototype const* item) const;
uint32 GetCurrentFoodBenefitLevel(uint32 itemlevel);
void SetDuration(int32 dur) { m_duration = dur; }
+
/*
bool UpdateStats(Stats stat);
bool UpdateAllStats();
@@ -149,18 +178,23 @@ class Pet : public Guardian
void UpdateAttackPowerAndDamage(bool ranged = false);
void UpdateDamagePhysical(WeaponAttackType attType);
*/
+
bool CanTakeMoreActiveSpells(uint32 SpellIconID);
void ToggleAutocast(uint32 spellid, bool apply);
+
bool HasSpell(uint32 spell) const;
+
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(uint32 spell_id,ActiveStates active = ACT_DECIDE, PetSpellState state = PETSPELL_NEW, PetSpellType type = PETSPELL_NORMAL);
bool learnSpell(uint32 spell_id);
void learnSpellHighRank(uint32 spellid);
@@ -168,24 +202,33 @@ class Pet : public Guardian
bool unlearnSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
bool removeSpell(uint32 spell_id, bool learn_prev, bool clear_ab = true);
void CleanupActionBar();
+
PetSpellMap m_spells;
AutoSpellList m_autospells;
+
void InitPetCreateSpells();
+
bool resetTalents(bool no_cost = false);
static void resetTalentsForAllPetsOf(Player* owner, Pet* online_pet = NULL);
uint32 resetTalentsCost() const;
void InitTalentForLevel();
+
uint8 GetMaxTalentPointsForLevel(uint32 level);
uint8 GetFreeTalentPoints() { return GetByteValue(UNIT_FIELD_BYTES_1, 1); }
void SetFreeTalentPoints(uint8 points) { SetByteValue(UNIT_FIELD_BYTES_1, 1, points); }
+
uint32 m_resetTalentsCost;
time_t m_resetTalentsTime;
uint32 m_usedTalentCount;
+
const uint64& GetAuraUpdateMaskForRaid() const { return m_auraRaidUpdateMask; }
void SetAuraUpdateMaskForRaid(uint8 slot) { m_auraRaidUpdateMask |= (uint64(1) << slot); }
void ResetAuraUpdateMaskForRaid() { m_auraRaidUpdateMask = 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)
+
Player *GetOwner() { return m_owner; }
protected:
Player *m_owner;
@@ -195,7 +238,9 @@ class Pet : public Guardian
uint64 m_auraRaidUpdateMask;
bool m_loading;
uint32 m_regenTimer;
+
DeclinedName *m_declinedname;
+
private:
void SaveToDB(uint32, uint8) // overwrited of Creature::SaveToDB - don't must be called
{
diff --git a/src/game/PetAI.cpp b/src/game/PetAI.cpp
index 64cf01b383d..534500e51ea 100644
--- a/src/game/PetAI.cpp
+++ b/src/game/PetAI.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -28,27 +29,34 @@
#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) : CreatureAI(c), i_tracker(TIME_INTERVAL_LOOK)
{
m_AllySet.clear();
UpdateAllies();
}
+
void PetAI::EnterEvadeMode()
{
}
+
bool PetAI::_needToStop() const
{
// This is needed for charmed creatures, as once their target was reset other effects can trigger threat
if(m_creature->isCharmed() && m_creature->getVictim() == m_creature->GetCharmer())
return true;
+
return !m_creature->canAttack(m_creature->getVictim());
}
+
void PetAI::_stopAttack()
{
if( !m_creature->isAlive() )
@@ -58,20 +66,25 @@ void PetAI::_stopAttack()
m_creature->GetMotionMaster()->MoveIdle();
m_creature->CombatStop();
m_creature->getHostilRefManager().deleteReferences();
+
return;
}
+
m_creature->AttackStop();
me->GetCharmInfo()->SetIsCommandAttack(false);
HandleReturnMovement();
}
+
void PetAI::UpdateAI(const uint32 diff)
{
Unit* owner = m_creature->GetCharmerOrOwner();
+
if(m_updateAlliesTimer <= diff)
// UpdateAllies self set update timer
UpdateAllies();
else
m_updateAlliesTimer -= diff;
+
// m_creature->getVictim() can't be used for check in case stop fighting, m_creature->getVictim() clear at Unit death etc.
if( m_creature->getVictim() )
{
@@ -81,11 +94,13 @@ void PetAI::UpdateAI(const uint32 diff)
_stopAttack();
return;
}
+
DoMeleeAttackIfReady();
}
else if(owner && m_creature->GetCharmInfo()) //no victim
{
Unit *nextTarget = SelectNextTarget();
+
if (nextTarget)
AttackStart(nextTarget);
else
@@ -93,28 +108,35 @@ void PetAI::UpdateAI(const uint32 diff)
}
else if (owner && !m_creature->hasUnitState(UNIT_STAT_FOLLOW)) // no charm info and no victim
m_creature->GetMotionMaster()->MoveFollow(owner,PET_FOLLOW_DIST, m_creature->GetFollowAngle());
+
if(!me->GetCharmInfo())
return;
+
bool inCombat = me->getVictim();
+
// Autocast (casted only in combat or persistent spells in any state)
if (m_creature->GetGlobalCooldown() == 0 && !m_creature->hasUnitState(UNIT_STAT_CASTING))
{
typedef std::vector<std::pair<Unit*, Spell*> > TargetSpellList;
TargetSpellList targetSpellStore;
+
for (uint8 i = 0; i < m_creature->GetPetAutoSpellSize(); ++i)
{
uint32 spellID = m_creature->GetPetAutoSpellOnPos(i);
if (!spellID)
continue;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellID);
if (!spellInfo)
continue;
+
// ignore some combinations of combat state and combat/noncombat spells
if (!inCombat)
{
// ignore attacking spells, and allow only self/around spells
if (!IsPositiveSpell(spellInfo->Id))
continue;
+
// non combat spells allowed
// only pet spells have IsNonCombatSpell and not fit this reqs:
// Consume Shadows, Lesser Invisibility, so ignore checks for its
@@ -124,6 +146,7 @@ void PetAI::UpdateAI(const uint32 diff)
int32 duration = GetSpellDuration(spellInfo);
if ((spellInfo->manaCost || spellInfo->ManaCostPercentage || spellInfo->manaPerSecond) && duration > 0)
continue;
+
// allow only spell without cooldown > duration
int32 cooldown = GetSpellRecoveryTime(spellInfo);
if (cooldown >= 0 && duration >= 0 && cooldown > duration)
@@ -136,7 +159,9 @@ void PetAI::UpdateAI(const uint32 diff)
if (IsNonCombatSpell(spellInfo))
continue;
}
+
Spell *spell = new Spell(m_creature, spellInfo, false, 0);
+
// Fix to allow pets on STAY to autocast
if (inCombat && _CanAttack(me->getVictim()) && spell->CanAutoCast(me->getVictim()))
{
@@ -149,9 +174,11 @@ void PetAI::UpdateAI(const uint32 diff)
for(std::set<uint64>::const_iterator tar = m_AllySet.begin(); tar != m_AllySet.end(); ++tar)
{
Unit* Target = ObjectAccessor::GetUnit(*m_creature,*tar);
+
//only buff targets that are in combat, unless the spell can only be cast while out of combat
if(!Target)
continue;
+
if(spell->CanAutoCast(Target))
{
targetSpellStore.push_back(std::make_pair<Unit*, Spell*>(Target, spell));
@@ -163,46 +190,60 @@ void PetAI::UpdateAI(const uint32 diff)
delete spell;
}
}
+
//found units to cast on to
if (!targetSpellStore.empty())
{
uint32 index = urand(0, targetSpellStore.size() - 1);
+
Spell* spell = targetSpellStore[index].second;
Unit* target = targetSpellStore[index].first;
+
targetSpellStore.erase(targetSpellStore.begin() + index);
+
SpellCastTargets targets;
targets.setUnitTarget( target );
+
if( !m_creature->HasInArc(M_PI, target) )
{
m_creature->SetInFront(target);
if( target->GetTypeId() == TYPEID_PLAYER )
m_creature->SendUpdateToPlayer( (Player*)target );
+
if(owner && owner->GetTypeId() == TYPEID_PLAYER)
m_creature->SendUpdateToPlayer( (Player*)owner );
}
+
m_creature->AddCreatureSpellCooldown(spell->m_spellInfo->Id);
+
spell->prepare(&targets);
}
+
// deleted cached Spell objects
for(TargetSpellList::const_iterator itr = targetSpellStore.begin(); itr != targetSpellStore.end(); ++itr)
delete itr->second;
}
}
+
void PetAI::UpdateAllies()
{
Unit* owner = m_creature->GetCharmerOrOwner();
Group *pGroup = NULL;
+
m_updateAlliesTimer = 10*IN_MILISECONDS; //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(m_creature->GetGUID());
if(pGroup) //add group
@@ -212,38 +253,47 @@ void PetAI::UpdateAllies()
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::KilledUnit(Unit *victim)
{
// Called from Unit::Kill() in case where pet or owner kills something
// if owner killed this victim, pet may still be attacking something else
if (me->getVictim() && me->getVictim() != victim)
return;
+
// Clear target just in case. May help problem where health / focus / mana
// regen gets stuck. Also resets attack command.
// Can't use _stopAttack() because that activates movement handlers and ignores
// next target selection
me->AttackStop();
me->GetCharmInfo()->SetIsCommandAttack(false);
+
Unit *nextTarget = SelectNextTarget();
+
if (nextTarget)
AttackStart(nextTarget);
else
HandleReturnMovement(); // Return
}
+
void PetAI::AttackStart(Unit *target)
{
// Overrides Unit::AttackStart to correctly evaluate Pet states
+
// Check all pet states to decide if we can attack this target
if (!_CanAttack(target))
return;
+
// We can attack, should we chase or not?
if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
DoAttack(target,true); // FOLLOW, attack with chase
@@ -255,28 +305,36 @@ void PetAI::AttackStart(Unit *target)
DoAttack(target,false); // STAY, target in range, attack not clicked so attack without chase
}
}
+
Unit *PetAI::SelectNextTarget()
{
// Provides next target selection after current target death
+
// Passive pets don't do next target selection
if (me->HasReactState(REACT_PASSIVE))
return NULL;
+
// Check pet's attackers first to prevent dragging mobs back
// to owner
if (me->getAttackerForHelper())
return me->getAttackerForHelper();
+
// Check owner's attackers if pet didn't have any
if (me->GetCharmerOrOwner()->getAttackerForHelper())
return me->GetCharmerOrOwner()->getAttackerForHelper();
+
// 3.0.2 - Pets now start attacking their owners target in defensive mode as soon as the hunter does
if (me->GetCharmerOrOwner()->getVictim())
return me->GetCharmerOrOwner()->getVictim();
+
// Default
return NULL;
}
+
void PetAI::HandleReturnMovement()
{
// Handles moving the pet back to stay or owner
+
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
{
if (!me->GetCharmInfo()->IsAtStay() && !me->GetCharmInfo()->IsReturning())
@@ -285,6 +343,7 @@ void PetAI::HandleReturnMovement()
if (!me->GetCharmInfo()->IsCommandAttack())
{
float x,y,z;
+
me->GetCharmInfo()->GetStayPosition(x, y, z);
me->GetCharmInfo()->SetIsReturning(true);
me->GetMotionMaster()->Clear();
@@ -304,15 +363,20 @@ void PetAI::HandleReturnMovement()
}
}
}
+
}
+
void PetAI::DoAttack(Unit *target, bool chase)
{
// Handles attack with or without chase and also resets all
// PetAI flags for next update / creature kill
+
// me->GetCharmInfo()->SetIsCommandAttack(false);
+
// The following conditions are true if chase == true
// (Follow && (Aggressive || Defensive))
// ((Stay || Follow) && (Passive && player clicked attack))
+
if (chase)
{
if (me->Attack(target,true))
@@ -332,6 +396,7 @@ void PetAI::DoAttack(Unit *target, bool chase)
me->Attack(target,true);
}
}
+
void PetAI::MovementInform(uint32 moveType, uint32 data)
{
// Receives notification when pet reaches stay or follow owner
@@ -352,6 +417,7 @@ void PetAI::MovementInform(uint32 moveType, uint32 data)
}
}
break;
+
case TARGETED_MOTION_TYPE:
{
// If data is owner's GUIDLow then we've reached follow point,
@@ -366,30 +432,39 @@ void PetAI::MovementInform(uint32 moveType, uint32 data)
}
}
break;
+
default:
break;
}
}
+
bool PetAI::_CanAttack(Unit *target)
{
// Evaluates wether a pet can attack a specific
// target based on CommandState, ReactState and other flags
+
// Returning - check first since pets returning ignore attacks
if (me->GetCharmInfo()->IsReturning())
return false;
+
// Passive - check now so we don't have to worry about passive in later checks
if (me->HasReactState(REACT_PASSIVE))
return me->GetCharmInfo()->IsCommandAttack();
+
// Pets commanded to attack should not stop their approach if attacked by another creature
if (me->getVictim() && (me->getVictim() != target))
return !me->GetCharmInfo()->IsCommandAttack();
+
// From this point on, pet will always be either aggressive or defensive
+
// Stay - can attack if target is within range or commanded to
if (me->GetCharmInfo()->HasCommandState(COMMAND_STAY))
return (me->IsWithinMeleeRange(target, MIN_MELEE_REACH) || me->GetCharmInfo()->IsCommandAttack());
+
// Follow
if (me->GetCharmInfo()->HasCommandState(COMMAND_FOLLOW))
return true;
+
// default, though we shouldn't ever get here
return false;
}
diff --git a/src/game/PetAI.h b/src/game/PetAI.h
index 6dee89541f1..226aa493202 100644
--- a/src/game/PetAI.h
+++ b/src/game/PetAI.h
@@ -17,32 +17,44 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_PETAI_H
#define TRINITY_PETAI_H
+
#include "CreatureAI.h"
#include "Timer.h"
+
class Creature;
class Spell;
+
class TRINITY_DLL_DECL PetAI : public CreatureAI
{
public:
+
explicit PetAI(Creature *c);
+
void EnterEvadeMode();
void JustDied(Unit *who) { _stopAttack(); }
+
void UpdateAI(const uint32);
static int Permissible(const Creature *);
+
void KilledUnit(Unit *victim);
void AttackStart(Unit *target);
void MovementInform(uint32 moveType, uint32 data);
+
private:
bool _isVisible(Unit *) const;
bool _needToStop(void) const;
void _stopAttack(void);
+
void UpdateAllies();
+
TimeTracker i_tracker;
bool inCombat;
std::set<uint64> m_AllySet;
uint32 m_updateAlliesTimer;
+
Unit *SelectNextTarget();
void HandleReturnMovement();
void DoAttack(Unit *target, bool chase);
diff --git a/src/game/PetHandler.cpp b/src/game/PetHandler.cpp
index 78813453722..733a857373b 100644
--- a/src/game/PetHandler.cpp
+++ b/src/game/PetHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -30,6 +31,7 @@
#include "Util.h"
#include "Pet.h"
#include "World.h"
+
void WorldSession::HandlePetAction( WorldPacket & recv_data )
{
uint64 guid1;
@@ -38,8 +40,10 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
recv_data >> guid1; //pet guid
recv_data >> data;
recv_data >> guid2; //tag guid
+
uint32 spellid = UNIT_ACTION_BUTTON_ACTION(data);
uint8 flag = UNIT_ACTION_BUTTON_TYPE(data); //delete = 0x07 CastSpell = C1
+
// used also for charmed creature
Unit* pet= ObjectAccessor::GetUnit(*_player, guid1);
sLog.outDetail("HandlePetAction.Pet %u flag is %u, spellid is %u, target %u.", uint32(GUID_LOPART(guid1)), uint32(flag), spellid, uint32(GUID_LOPART(guid2)) );
@@ -48,14 +52,17 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid1)) );
return;
}
+
if(pet != GetPlayer()->GetFirstControlled())
{
sLog.outError("HandlePetAction.Pet %u isn't pet of player %s.", uint32(GUID_LOPART(guid1)), GetPlayer()->GetName() );
return;
}
+
//TODO: allow control charmed player?
if(pet->GetTypeId() == TYPEID_PLAYER && !(flag == ACT_COMMAND && spellid == COMMAND_ATTACK))
return;
+
if(GetPlayer()->m_Controlled.size() == 1)
HandlePetActionHelper(pet, guid1, spellid, flag, guid2);
else
@@ -69,6 +76,7 @@ void WorldSession::HandlePetAction( WorldPacket & recv_data )
HandlePetActionHelper(*itr, guid1, spellid, flag, guid2);
}
}
+
void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2)
{
CharmInfo *charmInfo = pet->GetCharmInfo();
@@ -77,6 +85,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
sLog.outError("WorldSession::HandlePetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
+
switch(flag)
{
case ACT_COMMAND: //0x07
@@ -87,6 +96,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
pet->InterruptNonMeleeSpells(false);
pet->GetMotionMaster()->MoveIdle();
charmInfo->SetCommandState( COMMAND_STAY );
+
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(true);
charmInfo->SetIsFollowing(false);
@@ -98,6 +108,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
pet->InterruptNonMeleeSpells(false);
pet->GetMotionMaster()->MoveFollow(_player,PET_FOLLOW_DIST,pet->GetFollowAngle());
charmInfo->SetCommandState( COMMAND_FOLLOW );
+
charmInfo->SetIsCommandAttack(false);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsReturning(true);
@@ -112,31 +123,40 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
//TODO: Send proper error message to client
return;
}
+
// only place where pet can be player
Unit *TargetUnit = ObjectAccessor::GetUnit(*_player, guid2);
if(!TargetUnit)
return;
+
if(!pet->canAttack(TargetUnit))
return;
+
// Not let attack through obstructions
if(sWorld.getConfig(CONFIG_PET_LOS))
{
+
if(!pet->IsWithinLOSInMap(TargetUnit))
return;
+
}
+
pet->clearUnitState(UNIT_STAT_FOLLOW);
// This is true if pet has no target or has target but targets differs.
if(pet->getVictim() != TargetUnit || ( pet->getVictim() == TargetUnit && !pet->GetCharmInfo()->IsCommandAttack() ))
{
if (pet->getVictim())
pet->AttackStop();
+
if(pet->GetTypeId() != TYPEID_PLAYER && ((Creature*)pet)->IsAIEnabled)
{
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
+
((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);
@@ -150,10 +170,12 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
{
if(pet->getVictim() && pet->getVictim() != TargetUnit)
pet->AttackStop();
+
charmInfo->SetIsCommandAttack(true);
charmInfo->SetIsAtStay(false);
charmInfo->SetIsFollowing(false);
charmInfo->SetIsReturning(false);
+
pet->Attack(TargetUnit,true);
pet->SendPetAIReaction(guid1);
}
@@ -189,6 +211,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
{
case REACT_PASSIVE: //passive
pet->AttackStop();
+
case REACT_DEFENSIVE: //recovery
case REACT_AGGRESSIVE: //activete
if(pet->GetTypeId() == TYPEID_UNIT)
@@ -203,8 +226,10 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
Unit* unit_target = NULL;
if (((Creature*)pet)->GetGlobalCooldown() > 0)
return;
+
if(guid2)
unit_target = ObjectAccessor::GetUnit(*_player,guid2);
+
// do not cast unknown spells
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid );
if(!spellInfo)
@@ -212,14 +237,17 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
sLog.outError("WORLD: unknown PET spell id %i", spellid);
return;
}
+
for(uint32 i = 0; i < 3;++i)
{
if(spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_SRC || spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_AREA_ENEMY_DST || spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_DYNOBJ_ENEMY)
return;
}
+
// do not cast not learned spells
if(!pet->HasSpell(spellid) || IsPassiveSpell(spellid))
return;
+
// Clear the flags as if owner clicked 'attack'. AI will reset them
// after AttackStart, even if spell failed
if (pet->GetCharmInfo())
@@ -229,8 +257,11 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
pet->GetCharmInfo()->SetIsReturning(false);
pet->GetCharmInfo()->SetIsFollowing(false);
}
+
Spell *spell = new Spell(pet, spellInfo, false);
+
SpellCastResult result = spell->CheckPetCast(unit_target);
+
//auto turn to target unless possessed
if(result == SPELL_FAILED_UNIT_NOT_INFRONT && !pet->isPossessed() && !pet->IsVehicle())
{
@@ -251,10 +282,13 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
pet->SendUpdateToPlayer((Player*)powner);
result = SPELL_CAST_OK;
}
+
if(result == SPELL_CAST_OK)
{
((Creature*)pet)->AddCreatureSpellCooldown(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))
@@ -263,6 +297,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
{
pet->SendPetAIReaction(guid1);
}
+
if( unit_target && !GetPlayer()->IsFriendlyTo(unit_target) && !pet->isPossessed() && !pet->IsVehicle())
{
// This is true if pet has no target or has target but targets differs.
@@ -275,6 +310,7 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
((Creature*)pet)->AI()->AttackStart(unit_target);
}
}
+
spell->prepare(&(spell->m_targets));
}
else
@@ -283,10 +319,13 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
Spell::SendCastResult(GetPlayer(),spellInfo,0,result);
else
pet->SendPetCastFail(spellid, result);
+
if(!((Creature*)pet)->HasSpellCooldown(spellid))
GetPlayer()->SendClearCooldown(spellid, pet);
+
spell->finish(false);
delete spell;
+
// reset specific flags in case of spell fail. AI will reset other flags
if (pet->GetCharmInfo())
pet->GetCharmInfo()->SetIsCommandAttack(false);
@@ -297,25 +336,33 @@ void WorldSession::HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid
sLog.outError("WORLD: unknown PET flag Action %i and spellid %i.", uint32(flag), spellid);
}
}
+
void WorldSession::HandlePetNameQuery( WorldPacket & recv_data )
{
sLog.outDetail( "HandlePetNameQuery. CMSG_PET_NAME_QUERY" );
+
uint32 petnumber;
uint64 petguid;
+
recv_data >> petnumber;
recv_data >> petguid;
+
SendPetNameQuery(petguid,petnumber);
}
+
void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber)
{
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_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);
@@ -324,39 +371,52 @@ void WorldSession::SendPetNameQuery( uint64 petguid, uint32 petnumber)
}
else
data << uint8(0);
+
_player->GetSession()->SendPacket(&data);
}
+
void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
{
sLog.outDetail( "HandlePetSetAction. CMSG_PET_SET_ACTION" );
+
uint64 petguid;
uint8 count;
+
recv_data >> petguid;
+
Unit* pet = ObjectAccessor::GetUnit(*_player, petguid);
+
if(!pet || pet != _player->GetFirstControlled())
{
sLog.outError( "HandlePetSetAction: Unknown pet or pet owner." );
return;
}
+
CharmInfo *charmInfo = pet->GetCharmInfo();
if(!charmInfo)
{
sLog.outError("WorldSession::HandlePetSetAction: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
+
count = (recv_data.size() == 24) ? 2 : 1;
for(uint8 i = 0; i < count; ++i)
{
uint32 position;
uint32 data;
+
recv_data >> position;
recv_data >> data;
+
uint32 spell_id = UNIT_ACTION_BUTTON_ACTION(data);
uint8 act_state = UNIT_ACTION_BUTTON_TYPE(data);
+
sLog.outDetail( "Player %s has changed pet spell action. Position: %u, Spell: %u, State: 0x%X", _player->GetName(), position, spell_id, uint32(act_state));
+
//ignore invalid position
if(position >= MAX_UNIT_ACTION_BAR_INDEX)
return;
+
//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_PASSIVE) && spell_id && !pet->HasSpell(spell_id)))
{
@@ -375,49 +435,63 @@ void WorldSession::HandlePetSetAction( WorldPacket & recv_data )
((Pet*)pet)->ToggleAutocast(spell_id, false);
else
charmInfo->ToggleCreatureAutocast(spell_id, false);
+
}
+
charmInfo->SetActionBar(position,spell_id,ActiveStates(act_state));
}
}
}
+
void WorldSession::HandlePetRename( WorldPacket & recv_data )
{
sLog.outDetail( "HandlePetRename. CMSG_PET_RENAME" );
+
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;
+
PetNameInvalidReason res = ObjectMgr::CheckPetName(name);
if(res != PET_NAME_SUCCESS)
{
SendPetNameInvalid(res, name, NULL);
return;
}
+
if(objmgr.IsReservedName(name))
{
SendPetNameInvalid(PET_NAME_RESERVED, name, NULL);
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(uint8 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))
@@ -426,6 +500,7 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data )
return;
}
}
+
CharacterDatabase.BeginTransaction();
if(isdeclined)
{
@@ -435,18 +510,23 @@ void WorldSession::HandlePetRename( WorldPacket & recv_data )
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 )
{
uint64 guid;
recv_data >> guid; //pet guid
sLog.outDetail( "HandlePetAbandon. CMSG_PET_ABANDON pet guid is %u", GUID_LOPART(guid) );
+
if(!_player->IsInWorld())
return;
+
// pet/charmed
Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
if(pet)
@@ -458,25 +538,31 @@ void WorldSession::HandlePetAbandon( WorldPacket & recv_data )
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->StopCastingCharm();
}
}
+
void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket)
{
sLog.outDetail("CMSG_PET_UNLEARN");
uint64 guid;
recvPacket >> guid; // Pet guid
+
Pet* pet = _player->GetPet();
+
if(!pet || pet->getPetType() != HUNTER_PET || pet->m_usedTalentCount == 0)
return;
+
if(guid != pet->GetGUID())
{
sLog.outError( "HandlePetUnlearnOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
return;
}
+
CharmInfo *charmInfo = pet->GetCharmInfo();
if(!charmInfo)
{
@@ -486,6 +572,7 @@ void WorldSession::HandlePetUnlearnOpcode(WorldPacket& recvPacket)
pet->resetTalents();
_player->SendTalentsInfoData(true);
}
+
void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket )
{
sLog.outDetail("CMSG_PET_SPELL_AUTOCAST");
@@ -493,71 +580,93 @@ void WorldSession::HandlePetSpellAutocastOpcode( WorldPacket& recvPacket )
uint32 spellid;
uint8 state; //1 for on, 0 for off
recvPacket >> guid >> spellid >> state;
+
if(!_player->GetGuardianPet() && !_player->GetCharm())
return;
+
if(ObjectAccessor::FindPlayer(guid))
return;
+
Creature* pet=ObjectAccessor::GetCreatureOrPetOrVehicle(*_player,guid);
+
if(!pet || (pet != _player->GetGuardianPet() && pet != _player->GetCharm()))
{
sLog.outError( "HandlePetSpellAutocastOpcode.Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
return;
}
+
// do not add not learned spells/ passive spells
if(!pet->HasSpell(spellid) || IsAutocastableSpell(spellid))
return;
+
CharmInfo *charmInfo = pet->GetCharmInfo();
if(!charmInfo)
{
sLog.outError("WorldSession::HandlePetSpellAutocastOpcod: object (GUID: %u TypeId: %u) is considered pet-like but doesn't have a charminfo!", pet->GetGUIDLow(), pet->GetTypeId());
return;
}
+
if(pet->isPet())
((Pet*)pet)->ToggleAutocast(spellid, state);
else
pet->GetCharmInfo()->ToggleCreatureAutocast(spellid, state);
+
charmInfo->SetSpellAutocast(spellid,state);
}
+
void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
{
sLog.outDetail("WORLD: CMSG_PET_CAST_SPELL");
+
uint64 guid;
uint32 spellid;
uint8 cast_count;
uint8 unk_flags; // flags (if 0x02 - some additional data are received)
+
recvPacket >> guid >> cast_count >> spellid >> unk_flags;
+
sLog.outDebug("WORLD: CMSG_PET_CAST_SPELL, cast_count: %u, spellid %u, unk_flags %u", cast_count, spellid, unk_flags);
+
// This opcode is also sent from charmed and possessed units (players and creatures)
if(!_player->GetGuardianPet() && !_player->GetCharm())
return;
+
Unit* caster = ObjectAccessor::GetUnit(*_player, guid);
+
if(!caster || (caster != _player->GetGuardianPet() && caster != _player->GetCharm()))
{
sLog.outError( "HandlePetCastSpellOpcode: Pet %u isn't pet of player %s .", uint32(GUID_LOPART(guid)),GetPlayer()->GetName() );
return;
}
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellid);
if(!spellInfo)
{
sLog.outError("WORLD: unknown PET spell id %i", spellid);
return;
}
+
if (spellInfo->StartRecoveryCategory > 0) //Check if spell is affected by GCD
if (caster->GetTypeId() == TYPEID_UNIT && ((Creature*)caster)->GetGlobalCooldown() > 0)
{
caster->SendPetCastFail(spellid, SPELL_FAILED_NOT_READY);
return;
}
+
// do not cast not learned spells
if(!caster->HasSpell(spellid) || IsPassiveSpell(spellid))
return;
+
SpellCastTargets targets;
if(!targets.read(&recvPacket,caster))
return;
+
caster->clearUnitState(UNIT_STAT_FOLLOW);
+
Spell *spell = new Spell(caster, spellInfo, spellid == 33395); // water elemental can cast freeze as triggered
spell->m_cast_count = spellid == 33395 ? 0 : cast_count; // probably pending spell cast
spell->m_targets = targets;
+
// TODO: need to check victim?
SpellCastResult result;
if(caster->m_movedPlayer)
@@ -581,6 +690,7 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
pet->SendPetAIReaction(guid);
}
}
+
spell->prepare(&(spell->m_targets));
}
else
@@ -596,10 +706,12 @@ void WorldSession::HandlePetCastSpellOpcode( WorldPacket& recvPacket )
if(!((Creature*)caster)->HasSpellCooldown(spellid))
GetPlayer()->SendClearCooldown(spellid, caster);
}
+
spell->finish(false);
delete spell;
}
}
+
void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, DeclinedName *declinedName)
{
WorldPacket data(SMSG_PET_NAME_INVALID, 4 + name.size() + 1 + 1);
@@ -615,27 +727,37 @@ void WorldSession::SendPetNameInvalid(uint32 error, const std::string& name, Dec
data << uint8(0);
SendPacket(&data);
}
+
void WorldSession::HandlePetLearnTalent( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_PET_LEARN_TALENT");
+
uint64 guid;
uint32 talent_id, requested_rank;
recv_data >> guid >> talent_id >> requested_rank;
+
_player->LearnPetTalent(guid, talent_id, requested_rank);
_player->SendTalentsInfoData(true);
}
+
void WorldSession::HandleLearnPreviewTalentsPet( WorldPacket & recv_data )
{
sLog.outDebug("CMSG_LEARN_PREVIEW_TALENTS_PET");
+
uint64 guid;
recv_data >> guid;
+
uint32 talentsCount;
recv_data >> talentsCount;
+
uint32 talentId, talentRank;
+
for(uint32 i = 0; i < talentsCount; ++i)
{
recv_data >> talentId >> talentRank;
+
_player->LearnPetTalent(guid, talentId, talentRank);
}
+
_player->SendTalentsInfoData(true);
}
diff --git a/src/game/PetitionsHandler.cpp b/src/game/PetitionsHandler.cpp
index b9c1a465e2d..fe719b1b671 100644
--- a/src/game/PetitionsHandler.cpp
+++ b/src/game/PetitionsHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -29,11 +30,13 @@
#include "ArenaTeam.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
@@ -43,10 +46,12 @@
#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)
{
sLog.outDebug("Received opcode CMSG_PETITION_BUY");
//recv_data.hexlike();
+
uint64 guidNPC;
uint64 unk1, unk3, unk4, unk5, unk6, unk7;
uint32 unk2;
@@ -59,6 +64,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
recv_data >> unk1; // 0
recv_data >> unk2; // 0
recv_data >> name; // name
+
recv_data >> unk3; // 0
recv_data >> unk4; // 0
recv_data >> unk5; // 0
@@ -69,6 +75,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
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 = GetPlayer()->GetNPCIfCanInteractWith(guidNPC,UNIT_NPC_FLAG_PETITIONER);
if (!pCreature)
@@ -76,9 +83,11 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
uint32 charterid = 0;
uint32 cost = 0;
uint32 type = 0;
@@ -88,6 +97,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
// do not let if already in guild.
if(_player->GetGuildId())
return;
+
charterid = GUILD_CHARTER;
cost = GUILD_CHARTER_COST;
type = 9;
@@ -100,6 +110,7 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
SendNotification(LANG_ARENA_ONE_TOOLOW, sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL));
return;
}
+
switch(unk10)
{
case 1:
@@ -121,12 +132,14 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
sLog.outDebug("unknown selection at buy petition: %u", unk10);
return;
}
+
if(_player->GetArenaTeamId(unk10-1))
{
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, name, "", ERR_ALREADY_IN_ARENA_TEAM);
return;
}
}
+
if(type == 9)
{
if(objmgr.GetGuildByName(name))
@@ -153,17 +166,20 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
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)
@@ -171,31 +187,40 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
_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_1_1, charter->GetGUIDLow());
// ITEM_FIELD_ENCHANTMENT_1_1 is guild/arenateam id
// ITEM_FIELD_ENCHANTMENT_1_1+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
// we checked above, if this player is in an arenateam, so this must be
// datacorruption
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();
@@ -205,16 +230,20 @@ void WorldSession::HandlePetitionBuyOpcode(WorldPacket & recv_data)
_player->GetGUIDLow(), charter->GetGUIDLow(), name.c_str(), type);
CharacterDatabase.CommitTransaction();
}
+
void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
{
// 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 type FROM petition WHERE petitionguid = '%u'", petitionguid_low);
if(!result)
{
@@ -224,52 +253,66 @@ void WorldSession::HandlePetitionShowSignOpcode(WorldPacket & recv_data)
Field *fields = result->Fetch();
uint32 type = fields[0].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 Trinity always same as GUID_LOPART(petitionguid)
data << signs; // sign's count
+
for(uint8 i = 1; i <= signs; ++i)
{
Field *fields2 = result->Fetch();
uint64 plguid = fields2[0].GetUInt64();
+
data << plguid; // Player GUID
data << (uint32)0; // there 0 ...
+
result->NextRow();
}
delete result;
SendPacket(&data);
}
+
void WorldSession::HandlePetitionQueryOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_PETITION_QUERY"); // ok
//recv_data.hexlike();
+
uint32 guildguid;
uint64 petitionguid;
recv_data >> guildguid; // in Trinity 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, "
" type "
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
+
if(result)
{
Field* fields = result->Fetch();
@@ -284,6 +327,7 @@ void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
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 Trinity always same as GUID_LOPART(petition guid)
data << ownerguid; // charter owner guid
@@ -316,19 +360,25 @@ void WorldSession::SendPetitionQueryOpcode(uint64 petitionguid)
data << uint32(1);
SendPacket(&data);
}
+
void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
{
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 *result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
+
if(result)
{
Field* fields = result->Fetch();
@@ -340,6 +390,7 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
sLog.outDebug("CMSG_PETITION_QUERY failed for petition (GUID: %u)", GUID_LOPART(petitionguid));
return;
}
+
if(type == 9)
{
if(objmgr.GetGuildByName(newname))
@@ -366,43 +417,53 @@ void WorldSession::HandlePetitionRenameOpcode(WorldPacket & recv_data)
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)
{
sLog.outDebug("Received opcode CMSG_PETITION_SIGN"); // ok
//recv_data.hexlike();
+
Field *fields;
uint64 petitionguid;
uint8 unk;
recv_data >> petitionguid; // petition guid
recv_data >> unk;
+
QueryResult *result = CharacterDatabase.PQuery(
"SELECT ownerguid, "
" (SELECT COUNT(playerguid) FROM petition_sign WHERE petition_sign.petitionguid = '%u') AS signs, "
" type "
"FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid), GUID_LOPART(petitionguid));
+
if(!result)
{
sLog.outError("Petition %u is not found for player %u %s", GUID_LOPART(petitionguid), GetPlayer()->GetGUIDLow(), GetPlayer()->GetName());
return;
}
+
fields = result->Fetch();
uint64 ownerguid = MAKE_NEW_GUID(fields[0].GetUInt32(), 0, HIGHGUID_PLAYER);
uint8 signs = fields[1].GetUInt8();
uint32 type = fields[2].GetUInt32();
+
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))
{
@@ -412,6 +473,7 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_NOT_ALLIED);
return;
}
+
if(type != 9)
{
if(_player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
@@ -419,14 +481,17 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, "", _player->GetName(), ERR_ARENA_TEAM_PLAYER_TO_LOW);
return;
}
+
uint8 slot = ArenaTeam::GetSlotByType(type);
if(slot >= MAX_ARENA_SLOT)
return;
+
if(_player->GetArenaTeamId(slot))
{
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);
@@ -446,11 +511,14 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
return;
}
}
+
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;
@@ -458,43 +526,56 @@ void WorldSession::HandlePetitionSignOpcode(WorldPacket & recv_data)
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_1+1, signs);
+
// update for owner if online
if(Player *owner = objmgr.GetPlayer(ownerguid))
owner->GetSession()->SendPacket(&data);
}
+
void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
{
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
{
@@ -503,10 +584,12 @@ void WorldSession::HandlePetitionDeclineOpcode(WorldPacket & recv_data)
owner->GetSession()->SendPacket(&data);
}
}
+
void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
{
sLog.outDebug("Received opcode CMSG_OFFER_PETITION"); // ok
//recv_data.hexlike();
+
uint8 signs = 0;
uint64 petitionguid, plguid;
uint32 type, junk;
@@ -514,16 +597,21 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
recv_data >> junk; // this is not petition type!
recv_data >> petitionguid; // petition guid
recv_data >> plguid; // player guid
+
player = ObjectAccessor::FindPlayer(plguid);
if (!player)
return;
+
QueryResult *result = CharacterDatabase.PQuery("SELECT type FROM petition WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
if (!result)
return;
+
Field *fields = result->Fetch();
type = fields[0].GetUInt32();
delete result;
+
sLog.outDebug("OFFER PETITION: type %u, GUID1 %u, to player id: %u", type, GUID_LOPART(petitionguid), GUID_LOPART(plguid));
+
if (!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_INTERACTION_GUILD) && GetPlayer()->GetTeam() != player->GetTeam() )
{
if(type != 9)
@@ -532,6 +620,7 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
SendGuildCommandResult(GUILD_CREATE_S, "", GUILD_NOT_ALLIED);
return;
}
+
if(type != 9)
{
if(player->getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
@@ -540,15 +629,18 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, player->GetName(), "", ERR_ARENA_TEAM_PLAYER_TO_LOW);
return;
}
+
uint8 slot = ArenaTeam::GetSlotByType(type);
if(slot >= MAX_ARENA_SLOT)
return;
+
if(player->GetArenaTeamId(slot))
{
// player is already in an arena team
SendArenaTeamCommandResult(ERR_ARENA_TEAM_CREATE_S, 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);
@@ -562,43 +654,56 @@ void WorldSession::HandleOfferPetitionOpcode(WorldPacket & recv_data)
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_IN_GUILD);
return;
}
+
if(player->GetGuildIdInvited())
{
SendGuildCommandResult(GUILD_INVITE_S, _player->GetName(), ALREADY_INVITED_TO_GUILD);
return;
}
}
+
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 Trinity always same as GUID_LOPART(petition guid)
data << signs; // sign's count
+
for(uint8 i = 1; i <= signs; ++i)
{
Field *fields2 = result->Fetch();
plguid = fields2[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)
{
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)
@@ -614,6 +719,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
sLog.outError("petition table has broken data!");
return;
}
+
if(type == 9)
{
if(_player->GetGuildId())
@@ -629,6 +735,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
uint8 slot = ArenaTeam::GetSlotByType(type);
if(slot >= MAX_ARENA_SLOT)
return;
+
if(_player->GetArenaTeamId(slot))
{
//data.Initialize(SMSG_TURN_IN_PETITION_RESULTS, 4);
@@ -638,8 +745,10 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
return;
}
}
+
if(_player->GetGUIDLow() != ownerguidlo)
return;
+
// signs
uint8 signs;
result = CharacterDatabase.PQuery("SELECT playerguid FROM petition_sign WHERE petitionguid = '%u'", GUID_LOPART(petitionguid));
@@ -647,6 +756,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
signs = result->GetRowCount();
else
signs = 0;
+
uint32 count;
//if(signs < sWorld.getConfig(CONFIG_MIN_PETITION_SIGNS))
if(type == 9)
@@ -661,6 +771,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
delete result;
return;
}
+
if(type == 9)
{
if(objmgr.GetGuildByName(name))
@@ -679,6 +790,7 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
return;
}
}
+
// and at last charter item check
Item *item = _player->GetItemByGuid(petitionguid);
if(!item)
@@ -686,9 +798,12 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
delete result;
return;
}
+
// OK!
+
// delete charter item
_player->DestroyItem(item->GetBagSlot(),item->GetSlot(), true);
+
if(type == 9) // create guild
{
Guild* guild = new Guild;
@@ -698,8 +813,10 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
delete result;
return;
}
+
// register guild and add guildmaster
objmgr.AddGuild(guild);
+
// add members
for(uint8 i = 0; i < signs; ++i)
{
@@ -718,12 +835,16 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
delete result;
return;
}
+
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)
{
@@ -734,25 +855,33 @@ void WorldSession::HandleTurnInPetitionOpcode(WorldPacket & recv_data)
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)
{
sLog.outDebug("Received CMSG_PETITION_SHOWLIST"); // ok
//recv_data.hexlike();
+
uint64 guid;
recv_data >> guid;
+
SendPetitionShowList(guid);
}
+
void WorldSession::SendPetitionShowList(uint64 guid)
{
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_PETITIONER);
@@ -761,14 +890,17 @@ void WorldSession::SendPetitionShowList(uint64 guid)
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()->RemoveAurasByType(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
diff --git a/src/game/Player.cpp b/src/game/Player.cpp
index 0fb078fc7c6..2d49a7559e9 100644
--- a/src/game/Player.cpp
+++ b/src/game/Player.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -64,17 +65,23 @@
#include "GameEventMgr.h"
#include "AchievementMgr.h"
#include "SpellAuras.h"
+
#include <cmath>
+
#define ZONE_UPDATE_INTERVAL (1*IN_MILISECONDS)
+
#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,
@@ -111,16 +118,21 @@ enum CharacterFlags
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 chrClass, uint32 level)
{
// class specific initial known nodes
@@ -133,6 +145,7 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 leve
break;
}
}
+
// race specific initial known nodes: capital and taxi hub masks
switch(race)
{
@@ -148,6 +161,7 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 leve
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))
{
@@ -158,9 +172,11 @@ void PlayerTaxi::InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 leve
if(level>=68)
SetTaximaskNode(213); //Shattered Sun Staging Area
}
+
void PlayerTaxi::LoadTaxiMask(const char* data)
{
Tokens tokens = StrSplit(data, " ");
+
uint8 index;
Tokens::iterator iter;
for (iter = tokens.begin(), index = 0;
@@ -170,6 +186,7 @@ void PlayerTaxi::LoadTaxiMask(const char* data)
m_taximask[index] = sTaxiNodesMask[index] & uint32(atol((*iter).c_str()));
}
}
+
void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all )
{
if(all)
@@ -183,20 +200,26 @@ void PlayerTaxi::AppendTaximaskTo( ByteBuffer& data, bool all )
data << uint32(m_taximask[i]); // known nodes
}
}
+
bool PlayerTaxi::LoadTaxiDestinationsFromString( const std::string& values, uint32 team )
{
ClearTaxiDestinations();
+
Tokens tokens = StrSplit(values," ");
+
for(Tokens::iterator iter = tokens.begin(); iter != tokens.end(); ++iter)
{
uint32 node = uint32(atol(iter->c_str()));
AddTaxiDestination(node);
}
+
if(m_TaxiDestinations.empty())
return true;
+
// Check integrity
if(m_TaxiDestinations.size() < 2)
return false;
+
for(size_t i = 1; i < m_TaxiDestinations.size(); ++i)
{
uint32 cost;
@@ -205,29 +228,40 @@ bool PlayerTaxi::LoadTaxiDestinationsFromString( const std::string& values, uint
if(!path)
return false;
}
+
// can't load taxi path without mount set (quest taxi path?)
if(!objmgr.GetTaxiMountDisplayId(GetTaxiSource(),team,true))
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;
}
+
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
{
ss << "'";
@@ -236,62 +270,94 @@ std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi)
ss << "'";
return ss;
}
+
//== Player ====================================================
+
UpdateMask Player::updateVisualBits;
+
Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputationMgr(this)
{
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;
+
m_spellModTakingSpell = NULL;
m_pad = 0;
+
// players always accept
if(GetSession()->GetSecurity() == SEC_PLAYER)
SetAcceptWhispers(true);
+
m_curSelection = 0;
m_lootGuid = 0;
+
m_comboTarget = 0;
m_comboPoints = 0;
+
m_usedTalentCount = 0;
m_questRewardTalentCount = 0;
+
m_regenTimer = 0;
m_regenTimerCount = 0;
m_weaponChangeTimer = 0;
+
m_zoneUpdateId = 0;
m_zoneUpdateTimer = 0;
+
m_areaUpdateId = 0;
+
m_nextSave = sWorld.getConfig(CONFIG_INTERVAL_SAVE);
+
clearResurrectRequestData();
+
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_auraRaidUpdateMask = 0;
+
duel = NULL;
+
m_GuildIdInvited = 0;
m_ArenaTeamIdInvited = 0;
+
m_atLoginFlags = AT_LOGIN_NONE;
+
mSemaphoreTeleport_Near = false;
mSemaphoreTeleport_Far = false;
+
m_DelayedOperations = 0;
m_bCanDelayTeleport = false;
m_bHasDelayedTeleport = false;
m_teleport_options = 0;
+
pTrader = 0;
ClearTrade();
+
m_cinematic = 0;
+
PlayerTalkClass = new PlayerMenu( GetSession() );
m_currentBuybackSlot = BUYBACK_SLOT_START;
+
m_DailyQuestChanged = false;
m_lastDailyQuestTime = 0;
+
for (uint8 i=0; i<MAX_TIMERS; i++)
m_MirrorTimer[i] = DISABLED_MIRROR_TIMER;
+
m_MirrorTimerFlags = UNDERWATER_NONE;
m_MirrorTimerFlagsLast = UNDERWATER_NONE;
m_isInWater = false;
@@ -300,13 +366,17 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
m_restTime = 0;
m_deathTimer = 0;
m_deathExpireTime = 0;
+
m_swingErrorMsg = 0;
+
m_DetectInvTimer = 1*IN_MILISECONDS;
+
for (int j=0; j < PLAYER_MAX_BATTLEGROUND_QUEUES; ++j)
{
m_bgBattleGroundQueueID[j].bgQueueTypeId = BATTLEGROUND_QUEUE_NONE;
m_bgBattleGroundQueueID[j].invitedToInstance = 0;
}
+
m_logintime = time(NULL);
m_Last_tick = m_logintime;
m_WeaponProficiency = 0;
@@ -316,12 +386,14 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
m_canDualWield = false;
m_canTitanGrip = false;
m_ammoDPS = 0.0f;
+
m_temporaryUnsummonedPetNumber = 0;
//cache for UNIT_CREATED_BY_SPELL to allow
//returning reagents for temporarily removed pets
//when dying/logging out
m_oldpetspell = 0;
m_lastpetnumber = 0;
+
////////////////////Rest System/////////////////////
time_inn_enter=0;
inn_pos_mapid=0;
@@ -331,134 +403,187 @@ Player::Player (WorldSession *session): Unit(), m_achievementMgr(this), m_reputa
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 (uint8 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;
+
m_lastPotionId = 0;
+
m_activeSpec = 0;
m_specsCount = 0;
+
for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
{
for (int g = 0; g < MAX_GLYPH_SLOT_INDEX; ++g)
m_Glyphs[i][g] = 0;
+
m_talents[i] = new PlayerTalentMap();
}
+
for (int i = 0; i < BASEMOD_END; ++i)
{
m_auraBaseMod[i][FLAT_MOD] = 0.0f;
m_auraBaseMod[i][PCT_MOD] = 1.0f;
}
+
for (uint8 i = 0; i < MAX_COMBAT_RATING; i++)
m_baseRatingValue[i] = 0;
+
m_baseSpellPower = 0;
m_baseFeralAP = 0;
m_baseManaRegen = 0;
+
// 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_mover = this;
m_movedPlayer = this;
m_seer = this;
+
m_contestedPvPTimer = 0;
+
m_declinedname = NULL;
+
m_isActive = true;
+
m_runes = NULL;
+
m_lastFallTime = 0;
m_lastFallZ = 0;
+
m_ControlledByPlayer = true;
m_isWorldObject = true;
+
sWorld.IncreasePlayerCount();
+
m_ChampioningFaction = 0;
+
for(int i = 0; i < MAX_POWERS; ++i)
m_powerFraction[i] = 0;
}
+
Player::~Player ()
{
// it must be unloaded already in PlayerLogout and accessed only for loggined player
//m_social = NULL;
+
// Note: buy back item already deleted from DB when player was saved
for(uint8 i = 0; i < PLAYER_SLOTS_COUNT; ++i)
{
if(m_items[i])
delete m_items[i];
}
+
for (PlayerSpellMap::const_iterator itr = m_spells.begin(); itr != m_spells.end(); ++itr)
delete itr->second;
+
for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
{
for (PlayerTalentMap::const_iterator itr = m_talents[i]->begin(); itr != m_talents[i]->end(); ++itr)
delete itr->second;
delete m_talents[i];
}
+
//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;
+
for(size_t x = 0; x < ItemSetEff.size(); x++)
if(ItemSetEff[x])
delete ItemSetEff[x];
+
delete m_declinedname;
delete m_runes;
+
sWorld.DecreasePlayerCount();
}
+
void Player::CleanupsBeforeDelete()
{
TradeCancel(false);
DuelComplete(DUEL_INTERUPTED);
+
Unit::CleanupsBeforeDelete();
+
if (m_transport)
m_transport->RemovePassenger(this);
+
// 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);
}
+
bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8 class_, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair, uint8 outfitId )
{
//FIXME: outfitId not used in player creating
+
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 (uint8 i = 0; i < PLAYER_SLOTS_COUNT; i++)
m_items[i] = NULL;
+
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;
}
+
SetMap(MapManager::Instance().CreateMap(info->mapId, this, 0));
+
uint8 powertype = cEntry->powerType;
+
SetFloatValue(UNIT_FIELD_BOUNDINGRADIUS, DEFAULT_WORLD_OBJECT_SIZE);
SetFloatValue(UNIT_FIELD_COMBATREACH, 1.5f);
+
setFactionForRace(race);
+
uint32 RaceClassGender = ( race ) | ( class_ << 8 ) | ( gender << 16 );
+
SetUInt32Value(UNIT_FIELD_BYTES_0, ( RaceClassGender | ( powertype << 24 ) ) );
InitDisplayIds();
if(sWorld.getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || sWorld.getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP)
@@ -469,14 +594,18 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
SetFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_REGENERATE_POWER);
SetFloatValue(UNIT_MOD_CAST_SPEED, 1.0f); // fix cast time showed in spell tooltip on client
SetFloatValue(UNIT_FIELD_HOVERHEIGHT, 1.0f); // default for players in 3.0.3
+
// -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
SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES1, 0 ); // 0=disabled
SetUInt64Value( PLAYER__FIELD_KNOWN_TITLES2, 0 ); // 0=disabled
@@ -485,27 +614,34 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
SetUInt32Value( PLAYER_FIELD_LIFETIME_HONORABLE_KILLS, 0 );
SetUInt32Value( PLAYER_FIELD_TODAY_CONTRIBUTION, 0 );
SetUInt32Value( PLAYER_FIELD_YESTERDAY_CONTRIBUTION, 0 );
+
// set starting level
uint32 start_level = getClass() != CLASS_DEATH_KNIGHT
? sWorld.getConfig(CONFIG_START_PLAYER_LEVEL)
: sWorld.getConfig(CONFIG_START_HEROIC_PLAYER_LEVEL);
+
if (GetSession()->GetSecurity() >= SEC_MODERATOR)
{
uint32 gm_level = sWorld.getConfig(CONFIG_START_GM_LEVEL);
if(gm_level > start_level)
start_level = gm_level;
}
+
SetUInt32Value(UNIT_FIELD_LEVEL, start_level);
+
InitRunes();
+
SetUInt32Value (PLAYER_FIELD_COINAGE, sWorld.getConfig(CONFIG_START_PLAYER_MONEY));
SetUInt32Value (PLAYER_FIELD_HONOR_CURRENCY, sWorld.getConfig(CONFIG_START_HONOR_POINTS));
SetUInt32Value (PLAYER_FIELD_ARENA_CURRENCY, sWorld.getConfig(CONFIG_START_ARENA_POINTS));
+
// start with every map explored
if(sWorld.getConfig(CONFIG_START_ALL_EXPLORED))
{
for (uint8 i=0; i<64; i++)
SetFlag(PLAYER_EXPLORED_ZONES_1+i,0xFFFFFFFF);
}
+
//Reputations if "StartAllReputation" is enabled, -- TODO: Fix this in a better way
if(sWorld.getConfig(CONFIG_START_ALL_REP))
{
@@ -520,6 +656,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(934),42999);
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1038),42999);
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(1077),42999);
+
// Factions depending on team, like cities and some more stuff
switch(GetTeam())
{
@@ -547,16 +684,19 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
break;
}
}
+
// Played time
m_Last_tick = time(NULL);
m_Played_time[PLAYED_TIME_TOTAL] = 0;
m_Played_time[PLAYED_TIME_LEVEL] = 0;
+
// base stats and related field values
InitStatsForLevel();
InitTaxiNodesForLevel();
InitGlyphsForLevel();
InitTalentForLevel();
InitPrimaryProfessions(); // 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());
@@ -565,6 +705,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
UpdateMaxPower(POWER_MANA); // Update max Mana (for add bonus from intellect)
SetPower(POWER_MANA,GetMaxPower(POWER_MANA));
}
+
if(getPowerType() == POWER_RUNIC_POWER)
{
SetPower(POWER_RUNE, 8);
@@ -572,11 +713,14 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
SetPower(POWER_RUNIC_POWER, 0);
SetMaxPower(POWER_RUNIC_POWER, 1000);
}
+
// original spells
learnDefaultSpells();
+
// original action bar
for (PlayerCreateInfoActions::const_iterator action_itr = info->action.begin(); action_itr != info->action.end(); ++action_itr)
addActionButton(action_itr->button,action_itr->action,action_itr->type);
+
// original items
CharStartOutfitEntry const* oEntry = NULL;
for (uint32 i = 1; i < sCharStartOutfitStore.GetNumRows(); ++i)
@@ -590,24 +734,30 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
}
}
}
+
if(oEntry)
{
for(int j = 0; j < MAX_OUTFIT_ITEMS; ++j)
{
if(oEntry->ItemId[j] <= 0)
continue;
+
uint32 item_id = oEntry->ItemId[j];
+
// Hack for not existed item id in dbc 3.0.3
if(item_id==40582)
continue;
+
ItemPrototype const* iProto = objmgr.GetItemPrototype(item_id);
if(!iProto)
{
sLog.outErrorDb("Initial item id %u (race %u class %u) from CharStartOutfit.dbc not listed in `item_template`, ignoring.",item_id,getRace(),getClass());
continue;
}
+
// BuyCount by default
uint32 count = iProto->BuyCount;
+
// special amount for food/drink
if(iProto->Class==ITEM_CLASS_CONSUMABLE && iProto->SubClass==ITEM_SUBCLASS_FOOD)
{
@@ -623,11 +773,14 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
if(iProto->Stackable < count)
count = iProto->Stackable;
}
+
StoreNewItemInBestSlots(item_id, count);
}
}
+
for (PlayerCreateInfoItems::const_iterator item_id_itr = info->item.begin(); item_id_itr!=info->item.end(); ++item_id_itr++)
StoreNewItemInBestSlots(item_id_itr->item_id, item_id_itr->item_amount);
+
// 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
@@ -653,6 +806,7 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
RemoveItem(INVENTORY_SLOT_BAG_0, i,true);
pItem = StoreItem( sDest, pItem, true);
}
+
// if this is ammo then use it
msg = CanUseAmmo( pItem->GetEntry() );
if( msg == EQUIP_ERR_OK )
@@ -661,11 +815,14 @@ bool Player::Create( uint32 guidlow, const std::string& name, uint8 race, uint8
}
}
// all item positions resolved
+
return true;
}
+
bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
{
sLog.outDebug("STORAGE: Creating initial item, itemId = %u, count = %u",titem_id, titem_amount);
+
// attempt equip by one
while(titem_amount > 0)
{
@@ -673,12 +830,15 @@ bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
uint8 msg = CanEquipNewItem( NULL_SLOT, eDest, titem_id, false );
if( msg != EQUIP_ERR_OK )
break;
+
EquipNewItem( eDest, titem_id, true);
AutoUnequipOffhandIfNeed();
--titem_amount;
}
+
if(titem_amount == 0)
return true; // equipped
+
// attempt store
ItemPosCountVec sDest;
// store in main bag to simplify second pass (special bags can be not equipped yet at this moment)
@@ -688,10 +848,12 @@ bool Player::StoreNewItemInBestSlots(uint32 titem_id, uint32 titem_amount)
StoreNewItem( sDest, titem_id, true, Item::GenerateItemRandomPropertyId(titem_id) );
return true; // stored
}
+
// 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,getRace(),getClass(),msg);
return false;
}
+
void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 CurrentValue, int32 Regen)
{
if (int(MaxValue) == DISABLED_MIRROR_TIMER)
@@ -709,6 +871,7 @@ void Player::SendMirrorTimer(MirrorTimerType Type, uint32 MaxValue, uint32 Curre
data << (uint32)0; // spell id
GetSession()->SendPacket( &data );
}
+
void Player::StopMirrorTimer(MirrorTimerType Type)
{
m_MirrorTimer[Type] = DISABLED_MIRROR_TIMER;
@@ -716,10 +879,12 @@ void Player::StopMirrorTimer(MirrorTimerType Type)
data << (uint32)Type;
GetSession()->SendPacket( &data );
}
+
uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
{
if(!isAlive() || isGameMaster())
return 0;
+
// Absorb, resist some environmental damage type
uint32 absorb = 0;
uint32 resist = 0;
@@ -727,8 +892,11 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
CalcAbsorbResist(this, SPELL_SCHOOL_MASK_FIRE, DIRECT_DAMAGE, damage, &absorb, &resist);
else if (type == DAMAGE_SLIME)
CalcAbsorbResist(this, SPELL_SCHOOL_MASK_NATURE, DIRECT_DAMAGE, damage, &absorb, &resist);
+
damage-=absorb+resist;
+
DealDamageMods(this,damage,&absorb);
+
WorldPacket data(SMSG_ENVIRONMENTALDAMAGELOG, (21));
data << uint64(GetGUID());
data << uint8(type!=DAMAGE_FALL_TO_VOID ? type : DAMAGE_FALL);
@@ -736,7 +904,9 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
data << uint32(absorb);
data << uint32(resist);
SendMessageToSet(&data, true);
+
uint32 final_damage = DealDamage(this, damage, NULL, SELF_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, false);
+
if(!isAlive())
{
if(type==DAMAGE_FALL) // DealDamage not apply item durability loss at self damage
@@ -747,10 +917,13 @@ uint32 Player::EnvironmentalDamage(EnviromentalDamage type, uint32 damage)
WorldPacket data2(SMSG_DURABILITY_DAMAGE_DEATH, 0);
GetSession()->SendPacket(&data2);
}
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATHS_FROM, 1, type);
}
+
return final_damage;
}
+
int32 Player::getMaxTimer(MirrorTimerType timer)
{
switch (timer)
@@ -778,16 +951,19 @@ int32 Player::getMaxTimer(MirrorTimerType timer)
}
return 0;
}
+
void Player::UpdateMirrorTimers()
{
// Desync flags for update on next HandleDrowning
if (m_MirrorTimerFlags)
m_MirrorTimerFlagsLast = ~m_MirrorTimerFlags;
}
+
void Player::HandleDrowning(uint32 time_diff)
{
if (!m_MirrorTimerFlags)
return;
+
// In water
if (m_MirrorTimerFlags & UNDERWATER_INWATER)
{
@@ -823,6 +999,7 @@ void Player::HandleDrowning(uint32 time_diff)
else if (m_MirrorTimerFlagsLast & UNDERWATER_INWATER)
SendMirrorTimer(BREATH_TIMER, UnderWaterTime, m_MirrorTimer[BREATH_TIMER], 10);
}
+
// In dark water
if (m_MirrorTimerFlags & UNDERWARER_INDARKWATER)
{
@@ -860,6 +1037,7 @@ void Player::HandleDrowning(uint32 time_diff)
else if (m_MirrorTimerFlagsLast & UNDERWARER_INDARKWATER)
SendMirrorTimer(FATIGUE_TIMER, DarkWaterTime, m_MirrorTimer[FATIGUE_TIMER], 10);
}
+
if (m_MirrorTimerFlags & (UNDERWATER_INLAVA|UNDERWATER_INSLIME))
{
// Breath timer not activated - activate it
@@ -883,6 +1061,7 @@ void Player::HandleDrowning(uint32 time_diff)
}
else
m_MirrorTimer[FIRE_TIMER] = DISABLED_MIRROR_TIMER;
+
// Recheck timers flag
m_MirrorTimerFlags&=~UNDERWATER_EXIST_TIMERS;
for (uint8 i = 0; i< MAX_TIMERS; ++i)
@@ -893,13 +1072,16 @@ void Player::HandleDrowning(uint32 time_diff)
}
m_MirrorTimerFlagsLast = m_MirrorTimerFlags;
}
+
///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)
@@ -910,37 +1092,48 @@ DrunkenState Player::GetDrunkenstateByValue(uint16 value)
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 << uint64(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;
}
+
// If this is set during update SetSpellModTakingSpell call is missing somewhere in the code
// Having this would prevent more aura charges to be dropped, so let's crash
//assert (!m_spellModTakingSpell);
@@ -952,25 +1145,35 @@ void Player::Update( uint32 p_time )
assert(false);
m_spellModTakingSpell = NULL;
}
+
//used to implement delayed far teleports
SetCanDelayTeleport(true);
Unit::Update( p_time );
SetCanDelayTeleport(false);
+
time_t now = time (NULL);
+
UpdatePvPFlag(now);
+
UpdateContestedPvP(p_time);
+
UpdateDuelFlag(now);
+
CheckDuelDistance(now);
+
UpdateAfkReport(now);
+
if(isCharmed())
{
if(Unit *charmer = GetCharmer())
if(charmer->GetTypeId() == TYPEID_UNIT && charmer->isAlive())
UpdateCharmedAI();
}
+
// Update items that have just a limited lifetime
if (now>m_Last_tick)
UpdateItemDuration(uint32(now- m_Last_tick));
+
if (!m_timedquests.empty())
{
std::set<uint32>::iterator iter = m_timedquests.begin();
@@ -991,12 +1194,14 @@ void Player::Update( uint32 p_time )
}
}
}
+
if (hasUnitState(UNIT_STAT_MELEE_ATTACKING) && !hasUnitState(UNIT_STAT_CASTING))
{
if(Unit *pVictim = getVictim())
{
// default combat reach 10
// TODO add weapon,skill check
+
if (isAttackReady(BASE_ATTACK))
{
if(!IsWithinMeleeRange(pVictim))
@@ -1021,6 +1226,7 @@ void Player::Update( uint32 p_time )
else
{
m_swingErrorMsg = 0; // reset swing error state
+
// prevent base and off attack in same time, delay attack at 0.2 sec
if(haveOffhandWeapon())
{
@@ -1032,6 +1238,7 @@ void Player::Update( uint32 p_time )
resetAttackTimer(BASE_ATTACK);
}
}
+
if ( haveOffhandWeapon() && isAttackReady(OFF_ATTACK))
{
if(!IsWithinMeleeRange(pVictim))
@@ -1053,6 +1260,7 @@ void Player::Update( uint32 p_time )
resetAttackTimer(OFF_ATTACK);
}
}
+
/*Unit *owner = pVictim->GetOwner();
Unit *u = owner ? owner : pVictim;
if(u->IsPvP() && (!duel || duel->opponent != u))
@@ -1062,6 +1270,7 @@ void Player::Update( uint32 p_time )
}*/
}
}
+
if(HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING))
{
if(roll_chance_i(3) && GetTimeInnEnter() > 0) //freeze update
@@ -1076,6 +1285,7 @@ void Player::Update( uint32 p_time )
}
}
}
+
if (m_weaponChangeTimer > 0)
{
if(p_time >= m_weaponChangeTimer)
@@ -1083,12 +1293,14 @@ void Player::Update( uint32 p_time )
else
m_weaponChangeTimer -= p_time;
}
+
if (m_zoneUpdateTimer > 0)
{
if(p_time >= m_zoneUpdateTimer)
{
uint32 newzone, newarea;
GetZoneAndAreaId(newzone,newarea);
+
if( m_zoneUpdateId != newzone )
UpdateZone(newzone,newarea); // also update area
else
@@ -1097,21 +1309,25 @@ void Player::Update( uint32 p_time )
// needed for free far all arenas for example
if( m_areaUpdateId != newarea )
UpdateArea(newarea);
+
m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
}
}
else
m_zoneUpdateTimer -= p_time;
}
+
if (isAlive())
{
m_regenTimer += p_time;
RegenerateAll();
}
+
if (m_deathState == JUST_DIED)
{
KillPlayer();
}
+
if(m_nextSave > 0)
{
if(p_time >= m_nextSave)
@@ -1125,8 +1341,10 @@ void Player::Update( uint32 p_time )
m_nextSave -= p_time;
}
}
+
//Handle Water/drowning
HandleDrowning(p_time);
+
//Handle detect stealth players
if (m_DetectInvTimer > 0)
{
@@ -1138,6 +1356,7 @@ void Player::Update( uint32 p_time )
else
m_DetectInvTimer -= p_time;
}
+
// Played time
if (now > m_Last_tick)
{
@@ -1146,12 +1365,15 @@ void Player::Update( uint32 p_time )
m_Played_time[PLAYED_TIME_LEVEL] += elapsed; // Level played time
m_Last_tick = now;
}
+
if (m_drunk)
{
m_drunkTimer += p_time;
+
if (m_drunkTimer > 10*IN_MILISECONDS)
HandleSobering();
}
+
// not auto-free ghost from body in instances
if(m_deathTimer > 0 && !GetBaseMap()->Instanceable())
{
@@ -1164,25 +1386,32 @@ void Player::Update( uint32 p_time )
else
m_deathTimer -= p_time;
}
+
UpdateEnchantTime(p_time);
UpdateHomebindTime(p_time);
+
// group update
SendUpdateToOutOfRangeGroupMembers();
+
Pet* pet = GetPet();
if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && !pet->isPossessed())
//if(pet && !IsWithinDistInMap(pet, OWNER_MAX_DISTANCE) && (GetCharmGUID() && (pet->GetGUID() != GetCharmGUID())))
{
RemovePet(pet, PET_SAVE_NOT_IN_SLOT, true);
}
+
//we should execute delayed teleports only for alive(!) players
//because we don't want player's ghost teleported from graveyard
if(IsHasDelayedTeleport() && isAlive())
TeleportTo(m_teleport_dest, m_teleport_options);
}
+
void Player::setDeathState(DeathState s)
{
uint32 ressSpellId = 0;
+
bool cur = isAlive();
+
if(s == JUST_DIED)
{
if(!cur)
@@ -1190,17 +1419,23 @@ void Player::setDeathState(DeathState s)
sLog.outError("setDeathState: attempt to kill a dead player %s(%d)", GetName(), GetGUIDLow());
return;
}
+
// 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);
+
// save value before aura remove in Unit::setDeathState
ressSpellId = GetUInt32Value(PLAYER_SELF_RES_SPELL);
+
// passive spell
if(!ressSpellId)
ressSpellId = GetResurrectionSpellId();
@@ -1209,18 +1444,22 @@ void Player::setDeathState(DeathState s)
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DEATH_IN_DUNGEON, 1);
}
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);
}
}
+
bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
{
// 0 1 2 3 4 5 6 7
@@ -1229,35 +1468,45 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
// "characters.zone, characters.map, characters.position_x, characters.position_y, characters.position_z, guild_member.guildid, characters.playerFlags, "
// 15 16 17 18 19 20
// "characters.at_login, character_pet.entry, character_pet.modelid, character_pet.level, characters.data, character_declinedname.genitive "
+
Field *fields = result->Fetch();
+
uint32 guid = fields[0].GetUInt32();
uint8 pRace = fields[2].GetUInt8();
uint8 pClass = fields[3].GetUInt8();
+
PlayerInfo const *info = objmgr.GetPlayerInfo(pRace, pClass);
if(!info)
{
sLog.outError("Player %u has incorrect race/class pair. Don't build enum.", guid);
return false;
}
+
*p_data << uint64(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
*p_data << fields[1].GetString(); // name
*p_data << uint8(pRace); // race
*p_data << uint8(pClass); // class
*p_data << uint8(fields[4].GetUInt8()); // gender
+
uint32 playerBytes = fields[5].GetUInt32();
*p_data << uint8(playerBytes); // skin
*p_data << uint8(playerBytes >> 8); // face
*p_data << uint8(playerBytes >> 16); // hair style
*p_data << uint8(playerBytes >> 24); // hair color
+
uint32 playerBytes2 = fields[6].GetUInt32();
*p_data << uint8(playerBytes2 & 0xFF); // facial hair
+
*p_data << uint8(fields[7].GetUInt8()); // level
*p_data << uint32(fields[8].GetUInt32()); // zone
*p_data << uint32(fields[9].GetUInt32()); // map
+
*p_data << fields[10].GetFloat(); // x
*p_data << fields[11].GetFloat(); // y
*p_data << fields[12].GetFloat(); // z
+
*p_data << uint32(fields[13].GetUInt32()); // guild id
+
uint32 char_flags = 0;
uint32 playerFlags = fields[14].GetUInt32();
uint32 atLoginFlags = fields[15].GetUInt32();
@@ -1276,15 +1525,18 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
}
else
char_flags |= CHARACTER_FLAG_DECLINED;
+
*p_data << uint32(char_flags); // character flags
// character customize (flags?)
*p_data << uint32(atLoginFlags & AT_LOGIN_CUSTOMIZE ? 1 : 0);
*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 && !(playerFlags & PLAYER_FLAGS_GHOST) && (pClass == CLASS_WARLOCK || pClass == CLASS_HUNTER || pClass == CLASS_DEATH_KNIGHT))
{
@@ -1297,12 +1549,15 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
petFamily = cInfo->family;
}
}
+
*p_data << uint32(petDisplayId);
*p_data << uint32(petLevel);
*p_data << uint32(petFamily);
}
+
// TODO: do not access data field here
Tokens data = StrSplit(fields[19].GetCppString(), " ");
+
for (uint8 slot = 0; slot < EQUIPMENT_SLOT_END; slot++)
{
uint32 visualbase = PLAYER_VISIBLE_ITEM_1_ENTRYID + (slot * 2);
@@ -1315,7 +1570,9 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
*p_data << uint32(0);
continue;
}
+
SpellItemEnchantmentEntry const *enchant = NULL;
+
uint32 enchants = GetUInt32ValueFromArray(data, visualbase + 1);
for(uint8 enchantSlot = PERM_ENCHANTMENT_SLOT; enchantSlot <= TEMP_ENCHANTMENT_SLOT; ++enchantSlot)
{
@@ -1323,9 +1580,11 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
uint32 enchantId = 0x0000FFFF & (enchants >> enchantSlot*16);
if(!enchantId)
continue;
+
if ((enchant = sSpellItemEnchantmentStore.LookupEntry(enchantId)))
break;
}
+
*p_data << uint32(proto->DisplayInfoID);
*p_data << uint8(proto->InventoryType);
*p_data << uint32(enchant ? enchant->aura_id : 0);
@@ -1333,22 +1592,30 @@ bool Player::BuildEnumData( QueryResult * result, WorldPacket * p_data )
*p_data << uint32(0); // first bag display id
*p_data << uint8(0); // first bag inventory type
*p_data << uint32(0); // enchant?
+
return true;
}
+
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
@@ -1365,6 +1632,7 @@ uint8 Player::chatTag() const
else
return 0;
}
+
void Player::SendTeleportAckMsg()
{
WorldPacket data(MSG_MOVE_TELEPORT_ACK, 41);
@@ -1373,16 +1641,21 @@ void Player::SendTeleportAckMsg()
BuildMovementPacket(&data);
GetSession()->SendPacket(&data);
}
+
// this is not used anywhere
void Player::TeleportOutOfMap(Map *oldMap)
{
while(IsBeingTeleportedFar())
GetSession()->HandleMoveWorldportAckOpcode();
+
if(GetMap() != oldMap)
return;
+
TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
+
while(IsBeingTeleportedFar())
GetSession()->HandleMoveWorldportAckOpcode();
+
if(GetMap() == oldMap)
{
sLog.outCrash("Cannot teleport player out of map!");
@@ -1390,6 +1663,7 @@ void Player::TeleportOutOfMap(Map *oldMap)
assert(false);
}
}
+
bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientation, uint32 options)
{
if(!MapManager::IsValidMapCoord(mapid, x, y, z, orientation))
@@ -1397,33 +1671,43 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
sLog.outError("TeleportTo: invalid map %d or absent instance template.", mapid);
return false;
}
+
if((GetSession()->GetSecurity() < SEC_GAMEMASTER) && !sWorld.IsAllowedMap(mapid))
{
sLog.outError("Player %s tried to enter a forbidden map", GetName());
return false;
}
+
// preparing unsummon pet if lost (we must get pet before teleportation or will not find it later)
Pet* pet = GetPet();
+
MapEntry const* mEntry = sMapStore.LookupEntry(mapid);
+
// don't let enter battlegrounds without assigned battleground id (for example through areatrigger)...
// don't let gm level > 1 either
if(!InBattleGround() && mEntry->IsBattleGroundOrArena())
return false;
+
// client without expansion support
if(GetSession()->Expansion() < mEntry->Expansion())
{
sLog.outDebug("Player %s using client without required expansion tried teleport to non accessible map %u", GetName(), mapid);
+
if(GetTransport())
RepopAtGraveyard(); // teleport to near graveyard if on transport, looks blizz like :)
+
SendTransferAborted(mapid, TRANSFER_ABORT_INSUF_EXPAN_LVL, mEntry->Expansion());
+
return false; // normal client can't teleport to this map...
}
else
{
sLog.outDebug("Player %s is being teleported to map %u", GetName(), mapid);
}
+
// reset movement flags at teleport, because player will continue move with these flags after teleport
SetUnitMovementFlags(0);
+
if (m_transport)
{
if (options & TELE_TO_NOT_LEAVE_TRANSPORT)
@@ -1439,6 +1723,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
m_movementInfo.t_time = 0;
}
}
+
// The player was ported to another map and looses the duel immediately.
// We have to perform this check before the teleport, otherwise the
// ObjectAccessor won't find the flag.
@@ -1448,6 +1733,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
if (obj)
DuelComplete(DUEL_FLED);
}
+
if ((GetMapId() == mapid) && (!m_transport))
{
//lets reset far teleport flag if it wasn't reset during chained teleports
@@ -1464,17 +1750,21 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
m_teleport_options = options;
return true;
}
+
if (!(options & TELE_TO_NOT_UNSUMMON_PET))
{
//same map, only remove pet if out of range for new position
if(pet && !pet->IsWithinDist3d(x,y,z, OWNER_MAX_DISTANCE))
UnsummonPetTemporaryIfAny();
}
+
if(!(options & TELE_TO_NOT_LEAVE_COMBAT))
CombatStop();
+
// this will be used instead of the current location in SaveToDB
m_teleport_dest = WorldLocation(mapid, x, y, z, orientation);
SetFallInformation(0, z);
+
// code for finish transfer called in WorldSession::HandleMovementOpcodes()
// at client packet MSG_MOVE_TELEPORT_ACK
SetSemaphoreTeleportNear(true);
@@ -1493,13 +1783,16 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
if(getClass() == CLASS_DEATH_KNIGHT && GetMapId() == 609 && !isGameMaster()
&& !HasSpell(SPELL_ID_DEATH_GATE))
return false;
+
// far teleport to another map
Map* oldmap = IsInWorld() ? GetMap() : 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))
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);
@@ -1519,9 +1812,13 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
m_teleport_options = options;
return true;
}
+
SetSelection(0);
+
CombatStop();
+
ResetContestedPvP();
+
// remove player from battleground on far teleport (when changing maps)
if(BattleGround const* bg = GetBattleGround())
{
@@ -1531,18 +1828,23 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
if(bg->GetMapId() != mapid)
LeaveBattleground(false); // don't teleport to entry point
}
+
// remove pet on map change
if (pet)
UnsummonPetTemporaryIfAny();
+
// 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);
+
//remove auras before removing from map...
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_CHANGE_MAP | AURA_INTERRUPT_FLAG_MOVE | AURA_INTERRUPT_FLAG_TURNING);
+
if(!GetSession()->PlayerLogout())
{
// send transfer packets
@@ -1553,6 +1855,7 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
data << m_transport->GetEntry() << GetMapId();
}
GetSession()->SendPacket(&data);
+
data.Initialize(SMSG_NEW_WORLD, (20));
if (m_transport)
{
@@ -1565,13 +1868,16 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
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;
@@ -1579,10 +1885,12 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
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);
SetFallInformation(0, final_z);
// if the player is saved before worldportack (at logout for example)
// this will be used instead of the current location in SaveToDB
+
// move packet sent by client always after far teleport
// code for finish transfer to new map called in WorldSession::HandleMoveWorldportAckOpcode at client packet
SetSemaphoreTeleportFar(true);
@@ -1592,39 +1900,49 @@ bool Player::TeleportTo(uint32 mapid, float x, float y, float z, float orientati
}
return true;
}
+
bool Player::TeleportToBGEntryPoint()
{
ScheduleDelayedOperation(DELAYED_BG_MOUNT_RESTORE);
ScheduleDelayedOperation(DELAYED_BG_TAXI_RESTORE);
return TeleportTo(m_bgData.joinPos);
}
+
void Player::ProcessDelayedOperations()
{
if(m_DelayedOperations == 0)
return;
+
if(m_DelayedOperations & DELAYED_RESURRECT_PLAYER)
{
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();
}
+
if(m_DelayedOperations & DELAYED_SAVE_PLAYER)
{
SaveToDB();
}
+
if(m_DelayedOperations & DELAYED_SPELL_CAST_DESERTER)
{
CastSpell(this, SPELL_ID_DESERTER, true); // Deserter
}
+
if (m_DelayedOperations & DELAYED_BG_MOUNT_RESTORE)
{
if (m_bgData.mountSpell)
@@ -1633,6 +1951,7 @@ void Player::ProcessDelayedOperations()
m_bgData.mountSpell = 0;
}
}
+
if (m_DelayedOperations & DELAYED_BG_TAXI_RESTORE)
{
if (m_bgData.HasTaxiPath())
@@ -1640,24 +1959,29 @@ void Player::ProcessDelayedOperations()
m_taxi.AddTaxiDestination(m_bgData.taxiPath[0]);
m_taxi.AddTaxiDestination(m_bgData.taxiPath[1]);
m_bgData.ClearTaxiPath();
+
ContinueTaxiFlight();
}
}
+
//we have executed ALL delayed ops, so clear the flag
m_DelayedOperations = 0;
}
+
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(uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
{
if(m_items[i])
m_items[i]->AddToWorld();
}
}
+
void Player::RemoveFromWorld()
{
// cleanup
@@ -1669,17 +1993,21 @@ void Player::RemoveFromWorld()
UnsummonPetTemporaryIfAny();
sOutdoorPvPMgr.HandlePlayerLeaveZone(this, m_zoneUpdateId);
}
+
///- 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();
+
for(uint8 i = PLAYER_SLOT_START; i < PLAYER_SLOT_END; ++i)
{
if(m_items[i])
m_items[i]->RemoveFromWorld();
}
+
for (ItemMap::iterator iter = mMitems.begin(); iter != mMitems.end(); ++iter)
iter->second->RemoveFromWorld();
+
if(m_uint32Values)
{
if(WorldObject *viewpoint = GetViewpoint())
@@ -1689,13 +2017,18 @@ void Player::RemoveFromWorld()
}
}
}
+
void Player::RegenerateAll()
{
//if (m_regenTimer <= 500)
// return;
+
m_regenTimerCount += m_regenTimer;
+
Regenerate( POWER_ENERGY );
+
Regenerate( POWER_MANA );
+
if(m_regenTimerCount >= 2000)
{
// Not in combat or they have regeneration
@@ -1710,12 +2043,16 @@ void Player::RegenerateAll()
Regenerate(POWER_RUNIC_POWER);
}
}
+
if(getClass() == CLASS_DEATH_KNIGHT)
Regenerate( POWER_RUNE );
+
m_regenTimerCount -= 2000;
}
+
m_regenTimer = 0;
}
+
void Player::Regenerate(Powers power)
{
if(power == POWER_RUNE)
@@ -1724,16 +2061,21 @@ void Player::Regenerate(Powers power)
if(uint8 cd = GetRuneCooldown(i)) // if we have cooldown, reduce it...
SetRuneCooldown(i, cd - 1); // ... by 2 sec (because update is every 2 sec)
}
+
uint32 maxValue = GetMaxPower(power);
if(!maxValue)
return;
+
uint32 curValue = GetPower(power);
if(curValue == maxValue)
return;
+
// TODO: possible use of miscvalueb instead of amount
if (HasAuraTypeWithValue(SPELL_AURA_PREVENT_REGENERATE_POWER, power))
return;
+
float addvalue = 0.0f;
+
switch (power)
{
case POWER_MANA:
@@ -1769,6 +2111,7 @@ void Player::Regenerate(Powers power)
case POWER_HEALTH:
break;
}
+
// Mana regen calculated in Player::UpdateManaRegen()
// Exist only for POWER_MANA, POWER_ENERGY, POWER_FOCUS auras
if(power != POWER_MANA)
@@ -1778,11 +2121,14 @@ void Player::Regenerate(Powers power)
if ((*i)->GetMiscValue() == power)
addvalue *= ((*i)->GetAmount() + 100) / 100.0f;
}
+
addvalue += m_powerFraction[power];
uint32 integerValue = (uint32)addvalue;
+
if (power != POWER_RAGE && power != POWER_RUNIC_POWER)
{
curValue += integerValue;
+
if (curValue > maxValue)
{
curValue = maxValue;
@@ -1802,20 +2148,25 @@ void Player::Regenerate(Powers power)
{
curValue = 0;
m_powerFraction[power] = 0;
- }
+ }
}
if(m_regenTimerCount >= 2000)
SetPower(power, curValue);
else
UpdateUInt32Value(UNIT_FIELD_POWER1 + 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;
@@ -1831,61 +2182,79 @@ void Player::RegenerateHealth()
}
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;
}
+
Creature*
Player::GetNPCIfCanInteractWith(uint64 guid, uint32 npcflagmask)
{
// unit checks
if (!guid)
return NULL;
+
if(!IsInWorld())
return NULL;
+
// exist (we need look pets also for some interaction (quest/etc)
Creature *unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*this,guid);
if (!unit)
return NULL;
+
// player check
if(!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() || isAlive() ))
return NULL;
+
// not allow interaction under control, but allow with own pets
if(unit->GetCharmerGUID())
return NULL;
+
// not enemy
if( unit->IsHostileTo(this))
return NULL;
+
// not unfriendly
if(FactionTemplateEntry const* factionTemplate = sFactionTemplateStore.LookupEntry(unit->getFaction()))
if(factionTemplate->faction)
if(FactionEntry const* faction = sFactionStore.LookupEntry(factionTemplate->faction))
if(faction->reputationListID >= 0 && GetReputationMgr().GetRank(faction) <= REP_UNFRIENDLY)
return NULL;
+
// not too far
if(!unit->IsWithinDistInMap(this,INTERACTION_DISTANCE))
return NULL;
+
return unit;
}
+
GameObject* Player::GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes type) const
{
if(GameObject *go = GetMap()->GetGameObject(guid))
@@ -1908,33 +2277,41 @@ GameObject* Player::GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes
maxdist = INTERACTION_DISTANCE;
break;
}
+
if (go->IsWithinDistInMap(this, maxdist))
return go;
+
sLog.outError("IsGameObjectOfTypeInRange: GameObject '%s' [GUID: %u] is too far away from player %s [GUID: %u] to be used by him (distance=%f, maximal 10 is allowed)", go->GetGOInfo()->name,
go->GetGUIDLow(), GetName(), GetGUIDLow(), go->GetDistance(this));
}
}
return NULL;
}
+
bool Player::IsUnderWater() const
{
return IsInWater() &&
GetPositionZ() < (GetBaseMap()->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)
@@ -1942,15 +2319,19 @@ void Player::SetGameMaster(bool on)
m_ExtraFlags |= PLAYER_EXTRA_GM_ON;
setFaction(35);
SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
+
if (Pet* pet = GetPet())
{
pet->setFaction(35);
pet->getHostilRefManager().setOnlineOfflineState(false);
}
+
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
ResetContestedPvP();
+
getHostilRefManager().setOnlineOfflineState(false);
CombatStopWithPets();
+
SetPhaseMask(PHASEMASK_ANYWHERE,false); // see and visible in all phases
}
else
@@ -1958,29 +2339,37 @@ void Player::SetGameMaster(bool on)
// restore phase
AuraEffectList const& phases = GetAurasByType(SPELL_AURA_PHASE);
SetPhaseMask(!phases.empty() ? phases.front()->GetMiscValue() : PHASEMASK_NORMAL,false);
+
m_ExtraFlags &= ~ PLAYER_EXTRA_GM_ON;
setFactionForRace(getRace());
RemoveFlag(PLAYER_FLAGS, PLAYER_FLAGS_GM);
+
if (Pet* pet = GetPet())
{
pet->setFaction(getFaction());
pet->getHostilRefManager().setOnlineOfflineState(true);
}
+
// restore FFA PvP Server state
if(sWorld.IsFFAPvPRealm())
SetByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
+
// restore FFA PvP area state, remove not allowed for GM mounts
UpdateArea(m_areaUpdateId);
+
getHostilRefManager().setOnlineOfflineState(true);
}
+
//ObjectAccessor::UpdateVisibilityForPlayer(this);
SetToNotify();
}
+
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);
@@ -1992,11 +2381,14 @@ void Player::SetGMVisible(bool 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))
@@ -2006,12 +2398,14 @@ bool Player::IsGroupVisibleFor(Player* p) const
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()
@@ -2019,7 +2413,9 @@ void Player::UninviteFromGroup()
Group* group = GetGroupInvite();
if(!group)
return;
+
group->RemoveInvite(this);
+
if(group->GetMembersCount() <= 1) // group has just 1 member => disband
{
if(group->IsCreated())
@@ -2029,9 +2425,11 @@ void Player::UninviteFromGroup()
}
else
group->RemoveAllInvites();
+
delete group;
}
}
+
void Player::RemoveFromGroup(Group* group, uint64 guid)
{
if(group)
@@ -2045,6 +2443,7 @@ void Player::RemoveFromGroup(Group* group, uint64 guid)
}
}
}
+
void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP)
{
WorldPacket data(SMSG_LOG_XPGAIN, 21);
@@ -2059,49 +2458,64 @@ void Player::SendLogXPGain(uint32 GivenXP, Unit* victim, uint32 RestXP)
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();
+
// Favored experience increase START
uint32 zone = GetZoneId();
float favored_exp_mult = 0;
if( (HasAura(32096) || HasAura(32098)) && (zone == 3483 || zone == 3562 || zone == 3836 || zone == 3713 || zone == 3714) ) favored_exp_mult = 0.05; // Thrallmar's Favor and Honor Hold's Favor
xp *= (1 + favored_exp_mult);
// Favored experience increase END
+
// XP to money conversion processed in Player::RewardQuest
if(level >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
return;
+
// 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);
}
newXP = GetSession()->HandleOnGetXP(newXP);
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);
@@ -2117,24 +2531,36 @@ void Player::GiveLevel(uint32 level)
// end for
for(uint8 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, objmgr.GetXPForLevel(level));
+
//update level, max level of skills
m_Played_time[PLAYED_TIME_LEVEL] = 0; // Level Played Time reset
+
_ApplyAllLevelScaleItemMods(false);
+
SetLevel(level);
+
UpdateSkillsForLevel ();
+
// save base values (bonuses already included in stored stats
for(uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
SetCreateStat(Stats(i), info.stats[i]);
+
SetCreateHealth(classInfo.basehealth);
SetCreateMana(classInfo.basemana);
+
InitTalentForLevel();
InitTaxiNodesForLevel();
InitGlyphsForLevel();
+
UpdateAllStats();
+
if(sWorld.getConfig(CONFIG_ALWAYS_MAXSKILL)) // Max weapon skill when leveling up
UpdateSkillsToMaxSkillsForLevel();
+
// set current level health and mana/energy to maximum after applying all mods.
SetHealth(GetMaxHealth());
SetPower(POWER_MANA, GetMaxPower(POWER_MANA));
@@ -2143,12 +2569,16 @@ void Player::GiveLevel(uint32 level)
SetPower(POWER_RAGE, GetMaxPower(POWER_RAGE));
SetPower(POWER_FOCUS, 0);
SetPower(POWER_HAPPINESS, 0);
+
_ApplyAllLevelScaleItemMods(true);
+
// update level to hunter/summon pet
if (Pet* pet = GetPet())
pet->SynchronizeLevelWithOwner();
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_LEVEL);
}
+
void Player::InitTalentForLevel()
{
uint32 level = getLevel();
@@ -2169,7 +2599,9 @@ void Player::InitTalentForLevel()
m_specsCount = 1;
m_activeSpec = 0;
}
+
uint32 talentPointsForLevel = CalculateTalentsPoints();
+
// if used more that have then reset
if(m_usedTalentCount > talentPointsForLevel)
{
@@ -2182,37 +2614,53 @@ void Player::InitTalentForLevel()
else
SetFreeTalentPoints(talentPointsForLevel-m_usedTalentCount);
}
+
if(!GetSession()->PlayerLoading())
SendTalentsInfoData(false); // update at client
}
+
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, objmgr.GetXPForLevel(getLevel()));
+
UpdateSkillsForLevel ();
+
// 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(uint8 i = STAT_STRENGTH; i < MAX_STATS; ++i)
SetCreateStat(Stats(i), info.stats[i]);
+
for(uint8 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 (uint8 i = 0; i < 7; i++)
{
@@ -2220,34 +2668,42 @@ void Player::InitStatsForLevel(bool reapplyMods)
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 );
+
SetInt32Value(UNIT_FIELD_ATTACK_POWER, 0 );
SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, 0 );
SetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER,0.0f);
SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER, 0 );
SetInt32Value(UNIT_FIELD_RANGED_ATTACK_POWER_MODS,0 );
SetFloatValue(UNIT_FIELD_RANGED_ATTACK_POWER_MULTIPLIER,0.0f);
+
// Base crit values (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
SetFloatValue(PLAYER_CRIT_PERCENTAGE,0.0f);
SetFloatValue(PLAYER_OFFHAND_CRIT_PERCENTAGE,0.0f);
SetFloatValue(PLAYER_RANGED_CRIT_PERCENTAGE,0.0f);
+
// Init spell schools (will be recalculated in UpdateAllStats() at loading and in _ApplyAllStatBonuses() at reset
for (uint8 i = 0; i < 7; ++i)
SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1+i, 0.0f);
+
SetFloatValue(PLAYER_PARRY_PERCENTAGE, 0.0f);
SetFloatValue(PLAYER_BLOCK_PERCENTAGE, 0.0f);
SetUInt32Value(PLAYER_SHIELD_BLOCK, 0);
+
// Dodge percentage
SetFloatValue(PLAYER_DODGE_PERCENTAGE, 0.0f);
+
// set armor (resistance 0) to original value (create_agility*2)
SetArmor(int32(m_createStats[STAT_AGILITY]*2));
SetResistanceBuffMods(SpellSchools(0), true, 0.0f);
@@ -2259,6 +2715,7 @@ void Player::InitStatsForLevel(bool reapplyMods)
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(uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
@@ -2271,12 +2728,16 @@ void Player::InitStatsForLevel(bool reapplyMods)
SetUInt32Value(PLAYER_NO_REAGENT_COST_1 + i, 0);
// Init data for form but skip reapply item mods for form
InitDataForForm(reapplyMods);
+
// save new stats
for (uint8 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 |
@@ -2285,15 +2746,21 @@ void Player::InitStatsForLevel(bool reapplyMods)
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
+
SetFlag(UNIT_FIELD_FLAGS_2,UNIT_FLAG2_REGENERATE_POWER);// 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_ALLOW_ONLY_ABILITY);
+
RemoveStandFlags(UNIT_STAND_FLAGS_ALL); // one form stealth modified bytes
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP | UNIT_BYTE2_FLAG_SANCTUARY);
+
// 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));
@@ -2303,30 +2770,41 @@ void Player::InitStatsForLevel(bool reapplyMods)
SetPower(POWER_FOCUS, 0);
SetPower(POWER_HAPPINESS, 0);
SetPower(POWER_RUNIC_POWER, 0);
+
// update level to hunter/summon pet
if (Pet* pet = GetPet())
pet->SynchronizeLevelWithOwner();
}
+
void Player::SendInitialSpells()
{
time_t curTime = time(NULL);
time_t infTime = curTime + infinityCooldownDelayCheck;
+
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 << uint32(itr->first);
data << uint16(0); // it's not slot id
+
spellCount +=1;
}
+
data.put<uint16>(countPos,spellCount); // write real count value
+
uint16 spellCooldowns = m_spellCooldowns.size();
data << uint16(spellCooldowns);
for(SpellCooldowns::const_iterator itr=m_spellCooldowns.begin(); itr!=m_spellCooldowns.end(); ++itr)
@@ -2334,9 +2812,12 @@ void Player::SendInitialSpells()
SpellEntry const *sEntry = sSpellStore.LookupEntry(itr->first);
if(!sEntry)
continue;
+
data << uint32(itr->first);
+
data << uint16(itr->second.itemid); // cast item id
data << uint16(sEntry->Category); // spell category
+
// send infinity cooldown in special format
if(itr->second.end >= infTime)
{
@@ -2344,7 +2825,9 @@ void Player::SendInitialSpells()
data << uint32(0x80000000); // category cooldown
continue;
}
+
time_t cooldown = itr->second.end > curTime ? (itr->second.end-curTime)*IN_MILISECONDS : 0;
+
if(sEntry->Category) // may be wrong, but anyway better than nothing...
{
data << uint32(0); // cooldown
@@ -2356,9 +2839,12 @@ void Player::SendInitialSpells()
data << uint32(0); // category cooldown
}
}
+
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)
@@ -2371,6 +2857,7 @@ void Player::RemoveMail(uint32 id)
}
}
}
+
void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult mailError, uint32 equipError, uint32 item_guid, uint32 item_count)
{
WorldPacket data(SMSG_SEND_MAIL_RESULT, (4+4+4+(mailError == MAIL_ERR_EQUIP_ERROR?4:(mailAction == MAIL_ITEM_TAKEN?4+4:0))));
@@ -2386,6 +2873,7 @@ void Player::SendMailResult(uint32 mailId, MailResponseType mailAction, MailResp
}
GetSession()->SendPacket(&data);
}
+
void Player::SendNewMail()
{
// deliver undelivered mail
@@ -2393,6 +2881,7 @@ void Player::SendNewMail()
data << (uint32) 0;
GetSession()->SendPacket(&data);
}
+
void Player::UpdateNextMailTimeAndUnreads()
{
// calculate next delivery time (min. from non-delivered mails
@@ -2411,6 +2900,7 @@ void Player::UpdateNextMailTimeAndUnreads()
++unReadMails;
}
}
+
void Player::AddNewMailDeliverTime(time_t deliver_time)
{
if(deliver_time <= time(NULL)) // ready now
@@ -2424,6 +2914,7 @@ void Player::AddNewMailDeliverTime(time_t deliver_time)
m_nextMailDelivereTime = deliver_time;
}
}
+
bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
@@ -2437,8 +2928,10 @@ bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
}
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)
@@ -2449,8 +2942,10 @@ bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
}
else
sLog.outError("Player::addTalent: Broken spell #%u learning not allowed.",spell_id);
+
return false;
}
+
PlayerTalentMap::iterator itr = m_talents[spec]->find(spell_id);
if (itr != m_talents[spec]->end())
{
@@ -2466,6 +2961,7 @@ bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
uint32 rankSpellId = talentInfo->RankID[rank];
if(!rankSpellId || rankSpellId == spell_id)
continue;
+
PlayerTalentMap::iterator itr = m_talents[spec]->find(rankSpellId);
if (itr != m_talents[spec]->end())
{
@@ -2473,15 +2969,19 @@ bool Player::AddTalent(uint32 spell_id, uint8 spec, bool learning)
}
}
}
+
PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
PlayerTalent *newtalent = new PlayerTalent();
+
newtalent->state = state;
newtalent->spec = spec;
+
(*m_talents[spec])[spell_id] = newtalent;
return true;
}
return false;
}
+
bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
@@ -2495,8 +2995,10 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
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)
@@ -2507,13 +3009,18 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
else
sLog.outError("Player::addSpell: Broken spell #%u learning not allowed.",spell_id);
+
return false;
}
+
PlayerSpellState state = learning ? PLAYERSPELL_NEW : PLAYERSPELL_UNCHANGED;
+
bool dependent_set = false;
bool disabled_case = false;
bool superceded_old = false;
+
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
+
// Remove temporary spell if found to prevent conflicts
if (itr != m_spells.end() && itr->second->state == PLAYERSPELL_TEMPORARY)
RemoveTemporarySpell(spell_id);
@@ -2533,14 +3040,17 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
}
}
+
// not do anything if already known in expected state
if(itr->second->state != PLAYERSPELL_REMOVED && itr->second->active == active &&
itr->second->dependent == dependent && itr->second->disabled == disabled)
{
if(!IsInWorld() && !learning) // explicitly load from DB and then exist in it already and set correctly
itr->second->state = PLAYERSPELL_UNCHANGED;
+
return false;
}
+
// dependent spell known as not dependent, overwrite state
if (itr->second->state != PLAYERSPELL_REMOVED && !itr->second->dependent && dependent)
{
@@ -2549,14 +3059,17 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
itr->second->state = PLAYERSPELL_CHANGED;
dependent_set = true;
}
+
// update active state for known spell
if(itr->second->active != active && itr->second->state != PLAYERSPELL_REMOVED && !itr->second->disabled)
{
itr->second->active = active;
+
if(!IsInWorld() && !learning && !dependent_set) // explicitly load from DB and then exist in it already and set correctly
itr->second->state = PLAYERSPELL_UNCHANGED;
else if(itr->second->state != PLAYERSPELL_NEW)
itr->second->state = PLAYERSPELL_CHANGED;
+
if(active)
{
if (IsPassiveSpell(spell_id) && IsNeedCastPassiveSpellAtLearn(spellInfo))
@@ -2579,15 +3092,19 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
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)
@@ -2606,10 +3123,12 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
// can be in case spell loading but learned at some previous spell loading
if(!IsInWorld() && !learning && !dependent_set)
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)
@@ -2623,6 +3142,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
uint32 rankSpellId = talentInfo->RankID[rank];
if(!rankSpellId || rankSpellId == spell_id)
continue;
+
removeSpell(rankSpellId,false,false);
}
}
@@ -2635,11 +3155,13 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
else // at normal learning
learnSpell(prev_spell,true);
}
+
PlayerSpell *newspell = new PlayerSpell;
newspell->state = state;
newspell->active = active;
newspell->dependent = dependent;
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)
{
@@ -2648,6 +3170,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
if(itr2->second->state == PLAYERSPELL_REMOVED) continue;
SpellEntry const *i_spellInfo = sSpellStore.LookupEntry(itr2->first);
if(!i_spellInfo) continue;
+
if( spellmgr.IsRankSpellDueToSpell(spellInfo,itr2->first) )
{
if(itr2->second->active)
@@ -2661,6 +3184,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
data << uint32(spell_id);
GetSession()->SendPacket( &data );
}
+
// mark old spell as disable (SMSG_SUPERCEDED_SPELL replace it in client by new)
itr2->second->active = false;
if(itr2->second->state != PLAYERSPELL_NEW)
@@ -2676,6 +3200,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
data << uint32(itr2->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)
@@ -2685,12 +3210,16 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
}
}
+
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))
@@ -2709,27 +3238,37 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
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 = GetFreePrimaryProfessionPoints())
{
if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell_id))
SetFreePrimaryProfessions(freeProfs-1);
}
+
// add dependent skills
uint16 maxskill = GetMaxSkillValueForLevel();
+
SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
+
SkillLineAbilityMapBounds skill_bounds = spellmgr.GetSkillLineAbilityMapBounds(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
@@ -2740,8 +3279,10 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
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 ||
// lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
((pSkill->id==SKILL_LOCKPICKING || pSkill->id==SKILL_RUNEFORGING) && _spell_idx->second->max_value==0))
@@ -2763,8 +3304,10 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
}
}
}
+
// learn dependent spells
SpellLearnSpellMapBounds spell_bounds = spellmgr.GetSpellLearnSpellMapBounds(spell_id);
+
for(SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
{
if (!itr2->second.autoLearned)
@@ -2775,6 +3318,7 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
learnSpell(itr2->second.spell,true);
}
}
+
if (!GetSession()->PlayerLoading())
{
// not ranked skills
@@ -2783,11 +3327,14 @@ bool Player::addSpell(uint32 spell_id, bool active, bool learning, bool dependen
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LINE,_spell_idx->second->skillId);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILLLINE_SPELLS,_spell_idx->second->skillId);
}
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SPELL,spell_id);
}
+
// 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::AddTemporarySpell(uint32 spellId)
{
PlayerSpellMap::iterator itr = m_spells.find(spellId);
@@ -2801,6 +3348,7 @@ void Player::AddTemporarySpell(uint32 spellId)
newspell->disabled = false;
m_spells[spellId] = newspell;
}
+
void Player::RemoveTemporarySpell(uint32 spellId)
{
PlayerSpellMap::iterator itr = m_spells.find(spellId);
@@ -2813,20 +3361,26 @@ void Player::RemoveTemporarySpell(uint32 spellId)
delete itr->second;
m_spells.erase(itr);
}
+
bool Player::IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const
{
// note: form passives activated with shapeshift spells be implemented by HandleShapeshiftBoosts instead of spell_learn_spell
// talent dependent passives activated at form apply have proper stance data
bool need_cast = (!spellInfo->Stances || (m_form != 0 && (spellInfo->Stances & (1<<(m_form-1)))));
+
//Check CasterAuraStates
return need_cast && (!spellInfo->CasterAuraState || HasAuraState(AuraState(spellInfo->CasterAuraState)));
}
+
void Player::learnSpell(uint32 spell_id, bool dependent)
{
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,true,dependent,false);
+
// learn all disabled higher ranks (recursive)
if(disabled)
{
@@ -2838,20 +3392,25 @@ void Player::learnSpell(uint32 spell_id, bool dependent)
learnSpell(node->next,false);
}
}
+
// prevent duplicated entires in spell book, also not send if not in world (loading)
if(!learning || !IsInWorld ())
return;
+
WorldPacket data(SMSG_LEARNED_SPELL, 4);
data << uint32(spell_id);
GetSession()->SendPacket(&data);
}
+
void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
{
PlayerSpellMap::iterator itr = m_spells.find(spell_id);
if (itr == m_spells.end())
return;
+
if (itr->second->state == PLAYERSPELL_REMOVED || (disabled && itr->second->disabled) || itr->second->state == PLAYERSPELL_TEMPORARY)
return;
+
// unlearn non talent higher ranks (recursive)
SpellChainNode const* node = spellmgr.GetSpellChainNode(spell_id);
if (node)
@@ -2864,12 +3423,15 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
SpellsRequiringSpellMap::const_iterator itr2 = reqMap.find(spell_id);
for (uint32 i=reqMap.count(spell_id);i>0;i--,itr2++)
removeSpell(itr2->second,disabled,false);
+
// re-search, it can be corrupted in prev loop
itr = m_spells.find(spell_id);
if (itr == m_spells.end())
return; // already unleared
+
bool cur_active = itr->second->active;
bool cur_dependent = itr->second->dependent;
+
if (disabled)
{
itr->second->disabled = disabled;
@@ -2886,11 +3448,14 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
else
itr->second->state = PLAYERSPELL_REMOVED;
}
+
RemoveAurasDueToSpell(spell_id);
+
// remove pet auras
for(int i = 0; i < MAX_SPELL_EFFECTS; ++i)
if(PetAura const* petSpell = spellmgr.GetPetAura(spell_id, i))
RemovePetAura(petSpell);
+
// free talent points
uint32 talentCosts = GetTalentSpellCost(spell_id);
if(talentCosts > 0)
@@ -2900,6 +3465,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
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))
{
@@ -2907,6 +3473,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
if(freeProfs <= sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL))
SetFreePrimaryProfessions(freeProfs);
}
+
// remove dependent skill
SpellLearnSkillNode const* spellLearnSkill = spellmgr.GetSpellLearnSkill(spell_id);
if(spellLearnSkill)
@@ -2923,30 +3490,38 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
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
SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spell_id);
+
for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_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 &&
pSkill->categoryId != SKILL_CATEGORY_CLASS ||// not unlearn class skills (spellbook/talent pages)
// lockpicking/runeforging special case, not have ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL
@@ -2956,19 +3531,25 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
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
SpellLearnSpellMapBounds spell_bounds = spellmgr.GetSpellLearnSpellMapBounds(spell_id);
+
for(SpellLearnSpellMap::const_iterator itr2 = spell_bounds.first; itr2 != spell_bounds.second; ++itr2)
removeSpell(itr2->second.spell, disabled);
+
// activate lesser rank in spellbook/action bar, and cast it if need
bool prev_activate = false;
+
if (uint32 prev_id = spellmgr.GetPrevSpellInChain (spell_id))
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spell_id);
+
// if talent then lesser rank also talent and need learn
if (talentCosts)
{
@@ -2989,6 +3570,7 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
if (prev_itr->second->state != PLAYERSPELL_NEW)
prev_itr->second->state = PLAYERSPELL_CHANGED;
}
+
// now re-learn if need re-activate
if (cur_active && !prev_itr->second->active && learn_low_rank)
{
@@ -3005,10 +3587,13 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
}
}
}
+
if(spell_id == 46917 && m_canTitanGrip)
SetCanTitanGrip(false);
+
if(sWorld.getConfig(CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN))
AutoUnequipOffhandIfNeed();
+
// remove from spell book if not replaced by lesser rank
if(!prev_activate)
{
@@ -3017,12 +3602,15 @@ void Player::removeSpell(uint32 spell_id, bool disabled, bool learn_low_rank)
GetSession()->SendPacket(&data);
}
}
+
void Player::RemoveSpellCooldown( uint32 spell_id, bool update /* = false */ )
{
m_spellCooldowns.erase(spell_id);
+
if(update)
SendClearCooldown(spell_id, this);
}
+
// I am not sure which one is more efficient
void Player::RemoveCategoryCooldown( uint32 cat )
{
@@ -3031,11 +3619,13 @@ void Player::RemoveCategoryCooldown( uint32 cat )
for(SpellCategorySet::const_iterator i_scset = i_scstore->second.begin(); i_scset != i_scstore->second.end(); ++i_scset)
RemoveSpellCooldown(*i_scset, true);
}
+
void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */)
{
SpellCategoryStore::const_iterator ct = sSpellCategoryStore.find(cat);
if (ct == sSpellCategoryStore.end())
return;
+
const SpellCategorySet& ct_set = ct->second;
for (SpellCooldowns::const_iterator i = m_spellCooldowns.begin(); i != m_spellCooldowns.end();)
{
@@ -3045,6 +3635,7 @@ void Player::RemoveSpellCategoryCooldown(uint32 cat, bool update /* = false */)
++i;
}
}
+
void Player::RemoveArenaSpellCooldowns()
{
// remove cooldowns on spells that has < 15 min CD
@@ -3065,48 +3656,63 @@ void Player::RemoveArenaSpellCooldowns()
}
}
}
+
void Player::RemoveAllSpellCooldown()
{
if(!m_spellCooldowns.empty())
{
for(SpellCooldowns::const_iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end(); ++itr)
SendClearCooldown(itr->first, this);
+
m_spellCooldowns.clear();
}
}
+
void Player::_LoadSpellCooldowns(QueryResult *result)
{
// some cooldowns can be already set at aura loading...
+
//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 has 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);
time_t infTime = curTime + infinityCooldownDelayCheck;
+
// remove outdated and save active
for(SpellCooldowns::iterator itr = m_spellCooldowns.begin();itr != m_spellCooldowns.end();)
{
@@ -3121,6 +3727,7 @@ void Player::_SaveSpellCooldowns()
++itr;
}
}
+
uint32 Player::resetTalentsCost() const
{
// The first time reset costs 1 gold
@@ -3153,39 +3760,51 @@ uint32 Player::resetTalentsCost() const
}
}
}
+
bool Player::resetTalents(bool no_cost)
{
// not need after this call
if(HasAtLoginFlag(AT_LOGIN_RESET_TALENTS))
RemoveAtLoginFlag(AT_LOGIN_RESET_TALENTS,true);
+
uint32 talentPointsForLevel = CalculateTalentsPoints();
+
if (m_usedTalentCount == 0)
{
SetFreeTalentPoints(talentPointsForLevel);
return false;
}
+
uint32 cost = 0;
+
if(!no_cost && !sWorld.getConfig(CONFIG_NO_RESET_TALENT_COST))
{
cost = resetTalentsCost();
+
if (GetMoney() < cost)
{
SendBuyError( BUY_ERR_NOT_ENOUGHT_MONEY, 0, 0, 0);
return false;
}
}
+
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;
+
// 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;
+
// Re-use pre-dual talent way of resetting talents, to ensure talents aren't being stored in spell storage.
for(uint8 rank = 0; rank < MAX_TALENT_RANK; ++rank)
{
@@ -3196,8 +3815,10 @@ bool Player::resetTalents(bool no_cost)
++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[rank])
{
@@ -3215,21 +3836,26 @@ bool Player::resetTalents(bool no_cost)
++itr;
}
}
+
for (PlayerTalentMap::iterator itr2 = m_talents[m_activeSpec]->begin(); itr2 != m_talents[m_activeSpec]->end(); ++itr2)
{
removeSpell(itr2->first, !IsPassiveSpell(itr2->first),false);
itr2->second->state = PLAYERSPELL_REMOVED;
}
}
+
SetFreeTalentPoints(talentPointsForLevel);
+
if(!no_cost)
{
ModifyMoney(-(int32)cost);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TALENTS, cost);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_NUMBER_OF_TALENT_RESETS, 1);
+
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);
/* when prev line will dropped use next line
@@ -3239,8 +3865,10 @@ bool Player::resetTalents(bool no_cost)
RemovePet(NULL,PET_SAVE_NOT_IN_SLOT, true);
}
*/
+
return true;
}
+
Mail* Player::GetMail(uint32 id)
{
for(PlayerMails::iterator itr = m_mail.begin(); itr != m_mail.end(); ++itr)
@@ -3252,6 +3880,7 @@ Mail* Player::GetMail(uint32 id)
}
return NULL;
}
+
void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const
{
if(target == this)
@@ -3267,6 +3896,7 @@ void Player::_SetCreateBits(UpdateMask *updateMask, Player *target) const
}
}
}
+
void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const
{
if(target == this)
@@ -3279,9 +3909,11 @@ void Player::_SetUpdateBits(UpdateMask *updateMask, Player *target) const
*updateMask &= updateVisualBits;
}
}
+
void Player::InitVisibleBits()
{
updateVisualBits.SetCount(PLAYER_END);
+
updateVisualBits.SetBit(OBJECT_FIELD_GUID);
updateVisualBits.SetBit(OBJECT_FIELD_TYPE);
updateVisualBits.SetBit(OBJECT_FIELD_ENTRY);
@@ -3337,6 +3969,7 @@ void Player::InitVisibleBits()
updateVisualBits.SetBit(UNIT_FIELD_BASE_MANA);
updateVisualBits.SetBit(UNIT_FIELD_BYTES_2);
updateVisualBits.SetBit(UNIT_FIELD_HOVERHEIGHT);
+
updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 0);
updateVisualBits.SetBit(PLAYER_DUEL_ARBITER + 1);
updateVisualBits.SetBit(PLAYER_FLAGS);
@@ -3347,134 +3980,169 @@ void Player::InitVisibleBits()
updateVisualBits.SetBit(PLAYER_BYTES_3);
updateVisualBits.SetBit(PLAYER_DUEL_TEAM);
updateVisualBits.SetBit(PLAYER_GUILD_TIMESTAMP);
+
// PLAYER_QUEST_LOG_x also visible bit on official (but only on party/raid)...
for(uint16 i = PLAYER_QUEST_LOG_1_1; i < PLAYER_QUEST_LOG_25_2; i += 4)
updateVisualBits.SetBit(i);
+
// Players visible items are not inventory stuff
for(uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
uint16 offset = i * 2;
+
// item entry
updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENTRYID + offset);
// enchant
updateVisualBits.SetBit(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + offset);
}
+
updateVisualBits.SetBit(PLAYER_CHOSEN_TITLE);
}
+
void Player::BuildCreateUpdateBlockForPlayer( UpdateData *data, Player *target ) const
{
for(uint8 i = 0; i < EQUIPMENT_SLOT_END; i++)
{
if(m_items[i] == NULL)
continue;
+
m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
}
+
if(target == this)
{
for(uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
{
if(m_items[i] == NULL)
continue;
+
m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
}
for(uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
{
if(m_items[i] == NULL)
continue;
+
m_items[i]->BuildCreateUpdateBlockForPlayer( data, target );
}
}
+
Unit::BuildCreateUpdateBlockForPlayer( data, target );
}
+
void Player::DestroyForPlayer( Player *target, bool anim ) const
{
Unit::DestroyForPlayer( target, anim );
+
for(uint8 i = 0; i < INVENTORY_SLOT_BAG_END; i++)
{
if(m_items[i] == NULL)
continue;
+
m_items[i]->DestroyForPlayer( target );
}
+
if(target == this)
{
for(uint8 i = INVENTORY_SLOT_BAG_START; i < BANK_SLOT_BAG_END; i++)
{
if(m_items[i] == NULL)
continue;
+
m_items[i]->DestroyForPlayer( target );
}
for(uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_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(spell);
return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED &&
!itr->second->disabled);
}
+
bool Player::HasTalent(uint32 spell, uint8 spec) const
{
PlayerTalentMap::const_iterator itr = m_talents[spec]->find(spell);
return (itr != m_talents[spec]->end() && itr->second->state != PLAYERSPELL_REMOVED);
}
+
bool Player::HasActiveSpell(uint32 spell) const
{
PlayerSpellMap::const_iterator itr = m_spells.find(spell);
return (itr != m_spells.end() && itr->second->state != PLAYERSPELL_REMOVED &&
itr->second->active && !itr->second->disabled);
}
+
TrainerSpellState Player::GetTrainerSpellState(TrainerSpell const* trainer_spell) const
{
if (!trainer_spell)
return TRAINER_SPELL_RED;
+
if (!trainer_spell->learnedSpell)
return TRAINER_SPELL_RED;
+
// known spell
if(HasSpell(trainer_spell->learnedSpell))
return TRAINER_SPELL_GRAY;
+
// check race/class requirement
if(!IsSpellFitByClassAndRace(trainer_spell->learnedSpell))
return TRAINER_SPELL_RED;
+
// check level requirement
if(getLevel() < trainer_spell->reqLevel)
return TRAINER_SPELL_RED;
+
if(SpellChainNode const* spell_chain = spellmgr.GetSpellChainNode(trainer_spell->learnedSpell))
{
// check prev.rank requirement
if(spell_chain->prev && !HasSpell(spell_chain->prev))
return TRAINER_SPELL_RED;
}
+
if(uint32 spell_req = spellmgr.GetSpellRequired(trainer_spell->spell))
{
// check additional spell requirement
if(!HasSpell(spell_req))
return TRAINER_SPELL_RED;
}
+
// check skill requirement
if(trainer_spell->reqSkill && GetBaseSkillValue(trainer_spell->reqSkill) < trainer_spell->reqSkillValue)
return TRAINER_SPELL_RED;
+
// exist, already checked at loading
SpellEntry const* spell = sSpellStore.LookupEntry(trainer_spell->learnedSpell);
+
// secondary prof. or not prof. spell
uint32 skill = spell->EffectMiscValue[1];
+
if(spell->Effect[1] != SPELL_EFFECT_SKILL || !IsPrimaryProfessionSkill(skill))
return TRAINER_SPELL_GREEN;
+
// check primary prof. limit
if(spellmgr.IsPrimaryProfessionFirstRankSpell(spell->Id) && GetFreePrimaryProfessionPoints() == 0)
return TRAINER_SPELL_GREEN_DISABLED;
+
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)
@@ -3483,8 +4151,10 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
if(guild)
guild->DelMember(guid);
}
+
// remove from arena teams
LeaveAllArenaTeams(playerguid);
+
// the player was uninvited already on logout so just remove from group
QueryResult *resultGroup = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", guid);
if(resultGroup)
@@ -3497,8 +4167,10 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
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)
@@ -3506,6 +4178,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
do
{
Field *fields = resultMail->Fetch();
+
uint32 mail_id = fields[0].GetUInt32();
uint16 mailTemplateId= fields[1].GetUInt16();
uint32 sender = fields[2].GetUInt32();
@@ -3513,9 +4186,11 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
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)
{
@@ -3526,14 +4201,17 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
do
{
Field *fields2 = resultItems->Fetch();
+
uint32 item_guidlow = fields2[1].GetUInt32();
uint32 item_template = fields2[2].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),resultItems))
{
@@ -3541,22 +4219,30 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
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, 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)
@@ -3569,6 +4255,7 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
} while (resultPets->NextRow());
delete resultPets;
}
+
CharacterDatabase.PExecute("DELETE FROM characters WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM character_declinedname WHERE guid = '%u'",guid);
@@ -3597,9 +4284,11 @@ void Player::DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmC
CharacterDatabase.PExecute("DELETE FROM guild_eventlog WHERE PlayerGuid2 = '%u'",guid);
CharacterDatabase.PExecute("DELETE FROM guild_bank_eventlog WHERE PlayerGuid = '%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;
@@ -3617,6 +4306,7 @@ void Player::SetMovement(PlayerMovementType pType)
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
@@ -3626,18 +4316,22 @@ void Player::BuildPlayerRepop()
WorldPacket data(SMSG_PRE_RESURRECT, GetPackGUID().size());
data.append(GetPackGUID());
GetSession()->SendPacket(&data);
+
if(getRace() == RACE_NIGHTELF)
CastSpell(this, SPELL_ID_NE_GHOST, true);
CastSpell(this, SPELL_ID_GHOST, true);
+
// 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();
@@ -3647,21 +4341,30 @@ void Player::BuildPlayerRepop()
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?
+
// set and clear other
SetByteValue(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_ALWAYS_STAND);
}
+
void Player::SendDelayResponse(const uint32 ml_seconds)
{
//FIXME: is this delay time arg really need? 50msec by default in code
@@ -3670,6 +4373,7 @@ void Player::SendDelayResponse(const uint32 ml_seconds)
data << (uint32)0;
GetSession()->SendPacket( &data );
}
+
void Player::ResurrectPlayer(float restore_percent, bool applySickness)
{
WorldPacket data(SMSG_DEATH_RELEASE_LOC, 4*4); // remove spirit healer position
@@ -3678,16 +4382,22 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
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(SPELL_ID_NE_GHOST); // speed bonuses
RemoveAurasDueToSpell(SPELL_ID_GHOST); // 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)
{
@@ -3696,28 +4406,35 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
SetPower(POWER_RAGE, 0);
SetPower(POWER_ENERGY, uint32(GetMaxPower(POWER_ENERGY)*restore_percent));
}
+
// trigger update zone for alive state zone updates
uint32 newzone, newarea;
GetZoneAndAreaId(newzone,newarea);
UpdateZone(newzone,newarea);
+
// update visibility
//ObjectAccessor::UpdateVisibilityForPlayer(this);
SetToNotify();
+
if(!applySickness)
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;
+
if(Aura* Aur = GetAura(SPELL_ID_PASSIVE_RESURRECTION_SICKNESS, GetGUID()))
{
Aur->SetAuraDuration(delta*IN_MILISECONDS);
@@ -3725,6 +4442,7 @@ void Player::ResurrectPlayer(float restore_percent, bool applySickness)
}
}
}
+
/**
* FallMode = 0 implies that the player is dying, or already dead, and the proper death state will be set.
* = 1 simply causes the player to plummet towards the ground, and not suffer any damage.
@@ -3736,13 +4454,16 @@ bool Player::FallGround(uint8 FallMode)
// Let's abort after we called this function one time
if (getDeathState() == DEAD_FALLING && FallMode == 0)
return false;
+
float x, y, z;
GetPosition(x, y, z);
float ground_Z = GetMap()->GetVmapHeight(x, y, z, true);
float z_diff = 0.0f;
if ((z_diff = fabs(ground_Z - z)) < 0.1f)
return false;
+
GetMotionMaster()->MoveFall(ground_Z, EVENT_FALL_GROUND);
+
// Below formula for falling damage is from Player::HandleFall
if(FallMode == 2 && z_diff >= 14.57f)
{
@@ -3753,48 +4474,66 @@ bool Player::FallGround(uint8 FallMode)
Unit::setDeathState(DEAD_FALLING);
return true;
}
+
void Player::KillPlayer()
{
if(IsFlying() && !GetTransport()) FallGround();
+
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*IN_MILISECONDS;
+
UpdateCorpseReclaimDelay(); // dependent at use SetDeathPvP() call before kill
SendCorpseReclaimDelay();
+
// 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))
{
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;
@@ -3803,8 +4542,11 @@ void Player::CreateCorpse()
if(InBattleGround() && !InArena())
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;
@@ -3814,41 +4556,51 @@ void Player::CreateCorpse()
{
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(uint8 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(uint8 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(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
for(uint32 j = 0; j < pBag->GetBagSize(); j++)
@@ -3856,32 +4608,43 @@ void Player::DurabilityLossAll(double percent, bool inventory)
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(uint8 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(uint8 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(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
for(uint32 j = 0; j < pBag->GetBagSize(); j++)
@@ -3889,67 +4652,84 @@ void Player::DurabilityPointsLossAll(int32 points, bool inventory)
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(uint8 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(uint8 j = INVENTORY_SLOT_BAG_START; j < INVENTORY_SLOT_BAG_END; j++)
for(uint8 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 = item->GetProto();
+
DurabilityCostsEntry const *dcost = sDurabilityCostsStore.LookupEntry(ditemProto->ItemLevel);
if(!dcost)
{
sLog.outError("RepairDurability: Wrong item lvl %u", ditemProto->ItemLevel);
return TotalCost;
}
+
uint32 dQualitymodEntryId = (ditemProto->Quality+1)*2;
DurabilityQualityEntry const *dQualitymodEntry = sDurabilityQualityStore.LookupEntry(dQualitymodEntryId);
if(!dQualitymodEntry)
@@ -3957,11 +4737,15 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g
sLog.outError("RepairDurability: Wrong dQualityModEntry %u", dQualitymodEntryId);
return TotalCost;
}
+
uint32 dmultiplier = dcost->multiplier[ItemSubClassToDurabilityMultiplierId(ditemProto->Class,ditemProto->SubClass)];
uint32 costs = uint32(LostDurability*dmultiplier*double(dQualitymodEntry->quality_mod));
+
costs = uint32(costs * discountMod) * sWorld.getRate(RATE_REPAIRCOST);
+
if (costs==0) //fix for ITEM_QUALITY_ARTIFACT
costs = 1;
+
if (guildBank)
{
if (GetGuildId()==0)
@@ -3969,24 +4753,29 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g
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;
}
@@ -3999,32 +4788,41 @@ uint32 Player::DurabilityRepair(uint16 pos, bool cost, float discountMod, bool g
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() || GetPositionZ() < -500.0f)
{
ResurrectPlayer(0.5f);
SpawnCorpseBones();
}
+
WorldSafeLocsEntry const *ClosestGrave = NULL;
+
// Special handle for battleground maps
if( BattleGround *bg = GetBattleGround() )
ClosestGrave = bg->GetClosestGraveYard(this);
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)
@@ -4043,14 +4841,17 @@ void Player::RepopAtGraveyard()
else if(GetPositionZ() < -500.0f)
TeleportTo(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, GetOrientation());
}
+
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())
@@ -4060,38 +4861,50 @@ void Player::CleanupChannels()
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(); // store name, (*i)erase in LeftChannel
@@ -4101,6 +4914,7 @@ void Player::UpdateLocalChannels(uint32 newZone )
}
sLog.outDebug("Player: channels cleaned up!");
}
+
void Player::LeaveLFGChannel()
{
for(JoinedChannelsList::iterator i = m_channels.begin(); i != m_channels.end(); ++i )
@@ -4112,15 +4926,18 @@ void Player::LeaveLFGChannel()
}
}
}
+
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)
{
if(modGroup >= BASEMOD_END || modType >= MOD_END)
@@ -4128,7 +4945,9 @@ void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, floa
sLog.outError("ERROR in HandleBaseModValue(): non existed BaseModGroup of wrong BaseModType!");
return;
}
+
float val = 1.0f;
+
switch(modType)
{
case FLAT_MOD:
@@ -4137,12 +4956,15 @@ void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, floa
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;
@@ -4152,6 +4974,7 @@ void Player::HandleBaseModValue(BaseModGroup modGroup, BaseModType modType, floa
default: break;
}
}
+
float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
{
if(modGroup >= BASEMOD_END || modType > MOD_END)
@@ -4159,10 +4982,13 @@ float Player::GetBaseModValue(BaseModGroup modGroup, BaseModType modType) const
sLog.outError("trial to access non existed 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)
@@ -4170,28 +4996,38 @@ float Player::GetTotalBaseModValue(BaseModGroup modGroup) const
sLog.outError("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
{
float value = (m_auraBaseMod[SHIELD_BLOCK_VALUE][FLAT_MOD] + GetStat(STAT_STRENGTH) * 0.5f - 10)*m_auraBaseMod[SHIELD_BLOCK_VALUE][PCT_MOD];
+
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
@@ -4222,53 +5058,69 @@ float Player::GetDodgeFromAgility()
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.2f;
if (melee>33.0f) melee = 33.0f;
return uint32 (melee * damage /100.0f);
}
+
uint32 Player::GetRangedCritDamageReduction(uint32 damage) const
{
float ranged = GetRatingBonusValue(CR_CRIT_TAKEN_RANGED)*2.2f;
if (ranged>33.0f) ranged=33.0f;
return uint32 (ranged * damage /100.0f);
}
+
uint32 Player::GetSpellCritDamageReduction(uint32 damage) const
{
float spell = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL)*2.2f;
@@ -4277,6 +5129,7 @@ uint32 Player::GetSpellCritDamageReduction(uint32 damage) const
spell = 33.0f;
return uint32 (spell * damage / 100.0f);
}
+
uint32 Player::GetDotDamageReduction(uint32 damage) const
{
float spellDot = GetRatingBonusValue(CR_CRIT_TAKEN_SPELL);
@@ -4285,6 +5138,7 @@ uint32 Player::GetDotDamageReduction(uint32 damage) const
spellDot = 100.0f;
return uint32 (spellDot * damage / 100.0f);
}
+
float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
{
switch (attType)
@@ -4298,15 +5152,19 @@ float Player::GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const
}
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;
@@ -4315,23 +5173,29 @@ float Player::OCTRegenHPPerSpirit()
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)
{
m_baseRatingValue[cr]+=(apply ? value : -value);
+
int32 amount = uint32(m_baseRatingValue[cr]);
// Apply bonus from SPELL_AURA_MOD_RATING_FROM_STAT
// stat used stored in miscValueB for this aura
@@ -4342,9 +5206,12 @@ void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
if (amount < 0)
amount = 0;
SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + cr, uint32(amount));
+
float RatingCoeffecient = GetRatingCoefficient(cr);
float RatingChange = 0.0f;
+
bool affectStats = CanModifyStats();
+
switch (cr)
{
case CR_WEAPON_SKILL: // Implemented in Unit::RollMeleeOutcomeAgainst
@@ -4424,6 +5291,7 @@ void Player::ApplyRatingMod(CombatRating cr, int32 value, bool apply)
break;
}
}
+
void Player::SetRegularAttackTime()
{
for(uint8 i = 0; i < MAX_ATTACK; ++i)
@@ -4439,33 +5307,42 @@ void Player::SetRegularAttackTime()
}
}
}
+
//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 < max)
{
uint32 new_value = value+step;
if(new_value > max)
new_value = max;
+
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(new_value,max));
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,skill_id);
return true;
}
+
return false;
}
+
inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLevel, uint32 YellowLevel)
{
if ( SkillValue >= GrayLevel )
@@ -4476,15 +5353,19 @@ inline int SkillGainChance(uint32 SkillValue, uint32 GrayLevel, uint32 GreenLeve
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);
+
SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellid);
+
for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_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)
@@ -4492,7 +5373,9 @@ bool Player::UpdateCraftSkill(uint32 spellid)
if (uint32 discoveredSpell = GetSkillDiscoverySpell(_spell_idx->second->skillId, spellid, this))
learnSpell(discoveredSpell,false);
}
+
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,
@@ -4502,10 +5385,13 @@ bool Player::UpdateCraftSkill(uint32 spellid)
}
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)
{
@@ -4527,44 +5413,58 @@ bool Player::UpdateGatherSkill(uint32 SkillId, uint32 SkillValue, uint32 RedLeve
}
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);
}
+
// levels sync. with spell requirement for skill levels to learn
// bonus abilities in sSkillLineAbilityStore
// Used only to avoid scan DBC at each skill grow
static uint32 bonusSkillLevels[] = {75,150,225,300,375,450};
+
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));
for(uint32* bsl = &bonusSkillLevels[0]; *bsl; ++bsl)
{
@@ -4578,27 +5478,35 @@ bool Player::UpdateSkillPro(uint16 SkillId, int32 Chance, uint32 step)
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
+
if(pVictim && pVictim->GetTypeId() == TYPEID_UNIT && (((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_SKILLGAIN))
return;
+
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)
@@ -4616,6 +5524,7 @@ void Player::UpdateWeaponSkill (WeaponAttackType attType)
}
UpdateAllCritPercentages();
}
+
void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool defence)
{
uint32 plevel = getLevel(); // if defense than pVictim == attacker
@@ -4623,21 +5532,27 @@ void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool de
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 += chance * 0.02f * GetStat(STAT_INTELLECT);
}
+
chance = chance < 1.0f ? 1.0f : chance; //minimum chance to increase skill is 1%
+
if(roll_chance_f(chance))
{
if(defence)
@@ -4648,6 +5563,7 @@ void Player::UpdateCombatSkills(Unit *pVictim, WeaponAttackType attType, bool de
else
return;
}
+
void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
{
for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
@@ -4656,6 +5572,7 @@ void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
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
@@ -4663,23 +5580,30 @@ void Player::ModifySkillBonus(uint32 skillid,int32 val, bool talent)
return;
}
}
+
void Player::UpdateSkillsForLevel()
{
uint16 maxconfskill = sWorld.GetConfigMaxSkillValue();
uint32 maxSkill = GetMaxSkillValueForLevel();
+
bool alwaysMaxSkill = sWorld.getConfig(CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL);
+
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)
{
@@ -4692,6 +5616,7 @@ void Player::UpdateSkillsForLevel()
}
}
}
+
void Player::UpdateSkillsToMaxSkillsForLevel()
{
for (uint16 i=0; i < PLAYER_MAX_SKILLS; i++)
@@ -4701,22 +5626,28 @@ void Player::UpdateSkillsToMaxSkillsForLevel()
if( IsProfessionOrRidingSkill(pskill))
continue;
uint32 data = GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i));
+
uint32 max = SKILL_MAX(data);
+
if(max > 1)
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(max,max));
+
if(pskill == SKILL_DEFENSE)
UpdateDefenseBonusesMod();
}
}
+
// This functions sets a skill line value (and adds if doesn't exist yet)
// To "remove" a skill line, set it's values to zero
void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)
{
if(!id)
return;
+
uint16 i=0;
for (; i < PLAYER_MAX_SKILLS; i++)
if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == id) break;
+
if(i<PLAYER_MAX_SKILLS) //has skill
{
if(currVal)
@@ -4732,6 +5663,7 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)
SetUInt32Value(PLAYER_SKILL_INDEX(i),0);
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),0);
SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
// remove all spells that related to this skill
for (uint32 j=0; j<sSkillLineAbilityStore.GetNumRows(); ++j)
if(SkillLineAbilityEntry const *pAbility = sSkillLineAbilityStore.LookupEntry(j))
@@ -4758,24 +5690,29 @@ void Player::SetSkill(uint32 id, uint16 currVal, uint16 maxVal)
SetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i),MAKE_SKILL_VALUE(currVal,maxVal));
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_REACH_SKILL_LEVEL,id);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LEARN_SKILL_LEVEL,id);
+
// apply skill bonuses
SetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i),0);
+
// temporary bonuses
AuraEffectList const& mModSkill = GetAurasByType(SPELL_AURA_MOD_SKILL);
for(AuraEffectList::const_iterator j = mModSkill.begin(); j != mModSkill.end(); ++j)
if ((*j)->GetMiscValue() == int32(id))
(*j)->ApplyModifier(true);
+
// permanent bonuses
AuraEffectList const& mModSkillTalent = GetAurasByType(SPELL_AURA_MOD_SKILL_TALENT);
for(AuraEffectList::const_iterator j = mModSkillTalent.begin(); j != mModSkillTalent.end(); ++j)
if ((*j)->GetMiscValue() == int32(id))
(*j)->ApplyModifier(true);
+
// Learn all spells for skill
learnSkillRewardedSpells(id, currVal);
return;
}
}
}
+
bool Player::HasSkill(uint32 skill) const
{
if(!skill)return false;
@@ -4788,15 +5725,18 @@ bool Player::HasSkill(uint32 skill) const
}
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);
@@ -4805,6 +5745,7 @@ uint16 Player::GetSkillValue(uint32 skill) const
}
return 0;
}
+
uint16 Player::GetMaxSkillValue(uint32 skill) const
{
if(!skill)return 0;
@@ -4813,6 +5754,7 @@ uint16 Player::GetMaxSkillValue(uint32 skill) const
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);
@@ -4821,6 +5763,7 @@ uint16 Player::GetMaxSkillValue(uint32 skill) const
}
return 0;
}
+
uint16 Player::GetPureMaxSkillValue(uint32 skill) const
{
if(!skill)return 0;
@@ -4833,6 +5776,7 @@ uint16 Player::GetPureMaxSkillValue(uint32 skill) const
}
return 0;
}
+
uint16 Player::GetBaseSkillValue(uint32 skill) const
{
if(!skill)return 0;
@@ -4847,6 +5791,7 @@ uint16 Player::GetBaseSkillValue(uint32 skill) const
}
return 0;
}
+
uint16 Player::GetPureSkillValue(uint32 skill) const
{
if(!skill)return 0;
@@ -4859,10 +5804,12 @@ uint16 Player::GetPureSkillValue(uint32 skill) const
}
return 0;
}
+
int16 Player::GetSkillPermBonusValue(uint32 skill) const
{
if(!skill)
return 0;
+
for (uint8 i = 0; i < PLAYER_MAX_SKILLS; i++)
{
if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
@@ -4870,12 +5817,15 @@ int16 Player::GetSkillPermBonusValue(uint32 skill) const
return SKILL_PERM_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
}
}
+
return 0;
}
+
int16 Player::GetSkillTempBonusValue(uint32 skill) const
{
if(!skill)
return 0;
+
for (uint8 i = 0; i < PLAYER_MAX_SKILLS; i++)
{
if ((GetUInt32Value(PLAYER_SKILL_INDEX(i)) & 0x0000FFFF) == skill)
@@ -4883,11 +5833,14 @@ int16 Player::GetSkillTempBonusValue(uint32 skill) const
return SKILL_TEMP_BONUS(GetUInt32Value(PLAYER_SKILL_BONUS_INDEX(i)));
}
}
+
return 0;
}
+
void Player::SendActionButtons(uint32 state) const
{
sLog.outDetail( "Sending Action Buttons for '%u' spec '%u'", GetGUIDLow(), m_activeSpec);
+
WorldPacket data(SMSG_ACTION_BUTTONS, 1+(MAX_ACTION_BUTTONS*4));
data << uint8(state); // can be 0, 1, 2
for(int button = 0; button < MAX_ACTION_BUTTONS; ++button)
@@ -4898,9 +5851,11 @@ void Player::SendActionButtons(uint32 state) const
else
data << uint32(0);
}
+
GetSession()->SendPacket( &data );
sLog.outDetail( "Action Buttons for '%u' spec '%u' Sent", GetGUIDLow(), m_activeSpec );
}
+
ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
{
if(button >= MAX_ACTION_BUTTONS)
@@ -4908,11 +5863,13 @@ ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
sLog.outError( "Action %u not added into button %u for player %s: button must be < 132", action, button, GetName() );
return NULL;
}
+
if(action >= MAX_ACTION_BUTTON_ACTION_VALUE)
{
sLog.outError( "Action %u not added into button %u for player %s: action must be < %u", action, button, GetName(), MAX_ACTION_BUTTON_ACTION_VALUE );
return NULL;
}
+
switch(type)
{
case ACTION_BUTTON_SPELL:
@@ -4921,6 +5878,7 @@ ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
sLog.outError( "Action %u not added into button %u for player %s: spell not exist", action, button, GetName() );
return NULL;
}
+
if(!HasSpell(action))
{
sLog.outError( "Action %u not added into button %u for player %s: player don't known this spell", action, button, GetName() );
@@ -4938,29 +5896,36 @@ ActionButton* Player::addActionButton(uint8 button, uint32 action, uint8 type)
break; // pther cases not checked at this moment
}
+
// it create new button (NEW state) if need or return existed
ActionButton& ab = m_actionButtons[button];
+
// set data and update to CHANGED if not NEW
ab.SetActionAndType(action,ActionButtonType(type));
+
sLog.outDetail( "Player '%u' Added Action '%u' (type %u) to Button '%u'", GetGUIDLow(), action, uint32(type), button );
return &ab;
}
+
void Player::removeActionButton(uint8 button)
{
ActionButtonList::iterator buttonItr = m_actionButtons.find(button);
if (buttonItr==m_actionButtons.end())
return;
+
if (!buttonItr->second.canRemoveByClient)
{
- buttonItr->second.canRemoveByClient = true;
+ buttonItr->second.canRemoveByClient = true;
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() );
}
+
bool Player::SetPosition(float x, float y, float z, float orientation, bool teleport)
{
// prevent crash when a bad coord is sent by the client
@@ -4969,35 +5934,46 @@ bool Player::SetPosition(float x, float y, float z, float orientation, bool tele
sLog.outDebug("Player::SetPosition(%f, %f, %f, %f, %d) .. bad coordinates for player %d!",x,y,z,orientation,teleport,GetGUIDLow());
return false;
}
+
//if(movementInfo.flags & MOVEMENTFLAG_MOVING)
// mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE);
//if(movementInfo.flags & MOVEMENTFLAG_TURNING)
// mover->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
//AURA_INTERRUPT_FLAG_JUMP not sure
+
bool turn = (GetOrientation() != orientation);
bool move2d = (teleport || GetPositionX() != x || GetPositionY() != y);
+
if(turn)
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TURNING);
+
if(move2d || GetPositionZ() != z)
{
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOVE);
+
// move and update visible state if need
GetMap()->PlayerRelocation(this, x, y, z, orientation);
+
// reread after Map::Relocation
GetPosition(x, y, z);
+
// group update
if(move2d && GetGroup())
SetGroupUpdateFlag(GROUP_UPDATE_FLAG_POSITION);
+
// code block for underwater state update
UpdateUnderwaterState(GetMap(), x, y, z);
+
CheckExploreSystem();
}
else if(turn)
{
SetOrientation(orientation);
}
+
return true;
}
+
void Player::SaveRecallPosition()
{
m_recallMap = GetMapId();
@@ -5006,51 +5982,63 @@ void Player::SaveRecallPosition()
m_recallZ = GetPositionZ();
m_recallO = GetOrientation();
}
+
void Player::SendMessageToSet(WorldPacket *data, bool self)
{
if(self)
GetSession()->SendPacket(data);
+
// we use World::GetMaxVisibleDistance() because i cannot see why not use a distance
// update: replaced by GetMap()->GetVisibilityDistance()
Trinity::MessageDistDeliverer notifier(this, data, GetMap()->GetVisibilityDistance());
VisitNearbyWorldObject(GetMap()->GetVisibilityDistance(), notifier);
}
+
void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self)
{
if(self)
GetSession()->SendPacket(data);
+
Trinity::MessageDistDeliverer notifier(this, data, dist);
VisitNearbyWorldObject(dist, notifier);
}
+
void Player::SendMessageToSetInRange(WorldPacket *data, float dist, bool self, bool own_team_only)
{
if(self)
GetSession()->SendPacket(data);
+
Trinity::MessageDistDeliverer notifier(this, data, dist, own_team_only);
VisitNearbyWorldObject(dist, notifier);
}
+
void Player::SendDirectMessage(WorldPacket *data)
{
GetSession()->SendPacket(data);
}
+
void Player::SendCinematicStart(uint32 CinematicSequenceId)
{
WorldPacket data(SMSG_TRIGGER_CINEMATIC, 4);
data << uint32(CinematicSequenceId);
SendDirectMessage(&data);
}
+
void Player::SendMovieStart(uint32 MovieId)
{
WorldPacket data(SMSG_TRIGGER_MOVIE, 4);
data << uint32(MovieId);
SendDirectMessage(&data);
}
+
void Player::CheckExploreSystem()
{
if (!isAlive())
return;
+
if (isInFlight())
return;
+
if(!m_AreaID)
m_AreaID = GetAreaId();
if(m_AreaID != GetAreaId())
@@ -5058,21 +6046,27 @@ void Player::CheckExploreSystem()
m_AreaID = GetAreaId();
GetSession()->HandleOnAreaChange(GetAreaEntryByAreaID(m_AreaID));
}
+
uint16 areaFlag = GetBaseMap()->GetAreaFlag(GetPositionX(),GetPositionY(),GetPositionZ());
if(areaFlag==0xffff)
return;
int offset = areaFlag / 32;
+
if(offset >= 128)
{
sLog.outError("Wrong area flag %u in map data for (X: %f Y: %f) point to field PLAYER_EXPLORED_ZONES_1 + %u ( %u must be < 128 ).",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));
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EXPLORE_AREA);
+
AreaTableEntry const *p = GetAreaEntryByAreaFlagAndMap(areaFlag,GetMapId());
if(!p)
{
@@ -5100,12 +6094,14 @@ void Player::CheckExploreSystem()
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);
}
@@ -5113,6 +6109,7 @@ void Player::CheckExploreSystem()
}
}
}
+
uint32 Player::TeamForRace(uint8 race)
{
ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
@@ -5121,14 +6118,17 @@ uint32 Player::TeamForRace(uint8 race)
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 teamid %u in DBC: wrong DBC files?",uint32(race),rEntry->TeamID);
return ALLIANCE;
}
+
uint32 Player::getFactionForRace(uint8 race)
{
ChrRacesEntry const* rEntry = sChrRacesStore.LookupEntry(race);
@@ -5137,51 +6137,70 @@ uint32 Player::getFactionForRace(uint8 race)
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) );
}
+
ReputationRank Player::GetReputationRank(uint32 faction) const
{
FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction);
return GetReputationMgr().GetRank(factionEntry);
}
+
//Calculate total reputation percent player gain with quest/creature level
int32 Player::CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, int32 faction, bool for_quest)
{
float percent = 100.0f;
+
float rate = for_quest ? sWorld.getRate(RATE_REPUTATION_LOWLEVEL_QUEST) : sWorld.getRate(RATE_REPUTATION_LOWLEVEL_KILL);
+
if (rate != 1.0f && creatureOrQuestLevel <= MaNGOS::XP::GetGrayLevel(getLevel()))
percent *= rate;
+
float repMod = GetTotalAuraModifier(SPELL_AURA_MOD_REPUTATION_GAIN);
+
if (!for_quest)
repMod += GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_FACTION_REPUTATION_GAIN, faction);
+
percent += rep > 0 ? repMod : -repMod;
+
if (percent <= 0.0f)
return 0;
+
return int32(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;
+
if(((Creature*)pVictim)->IsReputationGainDisabled())
return;
+
ReputationOnKillEntry const* Rep = objmgr.GetReputationOnKilEntry(((Creature*)pVictim)->GetCreatureInfo()->Entry);
+
if(!Rep)
return;
+
uint32 ChampioningFaction = 0;
+
if(GetChampioningFaction())
{
// support for: Championing - http://www.wowwiki.com/Championing
+
Map const *pMap = GetMap();
if(pMap && pMap->IsDungeon())
{
bool Heroic = pMap->IsHeroic();
+
InstanceTemplate const *pInstance = objmgr.GetInstanceTemplate(pMap->GetId());
if(pInstance)
{
@@ -5194,14 +6213,18 @@ void Player::RewardReputation(Unit *pVictim, float rate)
}
}
}
+
// Favored reputation increase START
uint32 zone = GetZoneId();
uint32 team = GetTeam();
float favored_rep_mult = 0;
+
if( (HasAura(32096) || HasAura(32098)) && (zone == 3483 || zone == 3562 || zone == 3836 || zone == 3713 || zone == 3714) ) favored_rep_mult = 0.25; // Thrallmar's Favor and Honor Hold's Favor
else if( HasAura(30754) && (Rep->repfaction1 == 609 || Rep->repfaction2 == 609) && !ChampioningFaction ) favored_rep_mult = 0.25; // Cenarion Favor
+
if(favored_rep_mult > 0) favored_rep_mult *= 2; // Multiplied by 2 because the reputation is divided by 2 for some reason (See "donerep1 / 2" and "donerep2 / 2") -- if you know why this is done, please update/explain :)
// Favored reputation increase END
+
if(Rep->repfaction1 && (!Rep->team_dependent || team == ALLIANCE))
{
int32 donerep1 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue1, ChampioningFaction ? ChampioningFaction : Rep->repfaction1, false);
@@ -5210,6 +6233,7 @@ void Player::RewardReputation(Unit *pVictim, float rate)
uint32 current_reputation_rank1 = GetReputationMgr().GetRank(factionEntry1);
if (factionEntry1 && current_reputation_rank1 <= Rep->reputation_max_cap1)
GetReputationMgr().ModifyReputation(factionEntry1, donerep1);
+
// Wiki: Team factions value divided by 2 -- Deprecated, see ModifyReputation
/*if (factionEntry1 && Rep->is_teamaward1)
{
@@ -5218,6 +6242,7 @@ void Player::RewardReputation(Unit *pVictim, float rate)
GetReputationMgr().ModifyReputation(team1_factionEntry, donerep1 / 2);
}*/
}
+
if(Rep->repfaction2 && (!Rep->team_dependent || team == HORDE))
{
int32 donerep2 = CalculateReputationGain(pVictim->getLevel(), Rep->repvalue2, ChampioningFaction ? ChampioningFaction : Rep->repfaction2, false);
@@ -5226,6 +6251,7 @@ void Player::RewardReputation(Unit *pVictim, float rate)
uint32 current_reputation_rank2 = GetReputationMgr().GetRank(factionEntry2);
if (factionEntry2 && current_reputation_rank2 <= Rep->reputation_max_cap2)
GetReputationMgr().ModifyReputation(factionEntry2, donerep2);
+
// Wiki: Team factions value divided by 2 -- Deprecated, see ModifyReputation
/*if (factionEntry2 && Rep->is_teamaward2)
{
@@ -5235,6 +6261,7 @@ void Player::RewardReputation(Unit *pVictim, float rate)
}*/
}
}
+
//Calculate how many reputation points player gain with the quest
void Player::RewardReputation(Quest const *pQuest)
{
@@ -5249,25 +6276,32 @@ void Player::RewardReputation(Quest const *pQuest)
GetReputationMgr().ModifyReputation(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));
@@ -5279,8 +6313,10 @@ void Player::UpdateHonorFields()
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)
@@ -5291,35 +6327,47 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt
{
if(!uVictim || uVictim == this || uVictim->GetTypeId() != TYPEID_PLAYER)
return false;
+
if( GetBGTeam() == ((Player*)uVictim)->GetBGTeam() )
return false;
+
return true;
}
+
// 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
if(HasAura(SPELL_AURA_PLAYER_INACTIVE))
return false;
+
uint64 victim_guid = 0;
uint32 victim_rank = 0;
time_t now = time(NULL);
+
// need call before fields update to have chance move yesterday data to appropriate fields before today data change.
UpdateHonorFields();
+
// do not reward honor in arenas, but return true to enable onkill spellproc
if(InBattleGround() && GetBattleGround() && GetBattleGround()->isArena())
return true;
+
if(honor <= 0)
{
if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
return false;
+
victim_guid = uVictim->GetGUID();
+
if( uVictim->GetTypeId() == TYPEID_PLAYER )
{
Player *pVictim = (Player *)uVictim;
+
if( GetTeam() == pVictim->GetTeam() && !sWorld.IsFFAPvPRealm() )
return false;
+
float f = 1; //need for total kills (?? need more info)
uint32 k_grey = 0;
uint32 k_level = getLevel();
uint32 v_level = pVictim->getLevel();
+
{
// PLAYER_CHOSEN_TITLE VALUES DESCRIPTION
// [0] Just name
@@ -5342,13 +6390,19 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt
else
victim_guid = 0; // Don't show HK: <rank> message, only log.
}
+
k_grey = Trinity::XP::GetGrayLevel(k_level);
+
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
@@ -5360,21 +6414,27 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt
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;
+
// apply honor multiplier from aura (not stacking-get highest)
honor = int32(float(honor) * (float(GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HONOR_GAIN_PCT))+100.0f)/100.0f);
honor *= (((float)urand(8,12))/10); // approx honor: 80% - 120% of real honor
}
+
// honor - for show honor points in log
// victim_guid - for show victim name in log
// victim_rank [1..4] HK: <dishonored rank>
@@ -5384,14 +6444,19 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt
data << (uint32) honor;
data << (uint64) victim_guid;
data << (uint32) victim_rank;
+
GetSession()->SendPacket(&data);
+
// add honor points
ModifyHonorPoints(int32(honor));
+
ApplyModUInt32Value(PLAYER_FIELD_TODAY_CONTRIBUTION, uint32(honor), true);
+
if( sWorld.getConfig(CONFIG_PVP_TOKEN_ENABLE) && pvptoken )
{
if(!uVictim || uVictim == this || uVictim->HasAuraType(SPELL_AURA_NO_PVP_CREDIT))
return true;
+
if(uVictim->GetTypeId() == TYPEID_PLAYER)
{
// Check if allowed to receive it in current map
@@ -5400,27 +6465,33 @@ bool Player::RewardHonor(Unit *uVictim, uint32 groupsize, float honor, bool pvpt
|| (MapType == 2 && !HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP))
|| (MapType == 3 && !InBattleGround()) )
return true;
+
uint32 noSpaceForCount = 0;
uint32 itemId = sWorld.getConfig(CONFIG_PVP_TOKEN_ID);
int32 count = sWorld.getConfig(CONFIG_PVP_TOKEN_COUNT);
+
// check space and find places
ItemPosCountVec dest;
uint8 msg = CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, itemId, count, &noSpaceForCount );
if( msg != EQUIP_ERR_OK ) // convert to possible store amount
count = noSpaceForCount;
+
if( count == 0 || dest.empty()) // can't add any
{
// -- TODO: Send to mailbox if no space
ChatHandler(this).PSendSysMessage("You don't have any space in your bags for a token.");
return true;
}
+
Item* item = StoreNewItem( dest, itemId, true, Item::GenerateItemRandomPropertyId(itemId));
SendNewItem(item,count,true,false);
ChatHandler(this).PSendSysMessage("You have been awarded a token for slaying another player.");
}
}
+
return true;
}
+
void Player::ModifyHonorPoints( int32 value )
{
if(value < 0)
@@ -5433,6 +6504,7 @@ void Player::ModifyHonorPoints( int32 value )
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)
@@ -5445,15 +6517,18 @@ void Player::ModifyArenaPoints( int32 value )
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)
{
QueryResult* result = CharacterDatabase.PQuery("SELECT guildid FROM guild_member WHERE guid='%u'", GUID_LOPART(guid));
if(!result)
return 0;
+
uint32 id = result->Fetch()[0].GetUInt32();
delete result;
return id;
}
+
uint32 Player::GetRankFromDB(uint64 guid)
{
QueryResult *result = CharacterDatabase.PQuery( "SELECT rank FROM guild_member WHERE guid='%u'", GUID_LOPART(guid) );
@@ -5466,15 +6541,18 @@ uint32 Player::GetRankFromDB(uint64 guid)
else
return 0;
}
+
uint32 Player::GetArenaTeamIdFromDB(uint64 guid, uint8 type)
{
QueryResult *result = CharacterDatabase.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u' AND type='%u' LIMIT 1", GUID_LOPART(guid), type);
if(!result)
return 0;
+
uint32 id = (*result)[0].GetUInt32();
delete result;
return id;
}
+
uint32 Player::GetZoneIdFromDB(uint64 guid)
{
uint32 guidLow = GUID_LOPART(guid);
@@ -5484,6 +6562,7 @@ uint32 Player::GetZoneIdFromDB(uint64 guid)
Field* fields = result->Fetch();
uint32 zone = fields[0].GetUInt32();
delete result;
+
if (!zone)
{
// stored zone is zero, use generic and slow zone detection
@@ -5496,32 +6575,42 @@ uint32 Player::GetZoneIdFromDB(uint64 guid)
float posy = fields[2].GetFloat();
float posz = fields[3].GetFloat();
delete result;
+
zone = MapManager::Instance().GetZoneId(map,posx,posy,posz);
+
if (zone > 0)
CharacterDatabase.PExecute("UPDATE characters SET zone='%u' WHERE guid='%u'", zone, guidLow);
}
+
return zone;
}
+
uint32 Player::GetLevelFromDB(uint64 guid)
{
QueryResult *result = CharacterDatabase.PQuery( "SELECT level FROM characters WHERE guid='%u'", GUID_LOPART(guid) );
if (!result)
return 0;
+
Field* fields = result->Fetch();
uint32 level = fields[0].GetUInt32();
delete result;
+
return level;
}
+
void Player::UpdateArea(uint32 newArea)
{
// FFA_PVP flags are area and not zone id dependent
// so apply them accordingly
m_areaUpdateId = newArea;
+
AreaTableEntry const* area = GetAreaEntryByAreaID(newArea);
pvpInfo.inFFAPvPArea = area && (area->flags & AREA_FLAG_ARENA);
UpdatePvPState(true);
+
UpdateAreaDependentAuras(newArea);
}
+
void Player::UpdateZone(uint32 newZone, uint32 newArea)
{
if(m_zoneUpdateId != newZone)
@@ -5530,13 +6619,17 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
sOutdoorPvPMgr.HandlePlayerEnterZone(this, newZone);
SendInitWorldStates(newZone, newArea); // only if really enters to new zone, not just area change, works strange...
}
+
m_zoneUpdateId = newZone;
m_zoneUpdateTimer = ZONE_UPDATE_INTERVAL;
+
// zone changed, so area changed as well, update it
UpdateArea(newArea);
+
AreaTableEntry const* zone = GetAreaEntryByAreaID(newZone);
if(!zone)
return;
+
if (sWorld.getConfig(CONFIG_WEATHER))
{
Weather *wth = sWorld.FindWeather(zone->ID);
@@ -5553,6 +6646,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
}
}
}
+
// in PvP, any not controlled zone (except zone->team == 6, default case)
// in PvE, only opposition team capital
switch(zone->team)
@@ -5571,6 +6665,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
pvpInfo.inHostileArea = false;
break;
}
+
pvpInfo.inNoPvPArea = false;
if(zone->IsSanctuary()) // in sanctuary
{
@@ -5582,6 +6677,7 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
{
RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY);
}
+
if(zone->flags & AREA_FLAG_CAPITAL) // in capital city
{
SetFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING);
@@ -5608,34 +6704,44 @@ void Player::UpdateZone(uint32 newZone, uint32 newArea)
}
}
}
+
UpdatePvPState();
+
// 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 );
+
// check some item equip limitations (in result lost CanTitanGrip at talent reset, for example)
AutoUnequipOffhandIfNeed();
+
// 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 = GetMap()->GetGameObject(duelFlagGUID);
if(!obj)
return;
+
if(duel->outOfBound == 0)
{
if(!IsWithinDistInMap(obj, 50))
{
duel->outOfBound = currTime;
+
WorldPacket data(SMSG_DUEL_OUTOFBOUNDS, 0);
GetSession()->SendPacket(&data);
}
@@ -5645,6 +6751,7 @@ void Player::CheckDuelDistance(time_t currTime)
if(IsWithinDistInMap(obj, 40))
{
duel->outOfBound = 0;
+
WorldPacket data(SMSG_DUEL_INBOUNDS, 0);
GetSession()->SendPacket(&data);
}
@@ -5654,20 +6761,25 @@ void Player::CheckDuelDistance(time_t currTime)
}
}
}
+
bool Player::IsOutdoorPvPActive()
{
return (isAlive() && !HasInvisibilityAura() && !HasStealthAura() && (IsPvP() || sWorld.IsPvPRealm()) && !HasUnitMovementFlag(MOVEMENTFLAG_FLYING) && !isInFlight());
}
+
void Player::DuelComplete(DuelCompleteType type)
{
// duel not requested
if(!duel)
return;
+
sLog.outDebug("Duel Complete %s %s", GetName(), duel->opponent->GetName());
+
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
@@ -5676,21 +6788,25 @@ void Player::DuelComplete(DuelCompleteType type)
data << GetName();
SendMessageToSet(&data,true);
}
+
if (type == DUEL_WON)
{
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_LOSE_DUEL, 1);
if (duel->opponent)
{
duel->opponent->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_WIN_DUEL, 1);
+
//Credit for quest Death's Challenge
if (getClass() == CLASS_DEATH_KNIGHT && duel->opponent->GetQuestStatus(12733) == QUEST_STATUS_INCOMPLETE)
duel->opponent->CastSpell(duel->opponent, 52994, true);
}
}
+
//Remove Duel Flag object
GameObject* obj = GetMap()->GetGameObject(GetUInt64Value(PLAYER_DUEL_ARBITER));
if(obj)
duel->initiator->RemoveGameObject(obj,true);
+
/* remove auras */
AuraMap &itsAuras = duel->opponent->GetAuras();
for(AuraMap::iterator i = itsAuras.begin(); i != itsAuras.end();)
@@ -5702,6 +6818,7 @@ void Player::DuelComplete(DuelCompleteType type)
else
++i;
}
+
AuraMap &myAuras = GetAuras();
for(AuraMap::iterator i = myAuras.begin(); i != myAuras.end();)
{
@@ -5712,69 +6829,93 @@ void Player::DuelComplete(DuelCompleteType type)
else
++i;
}
+
// cleanup combo points
if(GetComboTarget()==duel->opponent->GetGUID())
ClearComboPoints();
else if(GetComboTarget()==duel->opponent->GetPetGUID())
ClearComboPoints();
+
if(duel->opponent->GetComboTarget()==GetGUID())
duel->opponent->ClearComboPoints();
else if(duel->opponent->GetComboTarget()==GetPetGUID())
duel->opponent->ClearComboPoints();
+
// Honor points after duel (the winner) - ImpConfig
if(uint32 amount = sWorld.getConfig(CONFIG_HONOR_AFTER_DUEL))
duel->opponent->RewardHonor(NULL,1,amount);
+
//cleanups
SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
SetUInt32Value(PLAYER_DUEL_TEAM, 0);
duel->opponent->SetUInt64Value(PLAYER_DUEL_ARBITER, 0);
duel->opponent->SetUInt32Value(PLAYER_DUEL_TEAM, 0);
+
delete duel->opponent->duel;
duel->opponent->duel = NULL;
delete duel;
duel = NULL;
}
+
//---------------------------------------------------------//
+
void Player::_ApplyItemMods(Item *item, uint8 slot,bool apply)
{
if(slot >= INVENTORY_SLOT_BAG_END || !item)
return;
+
// not apply 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());
+
uint8 attacktype = Player::GetAttackBySlot(slot);
+
//check disarm only on mod apply to allow remove item mods
if (!CanUseAttackType(attacktype) )
return;
+
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, bool only_level_scale /*= false*/)
{
if (slot >= INVENTORY_SLOT_BAG_END || !proto)
return;
+
ScalingStatDistributionEntry const *ssd = proto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(proto->ScalingStatDistribution) : NULL;
if (only_level_scale && !ssd)
return;
+
// req. check at equip, but allow use for extended range if range limit max level, set proper level
uint32 ssd_level = getLevel();
if (ssd && ssd_level > ssd->MaxLevel)
ssd_level = ssd->MaxLevel;
+
ScalingStatValuesEntry const *ssv = proto->ScalingStatValue ? sScalingStatValuesStore.LookupEntry(ssd_level) : NULL;
if (only_level_scale && !ssv)
return;
+
for (uint8 i = 0; i < MAX_ITEM_PROTO_STATS; ++i)
{
uint32 statType = 0;
@@ -5794,8 +6935,10 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
statType = proto->ItemStat[i].ItemStatType;
val = proto->ItemStat[i].ItemStatValue;
}
+
if(val == 0)
continue;
+
switch (statType)
{
case ITEM_MOD_MANA:
@@ -5939,12 +7082,14 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
break;
}
}
+
// Apply Spell Power from ScalingStatValue if set
if(ssv)
{
if (int32 spellbonus = ssv->getSpellBonus(proto->ScalingStatValue))
ApplySpellPowerBonus(spellbonus, apply);
}
+
// If set ScalingStatValue armor get it or use item armor
uint32 armor = proto->Armor;
if (ssv)
@@ -5955,24 +7100,34 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
// Add armor bonus from ArmorDamageModifier if > 0
if (proto->ArmorDamageModifier > 0)
armor += uint32(proto->ArmorDamageModifier);
+
if (armor)
HandleStatModifier(UNIT_MOD_ARMOR, BASE_VALUE, float(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 ))
@@ -5983,6 +7138,7 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
{
attType = OFF_ATTACK;
}
+
float minDamage = proto->Damage[0].DamageMin;
float maxDamage = proto->Damage[0].DamageMax;
int32 extraDPS = 0;
@@ -6002,11 +7158,13 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
SetBaseWeaponDamage(attType, MINDAMAGE, damage);
//sLog.outError("applying mindam: assigning %f to weapon mindamage, now is: %f", damage, GetWeaponDamageRange(attType, MINDAMAGE));
}
+
if (maxDamage > 0 )
{
damage = apply ? maxDamage : BASE_MAXDAMAGE;
SetBaseWeaponDamage(attType, MAXDAMAGE, damage);
}
+
// Apply feral bonus from ScalingStatValue if set
if (ssv)
{
@@ -6020,8 +7178,10 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
if (feral_bonus > 0)
ApplyFeralAPBonus(feral_bonus, apply);
}
+
if(IsInFeralForm() || !CanUseAttackType(attType))
return;
+
if (proto->Delay)
{
if(slot == EQUIPMENT_SLOT_RANGED)
@@ -6031,26 +7191,32 @@ void Player::_ApplyItemBonuses(ItemPrototype const *proto, uint8 slot, bool appl
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)
{
AuraEffectList const& auraCritList = GetAurasByType(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
for(AuraEffectList::const_iterator itr = auraCritList.begin(); itr!=auraCritList.end();++itr)
_ApplyWeaponDependentAuraCritMod(item,attackType,*itr,apply);
+
AuraEffectList const& auraDamageFlatList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
for(AuraEffectList::const_iterator itr = auraDamageFlatList.begin(); itr!=auraDamageFlatList.end();++itr)
_ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
+
AuraEffectList const& auraDamagePCTList = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
for(AuraEffectList::const_iterator itr = auraDamagePCTList.begin(); itr!=auraDamagePCTList.end();++itr)
_ApplyWeaponDependentAuraDamageMod(item,attackType,*itr,apply);
}
+
void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply)
{
// generic not weapon specific case processes in aura code
if(aura->GetSpellProto()->EquippedItemClass == -1)
return;
+
BaseModGroup mod = BASEMOD_END;
switch(attackType)
{
@@ -6059,19 +7225,23 @@ void Player::_ApplyWeaponDependentAuraCritMod(Item *item, WeaponAttackType attac
case RANGED_ATTACK: mod = RANGED_CRIT_PERCENTAGE; break;
default: return;
}
+
if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
{
HandleBaseModValue(mod, FLAT_MOD, float (aura->GetAmount()), apply);
}
}
+
void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply)
{
// ignore spell mods for not wands
if((aura->GetMiscValue() & 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)
{
@@ -6080,6 +7250,7 @@ void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType att
case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
default: return;
}
+
UnitModifierType unitModType = TOTAL_VALUE;
switch(aura->GetAuraName())
{
@@ -6087,34 +7258,43 @@ void Player::_ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType att
case SPELL_AURA_MOD_DAMAGE_PERCENT_DONE: unitModType = TOTAL_PCT; break;
default: return;
}
+
if (item->IsFitToSpellRequirements(aura->GetSpellProto()))
{
HandleStatModifier(unitMod, unitModType, float(aura->GetAmount()),apply);
}
}
+
void Player::ApplyItemEquipSpell(Item *item, bool apply, bool form_change)
{
if(!item)
return;
+
ItemPrototype const *proto = item->GetProto();
if(!proto)
return;
+
for (uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++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)
@@ -6122,6 +7302,7 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply
// Cannot be used in this stance/form
if(GetErrorAtShapeshiftedCast(spellInfo, m_form) != SPELL_CAST_OK)
return;
+
if(form_change) // check aura active state from other form
{
AuraMap const& auras = GetAuras();
@@ -6129,7 +7310,9 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply
if(!item || itr->second->GetCastItemGUID()==item->GetGUID())
return;
}
+
DEBUG_LOG("WORLD: cast %s Equip spellId - %i", (item ? "item" : "itemset"), spellInfo->Id);
+
CastSpell(this,spellInfo,true,item);
}
else
@@ -6140,12 +7323,14 @@ void Player::ApplyEquipSpell(SpellEntry const* spellInfo, Item* item, bool apply
if(GetErrorAtShapeshiftedCast(spellInfo, m_form)==SPELL_CAST_OK)
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 (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; i++)
@@ -6156,17 +7341,20 @@ void Player::UpdateEquipSpellsAtFormChange()
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
}
@@ -6176,6 +7364,7 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
{
if(!target || !target->isAlive() || target == this)
return;
+
for(int i = EQUIPMENT_SLOT_START; i < EQUIPMENT_SLOT_END; i++)
{
// If usable, try to cast item spell
@@ -6208,6 +7397,7 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
}
}
}
+
void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item *item, ItemPrototype const * proto)
{
// Can do effect if any damage done to target
@@ -6217,22 +7407,28 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
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)
{
if(spellData.SpellId == 52781) // Persuasive Strike
@@ -6254,10 +7450,12 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
{
chance = GetWeaponProcChance();
}
+
if (roll_chance_f(chance))
CastSpell(target, spellInfo->Id, true, item);
}
}
+
// item combat enchantments
for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
{
@@ -6268,7 +7466,9 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
{
if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_COMBAT_SPELL)
continue;
+
SpellEnchantProcEntry const* entry = spellmgr.GetSpellEnchantProcEvent(enchant_id);
+
if (entry && entry->procEx)
{
// Check hit/crit/dodge/parry requirement
@@ -6282,13 +7482,16 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
//if (!(damageInfo->procVictim & PROC_FLAG_TAKEN_ANY_DAMAGE))
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 (entry)
{
if(entry->PPMChance)
@@ -6296,8 +7499,10 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
else if(entry->customChance)
chance = entry->customChance;
}
+
// Apply spell mods
ApplySpellMod(pEnchant->spellid[s],SPELLMOD_CHANCE_OF_SUCCESS,chance);
+
if (roll_chance_f(chance))
{
if(IsPositiveSpell(pEnchant->spellid[s]))
@@ -6308,6 +7513,7 @@ void Player::CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32
}
}
}
+
void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex)
{
ItemPrototype const* proto = item->GetProto();
@@ -6316,6 +7522,7 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c
{
uint32 learn_spell_id = proto->Spells[0].SpellId;
uint32 learning_spell_id = proto->Spells[1].SpellId;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(learn_spell_id);
if(!spellInfo)
{
@@ -6323,6 +7530,7 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c
SendEquipError(EQUIP_ERR_NONE,item,NULL);
return;
}
+
Spell *spell = new Spell(this, spellInfo,false);
spell->m_CastItem = item;
spell->m_cast_count = cast_count; //set count of casts
@@ -6330,31 +7538,39 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c
spell->prepare(&targets);
return;
}
+
// use triggered flag only for items with many spell casts and for not first cast
int count = 0;
+
// item spells casted at use
for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
{
_Spell const& spellData = proto->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("Player::CastItemUseSpell: Item (Entry: %u) in have wrong spell id %u, ignoring",proto->ItemId, spellData.SpellId);
continue;
}
+
Spell *spell = new Spell(this, spellInfo, (count > 0));
spell->m_CastItem = item;
spell->m_cast_count = cast_count; // set count of casts
spell->m_glyphIndex = glyphIndex; // glyph index
spell->prepare(&targets);
+
++count;
}
+
// Item enchantments spells casted at use
for(int e_slot = 0; e_slot < MAX_ENCHANTMENT_SLOT; ++e_slot)
{
@@ -6365,24 +7581,29 @@ void Player::CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 c
{
if(pEnchant->type[s]!=ITEM_ENCHANTMENT_TYPE_USE_SPELL)
continue;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(pEnchant->spellid[s]);
if (!spellInfo)
{
sLog.outError("Player::CastItemUseSpell Enchant %i, cast unknown spell %i", pEnchant->ID, pEnchant->spellid[s]);
continue;
}
+
Spell *spell = new Spell(this, spellInfo, (count > 0));
spell->m_CastItem = item;
spell->m_cast_count = cast_count; // set count of casts
spell->m_glyphIndex = glyphIndex; // glyph index
spell->prepare(&targets);
+
++count;
}
}
}
+
void Player::_RemoveAllItemMods()
{
sLog.outDebug("_RemoveAllItemMods start.");
+
for (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; i++)
{
if(m_items[i])
@@ -6390,15 +7611,19 @@ void Player::_RemoveAllItemMods()
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 (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; i++)
{
if(m_items[i])
@@ -6408,36 +7633,47 @@ void Player::_RemoveAllItemMods()
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 (uint8 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 (uint8 i = 0; i < INVENTORY_SLOT_BAG_END; i++)
{
if(m_items[i])
@@ -6445,17 +7681,22 @@ void Player::_ApplyAllItemMods()
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::_ApplyAllLevelScaleItemMods(bool apply)
{
for (int i = 0; i < INVENTORY_SLOT_BAG_END; ++i)
@@ -6464,42 +7705,54 @@ void Player::_ApplyAllLevelScaleItemMods(bool apply)
{
if(m_items[i]->IsBroken())
continue;
+
ItemPrototype const *proto = m_items[i]->GetProto();
if(!proto)
continue;
+
_ApplyItemBonuses(proto,i, apply, true);
}
}
}
+
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 + ammo_proto->Damage[0].DamageMax ) / 2;
+
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)
{
@@ -6515,14 +7768,17 @@ bool Player::CheckAmmoCompatibility(const ItemPrototype *ammo_proto) const
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)
{
@@ -6530,39 +7786,48 @@ void Player::RemovedInsignia(Player* looterPlr)
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(),true);
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);
}
+
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)
{
if (uint64 lguid = GetLootGUID())
m_session->DoLootRelease(lguid);
+
Loot *loot = 0;
PermissionTypes permission = ALL_PERMISSION;
+
sLog.outDebug("Player::SendLoot");
if (IS_GAMEOBJECT_GUID(guid))
{
sLog.outDebug(" IS_GAMEOBJECT_GUID(guid)");
GameObject *go = GetMap()->GetGameObject(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)))
@@ -6570,10 +7835,13 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
SendLootRelease(guid);
return;
}
+
loot = &go->loot;
+
if (go->getLootState() == GO_READY)
{
uint32 lootid = go->GetGOInfo()->GetLootId();
+
//TODO: fix this big hack
if((go->GetEntry() == BG_AV_OBJECTID_MINE_N || go->GetEntry() == BG_AV_OBJECTID_MINE_S))
if( BattleGround *bg = GetBattleGround())
@@ -6583,30 +7851,37 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
SendLootRelease(guid);
return;
}
+
if (lootid)
{
sLog.outDebug(" if(lootid)");
loot->clear();
loot->FillLoot(lootid, LootTemplates_Gameobject, this, false);
}
+
if (loot_type == LOOT_FISHING)
go->getFishLoot(loot,this);
+
go->SetLootState(GO_ACTIVATED);
}
}
else if (IS_ITEM_GUID(guid))
{
Item *item = GetItemByGuid( guid );
+
if (!item)
{
SendLootRelease(guid);
return;
}
+
loot = &item->loot;
+
if (!item->m_lootGenerated)
{
item->m_lootGenerated = true;
loot->clear();
+
switch(loot_type)
{
case LOOT_DISENCHANTING:
@@ -6628,12 +7903,15 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
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;
@@ -6646,32 +7924,39 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
// 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 = GetMap()->GetCreature(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, false);
+
// Generate extra money for pick pocket loot
const uint32 a = urand(0, creature->getLevel()/2);
const uint32 b = urand(0, getLevel()/2);
@@ -6687,21 +7972,27 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
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, false);
+
loot->generateMoneyLoot(creature->GetCreatureInfo()->mingold,creature->GetCreatureInfo()->maxgold);
+
if (Group* group = recipient->GetGroup())
{
group->UpdateLooterGuid(creature,true);
+
switch (group->GetLootMethod())
{
case GROUP_LOOT:
@@ -6719,6 +8010,7 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
}
}
}
+
// possible only if creature->lootForBody && loot->empty() at spell cast check
if (loot_type == LOOT_SKINNING)
{
@@ -6754,7 +8046,9 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
}
}
}
+
SetLootGUID(guid);
+
// LOOT_INSIGNIA and LOOT_FISHINGHOLE unsupported by client
switch(loot_type)
{
@@ -6762,30 +8056,39 @@ void Player::SendLoot(uint64 guid, LootType loot_type)
case LOOT_FISHINGHOLE: loot_type = LOOT_FISHING; break;
default: break;
}
+
// need know merged fishing/corpse loot type for achievements
loot->loot_type = loot_type;
+
WorldPacket data(SMSG_LOOT_RESPONSE, (9+50)); // we guess size
+
data << uint64(guid);
data << uint8(loot_type);
data << LootView(*loot, 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);
@@ -6793,6 +8096,7 @@ void Player::SendUpdateWorldState(uint32 Field, uint32 Value)
data << Value;
GetSession()->SendPacket(&data);
}
+
void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
{
// data depends on zoneid/mapid...
@@ -6800,7 +8104,9 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
uint16 NumberOfFields = 0;
uint32 mapid = GetMapId();
OutdoorPvP * pvp = sOutdoorPvPMgr.GetOutdoorPvPToZoneId(zoneid);
+
sLog.outDebug("Sending SMSG_INIT_WORLD_STATES to Map:%u, Zone: %u", mapid, zoneid);
+
// may be exist better way to do this...
switch(zoneid)
{
@@ -6863,6 +8169,7 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
NumberOfFields = 12;
break;
}
+
WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(NumberOfFields*8)));
data << uint32(mapid); // mapid
data << uint32(zoneid); // zone id
@@ -7169,25 +8476,31 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
data << uint32(2502) << uint32(0x0); // 11
data << uint32(2493) << uint32(0x0); // 12
data << uint32(2491) << uint32(0x0); // 13
+
data << uint32(2495) << uint32(0x0); // 14
data << uint32(2494) << uint32(0x0); // 15
data << uint32(2497) << uint32(0x0); // 16
+
data << uint32(2762) << uint32(0x0); // 17
data << uint32(2662) << uint32(0x0); // 18
data << uint32(2663) << uint32(0x0); // 19
data << uint32(2664) << uint32(0x0); // 20
+
data << uint32(2760) << uint32(0x0); // 21
data << uint32(2670) << uint32(0x0); // 22
data << uint32(2668) << uint32(0x0); // 23
data << uint32(2669) << uint32(0x0); // 24
+
data << uint32(2761) << uint32(0x0); // 25
data << uint32(2667) << uint32(0x0); // 26
data << uint32(2665) << uint32(0x0); // 27
data << uint32(2666) << uint32(0x0); // 28
+
data << uint32(2763) << uint32(0x0); // 29
data << uint32(2659) << uint32(0x0); // 30
data << uint32(2660) << uint32(0x0); // 31
data << uint32(2661) << uint32(0x0); // 32
+
data << uint32(2671) << uint32(0x0); // 33
data << uint32(2676) << uint32(0x0); // 34
data << uint32(2677) << uint32(0x0); // 35
@@ -7308,21 +8621,27 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
}
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));
@@ -7331,6 +8650,7 @@ void Player::SendTalentWipeConfirm(uint64 guid)
data << cost;
GetSession()->SendPacket( &data );
}
+
void Player::SendPetSkillWipeConfirm()
{
Pet* pet = GetPet();
@@ -7341,9 +8661,11 @@ void Player::SendPetSkillWipeConfirm()
data << uint32(pet->resetTalentsCost());
GetSession()->SendPacket( &data );
}
+
/*********************************************************/
/*** STORAGE SYSTEM ***/
/*********************************************************/
+
void Player::SetVirtualItemSlot( uint8 i, Item* item)
{
assert(i < 3);
@@ -7363,6 +8685,7 @@ void Player::SetVirtualItemSlot( uint8 i, Item* item)
}
}
}
+
void Player::SetSheath( SheathState sheathed )
{
switch (sheathed)
@@ -7391,9 +8714,11 @@ void Player::SetSheath( SheathState sheathed )
}
Unit::SetSheath(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;
@@ -7448,6 +8773,7 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap
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())
@@ -7519,6 +8845,7 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap
default :
return NULL_SLOT;
}
+
if( slot != NULL_SLOT )
{
if( swap || !GetItemByPos( INVENTORY_SLOT_BAG_0, slot ) )
@@ -7542,6 +8869,7 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap
return slots[i];
}
}
+
// if not found free and can swap return first appropriate from used
for (uint8 i = 0; i < 4; i++)
{
@@ -7549,14 +8877,18 @@ uint8 Player::FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool 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(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
{
pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
@@ -7611,9 +8943,11 @@ uint8 Player::CanUnequipItems( uint32 item, uint32 count ) const
}
}
}
+
// not found req. item count and have unequippable items
return res;
}
+
uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) const
{
uint32 count = 0;
@@ -7635,6 +8969,7 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons
if( pBag )
count += pBag->GetItemCount(item,skipItem);
}
+
if(skipItem && skipItem->GetProto()->GemProperties)
{
for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
@@ -7644,6 +8979,7 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons
count += pItem->GetGemCountWithID(item);
}
}
+
if(inBankAlso)
{
for(uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
@@ -7658,6 +8994,7 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons
if( pBag )
count += pBag->GetItemCount(item,skipItem);
}
+
if(skipItem && skipItem->GetProto()->GemProperties)
{
for(uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
@@ -7668,8 +9005,10 @@ uint32 Player::GetItemCount( uint32 item, bool inBankAlso, Item* skipItem ) cons
}
}
}
+
return count;
}
+
Item* Player::GetItemByGuid( uint64 guid ) const
{
for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_ITEM_END; i++)
@@ -7684,6 +9023,7 @@ Item* Player::GetItemByGuid( uint64 guid ) const
if( pItem && pItem->GetGUID() == guid )
return pItem;
}
+
for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
{
Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i );
@@ -7710,14 +9050,17 @@ Item* Player::GetItemByGuid( uint64 guid ) const
}
}
}
+
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 < CURRENCYTOKEN_SLOT_END )) )
@@ -7731,6 +9074,7 @@ Item* Player::GetItemByPos( uint8 bag, uint8 slot ) const
}
return NULL;
}
+
Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) const
{
uint16 slot;
@@ -7741,26 +9085,35 @@ Item* Player::GetWeaponForAttack(WeaponAttackType attackType, bool useable) cons
case RANGED_ATTACK: slot = EQUIPMENT_SLOT_RANGED; break;
default: return NULL;
}
+
Item* item = GetUseableItemByPos(INVENTORY_SLOT_BAG_0, slot);
if (!item || item->GetProto()->Class != ITEM_CLASS_WEAPON)
return NULL;
+
if(!useable)
return item;
+
if( item->IsBroken() || IsInFeralForm())
return NULL;
+
return item;
}
+
Item* Player::GetShield(bool useable) const
{
Item* item = GetUseableItemByPos(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;
}
+
uint8 Player::GetAttackBySlot( uint8 slot )
{
switch(slot)
@@ -7771,6 +9124,7 @@ uint8 Player::GetAttackBySlot( uint8 slot )
default: return MAX_ATTACK;
}
}
+
bool Player::IsInventoryPos( uint8 bag, uint8 slot )
{
if( bag == INVENTORY_SLOT_BAG_0 && slot == NULL_SLOT )
@@ -7783,6 +9137,7 @@ bool Player::IsInventoryPos( uint8 bag, uint8 slot )
return true;
return false;
}
+
bool Player::IsEquipmentPos( uint8 bag, uint8 slot )
{
if( bag == INVENTORY_SLOT_BAG_0 && ( slot < EQUIPMENT_SLOT_END ) )
@@ -7791,6 +9146,7 @@ bool Player::IsEquipmentPos( uint8 bag, uint8 slot )
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 ) )
@@ -7801,6 +9157,7 @@ bool Player::IsBankPos( uint8 bag, uint8 slot )
return true;
return false;
}
+
bool Player::IsBagPos( uint16 pos )
{
uint8 bag = pos >> 8;
@@ -7811,62 +9168,79 @@ bool Player::IsBagPos( uint16 pos )
return true;
return false;
}
+
bool Player::IsValidPos( uint8 bag, uint8 slot )
{
// post selected
if(bag == NULL_BAG)
return true;
+
if (bag == INVENTORY_SLOT_BAG_0)
{
// any post selected
if (slot == NULL_SLOT)
return true;
+
// equipment
if (slot < EQUIPMENT_SLOT_END)
return true;
+
// bag equip slots
if (slot >= INVENTORY_SLOT_BAG_START && slot < INVENTORY_SLOT_BAG_END)
return true;
+
// backpack slots
if (slot >= INVENTORY_SLOT_ITEM_START && slot < INVENTORY_SLOT_ITEM_END)
return true;
+
// keyring slots
if (slot >= KEYRING_SLOT_START && slot < KEYRING_SLOT_END)
return true;
+
// bank main slots
if (slot >= BANK_SLOT_ITEM_START && slot < BANK_SLOT_ITEM_END)
return true;
+
// bank bag slots
if (slot >= BANK_SLOT_BAG_START && slot < BANK_SLOT_BAG_END)
return true;
+
return false;
}
+
// bag content slots
if (bag >= INVENTORY_SLOT_BAG_START && bag < INVENTORY_SLOT_BAG_END)
{
Bag* pBag = (Bag*)GetItemByPos (INVENTORY_SLOT_BAG_0, bag);
if(!pBag)
return false;
+
// any post selected
if (slot == NULL_SLOT)
return true;
+
return slot < pBag->GetBagSize();
}
+
// bank bag content slots
if( bag >= BANK_SLOT_BAG_START && bag < BANK_SLOT_BAG_END )
{
Bag* pBag = (Bag*)GetItemByPos (INVENTORY_SLOT_BAG_0, bag);
if(!pBag)
return false;
+
// any post selected
if (slot == NULL_SLOT)
return true;
+
return slot < pBag->GetBagSize();
}
+
// where this?
return false;
}
+
bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
{
uint32 tempcount = 0;
@@ -7906,6 +9280,7 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
}
}
}
+
if(inBankAlso)
{
for(uint8 i = BANK_SLOT_ITEM_START; i < BANK_SLOT_ITEM_END; i++)
@@ -7935,8 +9310,10 @@ bool Player::HasItemCount( uint32 item, uint32 count, bool inBankAlso ) const
}
}
}
+
return false;
}
+
bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except_slot ) const
{
uint32 tempcount = 0;
@@ -7944,6 +9321,7 @@ bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except
{
if(i == except_slot)
continue;
+
Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
if( pItem && pItem->GetEntry() == item)
{
@@ -7952,6 +9330,7 @@ bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except
return true;
}
}
+
ItemPrototype const *pProto = objmgr.GetItemPrototype(item);
if (pProto && pProto->GemProperties)
{
@@ -7959,6 +9338,7 @@ bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except
{
if(i == except_slot)
continue;
+
Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
if( pItem && pItem->GetProto()->Socket[0].Color)
{
@@ -7968,8 +9348,10 @@ bool Player::HasItemOrGemWithIdEquipped( uint32 item, uint32 count, uint8 except
}
}
}
+
return false;
}
+
bool Player::HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32 count, uint8 except_slot ) const
{
uint32 tempcount = 0;
@@ -7977,18 +9359,22 @@ bool Player::HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32
{
if(i == except_slot)
continue;
+
Item *pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
if (!pItem)
continue;
+
ItemPrototype const *pProto = pItem->GetProto();
if (!pProto)
continue;
+
if (pProto->ItemLimitCategory == limitCategory)
{
tempcount += pItem->GetCount();
if( tempcount >= count )
return true;
}
+
if( pProto->Socket[0].Color)
{
tempcount += pItem->GetGemCountWithLimitCategory(limitCategory);
@@ -7996,8 +9382,10 @@ bool Player::HasItemOrGemWithLimitCategoryEquipped( uint32 limitCategory, uint32
return true;
}
}
+
return false;
}
+
uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem, uint32* no_space_count ) const
{
ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
@@ -8007,18 +9395,23 @@ uint8 Player::_CanTakeMoreSimilarItems(uint32 entry, uint32 count, Item* pItem,
*no_space_count = count;
return EQUIP_ERR_CANT_CARRY_MORE_OF_THIS;
}
+
// no maximum
if(pProto->MaxCount <= 0 || pProto->MaxCount == 2147483647)
return EQUIP_ERR_OK;
+
uint32 curcount = GetItemCount(pProto->ItemId,true,pItem);
+
if (curcount + count > uint32(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;
@@ -8048,13 +9441,17 @@ bool Player::HasItemTotemCategory( uint32 TotemCategory ) const
}
return false;
}
+
uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool swap, Item* pSrcItem ) const
{
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)
{
@@ -8063,9 +9460,11 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV
// 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;
+
// currencytoken case
if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END && !(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS))
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;
@@ -8075,14 +9474,18 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV
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 (slot >= pBagProto->ContainerSlots)
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->GetMaxStackSize();
}
@@ -8092,14 +9495,18 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV
// check item type
if (pItem2->GetEntry() != pProto->ItemId)
return EQUIP_ERR_ITEM_CANT_STACK;
+
// check free space
if (pItem2->GetCount() >= pProto->GetMaxStackSize())
return EQUIP_ERR_ITEM_CANT_STACK;
+
// free stack space or infinity
need_space = pProto->GetMaxStackSize() - pItem2->GetCount();
}
+
if (need_space > count)
need_space = count;
+
ItemPosCount newPosition = ItemPosCount((bag << 8) | slot, need_space);
if (!newPosition.isContainedIn(dest))
{
@@ -8108,34 +9515,44 @@ uint8 Player::_CanStoreItem_InSpecificSlot( uint8 bag, uint8 slot, ItemPosCountV
}
return EQUIP_ERR_OK;
}
+
uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, bool non_specialized, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
{
// 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 < pBag->GetBagSize(); 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->GetMaxStackSize())
@@ -8143,11 +9560,13 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy
uint32 need_space = pProto->GetMaxStackSize() - 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;
}
@@ -8158,11 +9577,13 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy
uint32 need_space = pProto->GetMaxStackSize();
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;
}
@@ -8170,6 +9591,7 @@ uint8 Player::_CanStoreItem_InBag( uint8 bag, ItemPosCountVec &dest, ItemPrototy
}
return EQUIP_ERR_OK;
}
+
uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end, ItemPosCountVec &dest, ItemPrototype const *pProto, uint32& count, bool merge, Item* pSrcItem, uint8 skip_bag, uint8 skip_slot ) const
{
for(uint32 j = slot_begin; j < slot_end; j++)
@@ -8177,13 +9599,17 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end,
// 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->GetMaxStackSize())
@@ -8196,6 +9622,7 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end,
{
dest.push_back(newPosition);
count -= need_space;
+
if (count==0)
return EQUIP_ERR_OK;
}
@@ -8206,11 +9633,13 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end,
uint32 need_space = pProto->GetMaxStackSize();
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;
}
@@ -8218,9 +9647,11 @@ uint8 Player::_CanStoreItem_InInventorySlots( uint8 slot_begin, uint8 slot_end,
}
return EQUIP_ERR_OK;
}
+
uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint32 entry, uint32 count, Item *pItem, bool swap, uint32* no_space_count ) const
{
sLog.outDebug( "STORAGE: CanStoreItem bag = %u, slot = %u, item = %u, count = %u", bag, slot, entry, count);
+
ItemPrototype const *pProto = objmgr.GetItemPrototype(entry);
if (!pProto)
{
@@ -8228,12 +9659,14 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*no_space_count = count;
return swap ? EQUIP_ERR_ITEMS_CANT_BE_SWAPPED :EQUIP_ERR_ITEM_NOT_FOUND;
}
+
if (pItem && pItem->IsBindedNotWith(this))
{
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);
@@ -8247,6 +9680,7 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
}
count -= no_similar_count;
}
+
// in specific slot
if (bag != NULL_BAG && slot != NULL_SLOT)
{
@@ -8257,16 +9691,20 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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 space for partly store only in specific slot
+
// in specific bag
if (bag != NULL_BAG)
{
@@ -8282,14 +9720,17 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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)
{
@@ -8297,10 +9738,12 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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;
@@ -8312,22 +9755,26 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
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
{
@@ -8342,14 +9789,17 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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(CURRENCYTOKEN_SLOT_START,CURRENCYTOKEN_SLOT_END,dest,pProto,count,false,pItem,bag,slot);
if (res!=EQUIP_ERR_OK)
{
@@ -8357,10 +9807,12 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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;
@@ -8375,15 +9827,18 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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)
{
@@ -8391,10 +9846,12 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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;
@@ -8405,23 +9862,28 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
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)
{
@@ -8432,14 +9894,17 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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)
{
@@ -8447,14 +9912,17 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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(uint32 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
@@ -8462,31 +9930,37 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
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(uint32 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)
{
@@ -8500,10 +9974,12 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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;
@@ -8518,30 +9994,36 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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(uint32 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)
@@ -8550,69 +10032,86 @@ uint8 Player::_CanStoreItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, uint3
*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(uint8 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];
int inv_tokens[CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_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));
memset(inv_tokens,0,sizeof(int)*(CURRENCYTOKEN_SLOT_END-CURRENCYTOKEN_SLOT_START));
+
for(uint8 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(uint8 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(uint8 i = CURRENCYTOKEN_SLOT_START; i < CURRENCYTOKEN_SLOT_END; i++)
{
pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, i );
+
if (pItem2 && !pItem2->IsInTrade())
{
inv_tokens[i-CURRENCYTOKEN_SLOT_START] = pItem2->GetCount();
}
}
+
for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
{
if(Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
@@ -8627,30 +10126,39 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
}
+
// check free space for all items
for (int k = 0; k < count; ++k)
{
Item *pItem = pItems[k];
+
// no item
if (!pItem) continue;
+
sLog.outDebug( "STORAGE: CanStoreItems %i. item = %u, count = %u", k+1, pItem->GetEntry(), pItem->GetCount());
ItemPrototype const *pProto = pItem->GetProto();
+
// strange item
if( !pProto )
return EQUIP_ERR_ITEM_NOT_FOUND;
+
// item it 'bind'
if(pItem->IsBindedNotWith(this))
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 );
@@ -8662,6 +10170,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
if (b_found) continue;
+
for(int t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t)
{
pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
@@ -8673,6 +10182,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
if (b_found) continue;
+
for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
{
pItem2 = GetItemByPos( INVENTORY_SLOT_BAG_0, t );
@@ -8684,6 +10194,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
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 );
@@ -8703,6 +10214,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
if (b_found) continue;
}
+
// special bag case
if( pProto->BagFamily )
{
@@ -8720,7 +10232,9 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
}
+
if (b_found) continue;
+
if(pProto->BagFamily & BAG_FAMILY_MASK_CURRENCY_TOKENS)
{
for(uint32 t = CURRENCYTOKEN_SLOT_START; t < CURRENCYTOKEN_SLOT_END; ++t)
@@ -8733,13 +10247,16 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
}
+
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) )
@@ -8758,6 +10275,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
if (b_found) continue;
}
+
// search free slot
bool b_found = false;
for(int t = INVENTORY_SLOT_ITEM_START; t < INVENTORY_SLOT_ITEM_END; ++t)
@@ -8770,6 +10288,7 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
if (b_found) continue;
+
// search free slot in bags
for(int t = INVENTORY_SLOT_BAG_START; !b_found && t < INVENTORY_SLOT_BAG_END; ++t)
{
@@ -8777,9 +10296,11 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
if( pBag )
{
pBagProto = pBag->GetProto();
+
// special bag already checked
if( pBagProto && (pBagProto->Class != ITEM_CLASS_CONTAINER || pBagProto->SubClass != ITEM_SUBCLASS_CONTAINER))
continue;
+
for(uint32 j = 0; j < pBag->GetBagSize(); j++)
{
if( inv_bags[t-INVENTORY_SLOT_BAG_START][j] == 0 )
@@ -8791,12 +10312,15 @@ uint8 Player::CanStoreItems( Item **pItems,int count) const
}
}
}
+
// 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, bool swap ) const
{
@@ -8808,8 +10332,10 @@ uint8 Player::CanEquipNewItem( uint8 slot, uint16 &dest, uint32 item, bool 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;
@@ -8821,10 +10347,12 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
{
if(pItem->IsBindedNotWith(this))
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;
+
// check this only in game
if(not_loading)
{
@@ -8832,6 +10360,7 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
// ROOT, CONFUSED, DISTRACTED, FLEEING this needs to be checked.
if (hasUnitState(UNIT_STAT_STUNNED))
return EQUIP_ERR_YOU_ARE_STUNNED;
+
// do not allow equipping gear except weapons, offhands, projectiles, relics in
// - combat
// - in-progress arenas
@@ -8839,30 +10368,38 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
{
if( isInCombat() )
return EQUIP_ERR_NOT_IN_COMBAT;
+
if(BattleGround* bg = GetBattleGround())
if( bg->isArena() && bg->GetStatus() == STATUS_IN_PROGRESS )
return EQUIP_ERR_NOT_DURING_ARENA_MATCH;
}
+
if(isInCombat()&& pProto->Class == ITEM_CLASS_WEAPON && m_weaponChangeTimer != 0)
return EQUIP_ERR_CANT_DO_RIGHT_NOW; // maybe exist better err
+
if(IsNonMeleeSpellCasted(false))
return EQUIP_ERR_CANT_DO_RIGHT_NOW;
}
+
ScalingStatDistributionEntry const *ssd = pProto->ScalingStatDistribution ? sScalingStatDistributionStore.LookupEntry(pProto->ScalingStatDistribution) : 0;
// check allowed level (extend range to upper values if MaxLevel more or equal max player level, this let GM set high level with 1...max range items)
if (ssd && ssd->MaxLevel < DEFAULT_MAX_LEVEL && ssd->MaxLevel < getLevel())
return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
+
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;
+
// if swap ignore item (equipped also)
if (uint8 res2 = CanEquipUniqueItem(pItem, swap ? eslot : NULL_SLOT))
return res2;
+
// check unique-equipped special item classes
if (pProto->Class == ITEM_CLASS_QUIVER)
{
@@ -8883,7 +10420,9 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
}
}
}
+
uint32 type = pProto->InventoryType;
+
if (eslot == EQUIPMENT_SLOT_OFFHAND)
{
if (type == INVTYPE_WEAPON || type == INVTYPE_WEAPONOFFHAND)
@@ -8896,9 +10435,11 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
if (!CanDualWield() || !CanTitanGrip())
return EQUIP_ERR_CANT_DUAL_WIELD;
}
+
if (IsTwoHandUsed())
return EQUIP_ERR_CANT_EQUIP_WITH_TWOHANDED;
}
+
// equip two-hand weapon case (with possible unequip 2 items)
if (type == INVTYPE_2HWEAPON)
{
@@ -8909,6 +10450,7 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
}
else if (eslot != EQUIPMENT_SLOT_MAINHAND)
return EQUIP_ERR_ITEM_CANT_BE_EQUIPPED;
+
if (!CanTitanGrip())
{
// offhand item must can be stored in inventory for offhand item and it also must be unequipped
@@ -8924,21 +10466,28 @@ uint8 Player::CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bo
return EQUIP_ERR_OK;
}
}
+
return !swap ? EQUIP_ERR_ITEM_NOT_FOUND : 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
@@ -8946,29 +10495,38 @@ uint8 Player::CanUnequipItem( uint16 pos, bool swap ) const
{
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(this))
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)
{
@@ -8976,18 +10534,24 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
{
if (!pItem->IsBag())
return EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT;
+
if (slot - BANK_SLOT_BAG_START >= GetBankBagSlotCount())
return EQUIP_ERR_MUST_PURCHASE_THAT_BAG_SLOT;
+
if (uint8 cantuse = CanUseItem( pItem, not_loading ) != EQUIP_ERR_OK)
return cantuse;
}
+
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 space for partly store only in specific slot
+
// in specific bag
if( bag != NULL_BAG )
{
@@ -8997,6 +10561,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
if( pBag && !pBag->IsEmpty() )
return EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG;
}
+
// search stack in bag for merge to
if( pProto->Stackable != 1 )
{
@@ -9005,6 +10570,7 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
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;
}
@@ -9013,18 +10579,22 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
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;
}
@@ -9033,13 +10603,17 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
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 space for partly store only in specific bag
+
// search stack for merge to
if( pProto->Stackable != 1 )
{
@@ -9047,8 +10621,10 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
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 )
{
@@ -9057,19 +10633,23 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
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(uint8 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 )
{
@@ -9078,68 +10658,87 @@ uint8 Player::CanBankItem( uint8 bag, uint8 slot, ItemPosCountVec &dest, Item *p
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(uint8 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(this))
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;
+
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 )
@@ -9159,6 +10758,7 @@ bool Player::CanUseItem( ItemPrototype const *pProto )
}
return false;
}
+
uint8 Player::CanUseAmmo( uint32 item ) const
{
sLog.outDebug( "STORAGE: CanUseAmmo item = %u", item);
@@ -9187,20 +10787,25 @@ uint8 Player::CanUseAmmo( uint32 item ) const
*/
if( getLevel() < pProto->RequiredLevel )
return EQUIP_ERR_CANT_EQUIP_LEVEL_I;
+
// Requires No Ammo
if(HasAura(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)
{
@@ -9211,22 +10816,29 @@ void Player::SetAmmo( uint32 item )
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 )
{
@@ -9237,64 +10849,81 @@ Item* Player::StoreNewItem( ItemPosCountVec const& dest, uint32 item, bool updat
}
return pItem;
}
+
Item* Player::StoreItem( ItemPosCountVec const& dest, Item* pItem, bool update )
{
if( !pItem )
return NULL;
+
Item* lastItem = pItem;
uint32 entry = pItem->GetEntry();
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);
}
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_OWN_ITEM, entry);
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, guid = %u", bag, slot, pItem->GetEntry(), count, pItem->GetGUIDLow());
+
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( 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 );
+
// need update known currency
if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END)
UpdateKnownCurrencies(pItem->GetEntry(), true);
+
if (IsInWorld() && update)
{
pItem->AddToWorld();
pItem->SendUpdateToPlayer( this );
}
+
pItem->SetState(ITEM_CHANGED, this);
}
else if (Bag *pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, bag ))
@@ -9308,8 +10937,10 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo
pItem->SetState(ITEM_CHANGED, this);
pBag->SetState(ITEM_CHANGED, this);
}
+
AddEnchantmentDurations(pItem);
AddItemDurations(pItem);
+
return pItem;
}
else
@@ -9318,9 +10949,11 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo
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)
@@ -9329,17 +10962,23 @@ Item* Player::_StoreItem( uint16 pos, Item *pItem, uint32 count, bool clone, boo
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, bool update )
{
if (Item *pItem = Item::CreateItem( item, 1, this ))
@@ -9347,36 +10986,49 @@ Item* Player::EquipNewItem( uint16 pos, uint32 item, bool update )
ItemAddedQuestCheck( item, 1 );
return EquipItem( pos, pItem, update );
}
+
return NULL;
}
+
Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
{
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)
{
uint32 cooldownSpell = SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_5s;
+
if (getClass() == CLASS_ROGUE)
cooldownSpell = SPELL_ID_WEAPON_SWITCH_COOLDOWN_1_0s;
+
SpellEntry const* spellProto = sSpellStore.LookupEntry(cooldownSpell);
+
if (!spellProto)
sLog.outError("Weapon switch cooldown spell %u couldn't be found in Spell.dbc", cooldownSpell);
else
{
m_weaponChangeTimer = spellProto->StartRecoveryTime;
+
WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4);
data << uint64(GetGUID());
data << uint8(1);
@@ -9386,17 +11038,23 @@ Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
}
}
}
+
if( IsInWorld() && update )
{
pItem->AddToWorld();
pItem->SendUpdateToPlayer( this );
}
+
ApplyEquipCooldown(pItem);
+
// update expertise and armor penetration - passive auras may need it
+
if( slot == EQUIPMENT_SLOT_MAINHAND )
UpdateExpertise(BASE_ATTACK);
+
else if( slot == EQUIPMENT_SLOT_OFFHAND )
UpdateExpertise(OFF_ATTACK);
+
switch(slot)
{
case EQUIPMENT_SLOT_MAINHAND:
@@ -9412,6 +11070,7 @@ Item* Player::EquipItem( uint16 pos, Item *pItem, bool update )
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 )
@@ -9419,34 +11078,45 @@ Item* Player::EquipItem( uint16 pos, Item *pItem, bool 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;
}
+
// only for full equip instead adding to stack
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
+
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 );
}
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_EQUIP_ITEM, pItem->GetEntry());
}
}
+
void Player::SetVisibleItemSlot(uint8 slot, Item *pItem)
{
if(pItem)
@@ -9461,49 +11131,63 @@ void Player::SetVisibleItemSlot(uint8 slot, Item *pItem)
SetUInt32Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (slot * 2), 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( 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, update expertise
if ( slot == EQUIPMENT_SLOT_MAINHAND )
{
@@ -9517,6 +11201,7 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_0);
pItem->ClearEnchantment(PROP_ENCHANTMENT_SLOT_1);
}
+
UpdateExpertise(BASE_ATTACK);
}
else if( slot == EQUIPMENT_SLOT_OFFHAND )
@@ -9536,8 +11221,10 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
// need update known currency
else if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END)
UpdateKnownCurrencies(pItem->GetEntry(), false);
+
m_items[slot] = NULL;
SetUInt64Value(PLAYER_FIELD_INV_SLOT_HEAD + (slot * 2), 0);
+
if ( slot < EQUIPMENT_SLOT_END )
SetVisibleItemSlot(slot, NULL);
}
@@ -9554,6 +11241,7 @@ void Player::RemoveItem( uint8 bag, uint8 slot, bool update )
pItem->SendUpdateToPlayer( this );
}
}
+
// 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)
{
@@ -9569,57 +11257,72 @@ void Player::MoveItemFromInventory(uint8 bag, uint8 slot, bool update)
}
}
}
+
// 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 already 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 (uint8 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());
+
RemoveEnchantmentDurations(pItem);
RemoveItemDurations(pItem);
+
ItemRemovedQuestCheck( pItem->GetEntry(), pItem->GetCount() );
+
if( bag == INVENTORY_SLOT_BAG_0 )
{
SetUInt64Value(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);
+
// update expertise and armor penetration - passive auras may need it
switch(slot)
{
@@ -9630,35 +11333,43 @@ void Player::DestroyItem( uint8 bag, uint8 slot, bool update )
default:
break;
}
+
if( slot == EQUIPMENT_SLOT_MAINHAND )
UpdateExpertise(BASE_ATTACK);
+
else if( slot == EQUIPMENT_SLOT_OFFHAND )
UpdateExpertise(OFF_ATTACK);
+
// equipment visual show
SetVisibleItemSlot(slot, NULL);
}
// need update known currency
else if (slot >= CURRENCYTOKEN_SLOT_START && slot < CURRENCYTOKEN_SLOT_END)
UpdateKnownCurrencies(pItem->GetEntry(), false);
+
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);
uint32 remcount = 0;
+
// in inventory
for(uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
{
@@ -9671,6 +11382,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
// all items in inventory can unequipped
remcount += pItem->GetCount();
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
if (remcount >= count)
return;
}
@@ -9686,6 +11398,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
}
}
}
+
for(uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
{
if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
@@ -9697,6 +11410,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
// all keys can be unequipped
remcount += pItem->GetCount();
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
if (remcount >= count)
return;
}
@@ -9712,6 +11426,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
}
}
}
+
// in inventory bags
for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
{
@@ -9728,6 +11443,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
{
remcount += pItem->GetCount();
DestroyItem( i, j, update );
+
if (remcount >= count)
return;
}
@@ -9745,6 +11461,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
}
}
}
+
// in equipment and bag list
for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
{
@@ -9758,6 +11475,7 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
{
remcount += pItem->GetCount();
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
if (remcount >= count)
return;
}
@@ -9775,18 +11493,22 @@ void Player::DestroyItemCount( uint32 item, uint32 count, bool update, bool uneq
}
}
}
+
void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone )
{
sLog.outDebug( "STORAGE: DestroyZoneLimitedItem in map %u and area %u", GetMapId(), new_zone );
+
// in inventory
for(uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
for(uint8 i = KEYRING_SLOT_START; i < CURRENCYTOKEN_SLOT_END; ++i)
if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
// in inventory bags
for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
@@ -9794,22 +11516,26 @@ void Player::DestroyZoneLimitedItem( bool update, uint32 new_zone )
if (Item* pItem = pBag->GetItemByPos(j))
if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
DestroyItem(i, j, update);
+
// in equipment and bag list
for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
if (pItem->IsLimitedToAnotherMapOrZone(GetMapId(), new_zone))
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
}
+
void Player::DestroyConjuredItems( bool update )
{
// used when entering arena
// destroys all conjured items
sLog.outDebug( "STORAGE: DestroyConjuredItems" );
+
// in inventory
for(uint8 i = INVENTORY_SLOT_ITEM_START; i < INVENTORY_SLOT_ITEM_END; i++)
if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
if (pItem->IsConjuredConsumable())
DestroyItem( INVENTORY_SLOT_BAG_0, i, update);
+
// in inventory bags
for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; i++)
if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
@@ -9817,20 +11543,25 @@ void Player::DestroyConjuredItems( bool update )
if (Item* pItem = pBag->GetItemByPos(j))
if (pItem->IsConjuredConsumable())
DestroyItem( i, j, update);
+
// in equipment and bag list
for(uint8 i = EQUIPMENT_SLOT_START; i < INVENTORY_SLOT_BAG_END; i++)
if (Item* pItem = GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
if (pItem->IsConjuredConsumable())
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
@@ -9843,36 +11574,43 @@ void Player::DestroyItemCount( Item* pItem, uint32 &count, bool update )
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 )
@@ -9880,10 +11618,12 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
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 )
@@ -9893,6 +11633,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
SendEquipError( msg, pSrcItem, NULL );
return;
}
+
if( IsInWorld() )
pSrcItem->SendUpdateToPlayer( this );
pSrcItem->SetState(ITEM_CHANGED, this);
@@ -9902,6 +11643,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
{
// 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 )
@@ -9911,6 +11653,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
SendEquipError( msg, pSrcItem, NULL );
return;
}
+
if( IsInWorld() )
pSrcItem->SendUpdateToPlayer( this );
pSrcItem->SetState(ITEM_CHANGED, this);
@@ -9920,6 +11663,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
{
// 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 )
@@ -9929,6 +11673,7 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
SendEquipError( msg, pSrcItem, NULL );
return;
}
+
if( IsInWorld() )
pSrcItem->SendUpdateToPlayer( this );
pSrcItem->SetState(ITEM_CHANGED, this);
@@ -9936,29 +11681,38 @@ void Player::SplitItem( uint16 src, uint16 dst, uint32 count )
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;
}
+
// SRC checks
+
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 ))
{
@@ -9970,13 +11724,16 @@ void Player::SwapItem( uint16 src, uint16 dst )
return;
}
}
+
// prevent put equipped/bank bag in self
if( IsBagPos ( src ) && srcslot == dstbag)
{
SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
return;
}
+
// DST checks
+
if (pDstItem)
{
if(pDstItem->m_lootGenerated) // prevent swap looting item
@@ -9985,6 +11742,7 @@ void Player::SwapItem( uint16 src, uint16 dst )
SendEquipError( EQUIP_ERR_CANT_DO_RIGHT_NOW, pDstItem, NULL );
return;
}
+
// check unequip potability for equipped items and bank bags
if(IsEquipmentPos ( dst ) || IsBagPos ( dst ))
{
@@ -9997,8 +11755,10 @@ void Player::SwapItem( uint16 src, uint16 dst )
}
}
}
+
// NOW this is or item move (swap with empty), or swap with another item (including bags in bag possitions)
// or swap empty bag with another empty or not empty bag (with items exchange)
+
// Move case
if( !pDstItem )
{
@@ -10011,6 +11771,7 @@ void Player::SwapItem( uint16 src, uint16 dst )
SendEquipError( msg, pSrcItem, NULL );
return;
}
+
RemoveItem(srcbag, srcslot, true);
StoreItem( dest, pSrcItem, true);
}
@@ -10023,6 +11784,7 @@ void Player::SwapItem( uint16 src, uint16 dst )
SendEquipError( msg, pSrcItem, NULL );
return;
}
+
RemoveItem(srcbag, srcslot, true);
BankItem( dest, pSrcItem, true);
}
@@ -10035,12 +11797,15 @@ void Player::SwapItem( uint16 src, uint16 dst )
SendEquipError( msg, pSrcItem, NULL );
return;
}
+
RemoveItem(srcbag, srcslot, true);
EquipItem(dest, pSrcItem, true);
AutoUnequipOffhandIfNeed();
}
+
return;
}
+
// attempt merge to / fill target item
if(!pSrcItem->IsBag() && !pDstItem->IsBag())
{
@@ -10055,12 +11820,14 @@ void Player::SwapItem( uint16 src, uint16 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()->GetMaxStackSize())
{
RemoveItem(srcbag, srcslot, true);
+
if( IsInventoryPos( dst ) )
StoreItem( sDest, pSrcItem, true);
else if( IsBankPos ( dst ) )
@@ -10086,8 +11853,10 @@ void Player::SwapItem( uint16 src, uint16 dst )
return;
}
}
+
// impossible merge/fill, do real swap
uint8 msg;
+
// check src->dest move possibility
ItemPosCountVec sDest;
uint16 eDest = 0;
@@ -10101,11 +11870,13 @@ void Player::SwapItem( uint16 src, uint16 dst )
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 = 0;
@@ -10119,11 +11890,13 @@ void Player::SwapItem( uint16 src, uint16 dst )
if( msg == EQUIP_ERR_OK )
msg = CanUnequipItem( eDest2, true);
}
+
if( msg != EQUIP_ERR_OK )
{
SendEquipError( msg, pDstItem, pSrcItem );
return;
}
+
// Check bag swap with item exchange (one from empty in not bag possition (equipped (not possible in fact) or store)
if(pSrcItem->IsBag() && pDstItem->IsBag())
{
@@ -10139,16 +11912,20 @@ void Player::SwapItem( uint16 src, uint16 dst )
emptyBag = (Bag*)pDstItem;
fullBag = (Bag*)pSrcItem;
}
+
// bag swap (with items exchange) case
if(emptyBag && fullBag)
{
ItemPrototype const* emotyProto = emptyBag->GetProto();
+
uint32 count = 0;
+
for(uint32 i=0; i < fullBag->GetBagSize(); ++i)
{
Item *bagItem = fullBag->GetItemByPos(i);
if (!bagItem)
continue;
+
ItemPrototype const* bagItemProto = bagItem->GetProto();
if (!bagItemProto || !ItemCanGoIntoBag(bagItemProto, emotyProto))
{
@@ -10156,15 +11933,18 @@ void Player::SwapItem( uint16 src, uint16 dst )
SendEquipError( EQUIP_ERR_NONEMPTY_BAG_OVER_OTHER_BAG, pSrcItem, pDstItem );
return;
}
+
++count;
}
+
if (count > emptyBag->GetBagSize())
{
// too small targeted bag
SendEquipError( EQUIP_ERR_ITEMS_CANT_BE_SWAPPED, pSrcItem, pDstItem );
return;
}
+
// Items swap
count = 0; // will pos in new bag
for(uint32 i = 0; i< fullBag->GetBagSize(); ++i)
@@ -10172,16 +11952,20 @@ void Player::SwapItem( uint16 src, uint16 dst )
Item *bagItem = fullBag->GetItemByPos(i);
if (!bagItem)
continue;
+
fullBag->RemoveItem(i, true);
emptyBag->StoreItem(count, bagItem, true);
bagItem->SetState(ITEM_CHANGED, this);
+
++count;
}
}
}
+
// now do moves, remove...
RemoveItem(dstbag, dstslot, false);
RemoveItem(srcbag, srcslot, false);
+
// add to dest
if (IsInventoryPos(dst))
StoreItem(sDest, pSrcItem, true);
@@ -10189,6 +11973,7 @@ void Player::SwapItem( uint16 src, uint16 dst )
BankItem(sDest, pSrcItem, true);
else if (IsEquipmentPos(dst))
EquipItem(eDest, pSrcItem, true);
+
// add to src
if (IsInventoryPos(src))
StoreItem(sDest2, pDstItem, true);
@@ -10196,8 +11981,10 @@ void Player::SwapItem( uint16 src, uint16 dst )
BankItem(sDest2, pDstItem, true);
else if (IsEquipmentPos(src))
EquipItem(eDest2, pDstItem, true);
+
AutoUnequipOffhandIfNeed();
}
+
void Player::AddItemToBuyBackSlot( Item *pItem )
{
if (pItem)
@@ -10208,6 +11995,7 @@ void Player::AddItemToBuyBackSlot( Item *pItem )
{
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
@@ -10216,33 +12004,41 @@ void Player::AddItemToBuyBackSlot( Item *pItem )
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() );
if (ItemPrototype const *pProto = pItem->GetProto())
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);
@@ -10250,6 +12046,7 @@ Item* Player::GetItemFromBuyBackSlot( uint32 slot )
return m_items[slot];
return NULL;
}
+
void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
{
sLog.outDebug( "STORAGE: RemoveItemFromBuyBackSlot slot = %u", slot);
@@ -10261,37 +12058,46 @@ void Player::RemoveItemFromBuyBackSlot( uint32 slot, bool del )
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 != EQUIP_ERR_OK)
{
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" );
@@ -10303,6 +12109,7 @@ void Player::SendBuyError( uint8 msg, Creature* pCreature, uint32 item, uint32 p
data << uint8(msg);
GetSession()->SendPacket(&data);
}
+
void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32 param )
{
sLog.outDebug( "WORLD: Sent SMSG_SELL_ITEM" );
@@ -10314,6 +12121,7 @@ void Player::SendSellError( uint8 msg, Creature* pCreature, uint64 guid, uint32
data << uint8(msg);
GetSession()->SendPacket(&data);
}
+
void Player::ClearTrade()
{
tradeGold = 0;
@@ -10321,6 +12129,7 @@ void Player::ClearTrade()
for(uint8 i = 0; i < TRADE_SLOT_COUNT; i++)
tradeItems[i] = NULL_SLOT;
}
+
void Player::TradeCancel(bool sendback)
{
if (pTrader)
@@ -10333,6 +12142,7 @@ void Player::TradeCancel(bool sendback)
ws = pTrader->GetSession();
if (!ws->PlayerLogout())
ws->SendCancelTrade();
+
// cleanup
ClearTrade();
pTrader->ClearTrade();
@@ -10341,19 +12151,24 @@ void Player::TradeCancel(bool sendback)
pTrader = NULL;
}
}
+
void Player::UpdateItemDuration(uint32 time, bool realtimeonly)
{
if (m_itemDuration.empty())
return;
+
sLog.outDebug("Player::UpdateItemDuration(%u,%u)", time, realtimeonly);
+
for(ItemDurationList::const_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)
@@ -10377,17 +12192,20 @@ void Player::UpdateEnchantTime(uint32 time)
}
}
}
+
void Player::AddEnchantmentDurations(Item *item)
{
for(int x = 0; x < MAX_ENCHANTMENT_SLOT; ++x)
{
if (!item->GetEnchantmentId(EnchantmentSlot(x)))
continue;
+
uint32 duration = item->GetEnchantmentDuration(EnchantmentSlot(x));
if (duration > 0)
AddEnchantmentDuration(item, EnchantmentSlot(x), duration);
}
}
+
void Player::RemoveEnchantmentDurations(Item *item)
{
for(EnchantDurationList::iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end();)
@@ -10402,6 +12220,7 @@ void Player::RemoveEnchantmentDurations(Item *item)
++itr;
}
}
+
void Player::RemoveArenaEnchantments(EnchantmentSlot slot)
{
// remove enchantments from equipped items first to clean up the m_enchantDuration list
@@ -10429,6 +12248,7 @@ void Player::RemoveArenaEnchantments(EnchantmentSlot slot)
else
++next;
}
+
// remove enchants from inventory items
// NOTE: no need to remove these from stats, since these aren't equipped
// in inventory
@@ -10436,6 +12256,7 @@ void Player::RemoveArenaEnchantments(EnchantmentSlot slot)
if (Item* pItem = GetItemByPos(INVENTORY_SLOT_BAG_0, i))
if (pItem->GetEnchantmentId(slot))
pItem->ClearEnchantment(slot);
+
// in inventory bags
for(uint8 i = INVENTORY_SLOT_BAG_START; i < INVENTORY_SLOT_BAG_END; ++i)
if (Bag* pBag = (Bag*)GetItemByPos( INVENTORY_SLOT_BAG_0, i ))
@@ -10444,13 +12265,16 @@ void Player::RemoveArenaEnchantments(EnchantmentSlot slot)
if (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)
@@ -10466,27 +12290,35 @@ void Player::AddEnchantmentDuration(Item *item,EnchantmentSlot slot,uint32 durat
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;
+
if (!item->IsBroken())
{
for (int s = 0; s < 3; ++s)
@@ -10494,6 +12326,7 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool
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:
@@ -10558,6 +12391,7 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool
}
}
}
+
HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + enchant_spell_id), TOTAL_VALUE, float(enchant_amount), apply);
break;
case ITEM_ENCHANTMENT_TYPE_STAT:
@@ -10577,6 +12411,7 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool
}
}
}
+
sLog.outDebug("Adding %u to stat nb %u",enchant_amount,enchant_spell_id);
switch (enchant_spell_id)
{
@@ -10783,12 +12618,15 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool
} /*switch(enchant_display_type)*/
} /*for*/
}
+
// visualize enchantment at player and equipped items
if(slot == PERM_ENCHANTMENT_SLOT)
SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 0, apply ? item->GetEnchantmentId(slot) : 0);
+
if(slot == TEMP_ENCHANTMENT_SLOT)
SetUInt16Value(PLAYER_VISIBLE_ITEM_1_ENCHANTMENT + (item->GetSlot() * 2), 1, apply ? item->GetEnchantmentId(slot) : 0);
+
if(apply_dur)
{
if(apply)
@@ -10805,6 +12643,7 @@ void Player::ApplyEnchantment(Item *item, EnchantmentSlot slot, bool apply, bool
}
}
}
+
void Player::SendEnchantmentDurations()
{
for(EnchantDurationList::const_iterator itr = m_enchantDuration.begin(); itr != m_enchantDuration.end(); ++itr)
@@ -10812,6 +12651,7 @@ void Player::SendEnchantmentDurations()
GetSession()->SendItemEnchantTimeUpdate(GetGUID(), itr->item->GetGUID(), itr->slot, uint32(itr->leftduration) / 1000);
}
}
+
void Player::SendItemDurations()
{
for(ItemDurationList::const_iterator itr = m_itemDuration.begin(); itr != m_itemDuration.end(); ++itr)
@@ -10819,10 +12659,12 @@ void Player::SendItemDurations()
(*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 << uint64(GetGUID()); // player GUID
@@ -10837,19 +12679,23 @@ void Player::SendNewItem(Item *item, uint32 count, bool received, bool created,
data << uint32(item->GetItemRandomPropertyId()); // random item property id
data << uint32(count); // count of items
data << uint32(GetItemCount(item->GetEntry())); // count of items in inventory
+
if (broadcast && GetGroup())
GetGroup()->BroadcastPacket(&data, true);
else
GetSession()->SendPacket(&data);
}
+
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
+
void Player::PrepareQuestMenu( uint64 guid )
{
Object *pObject;
QuestRelations* pObjectQR;
QuestRelations* pObjectQIR;
+
// pets also can have quests
Creature *pCreature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, guid);
if( pCreature )
@@ -10874,8 +12720,10 @@ void Player::PrepareQuestMenu( uint64 guid )
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;
@@ -10887,31 +12735,39 @@ void Player::PrepareQuestMenu( uint64 guid )
//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_UNK2);
else if ( status == QUEST_STATUS_NONE && CanTakeQuest( pQuest, false ) )
qm.AddMenuItem(quest_id, DIALOG_STATUS_CHAT);
}
}
+
void Player::SendPreparedQuest(uint64 guid)
{
QuestMenu& questMenu = PlayerTalkClass->GetQuestMenu();
if (questMenu.Empty())
return;
+
QuestMenuItem const& qmi0 = questMenu.GetItem(0);
+
uint32 status = qmi0.m_qIcon;
+
// single element case
if (questMenu.MenuItemCount() == 1)
{
// Auto open -- maybe also should verify there is no greeting
uint32 quest_id = qmi0.m_qId;
Quest const* pQuest = objmgr.GetQuestTemplate(quest_id);
+
if (pQuest)
{
if (status == DIALOG_STATUS_UNK2 && !GetQuestRewardStatus(quest_id))
@@ -10933,6 +12789,7 @@ void Player::SendPreparedQuest(uint64 guid)
qe._Delay = 0;
qe._Emote = 0;
std::string title = "";
+
// need pet case for some quests
Creature *pCreature = ObjectAccessor::GetCreatureOrPetOrVehicle(*this,guid);
if (pCreature)
@@ -10948,9 +12805,11 @@ void Player::SendPreparedQuest(uint64 guid)
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)
{
@@ -10965,6 +12824,7 @@ void Player::SendPreparedQuest(uint64 guid)
else
{
title = gossiptext->Options[0].Text_1;
+
int loc_idx = GetSession()->GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -10981,16 +12841,20 @@ void Player::SendPreparedQuest(uint64 guid)
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::GetCreatureOrPetOrVehicle(*this,guid);
if( pCreature )
{
@@ -11014,14 +12878,17 @@ Quest const * Player::GetNextQuest( uint64 guid, Quest const *pQuest )
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 ) &&
@@ -11031,8 +12898,10 @@ bool Player::CanSeeStartQuest( Quest const *pQuest )
{
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 )
@@ -11042,16 +12911,19 @@ bool Player::CanTakeQuest( Quest const *pQuest, bool 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 msg2 = 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( msg2 == EQUIP_ERR_CANT_CARRY_MORE_OF_THIS )
return true;
@@ -11063,6 +12935,7 @@ bool Player::CanAddQuest( Quest const *pQuest, bool msg )
}
return true;
}
+
bool Player::CanCompleteQuest( uint32 quest_id )
{
if( quest_id )
@@ -11070,14 +12943,19 @@ bool Player::CanCompleteQuest( uint32 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_TRINITY_FLAGS_DELIVER ) )
{
for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++)
@@ -11086,33 +12964,41 @@ bool Player::CanCompleteQuest( uint32 quest_id )
return false;
}
}
+
if ( qInfo->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO) )
{
for(uint8 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_TRINITY_FLAGS_EXPLORATION_OR_EVENT ) && !q_status.m_explored )
return false;
+
if ( qInfo->HasFlag( QUEST_TRINITY_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 && GetReputationMgr().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.
@@ -11120,25 +13006,32 @@ bool Player::CanCompleteRepeatableQuest( Quest const *pQuest )
// Seem that all repeatable quest are DELIVER Flag so, no need to add more.
if( !CanTakeQuest(pQuest, false) )
return false;
+
if (pQuest->HasFlag( QUEST_TRINITY_FLAGS_DELIVER) )
for(uint8 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 (25 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_TRINITY_FLAGS_DELIVER ) )
{
@@ -11153,16 +13046,20 @@ bool Player::CanRewardQuest( Quest const *pQuest, bool msg )
}
}
}
+
// 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] )
@@ -11176,6 +13073,7 @@ bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
}
}
}
+
if ( pQuest->GetRewItemsCount() > 0 )
{
for (uint32 i = 0; i < pQuest->GetRewItemsCount(); ++i)
@@ -11192,73 +13090,94 @@ bool Player::CanRewardQuest( Quest const *pQuest, uint32 reward, bool msg )
}
}
}
+
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];
+
// check for repeatable quests status reset
questStatusData.m_status = QUEST_STATUS_INCOMPLETE;
questStatusData.m_explored = false;
+
if ( pQuest->HasFlag( QUEST_TRINITY_FLAGS_DELIVER ) )
{
for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
questStatusData.m_itemcount[i] = 0;
}
+
if ( pQuest->HasFlag(QUEST_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO) )
{
for(uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
questStatusData.m_creatureOrGOcount[i] = 0;
}
+
GiveQuestSourceItem( pQuest );
AdjustQuestReqItemCount( pQuest, questStatusData );
+
if( pQuest->GetRepObjectiveFaction() )
if(FactionEntry const* factionEntry = sFactionStore.LookupEntry(pQuest->GetRepObjectiveFaction()))
GetReputationMgr().SetVisible(factionEntry);
+
uint32 qtime = 0;
if( pQuest->HasFlag( QUEST_TRINITY_FLAGS_TIMED ) )
{
uint32 limittime = pQuest->GetLimitTime();
+
// shared timed quest
if(questGiver && questGiver->GetTypeId()==TYPEID_PLAYER)
limittime = ((Player*)questGiver)->getQuestStatusMap()[quest_id].m_timer / IN_MILISECONDS;
+
AddTimedQuest( quest_id );
questStatusData.m_timer = limittime * IN_MILISECONDS;
qtime = static_cast<uint32>(time(NULL)) + limittime;
}
else
questStatusData.m_timer = 0;
+
SetQuestSlot(log_slot, quest_id, qtime);
+
if (questStatusData.uState != QUEST_NEW)
questStatusData.uState = QUEST_CHANGED;
+
//starting initial quest script
if(questGiver && pQuest->GetQuestStartScript()!=0)
GetMap()->ScriptsStart(sQuestStartScripts, pQuest->GetQuestStartScript(), questGiver, this);
+
// Some spells applied at quest activation
SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,true);
if(saBounds.first != saBounds.second)
{
uint32 zone, area;
GetZoneAndAreaId(zone,area);
+
for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area))
if( !HasAura(itr->second->spellId) )
CastSpell(this,itr->second->spellId,true);
}
+
UpdateForQuestWorldObjects();
}
+
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) )
@@ -11268,28 +13187,35 @@ void Player::CompleteQuest( uint32 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 )
{
//this THING should be here to protect code from quest, which cast on player far teleport as a reward
//should work fine, cause far teleport will be executed in Player::Update()
SetCanDelayTeleport(true);
+
uint32 quest_id = pQuest->GetQuestId();
+
for (uint8 i = 0; i < QUEST_OBJECTIVES_COUNT; i++ )
{
if (pQuest->ReqItemId[i])
DestroyItemCount( pQuest->ReqItemId[i], pQuest->ReqItemCount[i], true);
}
+
TakeQuestSourceItem(quest_id, true); // take quest src item from player on completing quest
+
for(uint8 i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
{
if (pQuest->ReqSourceId[i])
@@ -11298,7 +13224,9 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
DestroyItemCount(pQuest->ReqSourceId[i], count ? count : 9999, true);
}
}
+
RemoveTimedQuest(quest_id);
+
if (pQuest->GetRewChoiceItemsCount() > 0)
{
if (uint32 itemId = pQuest->RewChoiceItemId[reward])
@@ -11311,6 +13239,7 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
}
}
}
+
if (pQuest->GetRewItemsCount() > 0)
{
for (uint32 i=0; i < pQuest->GetRewItemsCount(); ++i)
@@ -11326,17 +13255,23 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
}
}
}
+
RewardReputation( pQuest );
+
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));
+
// handle SPELL_AURA_MOD_XP_QUEST_PCT auras
Unit::AuraEffectList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_QUEST_PCT);
for(Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
XP = uint32(XP*(1.0f + (*i)->GetAmount() / 100.0f));
+
if (getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
GiveXP( XP , NULL );
else
@@ -11345,27 +13280,33 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
ModifyMoney( money );
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, money);
}
+
// Give player extra money if GetRewOrReqMoney > 0 and get ReqMoney if negative
if (pQuest->GetRewOrReqMoney())
{
ModifyMoney( pQuest->GetRewOrReqMoney() );
+
if (pQuest->GetRewOrReqMoney() > 0)
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_MONEY_FROM_QUEST_REWARD, pQuest->GetRewOrReqMoney());
}
+
// honor reward
if (pQuest->GetRewHonorableKills())
RewardHonor(NULL, 0, MaNGOS::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills()));
+
// title reward
if (pQuest->GetCharTitleId())
{
if (CharTitlesEntry const* titleEntry = sCharTitlesStore.LookupEntry(pQuest->GetCharTitleId()))
SetTitle(titleEntry);
}
+
if (pQuest->GetBonusTalents())
{
m_questRewardTalentCount+=pQuest->GetBonusTalents();
InitTalentForLevel();
}
+
// Send reward mail
if (pQuest->GetRewMailTemplateId())
{
@@ -11394,10 +13335,14 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
senderGuidOrEntry = GetGUIDLow();
break;
}
+
Loot questMailLoot;
+
questMailLoot.FillLoot(pQuest->GetQuestId(), LootTemplates_QuestMail, this,true);
+
// fill mail
MailItemsInfo mi; // item list preparing
+
uint32 max_slot = questMailLoot.GetMaxSlotInLootFor(this);
for(uint32 i = 0; mi.size() < MAX_MAIL_ITEMS && i < max_slot; ++i)
{
@@ -11410,87 +13355,110 @@ void Player::RewardQuest( Quest const *pQuest, uint32 reward, Object* questGiver
}
}
}
+
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);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_DAILY_QUEST, 1);
}
+
if (!pQuest->IsRepeatable())
SetQuestStatus(quest_id, QUEST_STATUS_COMPLETE);
else
SetQuestStatus(quest_id, QUEST_STATUS_NONE);
+
q_status.m_rewarded = true;
if (q_status.uState != QUEST_NEW)
q_status.uState = QUEST_CHANGED;
+
if (announce)
SendQuestReward( pQuest, XP, questGiver );
+
// cast spells after mark quest complete (some spells have quest completed state reqyurements in spell_area data)
if (pQuest->GetRewSpellCast() > 0)
CastSpell( this, pQuest->GetRewSpellCast(), true);
else if ( pQuest->GetRewSpell() > 0)
CastSpell( this, pQuest->GetRewSpell(), true);
+
if (pQuest->GetZoneOrSort() > 0)
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUESTS_IN_ZONE, pQuest->GetZoneOrSort());
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST_COUNT);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_COMPLETE_QUEST, pQuest->GetQuestId());
+
uint32 zone = 0;
uint32 area = 0;
+
// remove auras from spells with quest reward state limitations
SpellAreaForQuestMapBounds saEndBounds = spellmgr.GetSpellAreaForQuestEndMapBounds(quest_id);
if(saEndBounds.first != saEndBounds.second)
{
GetZoneAndAreaId(zone,area);
+
for(SpellAreaForAreaMap::const_iterator itr = saEndBounds.first; itr != saEndBounds.second; ++itr)
if(!itr->second->IsFitToRequirements(this,zone,area))
RemoveAurasDueToSpell(itr->second->spellId);
}
+
// Some spells applied at quest reward
SpellAreaForQuestMapBounds saBounds = spellmgr.GetSpellAreaForQuestMapBounds(quest_id,false);
if(saBounds.first != saBounds.second)
{
if(!zone || !area)
GetZoneAndAreaId(zone,area);
+
for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
if(itr->second->autocast && itr->second->IsFitToRequirements(this,zone,area))
if( !HasAura(itr->second->spellId) )
CastSpell(this,itr->second->spellId,true);
}
+
//lets remove flag for delayed teleports
SetCanDelayTeleport(false);
}
+
void Player::FailQuest(uint32 questId)
{
if (Quest const* pQuest = objmgr.GetQuestTemplate(questId))
{
SetQuestStatus(questId, QUEST_STATUS_FAILED);
+
uint16 log_slot = FindQuestSlot(questId);
+
if (log_slot < MAX_QUEST_LOG_SIZE)
{
SetQuestSlotTimer(log_slot, 1);
SetQuestSlotState(log_slot, QUEST_STATE_FAIL);
}
+
if (pQuest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
{
QuestStatusData& q_status = mQuestStatus[questId];
+
RemoveTimedQuest(questId);
q_status.m_timer = 0;
+
SendQuestTimerFailed(questId);
}
else
SendQuestFailed(questId);
}
}
+
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)
{
@@ -11498,6 +13466,7 @@ bool Player::SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg )
SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
return false;
}
+
// check class
if( skillOrClass < 0 )
{
@@ -11520,8 +13489,10 @@ bool Player::SatisfyQuestSkillOrClass( Quest const* qInfo, bool msg )
return false;
}
}
+
return true;
}
+
bool Player::SatisfyQuestLevel( Quest const* qInfo, bool msg )
{
if( getLevel() < qInfo->GetMinLevel() )
@@ -11532,11 +13503,13 @@ bool Player::SatisfyQuestLevel( Quest const* qInfo, bool msg )
}
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 );
@@ -11545,16 +13518,20 @@ bool Player::SatisfyQuestLog( bool msg )
}
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
@@ -11563,18 +13540,24 @@ bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
// 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 completed and rewarded
ObjectMgr::ExclusiveQuestGroups::iterator iter2 = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
+
assert(iter2!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
+
for(; iter2 != end; ++iter2)
{
uint32 exclude_Id = iter2->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 )
{
@@ -11592,18 +13575,24 @@ bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
// 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 iter2 = objmgr.mExclusiveQuestGroups.lower_bound(qPrevInfo->GetExclusiveGroup());
ObjectMgr::ExclusiveQuestGroups::iterator end = objmgr.mExclusiveQuestGroups.upper_bound(qPrevInfo->GetExclusiveGroup());
+
assert(iter2!=end); // always must be found if qPrevInfo->ExclusiveGroup != 0
+
for(; iter2 != end; ++iter2)
{
uint32 exclude_Id = iter2->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 &&
@@ -11618,12 +13607,15 @@ bool Player::SatisfyQuestPreviousQuest( Quest const* qInfo, bool msg )
}
}
}
+
// 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();
@@ -11637,6 +13629,7 @@ bool Player::SatisfyQuestRace( Quest const* qInfo, bool msg )
}
return true;
}
+
bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg )
{
uint32 fIdMin = qInfo->GetRequiredMinRepFaction(); //Min required rep
@@ -11646,6 +13639,7 @@ bool Player::SatisfyQuestReputation( Quest const* qInfo, bool msg )
SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
return false;
}
+
uint32 fIdMax = qInfo->GetRequiredMaxRepFaction(); //Max required rep
if(fIdMax && GetReputationMgr().GetReputation(fIdMax) >= qInfo->GetRequiredMaxRepValue())
{
@@ -11653,8 +13647,10 @@ bool Player::SatisfyQuestReputation( Quest const* qInfo, bool 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() );
@@ -11666,6 +13662,7 @@ bool Player::SatisfyQuestStatus( Quest const* qInfo, bool msg )
}
return true;
}
+
bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg)
{
if (m_timedquests.empty() && qInfo->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
@@ -11676,20 +13673,26 @@ bool Player::SatisfyQuestTimed(Quest const* qInfo, bool msg)
}
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;
+
// not allow have daily quest if daily quest from exclusive group already recently completed
Quest const* Nquest = objmgr.GetQuestTemplate(exclude_Id);
if( !SatisfyQuestDay(Nquest, false) )
@@ -11698,7 +13701,9 @@ bool Player::SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg )
SendCanTakeQuestResponse( INVALIDREASON_DONT_HAVE_REQ );
return false;
}
+
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) )
@@ -11710,10 +13715,12 @@ bool Player::SatisfyQuestExclusiveGroup( Quest const* qInfo, bool msg )
}
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()
@@ -11723,20 +13730,25 @@ bool Player::SatisfyQuestNextChain( Quest const* qInfo, bool 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
@@ -11748,35 +13760,43 @@ bool Player::SatisfyQuestPrevChain( Quest const* qInfo, bool msg )
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();
@@ -11785,6 +13805,7 @@ bool Player::GiveQuestSourceItem( Quest const *pQuest )
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 )
@@ -11800,8 +13821,10 @@ bool Player::GiveQuestSourceItem( Quest const *pQuest )
SendEquipError( msg, NULL, NULL );
return false;
}
+
return true;
}
+
bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
{
Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
@@ -11813,6 +13836,7 @@ bool Player::TakeQuestSourceItem( uint32 quest_id, bool msg )
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);
@@ -11822,11 +13846,13 @@ bool Player::TakeQuestSourceItem( uint32 quest_id, bool 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);
@@ -11837,10 +13863,12 @@ bool Player::GetQuestRewardStatus( uint32 quest_id ) const
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 )
@@ -11851,6 +13879,7 @@ QuestStatus Player::GetQuestStatus( uint32 quest_id ) const
}
return QUEST_STATUS_NONE;
}
+
bool Player::CanShareQuest(uint32 quest_id) const
{
Quest const* qInfo = objmgr.GetQuestTemplate(quest_id);
@@ -11862,28 +13891,36 @@ bool Player::CanShareQuest(uint32 quest_id) const
}
return false;
}
+
void Player::SetQuestStatus(uint32 quest_id, QuestStatus status)
{
if (Quest const* qInfo = objmgr.GetQuestTemplate(quest_id))
{
QuestStatusData& q_status = mQuestStatus[quest_id];
+
q_status.m_status = status;
+
if (q_status.uState != QUEST_NEW)
q_status.uState = QUEST_CHANGED;
}
+
UpdateForQuestWorldObjects();
}
+
// not used in TrinIty, 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, QuestStatusData& questStatusData )
{
if ( pQuest->HasFlag( QUEST_TRINITY_FLAGS_DELIVER ) )
@@ -11894,19 +13931,23 @@ void Player::AdjustQuestReqItemCount( Quest const* pQuest, QuestStatusData& ques
if( reqitemcount != 0 )
{
uint32 curitemcount = GetItemCount(pQuest->ReqItemId[i],true);
+
questStatusData.m_itemcount[i] = std::min(curitemcount, reqitemcount);
if (questStatusData.uState != QUEST_NEW) questStatusData.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 )
@@ -11915,6 +13956,7 @@ void Player::AreaExploredOrEventHappens( uint32 questId )
if( log_slot < MAX_QUEST_LOG_SIZE)
{
QuestStatusData& q_status = mQuestStatus[questId];
+
if(!q_status.m_explored)
{
q_status.m_explored = true;
@@ -11926,6 +13968,7 @@ void Player::AreaExploredOrEventHappens( uint32 questId )
CompleteQuest( questId );
}
}
+
//not used in Trinityd, function for external script library
void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject )
{
@@ -11934,6 +13977,7 @@ void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject
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);
@@ -11942,6 +13986,7 @@ void Player::GroupEventHappens( uint32 questId, WorldObject const* pEventObject
else
AreaExploredOrEventHappens(questId);
}
+
void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
{
for( uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
@@ -11949,12 +13994,16 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
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_TRINITY_FLAGS_DELIVER ) )
continue;
+
for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
{
uint32 reqitem = qInfo->ReqItemId[j];
@@ -11967,6 +14016,7 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
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 ) )
@@ -11977,6 +14027,7 @@ void Player::ItemAddedQuestCheck( uint32 entry, uint32 count )
}
UpdateForQuestWorldObjects();
}
+
void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
{
for( uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
@@ -11989,12 +14040,14 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
continue;
if( !qInfo->HasFlag( QUEST_TRINITY_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 )
@@ -12006,6 +14059,7 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 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;
@@ -12014,14 +14068,17 @@ void Player::ItemRemovedQuestCheck( uint32 entry, uint32 count )
}
UpdateForQuestWorldObjects();
}
+
void Player::KilledMonster( CreatureInfo const* cInfo, uint64 guid )
{
if(cInfo->Entry)
KilledMonsterCredit(cInfo->Entry,guid);
+
for(int i = 0; i < MAX_KILL_CREDIT; ++i)
if(cInfo->KillCredit[i])
KilledMonsterCredit(cInfo->KillCredit[i],guid);
}
+
void Player::KilledMonsterCredit( uint32 entry, uint64 guid )
{
uint32 addkillcount = 1;
@@ -12031,6 +14088,7 @@ void Player::KilledMonsterCredit( uint32 entry, uint64 guid )
uint32 questid = GetQuestSlotQuestId(i);
if(!questid)
continue;
+
Quest const* qInfo = objmgr.GetQuestTemplate(questid);
if( !qInfo )
continue;
@@ -12045,10 +14103,13 @@ void Player::KilledMonsterCredit( uint32 entry, uint64 guid )
// 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];
@@ -12057,10 +14118,12 @@ void Player::KilledMonsterCredit( uint32 entry, uint64 guid )
{
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;
}
@@ -12069,19 +14132,24 @@ void Player::KilledMonsterCredit( uint32 entry, uint64 guid )
}
}
}
+
void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
{
bool isCreature = IS_CRE_OR_VEH_GUID(guid);
+
uint32 addCastCount = 1;
for( uint8 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_TRINITY_FLAGS_KILL_OR_CAST ) )
@@ -12091,7 +14159,9 @@ void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
// skip kill creature objective (0) or wrong spell casts
if(qInfo->ReqSpell[j] != spell_id )
continue;
+
uint32 reqTarget = 0;
+
if(isCreature)
{
// creature activate objectives
@@ -12106,19 +14176,24 @@ void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
// 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;
}
@@ -12126,6 +14201,7 @@ void Player::CastedCreatureOrGO( uint32 entry, uint64 guid, uint32 spell_id )
}
}
}
+
void Player::TalkedToCreature( uint32 entry, uint64 guid )
{
uint32 addTalkCount = 1;
@@ -12134,10 +14210,13 @@ void Player::TalkedToCreature( uint32 entry, uint64 guid )
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_TRINITY_FLAGS_KILL_OR_CAST | QUEST_TRINITY_FLAGS_SPEAKTO ) )
@@ -12147,12 +14226,15 @@ void Player::TalkedToCreature( uint32 entry, uint64 guid )
// 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];
@@ -12161,10 +14243,12 @@ void Player::TalkedToCreature( uint32 entry, uint64 guid )
{
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;
}
@@ -12173,6 +14257,7 @@ void Player::TalkedToCreature( uint32 entry, uint64 guid )
}
}
}
+
void Player::MoneyChanged( uint32 count )
{
for( uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
@@ -12180,10 +14265,12 @@ void Player::MoneyChanged( uint32 count )
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())
@@ -12200,6 +14287,7 @@ void Player::MoneyChanged( uint32 count )
}
}
}
+
void Player::ReputationChanged(FactionEntry const* factionEntry )
{
for( uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
@@ -12227,6 +14315,7 @@ void Player::ReputationChanged(FactionEntry const* factionEntry )
}
}
}
+
bool Player::HasQuestForItem( uint32 itemid ) const
{
for( uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
@@ -12234,19 +14323,24 @@ bool Player::HasQuestForItem( uint32 itemid ) const
uint32 questid = GetQuestSlotQuestId(i);
if ( questid == 0 )
continue;
+
QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid);
if(qs_itr == mQuestStatus.end())
continue;
+
QuestStatusData const& q_status = qs_itr->second;
+
if (q_status.m_status == QUEST_STATUS_INCOMPLETE)
{
Quest const* qinfo = objmgr.GetQuestTemplate(questid);
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)
if(!InBattleGround()) //there are two ways.. we can make every bg-quest a raidquest, or add this code here.. i don't know if this can be exploited by other quests, but i think all other quests depend on a specific area.. but keep this in mind, if something strange happens later
continue;
+
// There should be no mixed ReqItem/ReqSource drop
// This part for ReqItem drop
for (int j = 0; j < QUEST_OBJECTIVES_COUNT; j++)
@@ -12261,9 +14355,11 @@ bool Player::HasQuestForItem( uint32 itemid ) const
if (qinfo->ReqSourceId[j] == itemid)
{
ItemPrototype const *pProto = objmgr.GetItemPrototype(itemid);
+
// 'unique' item
if (pProto->MaxCount && GetItemCount(itemid,true) < pProto->MaxCount)
return true;
+
// allows custom amount drop when not 0
if (qinfo->ReqSourceCount[j])
{
@@ -12277,6 +14373,7 @@ bool Player::HasQuestForItem( uint32 itemid ) const
}
return false;
}
+
void Player::SendQuestComplete( uint32 quest_id )
{
if( quest_id )
@@ -12287,6 +14384,7 @@ void Player::SendQuestComplete( uint32 quest_id )
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();
@@ -12294,6 +14392,7 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive
gameeventmgr.HandleQuestComplete(questid);
WorldPacket data( SMSG_QUESTGIVER_QUEST_COMPLETE, (4+4+4+4+4) );
data << uint32(questid);
+
if ( getLevel() < sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL) )
{
data << uint32(XP);
@@ -12304,12 +14403,15 @@ void Player::SendQuestReward( Quest const *pQuest, uint32 XP, Object * questGive
data << uint32(0);
data << uint32(pQuest->GetRewOrReqMoney() + int32(pQuest->GetRewMoneyMaxLevel() * sWorld.getRate(RATE_DROP_MONEY)));
}
+
data << uint32(10*Trinity::Honor::hk_honor_at_level(getLevel(), pQuest->GetRewHonorableKills()));
data << uint32(pQuest->GetBonusTalents()); // bonus talents
GetSession()->SendPacket( &data );
+
if (pQuest->GetQuestCompleteScript() != 0)
GetMap()->ScriptsStart(sQuestEndScripts, pQuest->GetQuestCompleteScript(), questGiver, this);
}
+
void Player::SendQuestFailed( uint32 quest_id )
{
if( quest_id )
@@ -12321,6 +14423,7 @@ void Player::SendQuestFailed( uint32 quest_id )
sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_FAILED");
}
}
+
void Player::SendQuestTimerFailed( uint32 quest_id )
{
if( quest_id )
@@ -12331,6 +14434,7 @@ void Player::SendQuestTimerFailed( uint32 quest_id )
sLog.outDebug("WORLD: Sent SMSG_QUESTUPDATE_FAILEDTIMER");
}
}
+
void Player::SendCanTakeQuestResponse( uint32 msg )
{
WorldPacket data( SMSG_QUESTGIVER_QUEST_INVALID, 4 );
@@ -12338,6 +14442,7 @@ void Player::SendCanTakeQuestResponse( uint32 msg )
GetSession()->SendPacket( &data );
sLog.outDebug("WORLD: Sent SMSG_QUESTGIVER_QUEST_INVALID");
}
+
void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg )
{
if( pPlayer )
@@ -12349,6 +14454,7 @@ void Player::SendPushToPartyResponse( Player *pPlayer, uint32 msg )
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, 0 );
@@ -12357,13 +14463,16 @@ void Player::SendQuestUpdateAddItem( Quest const* pQuest, uint32 item_idx, uint3
//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());
@@ -12372,13 +14481,16 @@ void Player::SendQuestUpdateAddCreatureOrGo( Quest const* pQuest, uint64 guid, u
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;
@@ -12391,7 +14503,9 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid )
}
else
delete_result = false;
+
Field *fields = result->Fetch();
+
if (!LoadValues( fields[1].GetString()))
{
sLog.outError("Player #%d have broken data in `data` field. Can't be loaded for character list.",GUID_LOPART(guid));
@@ -12399,55 +14513,76 @@ bool Player::MinimalLoadFromDB( QueryResult *result, uint32 guid )
delete result;
return false;
}
+
// overwrite possible wrong/corrupted guid
SetUInt64Value(OBJECT_FIELD_GUID, MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
+
m_name = fields[2].GetCppString();
+
Relocate(fields[3].GetFloat(),fields[4].GetFloat(),fields[5].GetFloat());
Map *map = MapManager::Instance().CreateMap(fields[6].GetUInt32(), this, 0);
SetMap(map);
+
// 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);
+
// the instance id is not needed at character enum
+
m_Played_time[PLAYED_TIME_TOTAL] = fields[7].GetUInt32();
m_Played_time[PLAYED_TIME_LEVEL] = fields[8].GetUInt32();
+
m_atLoginFlags = fields[9].GetUInt32();
+
// I don't see these used anywhere ..
/*_LoadGroup();
+
_LoadBoundInstances();*/
+
if (delete_result)
delete result;
+
for (uint8 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(uint8 i = 0; i < MAX_DECLINED_NAME_CASES; ++i)
m_declinedname->name[i] = fields[i].GetCppString();
+
delete result;
}
+
void Player::_LoadArenaTeamInfo(QueryResult *result)
{
// arenateamid, played_week, played_season, personal_rating
memset((void*)&m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1], 0, sizeof(uint32)*18);
if (!result)
return;
+
do
{
Field *fields = result->Fetch();
+
uint32 arenateamid = fields[0].GetUInt32();
uint32 played_week = fields[1].GetUInt32();
uint32 played_season = fields[2].GetUInt32();
uint32 personal_rating = fields[3].GetUInt32();
+
ArenaTeam* aTeam = objmgr.GetArenaTeamById(arenateamid);
if(!aTeam)
{
@@ -12455,43 +14590,55 @@ void Player::_LoadArenaTeamInfo(QueryResult *result)
continue;
}
uint8 arenaSlot = aTeam->GetSlot();
+
m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 6] = arenateamid; // TeamID
m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 6 + 1] = ((aTeam->GetCaptain() == GetGUID()) ? (uint32)0 : (uint32)1); // Captain 0, member 1
m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 6 + 2] = played_week; // Played Week
m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 6 + 3] = played_season; // Played Season
m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 6 + 4] = 0; // Unk
m_uint32Values[PLAYER_FIELD_ARENA_TEAM_INFO_1_1 + arenaSlot * 6 + 5] = personal_rating; // Personal Rating
+
}while (result->NextRow());
delete result;
}
+
void Player::_LoadEquipmentSets(QueryResult *result)
{
// SetPQuery(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS, "SELECT setguid, setindex, name, iconname, item0, item1, item2, item3, item4, item5, item6, item7, item8, item9, item10, item11, item12, item13, item14, item15, item16, item17, item18 FROM character_equipmentsets WHERE guid = '%u' ORDER BY setindex", GUID_LOPART(m_guid));
if (!result)
return;
+
uint32 count = 0;
do
{
Field *fields = result->Fetch();
+
EquipmentSet eqSet;
+
eqSet.Guid = fields[0].GetUInt64();
uint32 index = fields[1].GetUInt32();
eqSet.Name = fields[2].GetCppString();
eqSet.IconName = fields[3].GetCppString();
eqSet.state = EQUIPMENT_SET_UNCHANGED;
+
for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
eqSet.Items[i] = fields[4+i].GetUInt32();
+
m_EquipmentSets[index] = eqSet;
+
++count;
+
if(count >= MAX_EQUIPMENT_SET_INDEX) // client limit
break;
} while (result->NextRow());
delete result;
}
+
void Player::_LoadBGData(QueryResult* result)
{
if (!result)
return;
+
// Expecting only one row
Field *fields = result->Fetch();
/* bgInstanceID, bgTeam, x, y, z, o, map, taxi[0], taxi[1], mountSpell */
@@ -12505,46 +14652,61 @@ void Player::_LoadBGData(QueryResult* result)
m_bgData.taxiPath[0] = fields[7].GetUInt32();
m_bgData.taxiPath[1] = fields[8].GetUInt32();
m_bgData.mountSpell = fields[9].GetUInt32();
+
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)
{
// todo: cleanup in this, move to a separate function.
@@ -12581,27 +14743,35 @@ 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 33 34 35 36 37 38 39 40 41 42 43
//QueryResult *result = CharacterDatabase.PQuery("SELECT guid, account, data, name, race, class, gender, level, xp, money, playerBytes, playerBytes2, playerFlags, 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, extra_flags, stable_slots, at_login, zone, online, death_expire_time, taxi_path, dungeon_difficulty, arena_pending_points, instance_id, speccount, activespec FROM characters WHERE guid = '%u'", guid);
QueryResult *result = holder->GetResult(PLAYER_LOGIN_QUERY_LOADFROM);
+
if(!result)
{
sLog.outError("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() )
@@ -12610,8 +14780,11 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
delete result;
return false;
}
+
Object::_Create( guid, 0, HIGHGUID_PLAYER );
+
m_name = fields[3].GetCppString();
+
// check name limitations
if (ObjectMgr::CheckPlayerName(m_name) != CHAR_NAME_SUCCESS ||
(GetSession()->GetSecurity() == SEC_PLAYER && objmgr.IsReservedName(m_name)))
@@ -12620,20 +14793,24 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
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("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));
+
// overwrite some data fields
uint32 bytes0 = GetUInt32Value(UNIT_FIELD_BYTES_0) & 0xFF000000;
bytes0 |= fields[4].GetUInt8(); // race
bytes0 |= fields[5].GetUInt8() << 8; // class
bytes0 |= fields[6].GetUInt8() << 16; // gender
SetUInt32Value(UNIT_FIELD_BYTES_0, bytes0);
+
SetUInt32Value(UNIT_FIELD_LEVEL, fields[7].GetUInt8());
SetUInt32Value(PLAYER_XP, fields[8].GetUInt32());
SetUInt32Value(PLAYER_FIELD_COINAGE, fields[9].GetUInt32());
@@ -12641,33 +14818,42 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SetUInt32Value(PLAYER_BYTES_2, fields[11].GetUInt32());
SetUInt32Value(PLAYER_BYTES_3, (GetUInt32Value(PLAYER_BYTES_3) & ~1) | fields[6].GetUInt8());
SetUInt32Value(PLAYER_FLAGS, fields[12].GetUInt32());
+
InitDisplayIds();
+
// 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( 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();
+
//Need to call it to initialize m_team (m_team can be calculated from race)
//Other way is to saves m_team into characters table.
setFactionForRace(getRace());
+
// load home bind and check in same time class/race pair, it used later for restore broken positions
if(!_LoadHomeBind(holder->GetResult(PLAYER_LOGIN_QUERY_LOADHOMEBIND)))
{
delete result;
return false;
}
+
InitPrimaryProfessions(); // to max set before any spell loaded
+
// init saved position, and fix it later if problematic
uint32 transGUID = fields[31].GetUInt32();
Relocate(fields[13].GetFloat(),fields[14].GetFloat(),fields[15].GetFloat(),fields[17].GetFloat());
@@ -12675,28 +14861,38 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
uint32 instanceId = fields[41].GetFloat();
SetDifficulty(fields[39].GetUInt32()); // may be changed in _LoadGroup
std::string taxi_nodes = fields[38].GetCppString();
+
#define RelocateToHomebind(){ mapId = m_homebindMapId; instanceId = 0; Relocate(m_homebindX, m_homebindY, m_homebindZ); }
+
_LoadGroup(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGROUP));
+
_LoadArenaTeamInfo(holder->GetResult(PLAYER_LOGIN_QUERY_LOADARENAINFO));
+
uint32 arena_currency = GetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY) + fields[40].GetUInt32();
if (arena_currency > sWorld.getConfig(CONFIG_MAX_ARENA_POINTS))
arena_currency = sWorld.getConfig(CONFIG_MAX_ARENA_POINTS);
+
SetUInt32Value(PLAYER_FIELD_ARENA_CURRENCY, arena_currency);
+
// 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));
_LoadBGData(holder->GetResult(PLAYER_LOGIN_QUERY_LOADBGDATA));
+
MapEntry const * mapEntry = sMapStore.LookupEntry(mapId);
if(!mapEntry || !IsPositionValid())
{
@@ -12709,14 +14905,18 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
BattleGround *currentBg = NULL;
if(m_bgData.bgInstanceID) //saved in BattleGround
currentBg = sBattleGroundMgr.GetBattleGround(m_bgData.bgInstanceID, BATTLEGROUND_TYPE_NONE);
+
if(currentBg && currentBg->IsPlayerInBattleGround(GetGUID()))
{
BattleGroundQueueTypeId bgQueueTypeId = sBattleGroundMgr.BGQueueTypeId(currentBg->GetTypeID(), currentBg->GetArenaType());
AddBattleGroundQueueId(bgQueueTypeId);
+
m_bgData.bgTypeID = currentBg->GetTypeID();
+
//join player to battleground group
currentBg->EventPlayerLoggedIn(this, GetGUID());
currentBg->AddOrSetPlayerToCorrectBgGroup(this, GetGUID(), m_bgData.bgTeam);
+
SetInviteForBattleGroundQueueType(bgQueueTypeId,currentBg->GetInstanceID());
}
// Bg was not found - go to Entry Point
@@ -12725,6 +14925,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
// Do not look for instance if bg not found
const WorldLocation& _loc = GetBattleGroundEntryPoint();
mapId = _loc.GetMapId(); instanceId = 0;
+
if(mapId == MAPID_INVALID) // Battleground Entry Point not found (???)
{
sLog.outError("Player (guidlow %d) was in BG in database, but BG was not found, and entry point was invalid! Teleport to default race/class locations.",guid);
@@ -12732,6 +14933,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
} else {
Relocate(&_loc);
}
+
// We are not in BG anymore
m_bgData.bgInstanceID = 0;
}
@@ -12741,10 +14943,12 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
{
// There are no transports on instances
instanceId = 0;
+
m_movementInfo.t_x = fields[27].GetFloat();
m_movementInfo.t_y = fields[28].GetFloat();
m_movementInfo.t_z = fields[29].GetFloat();
m_movementInfo.t_o = fields[30].GetFloat();
+
if( !MaNGOS::IsValidMapCoord(
GetPositionX()+m_movementInfo.t_x,GetPositionY()+m_movementInfo.t_y,
GetPositionZ()+m_movementInfo.t_z,GetOrientation()+m_movementInfo.t_o) ||
@@ -12754,6 +14958,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
sLog.outError("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);
+
RelocateToHomebind();
}
else
@@ -12772,6 +14977,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
{
sLog.outError("Player (guidlow %d) have problems with transport guid (%u). Teleport to default race/class locations.",
guid,transGUID);
+
RelocateToHomebind();
}
}
@@ -12780,6 +14986,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
else if (!taxi_nodes.empty())
{
instanceId = 0;
+
// Not finish taxi flight path
if(m_bgData.HasTaxiPath())
{
@@ -12792,6 +14999,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
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());
@@ -12805,6 +15013,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
}
m_taxi.ClearTaxiDestinations();
}
+
if(uint32 node_id = m_taxi.GetTaxiSource())
{
// save source node as recall coord to prevent recall and fall from sky
@@ -12812,9 +15021,11 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
assert(nodeEntry); // checked in m_taxi.LoadTaxiDestinationsFromString
mapId = nodeEntry->map_id;
Relocate(nodeEntry->x, nodeEntry->y, nodeEntry->z,0.0f);
+
// flight will started later
}
}
+
// Map could be changed before
mapEntry = sMapStore.LookupEntry(mapId);
// client without expansion support
@@ -12823,14 +15034,17 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
sLog.outDebug("Player %s using client without required expansion tried login at non accessible map %u", GetName(), mapId);
RelocateToHomebind();
}
+
// fix crash (because of if(Map *map = _FindMap(instanceId)) in MapInstanced::CreateInstance)
if(instanceId)
if(InstanceSave * save = GetInstanceSave(mapId))
if(save->GetInstanceId() != instanceId)
instanceId = 0;
+
// NOW player must have valid map
// load the player's map here if it's not already loaded
Map *map = MapManager::Instance().CreateMap(mapId, this, instanceId);
+
if (!map)
{
instanceId = 0;
@@ -12846,6 +15060,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
sLog.outError("Player (guidlow %d) is teleported to home (Map: %u X: %f Y: %f Z: %f O: %f).",guid,mapId,GetPositionX(),GetPositionY(),GetPositionZ(),GetOrientation());
RelocateToHomebind();
}
+
map = MapManager::Instance().CreateMap(mapId, this, 0);
if(!map)
{
@@ -12862,6 +15077,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
}
}
}
+
// if the player is in an instance and it has been reset in the meantime teleport him to the entrance
if(instanceId && !sInstanceSaveManager.GetInstanceSave(instanceId))
{
@@ -12874,15 +15090,21 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
RelocateToHomebind();
}
}
+
SetMap(map);
+
// 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);
+
SaveRecallPosition();
+
time_t now = time(NULL);
time_t logoutTime = time_t(fields[23].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;
@@ -12892,46 +15114,61 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
soberFactor = 1-time_diff/(15.0f*MINUTE);
uint16 newDrunkenValue = uint16(soberFactor*(GetUInt32Value(PLAYER_BYTES_3) & 0xFFFE));
SetDrunkValue(newDrunkenValue);
+
m_rest_bonus = fields[22].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(time_diff > 0)
{
float bubble = fields[24].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[19].GetUInt32();
m_Played_time[PLAYED_TIME_TOTAL]= fields[20].GetUInt32();
m_Played_time[PLAYED_TIME_LEVEL]= fields[21].GetUInt32();
+
m_resetTalentsCost = fields[25].GetUInt32();
m_resetTalentsTime = time_t(fields[26].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[18].GetString() ); // must be before InitTaxiNodesForLevel
+
uint32 extraflags = fields[32].GetUInt32();
+
m_stableSlots = fields[33].GetUInt32();
if(m_stableSlots > MAX_PET_STABLES)
{
sLog.outError("Player can have not more %u stable slots, but have in DB %u",MAX_PET_STABLES,uint32(m_stableSlots));
m_stableSlots = MAX_PET_STABLES;
}
+
m_atLoginFlags = fields[34].GetUInt32();
+
// Honor system
// Update Honor kills data
m_lastHonorUpdateTime = logoutTime;
UpdateHonorFields();
+
m_deathExpireTime = (time_t)fields[37].GetUInt64();
if(m_deathExpireTime > now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP)
m_deathExpireTime = now+MAX_DEATH_COUNT*DEATH_EXPIRE_STEP-1;
+
// 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_SUMMONEDBY, 0);
SetUInt64Value(UNIT_FIELD_CHARMEDBY, 0);
@@ -12939,32 +15176,43 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SetUInt64Value(UNIT_FIELD_SUMMON, 0);
SetUInt64Value(PLAYER_FARSIGHT, 0);
SetCreatorGUID(0);
+
RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
+
// reset some aura modifiers before aura apply
SetUInt32Value(PLAYER_TRACK_CREATURES, 0 );
SetUInt32Value(PLAYER_TRACK_RESOURCES, 0 );
+
_LoadSkills();
+
// 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();
InitGlyphsForLevel();
InitRunes();
+
// 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();
+
m_specsCount = fields[42].GetUInt32();
m_activeSpec = fields[43].GetUInt32();
delete result;
+
// sanity check
if (m_specsCount > MAX_TALENT_SPECS || m_activeSpec > MAX_TALENT_SPEC ||
m_specsCount < MIN_TALENT_SPECS || m_activeSpec < MIN_TALENT_SPEC ) // if (m_specsCount < 2) is not logical
@@ -12972,29 +15220,40 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
m_activeSpec = 0;
sLog.outError("Player %s(GUID: %u) has SpecCount = %u and ActiveSpec = %u.", GetName(), GetGUIDLow(), m_specsCount, m_activeSpec);
}
+
_LoadTalents(holder->GetResult(PLAYER_LOGIN_QUERY_LOADTALENTS));
_LoadSpells(holder->GetResult(PLAYER_LOGIN_QUERY_LOADSPELLS));
+
_LoadGlyphs(holder->GetResult(PLAYER_LOGIN_QUERY_LOADGLYPHS));
_LoadAuras(holder->GetResult(PLAYER_LOGIN_QUERY_LOADAURAS), time_diff);
_LoadGlyphAuras();
// add ghost flag (must be after aura load: PLAYER_FLAGS_GHOST set in aura)
if( HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_GHOST) )
m_deathState = DEAD;
+
// after spell load, learn rewarded spell if need also
_LoadQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADQUESTSTATUS));
_LoadDailyQuestStatus(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDAILYQUESTSTATUS));
+
// after spell and quest load
InitTalentForLevel();
learnDefaultSpells();
+
// must be before inventory (some items required reputation check)
m_reputationMgr.LoadFromDB(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), true);
+
// 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());
+
// 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))
@@ -13002,22 +15261,29 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
if(!HasTitle(curTitle))
SetUInt32Value(PLAYER_CHOSEN_TITLE, 0);
}
+
// has to be called after last Relocate() in Player::LoadFromDB
SetFallInformation(0, GetPositionZ());
+
_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)
{
@@ -13031,6 +15297,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SetGameMaster(true);
break;
}
+
switch(sWorld.getConfig(CONFIG_GM_VISIBLE_STATE))
{
default:
@@ -13041,6 +15308,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SetGMVisible(false);
break;
}
+
/*switch(sWorld.getConfig(CONFIG_GM_ACCEPT_TICKETS))
{
default:
@@ -13051,6 +15319,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SetAcceptTicket(true);
break;
}*/
+
switch(sWorld.getConfig(CONFIG_GM_CHAT))
{
default:
@@ -13061,6 +15330,7 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
SetGMChat(true);
break;
}
+
switch(sWorld.getConfig(CONFIG_GM_WISPERING_TO))
{
default:
@@ -13072,16 +15342,22 @@ bool Player::LoadFromDB( uint32 guid, SqlQueryHolder *holder )
break;
}
}
+
_LoadDeclinedNames(holder->GetResult(PLAYER_LOGIN_QUERY_LOADDECLINEDNAMES));
+
m_achievementMgr.LoadFromDB(holder->GetResult(PLAYER_LOGIN_QUERY_LOADACHIEVEMENTS), holder->GetResult(PLAYER_LOGIN_QUERY_LOADCRITERIAPROGRESS));
m_achievementMgr.CheckAllAchievementCriteria();
+
_LoadEquipmentSets(holder->GetResult(PLAYER_LOGIN_QUERY_LOADEQUIPMENTSETS));
+
return true;
}
+
bool Player::isAllowedToLoot(Creature* creature)
{
if(creature->isDead() && !creature->IsDamageEnoughForLootingAndReward())
return false;
+
if(Player* recipient = creature->GetLootRecipient())
{
if (recipient == this)
@@ -13099,6 +15375,7 @@ bool Player::isAllowedToLoot(Creature* creature)
// prevent other players from looting if the recipient got disconnected
return !creature->hasLootRecipient();
}
+
void Player::_LoadActions(QueryResult *result, bool startup)
{
if(result)
@@ -13106,9 +15383,11 @@ void Player::_LoadActions(QueryResult *result, bool startup)
do
{
Field *fields = result->Fetch();
+
uint8 button = fields[0].GetUInt8();
uint32 action = fields[1].GetUInt32();
uint8 type = fields[2].GetUInt8();
+
if(ActionButton* ab = addActionButton(button, action, type))
{
ab->uState = ACTIONBUTTON_UNCHANGED;
@@ -13118,18 +15397,23 @@ void Player::_LoadActions(QueryResult *result, bool startup)
else
{
sLog.outError( " ...at loading, and will deleted in DB also");
+
// Will deleted in DB at next save (it can create data until save but marked as deleted)
m_actionButtons[button].uState = ACTIONBUTTON_DELETED;
}
}
while( result->NextRow() );
+
delete result;
}
}
+
void Player::_LoadAuras(QueryResult *result, uint32 timediff)
{
sLog.outDebug("Loading auras for player %u",GetGUIDLow());
+
//QueryResult *result = CharacterDatabase.PQuery("SELECT caster_guid,spell,effect_mask,stackcount,amount0,amount1,amount2,maxduration,remaintime,remaincharges FROM character_aura WHERE guid = '%u'",GetGUIDLow());
+
if(result)
{
do
@@ -13146,19 +15430,23 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff)
int32 maxduration = (int32)fields[7].GetUInt32();
int32 remaintime = (int32)fields[8].GetUInt32();
int32 remaincharges = (int32)fields[9].GetUInt32();
+
SpellEntry const* spellproto = sSpellStore.LookupEntry(spellid);
if(!spellproto)
{
sLog.outError("Unknown aura (spellid %u), ignore.",spellid);
continue;
}
+
// negative effects should continue counting down after logout
if (remaintime != -1 && !IsPositiveSpell(spellid))
{
if(remaintime <= int32(timediff))
continue;
+
remaintime -= timediff;
}
+
// prevent wrong values of remaincharges
if(spellproto->procCharges)
{
@@ -13167,6 +15455,7 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff)
}
else
remaincharges = 0;
+
Aura* aura = new Aura(spellproto, effmask, this, this, this);
aura->SetLoadedState(caster_guid,maxduration,remaintime,remaincharges, stackcount, &damage[0]);
if(!aura->CanBeSaved())
@@ -13178,11 +15467,14 @@ void Player::_LoadAuras(QueryResult *result, uint32 timediff)
sLog.outDetail("Added aura spellid %u, effectmask %u", spellproto->Id, effmask);
}
while( result->NextRow() );
+
delete result;
}
+
if(getClass() == CLASS_WARRIOR && !HasAuraType(SPELL_AURA_MOD_SHAPESHIFT))
CastSpell(this,SPELL_ID_PASSIVE_BATTLE_STANCE,true);
}
+
void Player::_LoadGlyphAuras()
{
for (uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
@@ -13206,11 +15498,13 @@ void Player::_LoadGlyphAuras()
}
else
sLog.outError("Player %s has not existing glyph entry %u on index %u", m_name.c_str(), glyph, i);
+
// On any error remove glyph
SetGlyph(i, 0);
}
}
}
+
void Player::LoadCorpse()
{
if( isAlive() )
@@ -13230,6 +15524,7 @@ void Player::LoadCorpse()
}
}
}
+
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());
@@ -13238,10 +15533,13 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
//the bagMap is filled before items in the bags are loaded
//NOTE2: the "order by `slot`" is needed because mainhand weapons are (wrongly?)
//expected to be equipped before offhand items (TODO: fixme)
+
uint32 zone = GetZoneId();
+
if (result)
{
std::list<Item*> problematicItems;
+
// prevent items from being added to the queue when stored
m_itemUpdateQueueBlocked = true;
do
@@ -13251,7 +15549,9 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
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);
@@ -13259,7 +15559,9 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
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 );
@@ -13268,6 +15570,7 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
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) )
{
@@ -13276,6 +15579,7 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
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)))
{
@@ -13284,12 +15588,15 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
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;
@@ -13314,6 +15621,7 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
else
success = false;
}
+
if(success)
{
// store bags that may contain items in them
@@ -13341,6 +15649,7 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
else
success = false;
}
+
// item's state may have changed after stored
if (success)
item->SetState(ITEM_UNCHANGED, this);
@@ -13351,26 +15660,33 @@ void Player::_LoadInventory(QueryResult *result, uint32 timediff)
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 preparing
+
for(uint8 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()->GetTrinityString(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)
{
@@ -13378,13 +15694,17 @@ void Player::_LoadMailedItems(Mail *mail)
QueryResult* result = CharacterDatabase.PQuery("SELECT data, item_guid, item_template FROM mail_items JOIN item_instance ON item_guid = guid WHERE mail_id='%u'", mail->messageID);
if(!result)
return;
+
do
{
Field *fields = result->Fetch();
uint32 item_guid_low = fields[1].GetUInt32();
uint32 item_template = fields[2].GetUInt32();
+
mail->AddItem(item_guid_low, item_template);
+
ItemPrototype const *proto = objmgr.GetItemPrototype(item_template);
+
if(!proto)
{
sLog.outError( "Player %u has unknown item_template (ProtoType) in mailed items(GUID: %u template: %u) in mail (%u), deleted.", GetGUIDLow(), item_guid_low, item_template,mail->messageID);
@@ -13392,7 +15712,9 @@ void Player::_LoadMailedItems(Mail *mail)
CharacterDatabase.PExecute("DELETE FROM item_instance WHERE guid = '%u'", item_guid_low);
continue;
}
+
Item *item = NewItemOrBag(proto);
+
if(!item->LoadFromDB(item_guid_low, 0, result))
{
sLog.outError( "Player::_LoadMailedItems - Item in mail (%u) doesn't exist !!!! - item guid: %u, deleted from mail", mail->messageID, item_guid_low);
@@ -13401,10 +15723,13 @@ void Player::_LoadMailedItems(Mail *mail)
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
@@ -13415,6 +15740,7 @@ void Player::_LoadMailInit(QueryResult *resultUnread, QueryResult *resultDeliver
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)
@@ -13424,6 +15750,7 @@ void Player::_LoadMailInit(QueryResult *resultUnread, QueryResult *resultDeliver
delete resultDelivery;
}
}
+
void Player::_LoadMail()
{
m_mail.clear();
@@ -13449,20 +15776,25 @@ void Player::_LoadMail()
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
@@ -13474,17 +15806,22 @@ void Player::LoadPet()
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);
@@ -13492,6 +15829,7 @@ void Player::_LoadQuestStatus(QueryResult *result)
{
// find or create
QuestStatusData& questStatusData = mQuestStatus[quest_id];
+
uint32 qstatus = fields[1].GetUInt32();
if(qstatus < MAX_QUEST_STATUS)
questStatusData.m_status = QuestStatus(qstatus);
@@ -13500,12 +15838,16 @@ void Player::_LoadQuestStatus(QueryResult *result)
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_TRINITY_FLAGS_TIMED ) && !GetQuestRewardStatus(quest_id) && questStatusData.m_status != QUEST_STATUS_NONE )
{
AddTimedQuest( quest_id );
+
if (quest_time <= sWorld.GetGameTime())
questStatusData.m_timer = 1;
else
@@ -13513,6 +15855,7 @@ void Player::_LoadQuestStatus(QueryResult *result)
}
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();
@@ -13521,7 +15864,9 @@ void Player::_LoadQuestStatus(QueryResult *result)
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 ||
@@ -13530,46 +15875,60 @@ void Player::_LoadQuestStatus(QueryResult *result)
(!questStatusData.m_rewarded || pQuest->IsDaily())))
{
SetQuestSlot(slot, quest_id, quest_time);
+
if (questStatusData.m_status == QUEST_STATUS_COMPLETE)
SetQuestSlotState(slot, QUEST_STATE_COMPLETE);
+
if (questStatusData.m_status == QUEST_STATUS_FAILED)
SetQuestSlotState(slot, QUEST_STATE_FAIL);
+
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()))
SetTitle(titleEntry);
}
+
if(pQuest->GetBonusTalents())
m_questRewardTalentCount += pQuest->GetBonusTalents();
}
+
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
@@ -13577,36 +15936,49 @@ void Player::_LoadDailyQuestStatus(QueryResult *result)
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::_LoadSpells(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT spell,active,disabled FROM character_spell WHERE guid = '%u'",GetGUIDLow());
+
if(result)
{
do
{
Field *fields = result->Fetch();
+
addSpell(fields[0].GetUInt32(), fields[1].GetBool(), false, false, fields[2].GetBool());
}
while( result->NextRow() );
+
delete result;
}
}
+
void Player::_LoadGroup(QueryResult *result)
{
//QueryResult *result = CharacterDatabase.PQuery("SELECT leaderGuid FROM group_member WHERE memberGuid='%u'", GetGUIDLow());
@@ -13627,11 +15999,14 @@ void Player::_LoadGroup(QueryResult *result)
}
}
}
+
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)
{
@@ -13646,6 +16021,7 @@ void Player::_LoadBoundInstances(QueryResult *result)
// 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
+
MapEntry const* mapEntry = sMapStore.LookupEntry(mapId);
if(!mapEntry || !mapEntry->IsDungeon())
{
@@ -13653,12 +16029,14 @@ void Player::_LoadBoundInstances(QueryResult *result)
CharacterDatabase.PExecute("DELETE FROM character_instance WHERE guid = '%d' AND instance = '%d'", GetGUIDLow(), instanceId);
continue;
}
+
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);
@@ -13666,17 +16044,20 @@ void Player::_LoadBoundInstances(QueryResult *result)
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;
}
+
InstanceSave * Player::GetInstanceSave(uint32 mapid)
{
InstancePlayerBind *pBind = GetBoundInstance(mapid, GetDifficulty());
@@ -13689,11 +16070,13 @@ InstanceSave * Player::GetInstanceSave(uint32 mapid)
}
return pSave;
}
+
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())
@@ -13703,6 +16086,7 @@ void Player::UnbindInstance(BoundInstancesMap::iterator &itr, uint8 difficulty,
m_boundInstances[difficulty].erase(itr++);
}
}
+
InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, bool load)
{
if(save)
@@ -13716,12 +16100,15 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, b
}
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());
@@ -13730,13 +16117,18 @@ InstancePlayerBind* Player::BindToInstance(InstanceSave *save, bool permanent, b
else
return NULL;
}
+
void Player::SendRaidInfo()
{
uint32 counter = 0;
+
WorldPacket data(SMSG_RAID_INSTANCE_INFO, 4);
+
size_t p_counter = data.wpos();
data << uint32(counter); // placeholder
+
time_t now = time(NULL);
+
for(uint8 i = 0; i < TOTAL_DIFFICULTIES; ++i)
{
for (BoundInstancesMap::iterator itr = m_boundInstances[i].begin(); itr != m_boundInstances[i].end(); ++itr)
@@ -13756,6 +16148,7 @@ void Player::SendRaidInfo()
data.put<uint32>(p_counter, counter);
GetSession()->SendPacket(&data);
}
+
/*
- called on every successful teleportation to a map
*/
@@ -13763,6 +16156,7 @@ 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)
@@ -13774,12 +16168,15 @@ void Player::SendSavedInstances()
}
}
}
+
//Send opcode 811. true or false 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)
@@ -13793,15 +16190,19 @@ void Player::SendSavedInstances()
}
}
}
+
/// 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++)
@@ -13821,17 +16222,20 @@ void Player::ConvertInstancesToGroup(Player *player, Group *group, uint64 player
}
}
}
+
// 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::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report)
{
if(!isGameMaster() && ar)
{
uint32 LevelMin = 0;
uint32 LevelMax = 0;
+
if(!sWorld.getConfig(CONFIG_INSTANCE_IGNORE_LEVEL))
{
if(ar->levelMin && getLevel() < ar->levelMin)
@@ -13841,6 +16245,7 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report
if(ar->levelMax && getLevel() > ar->levelMax)
LevelMax = ar->levelMax;
}
+
uint32 missingItem = 0;
if(ar->item)
{
@@ -13850,6 +16255,7 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report
}
else if(ar->item2 && !HasItemCount(ar->item2, 1))
missingItem = ar->item2;
+
uint32 missingKey = 0;
uint32 missingHeroicQuest = 0;
if(GetDifficulty() == DIFFICULTY_HEROIC)
@@ -13862,12 +16268,15 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report
}
else if(ar->heroicKey2 && !HasItemCount(ar->heroicKey2, 1))
missingKey = ar->heroicKey2;
+
if(ar->heroicQuest && !GetQuestRewardStatus(ar->heroicQuest))
missingHeroicQuest = ar->heroicQuest;
}
+
uint32 missingQuest = 0;
if(ar->quest && !GetQuestRewardStatus(ar->quest))
missingQuest = ar->quest;
+
if(LevelMin || LevelMax || missingItem || missingKey || missingQuest || missingHeroicQuest)
{
if(report)
@@ -13888,6 +16297,7 @@ bool Player::Satisfy(AccessRequirement const *ar, uint32 target_map, bool report
}
return true;
}
+
bool Player::_LoadHomeBind(QueryResult *result)
{
PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass());
@@ -13896,6 +16306,7 @@ bool Player::_LoadHomeBind(QueryResult *result)
sLog.outError("Player have incorrect race/class pair. Can't be loaded.");
return false;
}
+
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)
@@ -13907,7 +16318,9 @@ bool Player::_LoadHomeBind(QueryResult *result)
m_homebindY = fields[3].GetFloat();
m_homebindZ = fields[4].GetFloat();
delete result;
+
MapEntry const* bindMapEntry = sMapStore.LookupEntry(m_homebindMapId);
+
// accept saved data only for valid position (and non instanceable), and accessable
if( MapManager::IsValidMapCoord(m_homebindMapId,m_homebindX,m_homebindY,m_homebindZ) &&
!bindMapEntry->Instanceable() && GetSession()->Expansion() >= bindMapEntry->Expansion())
@@ -13917,6 +16330,7 @@ bool Player::_LoadHomeBind(QueryResult *result)
else
CharacterDatabase.PExecute("DELETE FROM character_homebind WHERE guid = '%u'", GetGUIDLow());
}
+
if(!ok)
{
m_homebindMapId = info->mapId;
@@ -13924,33 +16338,45 @@ bool Player::_LoadHomeBind(QueryResult *result)
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",
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);
+
//lets allow only players in world to be saved
if(IsBeingTeleportedFar())
{
ScheduleDelayedOperation(DELAYED_SAVE_PLAYER);
return;
}
+
// first save/honor gain after midnight will also update the player's honor fields
UpdateHonorFields();
+
sLog.outDebug("The value of player %s at save: ", m_name.c_str());
outDebugValues();
+
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,gender,level,xp,money,playerBytes,playerBytes2,playerFlags,"
"map, instance_id, dungeon_difficulty, position_x, position_y, position_z, orientation, data, "
@@ -13970,6 +16396,7 @@ void Player::SaveToDB()
<< GetUInt32Value(PLAYER_BYTES) << ", "
<< GetUInt32Value(PLAYER_BYTES_2) << ", "
<< GetUInt32Value(PLAYER_FLAGS) << ", ";
+
if(!IsBeingTeleported())
{
ss << GetMapId() << ", "
@@ -13990,17 +16417,24 @@ void Player::SaveToDB()
<< finiteAlways(GetTeleportDest().GetPositionZ()) << ", "
<< finiteAlways(GetTeleportDest().GetOrientation()) << ", '";
}
+
uint16 i;
for( i = 0; i < m_valuesCount; i++ )
{
ss << GetUInt32Value(i) << " ";
}
+
ss << "', ";
+
ss << m_taxi << ", "; // string with TaxiMaskSize numbers
+
ss << (IsInWorld() ? 1 : 0) << ", ";
+
ss << m_cinematic << ", ";
+
ss << m_Played_time[PLAYED_TIME_TOTAL] << ", ";
ss << m_Played_time[PLAYED_TIME_LEVEL] << ", ";
+
ss << finiteAlways(m_rest_bonus) << ", ";
ss << (uint64)time(NULL) << ", ";
ss << (HasFlag(PLAYER_FLAGS, PLAYER_FLAGS_RESTING) ? 1 : 0) << ", ";
@@ -14008,6 +16442,7 @@ void Player::SaveToDB()
//save, but in tavern/city
ss << m_resetTalentsCost << ", ";
ss << (uint64)m_resetTalentsTime << ", ";
+
ss << finiteAlways(m_movementInfo.t_x) << ", ";
ss << finiteAlways(m_movementInfo.t_y) << ", ";
ss << finiteAlways(m_movementInfo.t_z) << ", ";
@@ -14017,11 +16452,17 @@ void Player::SaveToDB()
else
ss << "0";
ss << ", ";
+
ss << m_ExtraFlags << ", ";
+
ss << uint32(m_stableSlots) << ", "; // to prevent save uint8 as char
+
ss << uint32(m_atLoginFlags) << ", ";
+
ss << GetZoneId() << ", ";
+
ss << (uint64)m_deathExpireTime << ", '";
+
ss << m_taxi.SaveTaxiDestinationsToString() << "', ";
ss << "'0', "; // arena_pending_points
ss << GetSession()->GetLatency();
@@ -14030,9 +16471,12 @@ void Player::SaveToDB()
ss << ", ";
ss << uint32(m_activeSpec);
ss << ")";
+
CharacterDatabase.Execute( ss.str().c_str() );
+
if(m_mailsUpdated) //save mails only when needed
_SaveMail();
+
_SaveBGData();
_SaveInventory();
_SaveQuestStatus();
@@ -14047,21 +16491,26 @@ void Player::SaveToDB()
_SaveEquipmentSets();
GetSession()->SaveTutorialsData(); // changed only while character in game
_SaveGlyphs();
+
CharacterDatabase.CommitTransaction();
+
// 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();
SaveGoldToDB();
}
+
void Player::SaveGoldToDB()
{
CharacterDatabase.PExecute("UPDATE characters SET money = '%u' WHERE guid = '%u'", GetMoney(), GetGUIDLow());
}
+
void Player::_SaveActions()
{
for(ActionButtonList::iterator itr = m_actionButtons.begin(); itr != m_actionButtons.end(); )
@@ -14090,14 +16539,17 @@ void Player::_SaveActions()
}
}
}
+
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)
{
if(!itr->second->CanBeSaved())
continue;
+
int32 amounts[MAX_SPELL_EFFECTS];
for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i)
{
@@ -14106,6 +16558,7 @@ void Player::_SaveAuras()
else
amounts[i]=0;
}
+
CharacterDatabase.PExecute("INSERT INTO character_aura (guid,caster_guid,spell,effect_mask,stackcount,amount0, amount1, amount2,maxduration,remaintime,remaincharges) "
"VALUES ('%u', '" UI64FMTD "', '%u', '%u', '%d', '%d', '%d', '%d', '%d', '%d', '%d')",
GetGUIDLow(), itr->second->GetCasterGUID(),(uint32)itr->second->GetId(), (uint32)itr->second->GetEffectMask(),
@@ -14113,6 +16566,7 @@ void Player::_SaveAuras()
,int(itr->second->GetAuraMaxDuration()),int(itr->second->GetAuraDuration()),int(itr->second->GetAuraCharges()));
}
}
+
void Player::_SaveInventory()
{
// force items in buyback slots to new state
@@ -14125,13 +16579,16 @@ void Player::_SaveInventory()
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++)
@@ -14139,6 +16596,7 @@ void Player::_SaveInventory()
Item *item = m_itemUpdateQueue[i];
if(!item || item->GetState() == ITEM_REMOVED) continue;
Item *test = GetItemByPos( item->GetBagSlot(), item->GetSlot());
+
if (test == NULL)
{
sLog.outCrash("Player(GUID: %u Name: %s)::_SaveInventory - the bag(%d) and slot(%d) values for the item with guid %d (state %d) are incorrect, the player doesn't have an item at that position!", GetGUIDLow(), GetName(), item->GetBagSlot(), item->GetSlot(), item->GetGUIDLow(), (int32)item->GetState());
@@ -14151,18 +16609,22 @@ void Player::_SaveInventory()
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:
@@ -14177,14 +16639,17 @@ void Player::_SaveInventory()
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);
@@ -14211,6 +16676,7 @@ void Player::_SaveMail()
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(); )
{
@@ -14224,8 +16690,10 @@ void Player::_SaveMail()
else
++itr;
}
+
m_mailsUpdated = false;
}
+
void Player::_SaveQuestStatus()
{
// we don't need transactions here.
@@ -14248,12 +16716,16 @@ void Player::_SaveQuestStatus()
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)
@@ -14261,15 +16733,18 @@ void Player::_SaveDailyQuestStatus()
CharacterDatabase.PExecute("INSERT INTO character_queststatus_daily (guid,quest,time) VALUES ('%u', '%u','" UI64FMTD "')",
GetGUIDLow(), GetUInt32Value(PLAYER_FIELD_DAILY_QUESTS_1+quest_daily_idx),uint64(m_lastDailyQuestTime));
}
+
void Player::_SaveSpells()
{
for (PlayerSpellMap::iterator itr = m_spells.begin(), next = m_spells.begin(); itr != m_spells.end();)
{
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);
+
// add only changed/new not dependent spells
if (!itr->second->dependent && (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED))
CharacterDatabase.PExecute("INSERT INTO character_spell (guid,spell,active,disabled) VALUES ('%u', '%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second->active ? 1 : 0,itr->second->disabled ? 1 : 0);
+
if (itr->second->state == PLAYERSPELL_REMOVED)
{
delete itr->second;
@@ -14280,12 +16755,15 @@ void Player::_SaveSpells()
itr->second->state = PLAYERSPELL_UNCHANGED;
++itr;
}
+
}
}
+
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));
@@ -14299,20 +16777,24 @@ void Player::outDebugValues() const
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)
{
@@ -14320,25 +16802,31 @@ void Player::UpdateSpeakTime()
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;
@@ -14349,17 +16837,21 @@ void Player::SavePositionInDB(uint32 mapid, float x,float y,float z,float o,uint
sLog.outDebug(ss.str().c_str());
CharacterDatabase.Execute(ss.str().c_str());
}
+
void Player::SaveDataFieldToDB()
{
std::ostringstream ss;
ss<<"UPDATE characters SET data='";
+
for(uint16 i = 0; i < m_valuesCount; i++ )
{
ss << GetUInt32Value(i) << " ";
}
ss<<"' WHERE guid='"<< GUID_LOPART(GetGUIDLow()) <<"'";
+
CharacterDatabase.Execute(ss.str().c_str());
}
+
bool Player::SaveValuesArrayInDB(Tokens const& tokens, uint64 guid)
{
std::ostringstream ss2;
@@ -14370,73 +16862,93 @@ bool Player::SaveValuesArrayInDB(Tokens const& tokens, uint64 guid)
ss2<<tokens[i]<<" ";
}
ss2<<"' WHERE guid='"<< GUID_LOPART(guid) <<"'";
+
return CharacterDatabase.Execute(ss2.str().c_str());
}
+
void Player::SetUInt32ValueInArray(Tokens& tokens,uint16 index, uint32 value)
{
char buf[11];
snprintf(buf,11,"%u",value);
+
if(index >= tokens.size())
return;
+
tokens[index] = buf;
}
+
void Player::SetUInt32ValueInDB(uint16 index, uint32 value, uint64 guid)
{
Tokens tokens;
if(!LoadValuesArrayFromDB(tokens,guid))
return;
+
if(index >= tokens.size())
return;
+
char buf[11];
snprintf(buf,11,"%u",value);
tokens[index] = buf;
+
SaveValuesArrayInDB(tokens,guid);
}
+
void Player::SetFloatValueInDB(uint16 index, float value, uint64 guid)
{
uint32 temp;
memcpy(&temp, &value, sizeof(value));
Player::SetUInt32ValueInDB(index, temp, guid);
}
+
void Player::Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair)
{
// 0
QueryResult* result = CharacterDatabase.PQuery("SELECT playerBytes2 FROM characters WHERE guid = '%u'", GUID_LOPART(guid));
if(!result)
return;
+
Field* fields = result->Fetch();
+
uint32 player_bytes2 = fields[0].GetUInt32();
player_bytes2 &= ~0xFF;
player_bytes2 |= facialHair;
+
CharacterDatabase.PExecute("UPDATE characters SET gender = '%u', playerBytes = '%u', playerBytes2 = '%u' WHERE guid = '%u'", gender, skin | (face << 8) | (hairStyle << 16) | (hairColor << 24), player_bytes2, GUID_LOPART(guid));
+
delete result;
}
+
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(Unit *target)
{
WorldPacket data(SMSG_CANCEL_AUTO_REPEAT, target->GetPackGUID().size());
data.append(target->GetPackGUID()); // may be it's target guid
GetSession()->SendPacket( &data );
}
+
void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
{
WorldPacket data( SMSG_EXPLORATION_EXPERIENCE, 8 );
@@ -14444,6 +16956,7 @@ void Player::SendExplorationExperience(uint32 Area, uint32 Experience)
data << Experience;
GetSession()->SendPacket(&data);
}
+
void Player::SendDungeonDifficulty(bool IsInGroup)
{
uint8 val = 0x00000001;
@@ -14453,18 +16966,22 @@ void Player::SendDungeonDifficulty(bool IsInGroup)
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;
@@ -14474,6 +16991,7 @@ void Player::ResetInstances(uint8 method)
++itr;
continue;
}
+
if(method == INSTANCE_RESET_ALL)
{
// the "reset all instances" method can only reset normal maps
@@ -14483,6 +17001,7 @@ void Player::ResetInstances(uint8 method)
continue;
}
}
+
// if the map is loaded, reset it
Map *map = MapManager::Instance().FindMap(p->GetMapId(), p->GetInstanceId());
if(map && map->IsDungeon())
@@ -14491,21 +17010,26 @@ void Player::ResetInstances(uint8 method)
++itr;
continue;
}
+
// 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
@@ -14514,9 +17038,11 @@ void Player::SendResetInstanceFailed(uint32 reason, uint32 MapId)
data << MapId;
GetSession()->SendPacket(&data);
}
+
/*********************************************************/
/*** Update timers ***/
/*********************************************************/
+
///checks the 15 afk reports per 5 minutes limit
void Player::UpdateAfkReport(time_t currTime)
{
@@ -14526,6 +17052,7 @@ void Player::UpdateAfkReport(time_t currTime)
m_bgData.bgAfkReportedTimer = currTime+5*MINUTE;
}
}
+
void Player::UpdateContestedPvP(uint32 diff)
{
if(!m_contestedPvPTimer||isInCombat())
@@ -14537,54 +17064,68 @@ void Player::UpdateContestedPvP(uint32 diff)
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;
}
+
Pet* Player::GetPet() const
{
if(uint64 pet_guid = GetPetGUID())
{
if(!IS_PET_GUID(pet_guid))
return NULL;
+
if(Pet* pet = ObjectAccessor::GetPet(pet_guid))
return pet;
+
//there may be a guardian in slot
//sLog.outError("Player::GetPet: Pet %u not exist.",GUID_LOPART(pet_guid));
//const_cast<Player*>(this)->SetPetGUID(0);
}
+
return NULL;
}
+
void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
{
if(!pet)
pet = GetPet();
+
if(pet)
{
sLog.outDebug("RemovePet %u, %u, %u", pet->GetEntry(), mode, returnreagent);
+
if(pet->m_removed)
return;
}
+
if(returnreagent && (pet || m_temporaryUnsummonedPetNumber) && !InBattleGround())
{
//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)
@@ -14604,9 +17145,12 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
}
m_temporaryUnsummonedPetNumber = 0;
}
+
if(!pet || pet->GetOwnerGUID()!=GetGUID())
return;
+
pet->CombatStop();
+
if(returnreagent)
{
switch(pet->GetEntry())
@@ -14620,25 +17164,32 @@ void Player::RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent)
break;
}
}
+
// only if current pet in slot
pet->SavePetToDB(mode);
+
SetMinion(pet, false);
+
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::StopCastingCharm()
{
Unit* charm = GetCharm();
if(!charm)
return;
+
if(charm->GetTypeId() == TYPEID_UNIT)
{
if(((Creature*)charm)->HasUnitTypeMask(UNIT_MASK_PUPPET))
@@ -14648,6 +17199,7 @@ void Player::StopCastingCharm()
}
if(GetCharmGUID())
charm->RemoveCharmAuras();
+
if(GetCharmGUID())
{
sLog.outCrash("Player %s (GUID: " UI64FMTD " is not able to uncharm unit (GUID: " UI64FMTD " Entry: %u, Type: %u)", GetName(), GetGUID(), GetCharmGUID(), charm->GetEntry(), charm->GetTypeId());
@@ -14660,6 +17212,7 @@ void Player::StopCastingCharm()
SetCharm(charm, false);
}
}
+
void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, const std::string& text, uint32 language) const
{
*data << (uint8)msgtype;
@@ -14671,47 +17224,58 @@ void Player::BuildPlayerChat(WorldPacket *data, uint8 msgtype, const std::string
*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);
+
if(sWorld.getConfig(CONFIG_CHATLOG_PUBLIC))
sLog.outChat("[SAY] Player %s says (language %u): %s",
GetName(), language, text.c_str());
}
+
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);
+
if(sWorld.getConfig(CONFIG_CHATLOG_PUBLIC))
sLog.outChat("[YELL] Player %s yells (language %u): %s",
GetName(), language, text.c_str());
}
+
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));
+
if(sWorld.getConfig(CONFIG_CHATLOG_PUBLIC))
sLog.outChat("[TEXTEMOTE] Player %s emotes: %s",
GetName(), text.c_str());
}
+
void Player::Whisper(const 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);
+
if(sWorld.getConfig(CONFIG_CHATLOG_WHISPER))
sLog.outChat("[WHISPER] Player %s tells %s: %s",
GetName(), rPlayer->GetName(), text.c_str());
+
// 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);
@@ -14721,36 +17285,48 @@ void Player::Whisper(const std::string& text, uint32 language,uint64 receiver)
// 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() && !isGameMaster() && !rPlayer->isGameMaster())
{
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)
return;
+
sLog.outDebug("Pet Spells Groups");
+
CharmInfo *charmInfo = pet->GetCharmInfo();
+
WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
data << uint64(pet->GetGUID());
data << uint16(pet->GetCreatureInfo()->family); // creature family (required for pet talents)
data << uint32(0);
data << uint8(pet->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
+
// action bar loop
charmInfo->BuildActionBar(&data);
+
size_t spellsCountPos = data.wpos();
+
// spells count
uint8 addlist = 0;
data << uint8(addlist); // placeholder
+
if (pet->IsPermanentPetFor(this))
{
// spells loop
@@ -14758,72 +17334,94 @@ void Player::PetSpellInitialize()
{
if(itr->second.state == PETSPELL_REMOVED)
continue;
+
data << uint32(MAKE_UNIT_ACTION_BUTTON(itr->first,itr->second.active));
++addlist;
}
}
+
data.put<uint8>(spellsCountPos, addlist);
+
uint8 cooldownsCount = pet->m_CreatureSpellCooldowns.size() + pet->m_CreatureCategoryCooldowns.size();
data << uint8(cooldownsCount);
+
time_t curTime = time(NULL);
+
for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureSpellCooldowns.begin(); itr != pet->m_CreatureSpellCooldowns.end(); ++itr)
{
time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILISECONDS : 0;
+
data << uint32(itr->first); // spellid
data << uint16(0); // spell category?
data << uint32(cooldown); // cooldown
data << uint32(0); // category cooldown
}
+
for(CreatureSpellCooldowns::const_iterator itr = pet->m_CreatureCategoryCooldowns.begin(); itr != pet->m_CreatureCategoryCooldowns.end(); ++itr)
{
time_t cooldown = (itr->second > curTime) ? (itr->second - curTime) * IN_MILISECONDS : 0;
+
data << uint32(itr->first); // spellid
data << uint16(0); // spell category?
data << uint32(0); // cooldown
data << uint32(cooldown); // category cooldown
}
+
data.hexlike();
+
GetSession()->SendPacket(&data);
}
+
void Player::PossessSpellInitialize()
{
Unit* charm = GetCharm();
if(!charm)
return;
+
CharmInfo *charmInfo = charm->GetCharmInfo();
+
if(!charmInfo)
{
sLog.outError("Player::PossessSpellInitialize(): charm ("UI64FMTD") has no charminfo!", charm->GetGUID());
return;
}
+
WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+1);
data << uint64(charm->GetGUID());
data << uint16(0);
data << uint32(0);
data << uint32(0);
+
charmInfo->BuildActionBar(&data);
+
data << uint8(0); // spells count
data << uint8(0); // cooldowns count
+
GetSession()->SendPacket(&data);
}
+
void Player::VehicleSpellInitialize()
{
Creature* veh = GetVehicleCreatureBase();
if(!veh)
return;
+
WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*10+1+1);
data << uint64(veh->GetGUID());
data << uint16(0);
data << uint32(0);
data << uint32(0x00000101);
+
for(uint32 i = 0; i < CREATURE_MAX_SPELLS; ++i)
{
uint32 spellId = ((Creature*)veh)->m_spells[i];
if(!spellId)
continue;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
continue;
+
if(IsPassiveSpell(spellId))
{
veh->CastSpell(veh, spellId, true);
@@ -14832,23 +17430,28 @@ void Player::VehicleSpellInitialize()
else
data << uint16(spellId) << uint8(0) << uint8(i+8);
}
+
for(uint32 i = CREATURE_MAX_SPELLS; i < MAX_SPELL_CONTROL_BAR; ++i)
data << uint16(0) << uint8(0) << uint8(i+8);
+
data << uint8(0);
data << uint8(0);
GetSession()->SendPacket(&data);
}
+
void Player::CharmSpellInitialize()
{
Unit* charm = GetFirstControlled();
if(!charm)
return;
+
CharmInfo *charmInfo = charm->GetCharmInfo();
if(!charmInfo)
{
sLog.outError("Player::CharmSpellInitialize(): the player's charm ("UI64FMTD") has no charminfo!", charm->GetGUID());
return;
}
+
uint8 addlist = 0;
if(charm->GetTypeId() != TYPEID_PLAYER)
{
@@ -14862,16 +17465,21 @@ void Player::CharmSpellInitialize()
}
}
}
+
WorldPacket data(SMSG_PET_SPELLS, 8+2+4+4+4*MAX_UNIT_ACTION_BAR_INDEX+1+4*addlist+1);
data << uint64(charm->GetGUID());
data << uint16(0);
data << uint32(0);
+
if(charm->GetTypeId() != TYPEID_PLAYER)
data << uint8(((Creature*)charm)->GetReactState()) << uint8(charmInfo->GetCommandState()) << uint16(0);
else
data << uint8(0) << uint8(0) << uint16(0);
+
charmInfo->BuildActionBar(&data);
+
data << uint8(addlist);
+
if(addlist)
{
for(uint32 i = 0; i < MAX_SPELL_CHARM; ++i)
@@ -14881,33 +17489,42 @@ void Player::CharmSpellInitialize()
data << uint32(cspell->packedData);
}
}
+
data << uint8(0); // cooldowns count
+
GetSession()->SendPacket(&data);
}
+
void Player::SendRemoveControlBar()
{
WorldPacket data(SMSG_PET_SPELLS, 8);
data << uint64(0);
GetSession()->SendPacket(&data);
}
+
bool Player::IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell * spell)
{
if (!mod || !spellInfo)
return false;
+
// Mod out of charges
if (spell && mod->charges == -1 && spell->m_appliedMods.find(mod->ownerAura) == spell->m_appliedMods.end())
return false;
+
return spellmgr.IsAffectedByMod(spellInfo, mod);
}
+
void Player::AddSpellMod(SpellModifier* mod, bool apply)
{
uint16 Opcode = (mod->type == SPELLMOD_FLAT) ? SMSG_SET_FLAT_SPELL_MODIFIER : SMSG_SET_PCT_SPELL_MODIFIER;
+
int i = 0;
flag96 _mask = 0;
for (int eff = 0; eff < 96; ++eff)
{
if (eff != 0 && eff%32 == 0)
_mask[i++] = 0;
+
_mask[i] = uint32(1) << (eff-(32*i));
if (mod->mask & _mask)
{
@@ -14925,6 +17542,7 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
SendDirectMessage(&data);
}
}
+
if (apply)
m_spellMods[mod->op].push_back(mod);
else
@@ -14933,44 +17551,54 @@ void Player::AddSpellMod(SpellModifier* mod, bool apply)
delete mod;
}
}
+
// Restore spellmods in case of failed cast
void Player::RestoreSpellMods(Spell * spell)
{
if (!spell || spell->m_appliedMods.empty())
return;
+
for(uint8 i=0;i<MAX_SPELLMOD;++i)
{
for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();++itr)
{
SpellModifier *mod = *itr;
+
// spellmods without aura set cannot be charged
if (!mod->ownerAura || !mod->ownerAura->GetAuraCharges())
continue;
+
// check if mod affected this spell
Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
if (iterMod == spell->m_appliedMods.end())
continue;
+
// remove from list
spell->m_appliedMods.erase(iterMod);
+
// add mod charges back to mod
if (mod->charges == -1)
mod->charges = 1;
else
mod->charges++;
+
// Do not set more spellmods than avalible
if (mod->ownerAura->GetAuraCharges() < mod->charges)
mod->charges = mod->ownerAura->GetAuraCharges();
+
// Skip this check for now - aura charges may change due to various reason
// TODO: trac these changes correctly
//assert (mod->ownerAura->GetAuraCharges() <= mod->charges);
}
}
}
+
void Player::RemoveSpellMods(Spell * spell)
{
if (!spell)
return;
std::set <Aura *> checkedSpells;
+
AuraEffectList const & auraList = GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE);
for(AuraEffectList::const_iterator itr = auraList.begin(); itr != auraList.end();)
{
@@ -14978,10 +17606,13 @@ void Player::RemoveSpellMods(Spell * spell)
++itr;
if (!aur->GetParentAura()->GetAuraCharges())
continue;
+
SpellEntry const * spellInfo = aur->GetSpellProto();
+
if (spellInfo->SpellFamilyName != spell->m_spellInfo->SpellFamilyName ||
checkedSpells.find(aur->GetParentAura()) != checkedSpells.end())
continue;
+
if (spell->m_spellInfo->SpellFamilyFlags & spellInfo->EffectSpellClassMask[aur->GetEffIndex()]
// this is for fingers of frost, look at spell::finish part, a charge will be taken by the triggering spell
&& aur->GetParentAura()->GetAuraDuration() != aur->GetParentAura()->GetAuraMaxDuration())
@@ -14992,28 +17623,35 @@ void Player::RemoveSpellMods(Spell * spell)
itr = auraList.begin();
}
}
+
if (spell->m_appliedMods.empty())
return;
+
for(uint8 i=0;i<MAX_SPELLMOD;++i)
{
for (SpellModList::iterator itr = m_spellMods[i].begin(); itr != m_spellMods[i].end();)
{
SpellModifier *mod = *itr;
++itr;
+
// spellmods without aura set cannot be charged
if (!mod->ownerAura || !mod->ownerAura->GetAuraCharges())
continue;
+
// check if mod affected this spell
Spell::UsedSpellMods::iterator iterMod = spell->m_appliedMods.find(mod->ownerAura);
if (iterMod == spell->m_appliedMods.end())
continue;
+
// remove from list
spell->m_appliedMods.erase(iterMod);
+
if (mod->ownerAura->DropAuraCharge())
itr = m_spellMods[i].begin();
}
}
}
+
void Player::DropModCharge(SpellModifier * mod, Spell * spell)
{
if (spell && mod->ownerAura && mod->charges > 0 )
@@ -15026,14 +17664,18 @@ void Player::DropModCharge(SpellModifier * mod, Spell * spell)
spell->m_appliedMods.insert(mod->ownerAura);
}
}
+
void Player::SetSpellModTakingSpell(Spell * spell, bool apply)
{
if (!spell || (m_spellModTakingSpell && m_spellModTakingSpell != spell))
return;
+
if (apply && spell->getState() == SPELL_STATE_FINISHED)
return;
+
m_spellModTakingSpell = apply ? spell : NULL;
}
+
// send Proficiency
void Player::SendProficiency(uint8 pr1, uint32 pr2)
{
@@ -15041,6 +17683,7 @@ void Player::SendProficiency(uint8 pr1, uint32 pr2)
data << pr1 << pr2;
GetSession()->SendPacket (&data);
}
+
void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
{
QueryResult *result = NULL;
@@ -15055,17 +17698,22 @@ void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
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)
{
@@ -15079,11 +17727,13 @@ void Player::RemovePetitionsAndSigns(uint64 guid, uint32 type)
}
CharacterDatabase.CommitTransaction();
}
+
void Player::LeaveAllArenaTeams(uint64 guid)
{
QueryResult *result = CharacterDatabase.PQuery("SELECT arena_team_member.arenateamid FROM arena_team_member JOIN arena_team ON arena_team_member.arenateamid = arena_team.arenateamid WHERE guid='%u'", GUID_LOPART(guid));
if(!result)
return;
+
do
{
Field *fields = result->Fetch();
@@ -15095,50 +17745,63 @@ void Player::LeaveAllArenaTeams(uint64 guid)
at->DelMember(guid);
}
} while (result->NextRow());
+
delete result;
}
+
void Player::SetRestBonus (float rest_bonus_new)
{
// Prevent resting on max level
if(getLevel() >= sWorld.getConfig(CONFIG_MAX_PLAYER_LEVEL))
rest_bonus_new = 0;
+
if(rest_bonus_new < 0)
rest_bonus_new = 0;
+
float rest_bonus_max = (float)GetUInt32Value(PLAYER_NEXT_LEVEL_XP)*1.5/2;
+
if(rest_bonus_new > rest_bonus_max)
m_rest_bonus = rest_bonus_max;
else
m_rest_bonus = rest_bonus_new;
+
// update data for client
if(m_rest_bonus>10)
SetByteValue(PLAYER_BYTES_2, 3, 0x01); // Set Reststate = Rested
else if(m_rest_bonus<=1)
SetByteValue(PLAYER_BYTES_2, 3, 0x02); // Set Reststate = Normal
+
//RestTickUpdate
SetUInt32Value(PLAYER_REST_STATE_EXPERIENCE, uint32(m_rest_bonus));
}
+
void Player::HandleStealthedUnitsDetection()
{
std::list<Unit*> stealthedUnits;
Trinity::AnyStealthedCheck u_check;
Trinity::UnitListSearcher<Trinity::AnyStealthedCheck > searcher(this, stealthedUnits, u_check);
VisitNearbyObject(GetMap()->GetVisibilityDistance(), searcher);
+
for (std::list<Unit*>::const_iterator i = stealthedUnits.begin(); i != stealthedUnits.end(); ++i)
{
if((*i)==this)
continue;
+
bool hasAtClient = HaveAtClient((*i));
bool hasDetected = canSeeOrDetect(*i, true);
+
if (hasDetected)
{
if(!hasAtClient)
{
(*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)
SendInitialVisiblePackets(*i);
@@ -15154,10 +17817,12 @@ void Player::HandleStealthedUnitsDetection()
}
}
}
+
bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc /*= NULL*/, uint32 spellid /*= 0*/)
{
if(nodes.size() < 2)
return false;
+
// not let cheating with start flight in time of logout process || while in combat || has type state: stunned || has type state: root
if(GetSession()->isLogingOut() || isInCombat() || hasUnitState(UNIT_STAT_STUNNED) || hasUnitState(UNIT_STAT_ROOT))
{
@@ -15166,8 +17831,10 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
GetSession()->SendPacket(&data);
return false;
}
+
if(HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE))
return false;
+
// taximaster case
if(npc)
{
@@ -15179,6 +17846,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
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);
@@ -15186,6 +17854,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
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(IsNonMeleeSpellCasted(false))
{
@@ -15199,17 +17868,23 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
else
{
RemoveAurasByType(SPELL_AURA_MOUNTED);
+
if( m_ShapeShiftFormSpellId && m_form != FORM_BATTLESTANCE && m_form != FORM_BERSERKERSTANCE && m_form != FORM_DEFENSIVESTANCE && m_form != FORM_SHADOW )
RemoveAurasDueToSpell(m_ShapeShiftFormSpellId);
+
if (Spell* spell = GetCurrentSpell(CURRENT_GENERIC_SPELL))
if (spell->m_spellInfo->Id != spellid)
InterruptSpell(CURRENT_GENERIC_SPELL,false);
+
InterruptSpell(CURRENT_AUTOREPEAT_SPELL,false);
+
if (Spell* spell = GetCurrentSpell(CURRENT_CHANNELED_SPELL))
if (spell->m_spellInfo->Id != spellid)
InterruptSpell(CURRENT_CHANNELED_SPELL,true);
}
+
uint32 sourcenode = nodes[0];
+
// starting node too far away (cheat?)
TaxiNodesEntry const* node = sTaxiNodesStore.LookupEntry(sourcenode);
if (!node)
@@ -15219,6 +17894,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
GetSession()->SendPacket(&data);
return false;
}
+
// check node starting pos data set case if provided
if (node->x != 0.0f || node->y != 0.0f || node->z != 0.0f)
{
@@ -15242,39 +17918,55 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
GetSession()->SendPacket(&data);
return false;
}
+
// Prepare to flight start now
+
// stop combat at start taxi flight if any
CombatStop();
+
StopCastingCharm();
StopCastingBindSight();
ExitVehicle();
+
// 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;
}
+
// get mount model (in case non taximaster (npc==NULL) allow more wide lookup)
//
// Hack-Fix for Alliance not being able to use Acherus taxi. There is
@@ -15282,6 +17974,7 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
// change but I couldn't find a suitable alternative. OK to use class because only DK
// can use this taxi.
uint32 mount_display_id = objmgr.GetTaxiMountDisplayId(sourcenode, GetTeam(), npc == NULL || (sourcenode == 315 && getClass() == CLASS_DEATH_KNIGHT));
+
// in spell case allow 0 model
if ((mount_display_id == 0 && spellid == 0) || sourcepath == 0)
{
@@ -15291,9 +17984,12 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
m_taxi.ClearTaxiDestinations();
return false;
}
+
uint32 money = GetMoney();
+
if (npc)
totalcost = (uint32)ceil(totalcost*GetReputationPriceDiscount(npc));
+
if(money < totalcost)
{
WorldPacket data(SMSG_ACTIVATETAXIREPLY, 4);
@@ -15302,12 +17998,15 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
m_taxi.ClearTaxiDestinations();
return false;
}
+
//Checks and preparations done, DO FLIGHT
ModifyMoney(-(int32)totalcost);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GOLD_SPENT_FOR_TRAVELLING, totalcost);
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FLIGHT_PATHS_TAKEN, 1);
+
// prevent stealth flight
//RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TALK);
+
if (sWorld.getConfig(CONFIG_INSTANT_TAXI))
{
TaxiNodesEntry const* lastnode = sTaxiNodesStore.LookupEntry(nodes[nodes.size()-1]);
@@ -15325,17 +18024,22 @@ bool Player::ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc
}
return true;
}
+
bool Player::ActivateTaxiPathTo( uint32 taxi_path_id, uint32 spellid /*= 0*/ )
{
TaxiPathEntry const* entry = sTaxiPathStore.LookupEntry(taxi_path_id);
if(!entry)
return false;
+
std::vector<uint32> nodes;
+
nodes.resize(2);
nodes[0] = entry->from;
nodes[1] = entry->to;
+
return ActivateTaxiPathTo(nodes,NULL,spellid);
}
+
void Player::CleanupAfterTaxiFlight()
{
m_taxi.ClearTaxiDestinations(); // not destinations, clear source node
@@ -15343,46 +18047,60 @@ void Player::CleanupAfterTaxiFlight()
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_DISABLE_MOVE | UNIT_FLAG_TAXI_FLIGHT);
getHostilRefManager().setOnlineOfflineState(true);
}
+
void Player::ContinueTaxiFlight()
{
uint32 sourceNode = m_taxi.GetTaxiSource();
if (!sourceNode)
return;
+
sLog.outDebug( "WORLD: Restart character %u taxi flight", GetGUIDLow() );
+
uint32 mountDisplayId = objmgr.GetTaxiMountDisplayId(sourceNode, GetTeam(),true);
uint32 path = 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-GetPositionX())*(nodeList[0].x-GetPositionX())+
(nodeList[0].y-GetPositionY())*(nodeList[0].y-GetPositionY())+
(nodeList[0].z-GetPositionZ())*(nodeList[0].z-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 != GetMapId())
continue;
+
distPrev = distNext;
+
distNext =
(node.x-GetPositionX())*(node.x-GetPositionX())+
(node.y-GetPositionY())*(node.y-GetPositionY())+
(node.z-GetPositionZ())*(node.z-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;
}
}
+
GetSession()->SendDoFlight(mountDisplayId, path, startNode);
}
+
void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
{
// last check 2.0.10
@@ -15401,11 +18119,14 @@ void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
ASSERT(spellInfo);
continue;
}
+
// Not send cooldown for this spells
if (spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE)
continue;
+
if(spellInfo->PreventionType != SPELL_PREVENTION_TYPE_SILENCE)
continue;
+
if((idSchoolMask & GetSpellSchoolMask(spellInfo)) && GetSpellCooldownDelay(unSpellId) < unTimeMs )
{
data << uint32(unSpellId);
@@ -15415,6 +18136,7 @@ void Player::ProhibitSpellScholl(SpellSchoolMask idSchoolMask, uint32 unTimeMs )
}
GetSession()->SendPacket(&data);
}
+
void Player::InitDataForForm(bool reapplyMods)
{
SpellShapeshiftEntry const* ssEntry = sSpellShapeshiftStore.LookupEntry(m_form);
@@ -15426,6 +18148,7 @@ void Player::InitDataForForm(bool reapplyMods)
}
else
SetRegularAttackTime();
+
switch(m_form)
{
case FORM_GHOUL:
@@ -15450,12 +18173,15 @@ void Player::InitDataForForm(bool reapplyMods)
break;
}
}
+
// update auras at form change, ignore this at mods reapply (.reset stats/etc) when form not change.
if (!reapplyMods)
UpdateEquipSpellsAtFormChange();
+
UpdateAttackPowerAndDamage();
UpdateAttackPowerAndDamage(true);
}
+
void Player::InitDisplayIds()
{
PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass());
@@ -15464,6 +18190,7 @@ void Player::InitDisplayIds()
sLog.outError("Player %u has incorrect race/class pair. Can't init display ids.", GetGUIDLow());
return;
}
+
uint8 gender = getGender();
switch(gender)
{
@@ -15480,22 +18207,27 @@ void Player::InitDisplayIds()
return;
}
}
+
// 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, uint8 bag, uint8 slot)
{
// cheating attempt
if (count < 1) count = 1;
+
// cheating attempt
if(slot > MAX_BAG_SIZE && slot !=NULL_SLOT)
return false;
+
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 = GetNPCIfCanInteractWith(vendorguid,UNIT_NPC_FLAG_VENDOR);
if (!pCreature)
{
@@ -15503,19 +18235,23 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
SendBuyError( BUY_ERR_DISTANCE_TOO_FAR, NULL, item, 0);
return false;
}
+
VendorItemData const* vItems = pCreature->GetVendorItems();
if(!vItems || vItems->Empty())
{
SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
return false;
}
+
size_t vendor_slot = vItems->FindItemSlot(item);
if (vendor_slot >= vItems->GetItemCount())
{
SendBuyError( BUY_ERR_CANT_FIND_ITEM, pCreature, item, 0);
return false;
}
+
VendorItem const* crItem = vItems->m_items[vendor_slot];
+
// check current item amount if it limited
if (crItem->maxcount != 0)
{
@@ -15525,6 +18261,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
return false;
}
}
+
if (pProto->RequiredReputationFaction && (uint32(GetReputationRank(pProto->RequiredReputationFaction)) < pProto->RequiredReputationRank))
{
SendBuyError( BUY_ERR_REPUTATION_REQUIRE, pCreature, item, 0);
@@ -15538,6 +18275,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
return false;
}
}
+
if (crItem->ExtendedCost)
{
ItemExtendedCostEntry const* iece = sItemExtendedCostStore.LookupEntry(crItem->ExtendedCost);
@@ -15546,18 +18284,21 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
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)
{
@@ -15567,6 +18308,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
return false;
}
}
+
// check for personal arena rating requirement
if( GetMaxPersonalArenaRatingRequirement() < iece->reqpersonalarenarating )
{
@@ -15575,14 +18317,18 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
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;
}
+
if ((bag == NULL_BAG && slot == NULL_SLOT) || IsInventoryPos(bag, slot))
{
ItemPosCountVec dest;
@@ -15592,6 +18338,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
SendEquipError( msg, NULL, NULL );
return false;
}
+
ModifyMoney( -(int32)price );
if (crItem->ExtendedCost) // case for new honor system
{
@@ -15606,15 +18353,18 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
DestroyItemCount(iece->reqitem[i], (iece->reqitemcount[i] * count), true);
}
}
+
if (Item *it = StoreNewItem( dest, item, true ))
{
uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
+
WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
data << pCreature->GetGUID();
data << (uint32)(vendor_slot+1); // numbered from 1 at client
data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
+
SendNewItem(it, pProto->BuyCount*count, true, false, false);
}
}
@@ -15625,6 +18375,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
SendEquipError( EQUIP_ERR_ITEM_CANT_BE_EQUIPPED, NULL, NULL );
return false;
}
+
uint16 dest;
uint8 msg = CanEquipNewItem( slot, dest, item, false );
if (msg != EQUIP_ERR_OK)
@@ -15632,6 +18383,7 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
SendEquipError( msg, NULL, NULL );
return false;
}
+
ModifyMoney( -(int32)price );
if (crItem->ExtendedCost) // case for new honor system
{
@@ -15646,16 +18398,20 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
DestroyItemCount(iece->reqitem[i], iece->reqitemcount[i], true);
}
}
+
if (Item *it = EquipNewItem( dest, item, true ))
{
uint32 new_count = pCreature->UpdateVendorItemCurrentCount(crItem,pProto->BuyCount * count);
+
WorldPacket data(SMSG_BUY_ITEM, (8+4+4+4));
data << pCreature->GetGUID();
data << (uint32)(vendor_slot+1); // numbered from 1 at client
data << (uint32)(crItem->maxcount > 0 ? new_count : 0xFFFFFFFF);
data << (uint32)count;
GetSession()->SendPacket(&data);
+
SendNewItem(it, pProto->BuyCount*count, true, false, false);
+
AutoUnequipOffhandIfNeed();
}
}
@@ -15664,8 +18420,10 @@ bool Player::BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint
SendEquipError( EQUIP_ERR_ITEM_DOESNT_GO_TO_SLOT, NULL, NULL );
return false;
}
+
return crItem->maxcount != 0;
}
+
uint32 Player::GetMaxPersonalArenaRatingRequirement()
{
// returns the maximal personal arena rating that can be used to purchase items requiring this condition
@@ -15685,6 +18443,7 @@ uint32 Player::GetMaxPersonalArenaRatingRequirement()
}
return max_personal_rating;
}
+
void Player::UpdateHomebindTime(uint32 time)
{
// GMs never get homebind timer online
@@ -15723,6 +18482,7 @@ void Player::UpdateHomebindTime(uint32 time)
sLog.outDebug("PLAYER: Player '%s' (GUID: %u) will be teleported to homebind in 60 seconds", GetName(),GetGUIDLow());
}
}
+
void Player::UpdatePvPState(bool onlyFFA)
{
// TODO: should we always synchronize UNIT_FIELD_BYTES_2, 1 of controller and controlled?
@@ -15742,8 +18502,10 @@ void Player::UpdatePvPState(bool onlyFFA)
for(ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
(*itr)->RemoveByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP);
}
+
if(onlyFFA)
return;
+
if(pvpInfo.inHostileArea) // in hostile area
{
if(!IsPvP() || pvpInfo.endTimer != 0)
@@ -15755,6 +18517,7 @@ void Player::UpdatePvPState(bool onlyFFA)
pvpInfo.endTimer = time(0); // start toggle-off
}
}
+
void Player::UpdatePvP(bool state, bool override)
{
if(!state || override)
@@ -15770,15 +18533,18 @@ void Player::UpdatePvP(bool state, bool override)
SetPvP(state);
}
}
+
void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 itemId, Spell* spell, bool infinityCooldown)
{
// 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(itemId)
{
if(ItemPrototype const* proto = ObjectMgr::GetItemPrototype(itemId))
@@ -15795,6 +18561,7 @@ void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 it
}
}
}
+
// if no cooldown found above then base at DBC data
if(rec < 0 && catrec < 0)
{
@@ -15802,9 +18569,12 @@ void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 it
rec = spellInfo->RecoveryTime;
catrec = spellInfo->CategoryRecoveryTime;
}
+
time_t curTime = time(NULL);
+
time_t catrecTime;
time_t recTime;
+
// overwrite time for selected category
if(infinityCooldown)
{
@@ -15819,23 +18589,30 @@ void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 it
// prevent 0 cooldowns set by another way
if (rec <= 0 && catrec <= 0 && (cat == 76 || IsAutoRepeatRangedSpell(spellInfo) && spellInfo->Id != SPELL_ID_AUTOSHOT))
rec = GetAttackTime(RANGED_ATTACK);
+
// Now we have cooldown data (if found any), time to apply mods
if(rec > 0)
ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, rec, spell);
+
if(catrec > 0)
ApplySpellMod(spellInfo->Id, SPELLMOD_COOLDOWN, catrec, spell);
+
// 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;
+
catrecTime = catrec ? curTime+catrec/IN_MILISECONDS : 0;
recTime = rec ? curTime+rec/IN_MILISECONDS : catrecTime;
}
+
// self spell cooldown
if(recTime > 0)
AddSpellCooldown(spellInfo->Id, itemId, recTime);
+
// category spells
if (cat && catrec > 0)
{
@@ -15846,11 +18623,13 @@ void Player::AddSpellAndCategoryCooldowns(SpellEntry const* spellInfo, uint32 it
{
if(*i_scset == spellInfo->Id) // skip main spell, already handled above
continue;
+
AddSpellCooldown(*i_scset, itemId, catrecTime);
}
}
}
}
+
void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
{
SpellCooldown sc;
@@ -15858,21 +18637,25 @@ void Player::AddSpellCooldown(uint32 spellid, uint32 itemid, time_t end_time)
sc.itemid = itemid;
m_spellCooldowns[spellid] = sc;
}
+
void Player::SendCooldownEvent(SpellEntry const *spellInfo, uint32 itemId, Spell* spell)
{
// start cooldowns at server side, if any
AddSpellAndCategoryCooldowns(spellInfo,itemId,spell);
+
// Send activate cooldown timer (possible 0) at client side
WorldPacket data(SMSG_COOLDOWN_EVENT, (4+8));
data << spellInfo->Id;
data << GetGUID();
SendDirectMessage(&data);
}
+
void Player::UpdatePotionCooldown(Spell* spell)
{
// no potion used i combat or still in combat
if(!m_lastPotionId || isInCombat())
return;
+
// Call not from spell cast, send cooldown event for item spells if no in combat
if(!spell)
{
@@ -15886,17 +18669,23 @@ void Player::UpdatePotionCooldown(Spell* spell)
// from spell cases (m_lastPotionId set in Spell::SendSpellCooldown)
else
SendCooldownEvent(spell->m_spellInfo,m_lastPotionId,spell);
+
m_lastPotionId = 0;
}
+
//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)
{
@@ -15910,19 +18699,25 @@ bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
uint32 enchant_id = pItem2->GetEnchantmentId(EnchantmentSlot(enchant_slot));
if(!enchant_id)
continue;
+
SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if(!enchantEntry)
continue;
+
uint32 gemid = enchantEntry->GemID;
if(!gemid)
continue;
+
ItemPrototype const* gemProto = sItemStorage.LookupEntry<ItemPrototype>(gemid);
if(!gemProto)
continue;
+
GemPropertiesEntry const* gemProperty = sGemPropertiesStore.LookupEntry(gemProto->GemProperties);
if(!gemProperty)
continue;
+
uint8 GemColor = gemProperty->color;
+
for(uint8 b = 0, tmpcolormask = 1; b < 4; b++, tmpcolormask <<= 1)
{
if(tmpcolormask & GemColor)
@@ -15931,14 +18726,19 @@ bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
}
}
}
+
bool activate = true;
+
for(uint8 i = 0; i < 5; i++)
{
if(!Condition->Color[i])
continue;
+
uint32 _cur_gem = curcount[Condition->Color[i] - 1];
+
// if have <CompareColor> use them as count, else use <value> from Condition
uint32 _cmp_gem = Condition->CompareColor[i] ? curcount[Condition->CompareColor[i] - 1]: Condition->Value[i];
+
switch(Condition->Comparator[i])
{
case 2: // requires less <color> than (<value> || <comparecolor>) gems
@@ -15952,9 +18752,12 @@ bool Player::EnchantmentFitsRequirements(uint32 enchantmentcondition, int8 slot)
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
@@ -15963,17 +18766,22 @@ void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
//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)
{
@@ -15990,6 +18798,7 @@ void Player::CorrectMetaGemEnchants(uint8 exceptslot, bool apply)
}
}
}
+
//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)
{
@@ -15999,18 +18808,23 @@ void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
//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)
@@ -16018,6 +18832,7 @@ void Player::ToggleMetaGemsActive(uint8 exceptslot, bool apply)
}
}
}
+
void Player::SetBattleGroundEntryPoint()
{
// Taxi path store
@@ -16026,6 +18841,7 @@ void Player::SetBattleGroundEntryPoint()
m_bgData.mountSpell = 0;
m_bgData.taxiPath[0] = m_taxi.GetTaxiSource();
m_bgData.taxiPath[1] = m_taxi.GetTaxiDestination();
+
// On taxi we don't need check for dungeon
m_bgData.joinPos = WorldLocation(GetMapId(), GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
return;
@@ -16033,6 +18849,7 @@ void Player::SetBattleGroundEntryPoint()
else
{
m_bgData.ClearTaxiPath();
+
// Mount spell id storing
if (IsMounted())
{
@@ -16042,6 +18859,7 @@ void Player::SetBattleGroundEntryPoint()
}
else
m_bgData.mountSpell = 0;
+
// If map is dungeon find linked graveyard
if(GetMap()->IsDungeon())
{
@@ -16060,14 +18878,17 @@ void Player::SetBattleGroundEntryPoint()
return;
}
}
+
// In error cases use homebind position
m_bgData.joinPos = WorldLocation(m_homebindMapId, m_homebindX, m_homebindY, m_homebindZ, 0.0f);
}
+
void Player::LeaveBattleground(bool teleportToEntryPoint)
{
if(BattleGround *bg = GetBattleGround())
{
bg->RemovePlayerAtLeave(GetGUID(), teleportToEntryPoint, true);
+
// call after remove to be sure that player resurrected for correct cast
if( bg->isBattleGround() && !isGameMaster() && sWorld.getConfig(CONFIG_BATTLEGROUND_CAST_DESERTER) )
{
@@ -16079,31 +18900,38 @@ void Player::LeaveBattleground(bool teleportToEntryPoint)
ScheduleDelayedOperation(DELAYED_SPELL_CAST_DESERTER);
return;
}
+
CastSpell(this, SPELL_ID_DESERTER, true); // Deserter
}
}
}
}
+
bool Player::CanJoinToBattleground() const
{
// check Deserter debuff
if(HasAura(SPELL_ID_DESERTER))
return false;
+
return true;
}
+
bool Player::CanReportAfkDueToLimit()
{
// a player can complain about 15 people per 5 minutes
if(m_bgData.bgAfkReportedCount++ >= 15)
return false;
+
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_bgData.bgAfkReporter.find(reporter->GetGUIDLow())==m_bgData.bgAfkReporter.end() && !HasAura(43680) && !HasAura(43681) && reporter->CanReportAfkDueToLimit())
{
@@ -16117,6 +18945,7 @@ void Player::ReportedAfkBy(Player* reporter)
}
}
}
+
WorldLocation Player::GetStartPosition() const
{
PlayerInfo const *info = objmgr.GetPlayerInfo(getRace(), getClass());
@@ -16125,14 +18954,17 @@ WorldLocation Player::GetStartPosition() const
mapId = 0;
return WorldLocation(mapId, info->positionX, info->positionY, info->positionZ, 0);
}
+
bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const
{
// Always can see self
if (m_mover == u || this == u)
return true;
+
// phased visibility (both must phased in same way)
if(!InSamePhase(u))
return false;
+
// player visible for other player if not logout and at same transport
// including case when player is out of world
bool at_same_transport =
@@ -16140,24 +18972,30 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool
&& !GetSession()->PlayerLogout() && !((Player*)u)->GetSession()->PlayerLogout()
&& !GetSession()->PlayerLoading() && !((Player*)u)->GetSession()->PlayerLoading()
&& GetTransport() == ((Player*)u)->GetTransport();
+
// not in world
if(!at_same_transport && (!IsInWorld() || !u->IsInWorld()))
return false;
+
// forbidden to seen (at GM respawn command)
//if(u->GetVisibility() == VISIBILITY_RESPAWN)
// return false;
+
Map& _map = *u->GetMap();
// Grid dead/alive checks
// non visible at grid for any stealth state
if(!u->IsVisibleInGridForPlayer(this))
return false;
+
// always seen by owner
if(uint64 guid = u->GetCharmerOrOwnerGUID())
if(GetGUID() == guid)
return true;
+
if(uint64 guid = GetUInt64Value(PLAYER_FARSIGHT))
if(u->GetGUID() == guid)
return true;
+
// different visible distance checks
if(isInFlight()) // what see player in flight
{
@@ -16190,6 +19028,7 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool
, is3dDistance))
return false;
}
+
if(u->GetVisibility() == VISIBILITY_OFF)
{
// GMs see any players, not higher GMs and all units
@@ -16202,6 +19041,7 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool
}
return false;
}
+
// GM's can see everyone with invisibilitymask with less or equal security level
if(m_mover->m_invisibilityMask || u->m_invisibilityMask)
{
@@ -16212,11 +19052,13 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool
else
return true;
}
+
// 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(!m_mover->canDetectInvisibilityOf(u))
if(!(u->GetTypeId()==TYPEID_PLAYER && !IsHostileTo(u) && IsGroupVisibleFor(((Player*)u))))
return false;
}
+
// GM invisibility checks early, invisibility if any detectable, so if not stealth then visible
if(u->GetVisibility() == VISIBILITY_GROUP_STEALTH && !isGameMaster())
{
@@ -16230,16 +19072,19 @@ bool Player::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool
if(!detect || !m_mover->canDetectStealthOf(u, GetDistance(u)))
return false;
}
+
// If use this server will be too laggy
// Now check is target visible with LoS
//return u->IsWithinLOS(GetPositionX(),GetPositionY(),GetPositionZ());
return true;
}
+
bool Player::IsVisibleInGridForPlayer( Player const * 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())
{
@@ -16247,14 +19092,17 @@ bool Player::IsVisibleInGridForPlayer( Player const * pl ) const
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())
{
@@ -16266,39 +19114,49 @@ bool Player::IsVisibleInGridForPlayer( Player const * pl ) const
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;
}
+
template<class T>
inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, T* target)
{
s64.insert(target->GetGUID());
}
+
template<>
inline void UpdateVisibilityOf_helper(std::set<uint64>& s64, GameObject* target)
{
if(!target->IsTransport())
s64.insert(target->GetGUID());
}
+
void Player::UpdateVisibilityOf(WorldObject* target)
{
if(HaveAtClient(target))
@@ -16307,6 +19165,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
{
target->DestroyForPlayer(this);
m_clientGUIDs.erase(target->GetGUID());
+
#ifdef TRINITY_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));
@@ -16319,12 +19178,15 @@ void Player::UpdateVisibilityOf(WorldObject* target)
{
//if(target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle)
// UpdateVisibilityOf(((Unit*)target)->m_Vehicle);
+
target->SendUpdateToPlayer(this);
UpdateVisibilityOf_helper(m_clientGUIDs, target);
+
#ifdef TRINITY_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->isType(TYPEMASK_UNIT))
@@ -16332,6 +19194,7 @@ void Player::UpdateVisibilityOf(WorldObject* target)
}
}
}
+
void Player::SendInitialVisiblePackets(Unit* target)
{
SendAurasForTarget(target);
@@ -16343,6 +19206,7 @@ void Player::SendInitialVisiblePackets(Unit* target)
target->SendMeleeAttackStart(target->getVictim());
}
}
+
template<class T>
void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObject*>& visibleNow)
{
@@ -16352,6 +19216,7 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObjec
{
target->BuildOutOfRangeUpdateBlock(&data);
m_clientGUIDs.erase(target->GetGUID());
+
#ifdef TRINITY_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));
@@ -16364,9 +19229,11 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObjec
{
//if(target->isType(TYPEMASK_UNIT) && ((Unit*)target)->m_Vehicle)
// UpdateVisibilityOf(((Unit*)target)->m_Vehicle, data, visibleNow);
+
visibleNow.insert(target);
target->BuildCreateUpdateBlockForPlayer(&data, this);
UpdateVisibilityOf_helper(m_clientGUIDs,target);
+
#ifdef TRINITY_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));
@@ -16374,27 +19241,32 @@ void Player::UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObjec
}
}
}
+
template void Player::UpdateVisibilityOf(Player* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
template void Player::UpdateVisibilityOf(Creature* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
template void Player::UpdateVisibilityOf(Corpse* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
template void Player::UpdateVisibilityOf(GameObject* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
template void Player::UpdateVisibilityOf(DynamicObject* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
+
void Player::InitPrimaryProfessions()
{
SetFreePrimaryProfessions(sWorld.getConfig(CONFIG_MAX_PRIMARY_TRADE_SKILL));
}
+
Unit * Player::GetSelectedUnit() const
{
if(m_curSelection)
return ObjectAccessor::GetUnit(*this, m_curSelection);
return NULL;
}
+
Player * Player::GetSelectedPlayer() const
{
if(m_curSelection)
return ObjectAccessor::GetPlayer(*this, m_curSelection);
return NULL;
}
+
void Player::SendComboPoints()
{
Unit *combotarget = ObjectAccessor::GetUnit(*this, m_comboTarget);
@@ -16413,13 +19285,17 @@ void Player::SendComboPoints()
GetSession()->SendPacket(&data);
}
}
+
void Player::AddComboPoints(Unit* target, int8 count, Spell * spell)
{
if(!count)
return;
+
int8 * comboPoints = spell ? &spell->m_comboPointGain : &m_comboPoints;
+
// without combo points lost (duration checked in aura)
RemoveAurasByType(SPELL_AURA_RETAIN_COMBO_POINTS);
+
if(target->GetGUID() == m_comboTarget)
{
*comboPoints += count;
@@ -16429,36 +19305,50 @@ void Player::AddComboPoints(Unit* target, int8 count, Spell * spell)
if(m_comboTarget)
if(Unit* target2 = ObjectAccessor::GetUnit(*this,m_comboTarget))
target2->RemoveComboPointHolder(GetGUIDLow());
+
m_comboTarget = target->GetGUID();
*comboPoints = count;
+
target->AddComboPointHolder(GetGUIDLow());
}
+
if (*comboPoints > 5) *comboPoints = 5;
else if (*comboPoints < 0) *comboPoints = 0;
+
if (!spell)
SendComboPoints();
}
+
void Player::GainSpellComboPoints(int8 count)
{
if(!count)
return;
+
m_comboPoints += count;
if (m_comboPoints > 5) m_comboPoints = 5;
else if (m_comboPoints < 0) m_comboPoints = 0;
+
SendComboPoints();
}
+
void Player::ClearComboPoints()
{
if(!m_comboTarget)
return;
+
// without combopoints lost (duration checked in aura)
RemoveAurasByType(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)
@@ -16471,45 +19361,58 @@ void Player::SetGroup(Group *group, int8 subgroup)
m_group.setSubGroup((uint8)subgroup);
}
}
+
void Player::SendInitialPacketsBeforeAddToMap()
{
WorldPacket data(SMSG_SET_REST_START_OBSOLETE, 4);
data << uint32(0); // unknown, may be rest state time or experience
GetSession()->SendPacket(&data);
+
GetSocial()->SendSocialList();
+
// 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
+
SendTalentsInfoData(false);
SendInitialSpells();
+
data.Initialize(SMSG_SEND_UNLEARN_SPELLS, 4);
data << uint32(0); // count, for(count) uint32;
GetSession()->SendPacket(&data);
+
SendInitialActionButtons();
m_reputationMgr.SendInitialReputations();
m_achievementMgr.SendAllAchievementData();
+
SendEquipmentSetList();
+
data.Initialize(SMSG_LOGIN_SETTIMESPEED, 4 + 4 + 4);
data << uint32(secsToTimeBitFields(sWorld.GetGameTime()));
data << (float)0.01666667f; // game speed
data << uint32(0); // added in 3.1.2
GetSession()->SendPacket( &data );
}
+
void Player::SendInitialPacketsAfterAddToMap()
{
// update zone
uint32 newzone, newarea;
GetZoneAndAreaId(newzone,newarea);
UpdateZone(newzone,newarea); // also call SendInitWorldStates();
+
WorldPacket data(SMSG_TIME_SYNC_REQ, 4); // new 2.0.x, enable movement
data << uint32(0x00000000); // on blizz it increments periodically
GetSession()->SendPacket(&data);
+
CastSpell(this, SPELL_ID_LOGINEFFECT, 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
@@ -16525,8 +19428,10 @@ void Player::SendInitialPacketsAfterAddToMap()
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))
{
@@ -16535,21 +19440,25 @@ void Player::SendInitialPacketsAfterAddToMap()
data2 << (uint32)2;
SendMessageToSet(&data2,true);
}
+
SendAurasForTarget(this);
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_auraRaidUpdateMask = 0;
if(Pet *pet = GetPet())
pet->ResetAuraUpdateMaskForRaid();
}
+
void Player::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg)
{
WorldPacket data(SMSG_TRANSFER_ABORTED, 4+2);
@@ -16565,6 +19474,7 @@ void Player::SendTransferAborted(uint32 mapid, uint8 reason, uint8 arg)
}
GetSession()->SendPacket(&data);
}
+
void Player::SendInstanceResetWarning( uint32 mapid, uint32 difficulty, uint32 time )
{
// type of warning, based on the time remaining until reset
@@ -16577,6 +19487,7 @@ void Player::SendInstanceResetWarning( uint32 mapid, uint32 difficulty, uint32 t
type = RAID_INSTANCE_WARNING_MIN;
else
type = RAID_INSTANCE_WARNING_MIN_SOON;
+
WorldPacket data(SMSG_RAID_INSTANCE_MESSAGE, 4+4+4+4);
data << uint32(type);
data << uint32(mapid);
@@ -16589,33 +19500,42 @@ void Player::SendInstanceResetWarning( uint32 mapid, uint32 difficulty, uint32 t
}
GetSession()->SendPacket(&data);
}
+
void Player::ApplyEquipCooldown( Item * pItem )
{
for(uint8 i = 0; i < MAX_ITEM_PROTO_SPELLS; ++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(bool myClassOnly)
{
// not need after this call
if(HasAtLoginFlag(AT_LOGIN_RESET_SPELLS))
RemoveAtLoginFlag(AT_LOGIN_RESET_SPELLS,true);
+
// 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();
+
uint32 family;
+
if(myClassOnly)
{
ChrClassesEntry const* clsEntry = sChrClassesStore.LookupEntry(getClass());
@@ -16623,6 +19543,7 @@ void Player::resetSpells(bool myClassOnly)
return;
family = clsEntry->spellfamily;
}
+
for(PlayerSpellMap::const_iterator iter = smap.begin();iter != smap.end(); ++iter)
{
if(myClassOnly)
@@ -16630,28 +19551,35 @@ void Player::resetSpells(bool myClassOnly)
SpellEntry const *spellInfo = sSpellStore.LookupEntry(iter->first);
if(!spellInfo)
continue;
+
// skip server-side/triggered spells
if(spellInfo->spellLevel == 0)
continue;
+
// skip wrong class/race skills
if(!IsSpellFitByClassAndRace(spellInfo->Id))
continue;
+
// skip other spell families
if(spellInfo->SpellFamilyName != family)
continue;
+
// 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,this,false))
continue;
}
removeSpell(iter->first,false,false); // only iter->first can be accessed, object by iter->second can be deleted already
}
+
learnDefaultSpells();
learnQuestRewardedSpells();
}
+
void Player::learnDefaultSpells()
{
// learn default race/class spells
@@ -16666,15 +19594,19 @@ void Player::learnDefaultSpells()
learnSpell(tspell,true);
}
}
+
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(uint8 i=0; i < 3; ++i)
@@ -16685,9 +19617,11 @@ void Player::learnQuestRewardedSpells(Quest const* quest)
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) )
@@ -16696,10 +19630,13 @@ void Player::learnQuestRewardedSpells(Quest const* quest)
uint32 first_spell = spellmgr.GetFirstSpellInChain(learned_0);
if( !HasSpell(first_spell) )
return;
+
SpellEntry const *learnedInfo = sSpellStore.LookupEntry(learned_0);
if(!learnedInfo)
return;
+
uint32 profSpell = spellmgr.GetSpellRequired(learned_0);
+
// specialization
if(learnedInfo->Effect[0]==SPELL_EFFECT_TRADE_SKILL && learnedInfo->Effect[1]==0 && profSpell)
{
@@ -16708,20 +19645,25 @@ void Player::learnQuestRewardedSpells(Quest const* quest)
{
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.GetSpellRequired(itr->first) == profSpell)
return;
}
}
}
+
CastSpell( this, spell_id, true);
}
+
void Player::learnQuestRewardedSpells()
{
// learn spells received from quest completing
@@ -16730,12 +19672,15 @@ void Player::learnQuestRewardedSpells()
// 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 skill_value )
{
uint32 raceMask = getRaceMask();
@@ -16751,6 +19696,7 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value )
// Check class if set
if (pAbility->classmask && !(pAbility->classmask & classMask))
continue;
+
if (sSpellStore.LookupEntry(pAbility->spellId))
{
// need unlearn spell
@@ -16764,24 +19710,29 @@ void Player::learnSkillRewardedSpells(uint32 skill_id, uint32 skill_value )
}
}
}
+
void Player::SendAurasForTarget(Unit *target)
{
if(!target || target->GetVisibleAuras()->empty()) // speedup things
return;
+
WorldPacket data(SMSG_AURA_UPDATE_ALL);
data.append(target->GetPackGUID());
+
Unit::VisibleAuraMap const *visibleAuras = target->GetVisibleAuras();
for(Unit::VisibleAuraMap::const_iterator itr = visibleAuras->begin(); itr != visibleAuras->end(); ++itr)
{
Aura * aura=itr->second;
data << uint8(aura->GetAuraSlot());
data << uint32(aura->GetId());
+
// flags
data << aura->m_auraFlags;
// level
data << aura->m_auraLevel;
// charges
data << uint8(aura->GetStackAmount()>1 ? aura->GetStackAmount() : aura->GetAuraCharges());
+
if(!(aura->m_auraFlags & AFLAG_CASTER))
{
if (Unit * caster = aura->GetCaster())
@@ -16789,14 +19740,17 @@ void Player::SendAurasForTarget(Unit *target)
else
data << uint8(0);
}
+
if(aura->m_auraFlags & AFLAG_DURATION) // include aura duration
{
data << uint32(aura->GetAuraMaxDuration());
data << uint32(aura->GetAuraDuration());
}
}
+
GetSession()->SendPacket(&data);
}
+
void Player::SetDailyQuestStatus( uint32 quest_id )
{
for(uint32 quest_daily_idx = 0; quest_daily_idx < PLAYER_MAX_DAILY_QUESTS; ++quest_daily_idx)
@@ -16810,43 +19764,54 @@ void Player::SetDailyQuestStatus( uint32 quest_id )
}
}
}
+
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(), m_bgData.bgTypeID);
}
+
bool Player::InArena() const
{
BattleGround *bg = GetBattleGround();
if(!bg || !bg->isArena())
return false;
+
return true;
}
+
bool Player::GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const
{
// get a template bg instead of running one
BattleGround *bg = sBattleGroundMgr.GetBattleGroundTemplate(bgTypeId);
if(!bg)
return false;
+
if(getLevel() < bg->GetMinLevel() || getLevel() > bg->GetMaxLevel())
return false;
+
return true;
}
+
BGQueueIdBasedOnLevel Player::GetBattleGroundQueueIdFromLevel(BattleGroundTypeId bgTypeId) const
{
//returned to hardcoded version of this function, because there is no way to code it dynamic
uint32 level = getLevel();
if( bgTypeId == BATTLEGROUND_AV )
level--;
+
uint32 queue_id = (level / 10) - 1; // for ranges 0 - 19, 20 - 29, 30 - 39, 40 - 49, 50 - 59, 60 - 69, 70 -79, 80
if( queue_id >= MAX_BATTLEGROUND_QUEUES )
{
@@ -16855,35 +19820,45 @@ BGQueueIdBasedOnLevel Player::GetBattleGroundQueueIdFromLevel(BattleGroundTypeId
}
return BGQueueIdBasedOnLevel(queue_id);
}
+
float Player::GetReputationPriceDiscount( Creature const* pCreature ) const
{
FactionTemplateEntry const* vendor_faction = pCreature->getFactionTemplateEntry();
if(!vendor_faction || !vendor_faction->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();
+
SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spell_id);
if (bounds.first==bounds.second)
return true;
+
for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
// skip wrong race skills
if (_spell_idx->second->racemask && (_spell_idx->second->racemask & racemask) == 0)
continue;
+
// skip wrong class skills
if (_spell_idx->second->classmask && (_spell_idx->second->classmask & classmask) == 0)
continue;
+
return true;
}
+
return false;
}
+
bool Player::HasQuestForGO(int32 GOId) const
{
for( uint8 i = 0; i < MAX_QUEST_LOG_SIZE; ++i )
@@ -16891,21 +19866,27 @@ bool Player::HasQuestForGO(int32 GOId) const
uint32 questid = GetQuestSlotQuestId(i);
if ( questid == 0 )
continue;
+
QuestStatusMap::const_iterator qs_itr = mQuestStatus.find(questid);
if(qs_itr == mQuestStatus.end())
continue;
+
QuestStatusData const& qs = qs_itr->second;
+
if (qs.m_status == QUEST_STATUS_INCOMPLETE)
{
Quest const* qinfo = objmgr.GetQuestTemplate(questid);
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;
}
@@ -16913,10 +19894,12 @@ bool Player::HasQuestForGO(int32 GOId) const
}
return false;
}
+
void Player::UpdateForQuestWorldObjects()
{
if(m_clientGUIDs.empty())
return;
+
UpdateData udata;
WorldPacket packet;
for(ClientGUIDs::iterator itr=m_clientGUIDs.begin(); itr!=m_clientGUIDs.end(); ++itr)
@@ -16932,9 +19915,11 @@ void Player::UpdateForQuestWorldObjects()
Creature *obj = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, *itr);
if(!obj)
continue;
+
// check if this unit requires quest specific flags
if(!obj->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK))
continue;
+
SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(obj->GetEntry());
for(SpellClickInfoMap::const_iterator _itr = clickPair.first; _itr != clickPair.second; ++_itr)
{
@@ -16949,6 +19934,7 @@ void Player::UpdateForQuestWorldObjects()
udata.BuildPacket(&packet);
GetSession()->SendPacket(&packet);
}
+
void Player::SummonIfPossible(bool agree)
{
if(!agree)
@@ -16956,23 +19942,30 @@ void Player::SummonIfPossible(bool 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();
CleanupAfterTaxiFlight();
}
+
// drop flag at summon
// this code can be reached only when GM is summoning player who carries flag, because player should be immune to summoning spells when he carries flag
if(BattleGround *bg = GetBattleGround())
bg->EventPlayerDroppedFlag(this);
+
m_summon_expire = 0;
+
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_ACCEPTED_SUMMONINGS, 1);
+
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)
@@ -16984,6 +19977,7 @@ void Player::RemoveItemDurations( Item *item )
}
}
}
+
void Player::AddItemDurations( Item *item )
{
if(item->GetUInt32Value(ITEM_FIELD_DURATION))
@@ -16992,14 +19986,17 @@ void Player::AddItemDurations( Item *item )
item->SendTimeUpdate(this);
}
}
+
void Player::AutoUnequipOffhandIfNeed()
{
Item *offItem = GetItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND );
if(!offItem)
return;
+
// need unequip offhand for 2h-weapon without TitanGrip (in any from hands)
if (CanTitanGrip() || (offItem->GetProto()->InventoryType != INVTYPE_2HWEAPON && !IsTwoHandUsed()))
return;
+
ItemPosCountVec off_dest;
uint8 off_msg = CanStoreItem( NULL_BAG, NULL_SLOT, off_dest, offItem, false );
if( off_msg == EQUIP_ERR_OK )
@@ -17016,18 +20013,22 @@ void Player::AutoUnequipOffhandIfNeed()
offItem->DeleteFromInventoryDB(); // deletes item from character's inventory
offItem->SaveToDB(); // recursive and not have transaction guard into self, item not in inventory and can be save standalone
CharacterDatabase.CommitTransaction();
+
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);
}
}
+
OutdoorPvP * Player::GetOutdoorPvP() const
{
return sOutdoorPvPMgr.GetOutdoorPvPToZoneId(GetZoneId());
}
+
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)
@@ -17047,28 +20048,34 @@ bool Player::HasItemFitToSpellReqirements(SpellEntry const* spellInfo, Item cons
if(Item *item = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, i ))
if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
return true;
+
// shields can be equipped to offhand slot
if(Item *item = GetUseableItemByPos( 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 = GetUseableItemByPos( INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED))
if(item!=ignoreItem && item->IsFitToSpellRequirements(spellInfo))
return true;
+
break;
}
default:
sLog.outError("HasItemFitToSpellReqirements: Not handled spell requirement for item class %u",spellInfo->EquippedItemClass);
break;
}
+
return false;
}
+
bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const
{
// don't take reagents for spells with SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP
if (spellInfo->AttributesEx5 & SPELL_ATTR_EX5_NO_REAGENT_WHILE_PREP &&
HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION))
return true;
+
// Check no reagent use mask
flag96 noReagentMask;
noReagentMask[0] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1);
@@ -17076,14 +20083,17 @@ bool Player::CanNoReagentCast(SpellEntry const* spellInfo) const
noReagentMask[2] = GetUInt32Value(PLAYER_NO_REAGENT_COST_1+2);
if (spellInfo->SpellFamilyFlags & noReagentMask)
return true;
+
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())
@@ -17091,21 +20101,25 @@ void Player::RemoveItemDependentAurasAndCasts( Item * pItem )
++itr;
continue;
}
+
// skip if not item dependent or have alternative item
if(HasItemFitToSpellReqirements(spellInfo,pItem))
{
++itr;
continue;
}
+
// no alt item, remove aura, restart check
RemoveAura(itr);
}
+
// currently casted spells can be dependent from item
for (uint32 i = 0; i < CURRENT_MAX_SPELL; ++i)
if (Spell* spell = GetCurrentSpell(CurrentSpellTypes(i)))
if (spell->getState()!=SPELL_STATE_DELAYED && !HasItemFitToSpellReqirements(spell->m_spellInfo,pItem) )
InterruptSpell(CurrentSpellTypes(i));
}
+
uint32 Player::GetResurrectionSpellId()
{
// search priceless resurrection possibilities
@@ -17130,6 +20144,7 @@ uint32 Player::GetResurrectionSpellId()
sLog.outError("Unhandled spell %u: S.Resurrection",(*itr)->GetId());
continue;
}
+
prio = 3;
}
// Twisting Nether // prio: 2 (max)
@@ -17139,19 +20154,24 @@ uint32 Player::GetResurrectionSpellId()
spell_id = 23700;
}
}
+
// Reincarnation (passive spell) // prio: 1 // Glyph of Renewed Life
if(prio < 1 && HasSpell(20608) && !HasSpellCooldown(21169) && (HasAura(58059) || HasItemCount(17030,1)))
spell_id = 21169;
+
return spell_id;
}
+
// Used in triggers for check "Only to targets that grant experience or honor" req
bool Player::isHonorOrXPTarget(Unit* pVictim)
{
uint32 v_level = pVictim->getLevel();
uint32 k_grey = Trinity::XP::GetGrayLevel(getLevel());
+
// Victim level less gray level
if(v_level<=k_grey)
return false;
+
if(pVictim->GetTypeId() == TYPEID_UNIT)
{
if (((Creature*)pVictim)->isTotem() ||
@@ -17161,58 +20181,73 @@ bool Player::isHonorOrXPTarget(Unit* pVictim)
}
return true;
}
+
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;
Player* not_gray_member_with_max_level = NULL;
+
pGroup->GetDataForXPAtKill(pVictim,count,sum_level,member_with_max_level,not_gray_member_with_max_level);
+
if(member_with_max_level)
{
// PvP kills doesn't yield experience
// also no XP gained if there is no member below gray level
xp = (PvP || !not_gray_member_with_max_level || GetVehicle()) ? 0 : Trinity::XP::Gain(not_gray_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 = Trinity::XP::xp_in_group_rate(count,is_raid);
+
for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* pGroupGuy = itr->getSource();
if(!pGroupGuy)
continue;
+
if(!pGroupGuy->IsAtGroupRewardDistance(pVictim))
continue; // member (alive or dead) or his corpse at req. distance
+
// honor can be in PvP and !PvP (racial leader) cases (for alive)
if(pGroupGuy->isAlive() && pGroupGuy->RewardHonor(pVictim,count, -1, true) && pGroupGuy==this)
honored_kill = true;
+
// xp and reputation only in !PvP case
if(!PvP)
{
float rate = group_rate * float(pGroupGuy->getLevel()) / sum_level;
+
// if is in dungeon then all receive full reputation at kill
// rewarded any alive/dead/near_corpse group member
pGroupGuy->RewardReputation(pVictim,is_dungeon ? 1.0f : rate);
+
// XP updated only for alive group member
if(pGroupGuy->isAlive() && not_gray_member_with_max_level &&
pGroupGuy->getLevel() <= not_gray_member_with_max_level->getLevel())
{
uint32 itr_xp = (member_with_max_level == not_gray_member_with_max_level) ? uint32(xp*rate) : uint32((xp*rate/2)+1);
+
// handle SPELL_AURA_MOD_XP_PCT auras
Unit::AuraEffectList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT);
for(Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
itr_xp = uint32(itr_xp*(1.0f + (*i)->GetAmount() / 100.0f));
+
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())
{
@@ -17227,20 +20262,26 @@ bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim)
else // if (!pGroup)
{
xp = (PvP || GetVehicle()) ? 0 : Trinity::XP::Gain(this, pVictim);
+
// honor can be in PvP and !PvP (racial leader) cases
if(RewardHonor(pVictim,1, -1, true))
honored_kill = true;
+
// xp and reputation only in !PvP case
if(!PvP)
{
RewardReputation(pVictim,1);
+
// handle SPELL_AURA_MOD_XP_PCT auras
Unit::AuraEffectList const& ModXPPctAuras = GetAurasByType(SPELL_AURA_MOD_XP_PCT);
for(Unit::AuraEffectList::const_iterator i = ModXPPctAuras.begin();i != ModXPPctAuras.end(); ++i)
xp = uint32(xp*(1.0f + (*i)->GetAmount() / 100.0f));
+
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(((Creature*)pVictim)->GetCreatureInfo(), pVictim->GetGUID());
@@ -17248,9 +20289,11 @@ bool Player::RewardPlayerAndGroupAtKill(Unit* pVictim)
}
return xp || honored_kill;
}
+
void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewardSource)
{
uint64 creature_guid = pRewardSource->GetTypeId()==TYPEID_UNIT ? pRewardSource->GetGUID() : uint64(0);
+
// prepare data for near group iteration
if(Group *pGroup = GetGroup())
{
@@ -17259,8 +20302,10 @@ void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewar
Player* pGroupGuy = itr->getSource();
if(!pGroupGuy)
continue;
+
if(!pGroupGuy->IsAtGroupRewardDistance(pRewardSource))
continue; // member (alive or dead) or his corpse at req. distance
+
// quest objectives updated only for alive group member or dead but with not released body
if(pGroupGuy->isAlive()|| !pGroupGuy->GetCorpse())
pGroupGuy->KilledMonsterCredit(creature_id, creature_guid);
@@ -17269,30 +20314,38 @@ void Player::RewardPlayerAndGroupAtEvent(uint32 creature_id, WorldObject* pRewar
else // if (!pGroup)
KilledMonsterCredit(creature_id, creature_guid);
}
+
bool Player::IsAtGroupRewardDistance(WorldObject const* pRewardSource) const
{
const WorldObject* player = GetCorpse();
if(!player || isAlive())
player = this;
+
if(player->GetMapId() != pRewardSource->GetMapId() || player->GetInstanceId() != pRewardSource->GetInstanceId())
return false;
+
return pRewardSource->GetDistance(player) <= sWorld.getConfig(CONFIG_GROUP_XP_DISTANCE);
}
+
uint32 Player::GetBaseWeaponSkillValue (WeaponAttackType attType) const
{
Item* item = GetWeaponForAttack(attType,true);
+
// unarmed only with base attack
if(attType != BASE_ATTACK && !item)
return 0;
+
// weapon skill or (unarmed for base attack)
uint32 skill = item ? item->GetSkill() : uint32(SKILL_UNARMED);
return GetBaseSkillValue(skill);
}
+
void Player::ResurectUsingRequestData()
{
/// Teleport before resurrecting by player, otherwise the player might get attacked from creatures near his corpse
if(IS_PLAYER_GUID(m_resurrectGUID))
TeleportTo(m_resurrectMap, m_resurrectX, m_resurrectY, m_resurrectZ, GetOrientation());
+
//we cannot resurrect player when we triggered far teleport
//player will be resurrected upon teleportation
if(IsBeingTeleportedFar())
@@ -17300,19 +20353,26 @@ void Player::ResurectUsingRequestData()
ScheduleDelayedOperation(DELAYED_RESURRECT_PLAYER);
return;
}
+
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();
}
+
void Player::SetClientControl(Unit* target, uint8 allowMove)
{
WorldPacket data(SMSG_CLIENT_CONTROL_UPDATE, target->GetPackGUID().size()+1);
@@ -17322,6 +20382,7 @@ void Player::SetClientControl(Unit* target, uint8 allowMove)
if(target == this)
SetMover(this);
}
+
void Player::UpdateZoneDependentAuras( uint32 newZone )
{
// Some spells applied at enter into zone (with subzones), aura removed in UpdateAreaDependentAuras that called always at zone->area update
@@ -17331,6 +20392,7 @@ void Player::UpdateZoneDependentAuras( uint32 newZone )
if( !HasAura(itr->second->spellId) )
CastSpell(this,itr->second->spellId,true);
}
+
void Player::UpdateAreaDependentAuras( uint32 newArea )
{
// remove auras from spells with area limitations
@@ -17342,12 +20404,14 @@ void Player::UpdateAreaDependentAuras( uint32 newArea )
else
++iter;
}
+
// some auras applied at subzone enter
SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAreaMapBounds(newArea);
for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
if(itr->second->autocast && itr->second->IsFitToRequirements(this,m_zoneUpdateId,newArea))
if( !HasAura(itr->second->spellId) )
CastSpell(this,itr->second->spellId,true);
+
if(newArea == 4273 && GetVehicle() && GetPositionX() > 400) // Ulduar
{
switch(GetVehicleBase()->GetEntry())
@@ -17360,6 +20424,7 @@ void Player::UpdateAreaDependentAuras( uint32 newArea )
}
}
}
+
uint32 Player::GetCorpseReclaimDelay(bool pvp) const
{
if(pvp)
@@ -17369,18 +20434,22 @@ uint32 Player::GetCorpseReclaimDelay(bool pvp) const
}
else if(!sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
return 0;
+
time_t now = time(NULL);
// 0..2 full period
// should be ceil(x)-1 but not floor(x)
uint32 count = (now < m_deathExpireTime - 1) ? (m_deathExpireTime - 1 - 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)
{
@@ -17394,21 +20463,25 @@ void Player::UpdateCorpseReclaimDelay()
else
m_deathExpireTime = now+DEATH_EXPIRE_STEP;
}
+
void Player::SendCorpseReclaimDelay(bool load)
{
Corpse* corpse = GetCorpse();
if(load && !corpse)
return;
+
bool pvp;
if(corpse)
pvp = (corpse->GetType() == CORPSE_RESURRECTABLE_PVP);
else
pvp = (m_ExtraFlags & PLAYER_EXTRA_PVP_DEATH);
+
uint32 delay;
if(load)
{
if(corpse->GetGhostTime() > m_deathExpireTime)
return;
+
uint32 count;
if( pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVP) ||
!pvp && sWorld.getConfig(CONFIG_DEATH_CORPSE_RECLAIM_DELAY_PVE) )
@@ -17419,59 +20492,77 @@ void Player::SendCorpseReclaimDelay(bool load)
}
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(pvp);
+
if(!delay) return;
+
//! corpse reclaim delay 30 * 1000ms or longer at often deaths
WorldPacket data(SMSG_CORPSE_RECLAIM_DELAY, 4);
data << uint32(delay*IN_MILISECONDS);
GetSession()->SendPacket( &data );
}
+
Player* Player::GetNextRandomRaidMember(float radius)
{
Group *pGroup = GetGroup();
if(!pGroup)
return NULL;
+
std::vector<Player*> nearMembers;
nearMembers.reserve(pGroup->GetMembersCount());
+
for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* Target = itr->getSource();
+
// IsHostileTo check duel and controlled by enemy
if( Target && Target != this && IsWithinDistInMap(Target, radius) &&
!Target->HasInvisibilityAura() && !IsHostileTo(Target) )
nearMembers.push_back(Target);
}
+
if (nearMembers.empty())
return NULL;
+
uint32 randTarget = urand(0,nearMembers.size()-1);
return nearMembers[randTarget];
}
+
PartyResult Player::CanUninviteFromGroup() const
{
const Group* grp = GetGroup();
if(!grp)
return PARTY_RESULT_YOU_NOT_IN_GROUP;
+
if(!grp->IsLeader(GetGUID()) && !grp->IsAssistant(GetGUID()))
return PARTY_RESULT_YOU_NOT_LEADER;
+
if(InBattleGround())
return PARTY_RESULT_INVITE_RESTRICTED;
+
return PARTY_RESULT_OK;
}
+
void Player::SetBattleGroundRaid(Group* group, int8 subgroup)
{
//we must move references from m_group to m_originalGroup
SetOriginalGroup(GetGroup(), GetSubGroup());
+
m_group.unlink();
m_group.link(group, this);
m_group.setSubGroup((uint8)subgroup);
}
+
void Player::RemoveFromBattleGroundRaid()
{
//remove existing reference
@@ -17483,6 +20574,7 @@ void Player::RemoveFromBattleGroundRaid()
}
SetOriginalGroup(NULL);
}
+
void Player::SetOriginalGroup(Group *group, int8 subgroup)
{
if( group == NULL )
@@ -17495,6 +20587,7 @@ void Player::SetOriginalGroup(Group *group, int8 subgroup)
m_originalGroup.setSubGroup((uint8)subgroup);
}
}
+
void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
{
LiquidData liquid_status;
@@ -17507,6 +20600,7 @@ void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
m_MirrorTimerFlags|=UNDERWATER_INWATER;
return;
}
+
// All liquids type - check under water position
if (liquid_status.type&(MAP_LIQUID_TYPE_WATER|MAP_LIQUID_TYPE_OCEAN|MAP_LIQUID_TYPE_MAGMA|MAP_LIQUID_TYPE_SLIME))
{
@@ -17515,11 +20609,13 @@ void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
else
m_MirrorTimerFlags &= ~UNDERWATER_INWATER;
}
+
// Allow travel in dark water on taxi or transport
if ((liquid_status.type & MAP_LIQUID_TYPE_DARK_WATER) && !isInFlight() && !GetTransport())
m_MirrorTimerFlags |= UNDERWARER_INDARKWATER;
else
m_MirrorTimerFlags &= ~UNDERWARER_INDARKWATER;
+
// in lava check, anywhere in lava level
if (liquid_status.type&MAP_LIQUID_TYPE_MAGMA)
{
@@ -17537,20 +20633,25 @@ void Player::UpdateUnderwaterState( Map* m, float x, float y, float z )
m_MirrorTimerFlags &= ~UNDERWATER_INSLIME;
}
}
+
void Player::SetCanParry( bool value )
{
if(m_canParry==value)
return;
+
m_canParry = value;
UpdateParryPercentage();
}
+
void Player::SetCanBlock( bool value )
{
if(m_canBlock==value)
return;
+
m_canBlock = value;
UpdateBlockPercentage();
}
+
bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
{
for(ItemPosCountVec::const_iterator itr = vec.begin(); itr != vec.end();++itr)
@@ -17558,9 +20659,11 @@ bool ItemPosCount::isContainedIn(ItemPosCountVec const& vec) const
return true;
return false;
}
+
//***********************************
//-------------TRINITY---------------
//***********************************
+
void Player::StopCastingBindSight()
{
if(WorldObject* target = GetViewpoint())
@@ -17573,43 +20676,53 @@ void Player::StopCastingBindSight()
}
}
}
+
void Player::SetViewpoint(WorldObject* target, bool apply)
{
if(apply)
{
sLog.outDebug("Player::CreateViewpoint: Player %s create seer %u (TypeId: %u).", GetName(), target->GetEntry(), target->GetTypeId());
+
if(!AddUInt64Value(PLAYER_FARSIGHT, target->GetGUID()))
{
sLog.outCrash("Player::CreateViewpoint: Player %s cannot add new viewpoint!", GetName());
return;
}
+
// farsight dynobj or puppet may be very far away
UpdateVisibilityOf(target);
+
if(target->isType(TYPEMASK_UNIT) && !GetVehicle())
((Unit*)target)->AddPlayerToVision(this);
}
else
{
sLog.outDebug("Player::CreateViewpoint: Player %s remove seer", GetName());
+
if(!RemoveUInt64Value(PLAYER_FARSIGHT, target->GetGUID()))
{
sLog.outCrash("Player::CreateViewpoint: Player %s cannot remove current viewpoint!", GetName());
return;
}
+
if(target->isType(TYPEMASK_UNIT) && !GetVehicle())
((Unit*)target)->RemovePlayerFromVision(this);
+
//must immediately set seer back otherwise may crash
m_seer = this;
+
//WorldPacket data(SMSG_CLEAR_FAR_SIGHT_IMMEDIATE, 0);
//GetSession()->SendPacket(&data);
}
}
+
WorldObject* Player::GetViewpoint() const
{
if(uint64 guid = GetUInt64Value(PLAYER_FARSIGHT))
return (WorldObject*)ObjectAccessor::GetObjectByTypeMask(*this, guid, TYPEMASK_SEER);
return NULL;
}
+
bool Player::CanUseBattleGroundObject()
{
// TODO : some spells gives player ForceReaction to one faction (ReputationMgr::ApplyForceReaction)
@@ -17626,6 +20739,7 @@ bool Player::CanUseBattleGroundObject()
isAlive() // live player
);
}
+
bool Player::CanCaptureTowerPoint()
{
return ( !HasStealthAura() && // not stealthed
@@ -17633,36 +20747,50 @@ bool Player::CanCaptureTowerPoint()
isAlive() // live player
);
}
+
uint32 Player::GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair)
{
uint32 level = getLevel();
+
if(level > GT_MAX_LEVEL)
level = GT_MAX_LEVEL; // max level in this dbc
+
uint8 hairstyle = GetByteValue(PLAYER_BYTES, 2);
uint8 haircolor = GetByteValue(PLAYER_BYTES, 3);
uint8 facialhair = GetByteValue(PLAYER_BYTES_2, 0);
+
if((hairstyle == newhairstyle) && (haircolor == newhaircolor) && (facialhair == newfacialhair))
return 0;
+
GtBarberShopCostBaseEntry const *bsc = sGtBarberShopCostBaseStore.LookupEntry(level - 1);
+
if(!bsc) // shouldn't happen
return 0xFFFFFFFF;
+
float cost = 0;
+
if(hairstyle != newhairstyle)
cost += bsc->cost; // full price
+
if((haircolor != newhaircolor) && (hairstyle == newhairstyle))
cost += bsc->cost * 0.5f; // +1/2 of price
+
if(facialhair != newfacialhair)
cost += bsc->cost * 0.75f; // +3/4 of price
+
return uint32(cost);
}
+
void Player::InitGlyphsForLevel()
{
for(uint32 i = 0; i < sGlyphSlotStore.GetNumRows(); ++i)
if(GlyphSlotEntry const * gs = sGlyphSlotStore.LookupEntry(i))
if(gs->Order)
SetGlyphSlot(gs->Order - 1, gs->Id);
+
uint32 level = getLevel();
uint32 value = 0;
+
// 0x3F = 0x01 | 0x02 | 0x04 | 0x08 | 0x10 | 0x20 for 80 level
if(level >= 15)
value |= (0x01 | 0x02);
@@ -17674,11 +20802,14 @@ void Player::InitGlyphsForLevel()
value |= 0x10;
if(level >= 80)
value |= 0x20;
+
SetUInt32Value(PLAYER_GLYPHS_ENABLED, value);
}
+
bool Player::isTotalImmune()
{
AuraEffectList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY);
+
uint32 immuneMask = 0;
for(AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
{
@@ -17688,39 +20819,48 @@ bool Player::isTotalImmune()
}
return false;
}
+
bool Player::HasTitle(uint32 bitIndex)
{
if (bitIndex > MAX_TITLE_INDEX)
return false;
+
uint32 fieldIndexOffset = bitIndex / 32;
uint32 flag = 1 << (bitIndex % 32);
return HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
}
+
void Player::SetTitle(CharTitlesEntry const* title, bool lost)
{
uint32 fieldIndexOffset = title->bit_index / 32;
uint32 flag = 1 << (title->bit_index % 32);
+
if(lost)
{
if(!HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
return;
+
RemoveFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
}
else
{
if(HasFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag))
return;
+
SetFlag(PLAYER__FIELD_KNOWN_TITLES + fieldIndexOffset, flag);
}
+
WorldPacket data(SMSG_TITLE_EARNED, 4 + 4);
data << uint32(title->bit_index);
data << uint32(lost ? 0 : 1); // 1 - earned, 0 - lost
GetSession()->SendPacket(&data);
}
+
/*-----------------------TRINITY--------------------------*/
bool Player::isTotalImmunity()
{
AuraEffectList const& immune = GetAurasByType(SPELL_AURA_SCHOOL_IMMUNITY);
+
for(AuraEffectList::const_iterator itr = immune.begin(); itr != immune.end(); ++itr)
{
if (((*itr)->GetMiscValue() & SPELL_SCHOOL_MASK_ALL) !=0) // total immunity
@@ -17740,10 +20880,12 @@ bool Player::isTotalImmunity()
}
return false;
}
+
void Player::UpdateCharmedAI()
{
//This should only called in Player::Update
Creature *charmer = (Creature*)GetCharmer();
+
//kill self if charm aura has infinite duration
if(charmer->IsInEvadeMode())
{
@@ -17755,26 +20897,32 @@ void Player::UpdateCharmedAI()
return;
}
}
+
if(!charmer->isInCombat())
GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, PET_FOLLOW_ANGLE);
+
Unit *target = getVictim();
if(!target || !charmer->canAttack(target))
{
target = charmer->SelectNearestTarget();
if(!target)
return;
+
GetMotionMaster()->MoveChase(target);
Attack(target, true);
}
}
+
void Player::ConvertRune(uint8 index, RuneType newType)
{
SetCurrentRune(index, newType);
+
WorldPacket data(SMSG_CONVERT_RUNE, 2);
data << uint8(index);
data << uint8(newType);
GetSession()->SendPacket(&data);
}
+
void Player::ResyncRunes(uint8 count)
{
WorldPacket data(SMSG_RESYNC_RUNES, count * 2);
@@ -17785,12 +20933,14 @@ void Player::ResyncRunes(uint8 count)
}
GetSession()->SendPacket(&data);
}
+
void Player::AddRunePower(uint8 index)
{
WorldPacket data(SMSG_ADD_RUNE_POWER, 4);
data << uint32(1 << index); // mask (0x00-0x3F probably)
GetSession()->SendPacket(&data);
}
+
static RuneType runeSlotTypes[MAX_RUNES] = {
/*0*/ RUNE_BLOOD,
/*1*/ RUNE_BLOOD,
@@ -17799,13 +20949,17 @@ static RuneType runeSlotTypes[MAX_RUNES] = {
/*4*/ RUNE_FROST,
/*5*/ RUNE_FROST
};
+
void Player::InitRunes()
{
if(getClass() != CLASS_DEATH_KNIGHT)
return;
+
m_runes = new Runes;
+
m_runes->runeState = 0;
m_runes->lastUsedRune = RUNE_BLOOD;
+
for(uint32 i = 0; i < MAX_RUNES; ++i)
{
SetBaseRune(i, runeSlotTypes[i]); // init base types
@@ -17813,24 +20967,30 @@ void Player::InitRunes()
SetRuneCooldown(i, 0); // reset cooldowns
m_runes->SetRuneState(i);
}
+
for(uint32 i = 0; i < NUM_RUNE_TYPES; ++i)
SetFloatValue(PLAYER_RUNE_REGEN_1 + i, 0.1f);
}
+
bool Player::IsBaseRuneSlotsOnCooldown(RuneType runeType) const
{
for(uint32 i = 0; i < MAX_RUNES; ++i)
if (GetBaseRune(i) == runeType && GetRuneCooldown(i) == 0)
return false;
+
return true;
}
+
void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast)
{
Loot loot;
loot.FillLoot (loot_id,store,this,true);
+
uint32 max_slot = loot.GetMaxSlotInLootFor(this);
for(uint32 i = 0; i < max_slot; ++i)
{
LootItem* lootItem = loot.LootItemInSlot(i,this);
+
ItemPosCountVec dest;
uint8 msg = CanStoreNewItem (bag,slot,dest,lootItem->itemid,lootItem->count);
if(msg != EQUIP_ERR_OK && slot != NULL_SLOT)
@@ -17842,50 +21002,65 @@ void Player::AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore cons
SendEquipError( msg, NULL, NULL );
continue;
}
+
Item* pItem = StoreNewItem (dest,lootItem->itemid,true,lootItem->randomPropertyId);
SendNewItem(pItem, lootItem->count, false, false, broadcast);
}
}
+
uint32 Player::CalculateTalentsPoints() const
{
uint32 base_talent = getLevel() < 10 ? 0 : getLevel()-9;
+
if(getClass() != CLASS_DEATH_KNIGHT || GetMapId() != 609)
return uint32(base_talent * sWorld.getRate(RATE_TALENT));
+
uint32 talentPointsForLevel = getLevel() < 56 ? 0 : getLevel() - 55;
talentPointsForLevel += m_questRewardTalentCount;
+
if(talentPointsForLevel > base_talent)
talentPointsForLevel = base_talent;
+
return uint32(talentPointsForLevel * sWorld.getRate(RATE_TALENT));
}
+
bool Player::IsKnowHowFlyIn(uint32 mapid, uint32 zone) const
{
// continent checked in SpellMgr::GetSpellAllowedInLocationError at cast and area update
uint32 v_map = GetVirtualMapForMapAndZone(mapid, zone);
return v_map != 571 || HasSpell(54197) && zone != 4197; // Cold Weather Flying
}
+
void Player::learnSpellHighRank(uint32 spellid)
{
learnSpell(spellid,false);
+
if(uint32 next = spellmgr.GetNextSpellInChain(spellid))
learnSpellHighRank(next);
}
+
void Player::_LoadSkills()
{
// Note: skill data itself loaded from `data` field. This is only cleanup part of load
+
// 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));
+
// set fixed skill ranges
switch(GetSkillRangeType(pSkill,false))
{
@@ -17898,9 +21073,11 @@ void Player::_LoadSkills()
default:
break;
}
+
uint32 vskill = SKILL_VALUE(GetUInt32Value(PLAYER_SKILL_VALUE_INDEX(i)));
learnSkillRewardedSpells(id, vskill);
}
+
// special settings
if(getClass()==CLASS_DEATH_KNIGHT)
{
@@ -17910,6 +21087,7 @@ void Player::_LoadSkills()
uint32 base_skill = (base_level-1)*5; // 270 at starting level 55
if(base_skill < 1)
base_skill = 1; // skill mast be known and then > 0 in any case
+
if(GetPureSkillValue (SKILL_FIRST_AID) < base_skill)
SetSkill(SKILL_FIRST_AID,base_skill, base_skill);
if(GetPureSkillValue (SKILL_AXES) < base_skill)
@@ -17928,6 +21106,7 @@ void Player::_LoadSkills()
SetSkill(SKILL_UNARMED, base_skill,base_skill);
}
}
+
uint32 Player::GetPhaseMaskForSpawn() const
{
uint32 phase = PHASEMASK_NORMAL;
@@ -17939,17 +21118,22 @@ uint32 Player::GetPhaseMaskForSpawn() const
if(!phases.empty())
phase = phases.front()->GetMiscValue();
}
+
// some aura phases include 1 normal map in addition to phase itself
if(uint32 n_phase = phase & ~PHASEMASK_NORMAL)
return n_phase;
+
return PHASEMASK_NORMAL;
}
+
uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) const
{
ItemPrototype const* pProto = pItem->GetProto();
+
// proto based limitations
if(uint8 res = CanEquipUniqueItem(pProto,eslot,limit_count))
return res;
+
// check unique-equipped on gems
for(uint32 enchant_slot = SOCK_ENCHANTMENT_SLOT; enchant_slot < SOCK_ENCHANTMENT_SLOT+3; ++enchant_slot)
{
@@ -17959,17 +21143,22 @@ uint8 Player::CanEquipUniqueItem(Item* pItem, uint8 eslot, uint32 limit_count) c
SpellItemEnchantmentEntry const* enchantEntry = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if(!enchantEntry)
continue;
+
ItemPrototype const* pGem = objmgr.GetItemPrototype(enchantEntry->GemID);
if(!pGem)
continue;
+
// include for check equip another gems with same limit category for not equipped item (and then not counted)
uint32 gem_limit_count = !pItem->IsEquipped() && pGem->ItemLimitCategory
? pItem->GetGemCountWithLimitCategory(pGem->ItemLimitCategory) : 1;
+
if(uint8 res = CanEquipUniqueItem(pGem, eslot,gem_limit_count))
return res;
}
+
return EQUIP_ERR_OK;
}
+
uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot, uint32 limit_count) const
{
// check unique-equipped on item
@@ -17979,25 +21168,31 @@ uint8 Player::CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_s
if(HasItemOrGemWithIdEquipped(itemProto->ItemId,1,except_slot))
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
}
+
// check unique-equipped limit
if (itemProto->ItemLimitCategory)
{
ItemLimitCategoryEntry const* limitEntry = sItemLimitCategoryStore.LookupEntry(itemProto->ItemLimitCategory);
if(!limitEntry)
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
+
if(limit_count > limitEntry->maxCount)
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE; // attempt add too many limit category items (gems)
+
// there is an equip limit on this item
if(HasItemOrGemWithLimitCategoryEquipped(itemProto->ItemLimitCategory,limitEntry->maxCount-limit_count+1,except_slot))
return EQUIP_ERR_ITEM_UNIQUE_EQUIPABLE;
}
+
return EQUIP_ERR_OK;
}
+
void Player::HandleFall(MovementInfo const& movementInfo)
{
// calculate total z distance of the fall
float z_diff = m_lastFallZ - movementInfo.z;
//sLog.outDebug("zDiff = %f", z_diff);
+
//Players with low fall distance, Feather Fall or physical immunity (charges used) are ignored
// 14.57 can be calculated by resolving damageperc formula below to 0
if (z_diff >= 14.57f && !isDead() && !isGameMaster() &&
@@ -18006,55 +21201,74 @@ void Player::HandleFall(MovementInfo const& movementInfo)
{
//Safe fall, fall height reduction
int32 safe_fall = GetTotalAuraModifier(SPELL_AURA_SAFE_FALL);
+
float damageperc = 0.018f*(z_diff-safe_fall)-0.2426f;
+
if(damageperc >0 )
{
uint32 damage = (uint32)(damageperc * GetMaxHealth()*sWorld.getRate(RATE_DAMAGE_FALL));
+
float height = movementInfo.z;
UpdateGroundPositionZ(movementInfo.x,movementInfo.y,height);
+
if (damage > 0)
{
//Prevent fall damage from being more than the player maximum health
if (damage > GetMaxHealth())
damage = GetMaxHealth();
+
// Gust of Wind
if (HasAura(43621))
damage = GetMaxHealth()/2;
+
uint32 original_health = GetHealth();
uint32 final_damage = EnvironmentalDamage(DAMAGE_FALL, damage);
+
// recheck alive, might have died of EnvironmentalDamage, avoid cases when player die in fact like Spirit of Redemption case
if (isAlive() && final_damage < original_health)
GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_FALL_WITHOUT_DYING, uint32(z_diff*100));
}
+
//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, GetPositionZ(), movementInfo.fallTime, height, damage, safe_fall);
}
}
}
+
void Player::UpdateAchievementCriteria( AchievementCriteriaTypes type, uint32 miscvalue1/*=0*/, uint32 miscvalue2/*=0*/, Unit *unit/*=NULL*/, uint32 time/*=0*/ )
{
GetAchievementMgr().UpdateAchievementCriteria(type, miscvalue1, miscvalue2, unit, time);
}
+
void Player::CompletedAchievement(AchievementEntry const* entry)
{
GetAchievementMgr().CompletedAchievement(entry);
}
+
void Player::LearnTalent(uint32 talentId, uint32 talentRank)
{
uint32 CurTalentPoints = GetFreeTalentPoints();
+
if(CurTalentPoints == 0)
return;
+
if (talentRank >= MAX_TALENT_RANK)
return;
+
TalentEntry const *talentInfo = sTalentStore.LookupEntry( talentId );
+
if(!talentInfo)
return;
+
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentInfo->TalentTab );
+
if(!talentTabInfo)
return;
+
// prevent learn talent for different class (cheating)
if( (getClassMask() & talentTabInfo->ClassMask) == 0 )
return;
+
// find current max talent rank (0~5)
uint8 curtalent_maxrank = 0; // 0 = not learned any rank
for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
@@ -18065,12 +21279,15 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
break;
}
}
+
// we already have same or higher talent rank learned
if(curtalent_maxrank >= (talentRank + 1))
return;
+
// check if we have enough talent points
if(CurTalentPoints < (talentRank - curtalent_maxrank + 1))
return;
+
// Check if it requires another talent
if (talentInfo->DependsOn > 0)
{
@@ -18087,8 +21304,10 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
return;
}
}
+
// Find out how many points we have in this field
uint32 spentPoints = 0;
+
uint32 tTab = talentInfo->TalentTab;
if (talentInfo->Row > 0)
{
@@ -18115,9 +21334,11 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
}
}
}
+
// not have required min points spent in talent tree
if(spentPoints < (talentInfo->Row * MAX_TALENT_RANK))
return;
+
// spell not set in talent.dbc
uint32 spellid = talentInfo->RankID[talentRank];
if( spellid == 0 )
@@ -18125,45 +21346,66 @@ void Player::LearnTalent(uint32 talentId, uint32 talentRank)
sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
return;
}
+
// already known
if(HasSpell(spellid))
return;
+
// learn! (other talent ranks will unlearned at learning)
learnSpell(spellid, false);
AddTalent(spellid, m_activeSpec, true);
+
sLog.outDetail("TalentID: %u Rank: %u Spell: %u Spec: %u\n", talentId, talentRank, spellid, m_activeSpec);
+
// update free talent points
SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}
+
void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
{
Pet *pet = GetPet();
+
if(!pet)
return;
+
if(petGuid != pet->GetGUID())
return;
+
uint32 CurTalentPoints = pet->GetFreeTalentPoints();
+
if(CurTalentPoints == 0)
return;
+
if (talentRank >= MAX_PET_TALENT_RANK)
return;
+
TalentEntry const *talentInfo = sTalentStore.LookupEntry(talentId);
+
if(!talentInfo)
return;
+
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry(talentInfo->TalentTab);
+
if(!talentTabInfo)
return;
+
CreatureInfo const *ci = pet->GetCreatureInfo();
+
if(!ci)
return;
+
CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
+
if(!pet_family)
return;
+
if(pet_family->petTalentType < 0) // not hunter pet
return;
+
// prevent learn talent for different family (cheating)
if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
return;
+
// find current max talent rank (0~5)
uint8 curtalent_maxrank = 0; // 0 = not learned any rank
for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
@@ -18174,12 +21416,15 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
break;
}
}
+
// we already have same or higher talent rank learned
if(curtalent_maxrank >= (talentRank + 1))
return;
+
// check if we have enough talent points
if(CurTalentPoints < (talentRank - curtalent_maxrank + 1))
return;
+
// Check if it requires another talent
if (talentInfo->DependsOn > 0)
{
@@ -18196,8 +21441,10 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
return;
}
}
+
// Find out how many points we have in this field
uint32 spentPoints = 0;
+
uint32 tTab = talentInfo->TalentTab;
if (talentInfo->Row > 0)
{
@@ -18224,9 +21471,11 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
}
}
}
+
// not have required min points spent in talent tree
if(spentPoints < (talentInfo->Row * MAX_PET_TALENT_RANK))
return;
+
// spell not set in talent.dbc
uint32 spellid = talentInfo->RankID[talentRank];
if( spellid == 0 )
@@ -18234,15 +21483,19 @@ void Player::LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank)
sLog.outError("Talent.dbc have for talent: %u Rank: %u spell id = 0", talentId, talentRank);
return;
}
+
// already known
if(pet->HasSpell(spellid))
return;
+
// learn! (other talent ranks will unlearned at learning)
pet->learnSpell(spellid);
sLog.outDetail("PetTalentID: %u Rank: %u Spell: %u\n", talentId, talentRank, spellid);
+
// update free talent points
pet->SetFreeTalentPoints(CurTalentPoints - (talentRank - curtalent_maxrank + 1));
}
+
void Player::UpdateKnownCurrencies(uint32 itemId, bool apply)
{
if(CurrencyTypesEntry const* ctEntry = sCurrencyTypesStore.LookupEntry(itemId))
@@ -18253,54 +21506,69 @@ void Player::UpdateKnownCurrencies(uint32 itemId, bool apply)
RemoveFlag64(PLAYER_FIELD_KNOWN_CURRENCIES,(1LL << (ctEntry->BitIndex-1)));
}
}
+
void Player::UpdateFallInformationIfNeed( MovementInfo const& minfo,uint16 opcode )
{
if (m_lastFallTime >= minfo.fallTime || m_lastFallZ <=minfo.z || opcode == MSG_MOVE_FALL_LAND)
SetFallInformation(minfo.fallTime, minfo.z);
}
+
void Player::UnsummonPetTemporaryIfAny()
{
Pet* pet = GetPet();
if(!pet)
return;
+
if(!m_temporaryUnsummonedPetNumber && pet->isControlled() && !pet->isTemporarySummoned() )
{
m_temporaryUnsummonedPetNumber = pet->GetCharmInfo()->GetPetNumber();
m_oldpetspell = pet->GetUInt32Value(UNIT_CREATED_BY_SPELL);
}
+
RemovePet(pet, PET_SAVE_AS_CURRENT);
}
+
void Player::ResummonPetTemporaryUnSummonedIfAny()
{
if(!m_temporaryUnsummonedPetNumber)
return;
+
// not resummon in not appropriate state
if(IsPetNeedBeTemporaryUnsummoned())
return;
+
if(GetPetGUID())
return;
+
Pet* NewPet = new Pet(this);
if(!NewPet->LoadPetFromDB(this, 0, m_temporaryUnsummonedPetNumber, true))
delete NewPet;
+
m_temporaryUnsummonedPetNumber = 0;
}
+
bool Player::canSeeSpellClickOn(Creature const *c) const
{
if(!c->HasFlag(UNIT_NPC_FLAGS,UNIT_NPC_FLAG_SPELLCLICK))
return false;
+
SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(c->GetEntry());
if(clickPair.first == clickPair.second)
return true;
+
for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
if(itr->second.IsFitToRequirements(this, c))
return true;
+
return false;
}
+
void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
{
*data << uint32(GetFreeTalentPoints()); // unspentTalentPoints
*data << uint8(m_specsCount); // talent group count (0, 1 or 2)
*data << uint8(m_activeSpec); // talent group index (0 or 1)
+
if(m_specsCount)
{
// loop through all specs (only 1 for now)
@@ -18309,19 +21577,24 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
uint8 talentIdCount = 0;
size_t pos = data->wpos();
*data << uint8(talentIdCount); // [PH], talentIdCount
+
// find class talent tabs (all players have 3 talent tabs)
uint32 const* talentTabIds = GetTalentTabPages(getClass());
+
for(uint8 i = 0; i < 3; ++i)
{
uint32 talentTabId = talentTabIds[i];
+
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 max talent rank (0~4)
int8 curtalent_maxrank = -1;
for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
@@ -18332,55 +21605,73 @@ void Player::BuildPlayerTalentsInfoData(WorldPacket *data)
break;
}
}
+
// not learned talent
if(curtalent_maxrank < 0)
continue;
+
*data << uint32(talentInfo->TalentID); // Talent.dbc
*data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
+
++talentIdCount;
}
}
+
data->put<uint8>(pos, talentIdCount); // put real count
+
*data << uint8(MAX_GLYPH_SLOT_INDEX); // glyphs count
+
for(uint8 i = 0; i < MAX_GLYPH_SLOT_INDEX; ++i)
*data << uint16(m_Glyphs[specIdx][i]); // GlyphProperties.dbc
}
}
}
+
void Player::BuildPetTalentsInfoData(WorldPacket *data)
{
uint32 unspentTalentPoints = 0;
size_t pointsPos = data->wpos();
*data << uint32(unspentTalentPoints); // [PH], unspentTalentPoints
+
uint8 talentIdCount = 0;
size_t countPos = data->wpos();
*data << uint8(talentIdCount); // [PH], talentIdCount
+
Pet *pet = GetPet();
if(!pet)
return;
+
unspentTalentPoints = pet->GetFreeTalentPoints();
+
data->put<uint32>(pointsPos, unspentTalentPoints); // put real points
+
CreatureInfo const *ci = pet->GetCreatureInfo();
if(!ci)
return;
+
CreatureFamilyEntry const *pet_family = sCreatureFamilyStore.LookupEntry(ci->family);
if(!pet_family || pet_family->petTalentType < 0)
return;
+
for(uint32 talentTabId = 1; talentTabId < sTalentTabStore.GetNumRows(); ++talentTabId)
{
TalentTabEntry const *talentTabInfo = sTalentTabStore.LookupEntry( talentTabId );
if(!talentTabInfo)
continue;
+
if(!((1 << pet_family->petTalentType) & talentTabInfo->petTalentMask))
continue;
+
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 max talent rank (0~4)
int8 curtalent_maxrank = -1;
for(int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
@@ -18391,17 +21682,23 @@ void Player::BuildPetTalentsInfoData(WorldPacket *data)
break;
}
}
+
// not learned talent
if(curtalent_maxrank < 0)
continue;
+
*data << uint32(talentInfo->TalentID); // Talent.dbc
*data << uint8(curtalent_maxrank); // talentMaxRank (0-4)
+
++talentIdCount;
}
+
data->put<uint8>(countPos, talentIdCount); // put real count
+
break;
}
}
+
void Player::SendTalentsInfoData(bool pet)
{
WorldPacket data(SMSG_TALENTS_INFO, 50);
@@ -18412,37 +21709,51 @@ void Player::SendTalentsInfoData(bool pet)
BuildPlayerTalentsInfoData(&data);
GetSession()->SendPacket(&data);
}
+
void Player::BuildEnchantmentsInfoData(WorldPacket *data)
{
uint32 slotUsedMask = 0;
size_t slotUsedMaskPos = data->wpos();
*data << uint32(slotUsedMask); // slotUsedMask < 0x80000
+
for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
{
Item *item = GetItemByPos(INVENTORY_SLOT_BAG_0, i);
+
if(!item)
continue;
+
slotUsedMask |= (1 << i);
+
*data << uint32(item->GetEntry()); // item entry
+
uint16 enchantmentMask = 0;
size_t enchantmentMaskPos = data->wpos();
*data << uint16(enchantmentMask); // enchantmentMask < 0x1000
+
for(uint32 j = 0; j < MAX_ENCHANTMENT_SLOT; ++j)
{
uint32 enchId = item->GetEnchantmentId(EnchantmentSlot(j));
+
if(!enchId)
continue;
+
enchantmentMask |= (1 << j);
+
*data << uint16(enchId); // enchantmentId?
}
+
data->put<uint16>(enchantmentMaskPos, enchantmentMask);
+
*data << uint16(0); // unknown
data->appendPackGUID(item->GetUInt64Value(ITEM_FIELD_CREATOR)); // item creator
*data << uint32(0); // seed?
}
+
data->put<uint32>(slotUsedMaskPos, slotUsedMask);
}
+
void Player::SendEquipmentSetList()
{
uint32 count = 0;
@@ -18459,16 +21770,19 @@ void Player::SendEquipmentSetList()
data << itr->second.IconName;
for(uint32 i = 0; i < EQUIPMENT_SLOT_END; ++i)
data.appendPackGUID(MAKE_NEW_GUID(itr->second.Items[i], 0, HIGHGUID_ITEM));
+
++count; // client have limit but it checked at loading and set
}
data.put<uint32>(count_pos, count);
GetSession()->SendPacket(&data);
}
+
void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
{
if(eqset.Guid != 0)
{
bool found = false;
+
for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
{
if((itr->second.Guid == eqset.Guid) && (itr->first == index))
@@ -18477,25 +21791,33 @@ void Player::SetEquipmentSet(uint32 index, EquipmentSet eqset)
break;
}
}
+
if(!found) // something wrong...
{
sLog.outError("Player %s tried to save equipment set "UI64FMTD" (index %u), but that equipment set not found!", GetName(), eqset.Guid, index);
return;
}
}
+
EquipmentSet& eqslot = m_EquipmentSets[index];
+
EquipmentSetUpdateState old_state = eqslot.state;
+
eqslot = eqset;
+
if(eqset.Guid == 0)
{
eqslot.Guid = objmgr.GenerateEquipmentSetGuid();
+
WorldPacket data(SMSG_EQUIPMENT_SET_SAVED, 4 + 1);
data << uint32(index);
data.appendPackGUID(eqslot.Guid);
GetSession()->SendPacket(&data);
}
+
eqslot.state = old_state == EQUIPMENT_SET_NEW ? EQUIPMENT_SET_NEW : EQUIPMENT_SET_CHANGED;
}
+
void Player::_SaveEquipmentSets()
{
for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end();)
@@ -18528,6 +21850,7 @@ void Player::_SaveEquipmentSets()
}
}
}
+
void Player::_SaveBGData()
{
CharacterDatabase.PExecute("DELETE FROM character_battleground_data WHERE guid='%u'", GetGUIDLow());
@@ -18539,6 +21862,7 @@ void Player::_SaveBGData()
m_bgData.joinPos.GetOrientation(), m_bgData.joinPos.GetMapId(), m_bgData.taxiPath[0], m_bgData.taxiPath[1], m_bgData.mountSpell);
}
}
+
void Player::DeleteEquipmentSet(uint64 setGuid)
{
for(EquipmentSets::iterator itr = m_EquipmentSets.begin(); itr != m_EquipmentSets.end(); ++itr)
@@ -18553,12 +21877,15 @@ void Player::DeleteEquipmentSet(uint64 setGuid)
}
}
}
+
void Player::RemoveAtLoginFlag( AtLoginFlags f, bool in_db_also /*= false*/ )
{
m_atLoginFlags &= ~f;
+
if(in_db_also)
CharacterDatabase.PExecute("UPDATE characters set at_login = at_login & ~ %u WHERE guid ='%u'", uint32(f), GetGUIDLow());
}
+
void Player::SendClearCooldown( uint32 spell_id, Unit* target )
{
WorldPacket data(SMSG_CLEAR_COOLDOWN, 4+8);
@@ -18566,6 +21893,7 @@ void Player::SendClearCooldown( uint32 spell_id, Unit* target )
data << uint64(target->GetGUID());
SendDirectMessage(&data);
}
+
void Player::ResetMap()
{
// this may be called during Map::Update
@@ -18577,31 +21905,39 @@ void Player::ResetMap()
Unit::ResetMap();
GetMapRef().unlink();
}
+
void Player::SetMap(Map * map)
{
Unit::SetMap(map);
m_mapRef.link(map, this);
}
+
void Player::_LoadGlyphs(QueryResult *result)
{
// SetPQuery(PLAYER_LOGIN_QUERY_LOADGLYPHS, "SELECT spec, glyph1, glyph2, glyph3, glyph4, glyph5, glyph6 from character_glyphs WHERE guid = '%u'", GUID_LOPART(m_guid));
if (!result)
return;
+
do
{
Field *fields = result->Fetch();
+
uint8 spec = fields[0].GetUInt8();
if (spec >= m_specsCount)
continue;
+
m_Glyphs[spec][0] = fields[1].GetUInt32();
m_Glyphs[spec][1] = fields[2].GetUInt32();
m_Glyphs[spec][2] = fields[3].GetUInt32();
m_Glyphs[spec][3] = fields[4].GetUInt32();
m_Glyphs[spec][4] = fields[5].GetUInt32();
m_Glyphs[spec][5] = fields[6].GetUInt32();
+
} while (result->NextRow());
+
delete result;
}
+
void Player::_SaveGlyphs()
{
CharacterDatabase.PExecute("DELETE FROM character_glyphs WHERE guid='%u'",GetGUIDLow());
@@ -18611,6 +21947,7 @@ void Player::_SaveGlyphs()
GetGUIDLow(), spec, m_Glyphs[spec][0], m_Glyphs[spec][1], m_Glyphs[spec][2], m_Glyphs[spec][3], m_Glyphs[spec][4], m_Glyphs[spec][5]);
}
}
+
void Player::_LoadTalents(QueryResult *result)
{
// SetPQuery(PLAYER_LOGIN_QUERY_LOADTALENTS, "SELECT spell, spec FROM character_talent WHERE guid = '%u'", GUID_LOPART(m_guid));
@@ -18619,12 +21956,15 @@ void Player::_LoadTalents(QueryResult *result)
do
{
Field *fields = result->Fetch();
+
AddTalent(fields[0].GetUInt32(), fields[1].GetUInt32(), false);
}
while( result->NextRow() );
+
delete result;
}
}
+
void Player::_SaveTalents()
{
for (uint8 i = 0; i < MAX_TALENT_SPECS; ++i)
@@ -18633,8 +21973,10 @@ void Player::_SaveTalents()
{
if (itr->second->state == PLAYERSPELL_REMOVED || itr->second->state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("DELETE FROM character_talent WHERE guid = '%u' and spell = '%u' and spec = '%u'", GetGUIDLow(), itr->first, itr->second->spec);
+
if (itr->second->state == PLAYERSPELL_NEW || itr->second->state == PLAYERSPELL_CHANGED)
CharacterDatabase.PExecute("INSERT INTO character_talent (guid,spell,spec) VALUES ('%u', '%u', '%u')", GetGUIDLow(), itr->first, itr->second->spec);
+
if (itr->second->state == PLAYERSPELL_REMOVED)
{
delete itr->second;
@@ -18648,10 +21990,12 @@ void Player::_SaveTalents()
}
}
}
+
void Player::UpdateSpecCount(uint8 count)
{
if(GetSpecsCount() == count)
return;
+
if(count == MIN_TALENT_SPECS)
{
_SaveActions(); // make sure the button list is cleaned up
@@ -18672,57 +22016,76 @@ void Player::UpdateSpecCount(uint8 count)
{
return;
}
+
SetSpecsCount(count);
+
SendTalentsInfoData(false);
}
+
void Player::ActivateSpec(uint8 spec)
{
if (GetActiveSpec() == spec)
return;
+
if (GetSpecsCount() != MAX_TALENT_SPECS)
return;
+
if (GetMap()->IsBattleGround() && !HasAura(44521)) // In BattleGround with no Preparation buff
return;
+
RemoveAllAuras(this->GetGUID(), NULL, false, true); // remove all positive auras (ie: buffs from another spec)
+
_SaveActions();
+
UnsummonPetTemporaryIfAny();
+
uint32 const* talentTabIds = GetTalentTabPages(getClass());
+
for (uint8 i = 0; i < 3; ++i)
{
uint32 talentTabId = talentTabIds[i];
+
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;
+
// remove all talent ranks, starting at highest rank
for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
if (talentInfo->RankID[rank] != 0 && HasTalent(talentInfo->RankID[rank], m_activeSpec))
removeSpell(talentInfo->RankID[rank], true);
}
}
+
// set glyphs
for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
// remove secondary glyph
if (uint32 oldglyph = m_Glyphs[m_activeSpec][slot])
if (GlyphPropertiesEntry const *old_gp = sGlyphPropertiesStore.LookupEntry(oldglyph))
RemoveAurasDueToSpell(old_gp->SpellId);
+
SetActiveSpec(spec);
uint32 spentTalents = 0;
+
for (uint8 i = 0; i < 3; ++i)
{
uint32 talentTabId = talentTabIds[i];
+
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;
+
// learn highest talent rank that exists in newly activated spec
for (int8 rank = MAX_TALENT_RANK-1; rank >= 0; --rank)
{
@@ -18734,28 +22097,37 @@ void Player::ActivateSpec(uint8 spec)
}
}
}
+
// set glyphs
- for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
+ for (uint8 slot = 0; slot < MAX_GLYPH_SLOT_INDEX; ++slot)
{
uint32 glyph = m_Glyphs[m_activeSpec][slot];
+
// apply primary glyph
if (glyph)
if (GlyphPropertiesEntry const *gp = sGlyphPropertiesStore.LookupEntry(glyph))
CastSpell(this, gp->SpellId, true);
+
SetGlyph(slot, glyph);
}
+
m_usedTalentCount = spentTalents;
InitTalentForLevel();
+
m_actionButtons.clear();
if (QueryResult *result = CharacterDatabase.PQuery("SELECT button,action,type FROM character_action WHERE guid = '%u' AND spec = '%u' ORDER BY button", GetGUIDLow(), m_activeSpec))
_LoadActions(result, false);
+
ResummonPetTemporaryUnSummonedIfAny();
SendActionButtons(1);
+
Powers pw = getPowerType();
if (pw != POWER_MANA)
SetPower(POWER_MANA, 0); // what on earth is this for?!
+
SetPower(pw, 0);
}
+
void Player::SetReputation(uint32 factionentry, uint32 value)
{
GetReputationMgr().SetReputation(sFactionStore.LookupEntry(factionentry),value);
diff --git a/src/game/Player.h b/src/game/Player.h
index 655a914c23b..1a218879aa6 100644
--- a/src/game/Player.h
+++ b/src/game/Player.h
@@ -17,12 +17,15 @@
* 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"
@@ -35,8 +38,10 @@
#include "AchievementMgr.h"
#include "ReputationMgr.h"
#include "BattleGround.h"
+
#include<string>
#include<vector>
+
struct Mail;
class Channel;
class DynamicObject;
@@ -47,15 +52,19 @@ class UpdateMask;
class SpellCastTargets;
class PlayerSocial;
class OutdoorPvP;
+
typedef std::deque<Mail*> PlayerMails;
+
#define PLAYER_MAX_SKILLS 127
#define PLAYER_MAX_DAILY_QUESTS 25
+
// Note: SPELLMOD_* values is aura types in fact
enum SpellModType
{
SPELLMOD_FLAT = 107, // SPELL_AURA_ADD_FLAT_MODIFIER
SPELLMOD_PCT = 108 // SPELL_AURA_ADD_PCT_MODIFIER
};
+
// 2^n values, Player::m_isunderwater is a bitmask. These are mangos internal values, they are never send to any client
enum PlayerUnderwaterState
{
@@ -64,8 +73,10 @@ enum PlayerUnderwaterState
UNDERWATER_INLAVA = 0x02, // terrain type is lava and player is afflicted by it
UNDERWATER_INSLIME = 0x04, // terrain type is lava and player is afflicted by it
UNDERWARER_INDARKWATER = 0x08, // terrain type is dark water and player is afflicted by it
+
UNDERWATER_EXIST_TIMERS = 0x10
};
+
enum PlayerSpellState
{
PLAYERSPELL_UNCHANGED = 0,
@@ -74,6 +85,7 @@ enum PlayerSpellState
PLAYERSPELL_REMOVED = 3,
PLAYERSPELL_TEMPORARY = 4
};
+
struct PlayerSpell
{
PlayerSpellState state : 8;
@@ -81,11 +93,13 @@ struct PlayerSpell
bool dependent : 1; // learned as result another spell learn, skill grow, quest reward, etc
bool disabled : 1; // first rank has been learned in result talent learn but currently talent unlearned, save max learned ranks
};
+
struct PlayerTalent
{
PlayerSpellState state : 8;
uint8 spec : 8;
};
+
// Spell modifier (used for modify other spells)
struct SpellModifier
{
@@ -98,15 +112,19 @@ struct SpellModifier
uint32 spellId;
Aura *const ownerAura;
};
+
typedef UNORDERED_MAP<uint32, PlayerTalent*> PlayerTalentMap;
typedef UNORDERED_MAP<uint32, PlayerSpell*> PlayerSpellMap;
typedef std::list<SpellModifier*> SpellModList;
+
struct SpellCooldown
{
time_t end;
uint16 itemid;
};
+
typedef std::map<uint32, SpellCooldown> SpellCooldowns;
+
enum TrainerSpellState
{
TRAINER_SPELL_GREEN = 0,
@@ -114,6 +132,7 @@ enum TrainerSpellState
TRAINER_SPELL_GRAY = 2,
TRAINER_SPELL_GREEN_DISABLED = 10 // custom value, not send to client: formally green but learn not allowed
};
+
enum ActionButtonUpdateState
{
ACTIONBUTTON_UNCHANGED = 0,
@@ -121,6 +140,7 @@ enum ActionButtonUpdateState
ACTIONBUTTON_NEW = 2,
ACTIONBUTTON_DELETED = 3
};
+
enum ActionButtonType
{
ACTION_BUTTON_SPELL = 0x00,
@@ -130,15 +150,19 @@ enum ActionButtonType
ACTION_BUTTON_CMACRO = ACTION_BUTTON_C | ACTION_BUTTON_MACRO,
ACTION_BUTTON_ITEM = 0x80
};
+
#define ACTION_BUTTON_ACTION(X) (uint32(X) & 0x00FFFFFF)
#define ACTION_BUTTON_TYPE(X) ((uint32(X) & 0xFF000000) >> 24)
#define MAX_ACTION_BUTTON_ACTION_VALUE (0x00FFFFFF+1)
+
struct ActionButton
{
ActionButton() : packedData(0), uState( ACTIONBUTTON_NEW ), canRemoveByClient(true){}
+
uint32 packedData;
ActionButtonUpdateState uState;
bool canRemoveByClient;
+
// helpers
ActionButtonType GetType() const { return ActionButtonType(ACTION_BUTTON_TYPE(packedData)); }
uint32 GetAction() const { return ACTION_BUTTON_ACTION(packedData); }
@@ -153,47 +177,63 @@ struct ActionButton
}
}
};
+
#define MAX_ACTION_BUTTONS 132 //checked in 2.3.0
+
typedef std::map<uint8,ActionButton> ActionButtonList;
+
struct PlayerCreateInfoItem
{
PlayerCreateInfoItem(uint32 id, uint32 amount) : item_id(id), item_amount(amount) {}
+
uint32 item_id;
uint32 item_amount;
};
+
typedef std::list<PlayerCreateInfoItem> PlayerCreateInfoItems;
+
struct PlayerClassLevelInfo
{
PlayerClassLevelInfo() : basehealth(0), basemana(0) {}
uint16 basehealth;
uint16 basemana;
};
+
struct PlayerClassInfo
{
PlayerClassInfo() : levelInfo(NULL) { }
+
PlayerClassLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
};
+
struct PlayerLevelInfo
{
PlayerLevelInfo() { for(uint8 i=0; i < MAX_STATS; ++i ) stats[i] = 0; }
+
uint8 stats[MAX_STATS];
};
+
typedef std::list<uint32> PlayerCreateInfoSpells;
+
struct PlayerCreateInfoAction
{
PlayerCreateInfoAction() : button(0), type(0), action(0) {}
PlayerCreateInfoAction(uint8 _button, uint32 _action, uint8 _type) : button(_button), type(_type), action(_action) {}
+
uint8 button;
uint8 type;
uint32 action;
};
+
typedef std::list<PlayerCreateInfoAction> PlayerCreateInfoActions;
+
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;
@@ -204,25 +244,31 @@ struct PlayerInfo
PlayerCreateInfoItems item;
PlayerCreateInfoSpells spell;
PlayerCreateInfoActions action;
+
PlayerLevelInfo* levelInfo; //[level-1] 0..MaxPlayerLevel-1
};
+
struct PvPInfo
{
PvPInfo() : inHostileArea(false), inNoPvPArea(false), inFFAPvPArea(false), endTimer(0) {}
+
bool inHostileArea;
bool inNoPvPArea;
bool inFFAPvPArea;
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;
@@ -232,8 +278,10 @@ struct Areas
float y1;
float y2;
};
+
#define MAX_RUNES 6
#define RUNE_COOLDOWN 5 // 5*2=10 sec
+
enum RuneType
{
RUNE_BLOOD = 0,
@@ -242,17 +290,20 @@ enum RuneType
RUNE_DEATH = 3,
NUM_RUNE_TYPES = 4
};
+
struct RuneInfo
{
uint8 BaseRune;
uint8 CurrentRune;
uint8 Cooldown;
};
+
struct Runes
{
RuneInfo runes[MAX_RUNES];
uint8 runeState; // mask of available runes
RuneType lastUsedRune;
+
void SetRuneState(uint8 index, bool set = true)
{
if(set)
@@ -261,17 +312,21 @@ struct Runes
runeState &= ~(1 << index); // on cooldown
}
};
+
struct EnchantDuration
{
EnchantDuration() : item(NULL), slot(MAX_ENCHANTMENT_SLOT), leftduration(0) {};
- EnchantDuration(Item * _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot),
+ EnchantDuration(Item * _item, EnchantmentSlot _slot, uint32 _leftduration) : item(_item), slot(_slot),
leftduration(_leftduration){ assert(item); };
+
Item * item;
EnchantmentSlot slot;
uint32 leftduration;
};
+
typedef std::list<EnchantDuration> EnchantDurationList;
typedef std::list<Item*> ItemDurationList;
+
enum LfgType
{
LFG_TYPE_NONE = 0,
@@ -281,6 +336,7 @@ enum LfgType
LFG_TYPE_ZONE = 4,
LFG_TYPE_HEROIC_DUNGEON = 5
};
+
enum LfgRoles
{
LEADER = 1,
@@ -288,6 +344,7 @@ enum LfgRoles
HEALER = 4,
DAMAGE = 8
};
+
struct LookingForGroupSlot
{
LookingForGroupSlot() : entry(0), type(0) {}
@@ -296,10 +353,13 @@ struct LookingForGroupSlot
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 == LFG_TYPE_DUNGEON || type == LFG_TYPE_HEROIC_DUNGEON); }
+
uint32 entry;
uint32 type;
};
+
#define MAX_LOOKING_FOR_GROUP_SLOT 3
+
struct LookingForGroup
{
LookingForGroup() {}
@@ -311,6 +371,7 @@ struct LookingForGroup
return true;
return false;
}
+
bool canAutoJoin() const
{
for(uint8 i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
@@ -318,6 +379,7 @@ struct LookingForGroup
return true;
return false;
}
+
bool Empty() const
{
for(uint8 i = 0; i < MAX_LOOKING_FOR_GROUP_SLOT; ++i)
@@ -325,11 +387,13 @@ struct LookingForGroup
return false;
return more.Empty();
}
+
LookingForGroupSlot slots[MAX_LOOKING_FOR_GROUP_SLOT];
LookingForGroupSlot more;
std::string comment;
uint8 roles;
};
+
enum PlayerMovementType
{
MOVE_ROOT = 1,
@@ -337,6 +401,7 @@ enum PlayerMovementType
MOVE_WATER_WALK = 3,
MOVE_LAND_WALK = 4
};
+
enum DrunkenState
{
DRUNKEN_SOBER = 0,
@@ -344,7 +409,9 @@ enum DrunkenState
DRUNKEN_DRUNK = 2,
DRUNKEN_SMASHED = 3
};
+
#define MAX_DRUNKEN 4
+
enum PlayerFlags
{
PLAYER_FLAGS_GROUP_LEADER = 0x00000001,
@@ -373,6 +440,7 @@ enum PlayerFlags
PLAYER_ALLOW_ONLY_ABILITY = 0x00800000, // used by bladestorm and killing spree
PLAYER_FLAGS_UNK25 = 0x01000000 // disabled all melee ability on tab include autoattack
};
+
// used for PLAYER__FIELD_KNOWN_TITLES field (uint64), (1<<bit_index) without (-1)
// can't use enum for uint64 values
#define PLAYER_TITLE_DISABLED UI64LIT(0x0000000000000000)
@@ -417,7 +485,9 @@ enum PlayerFlags
#define PLAYER_TITLE_OF_THE_SHATTERED_SUN UI64LIT(0x0000004000000000) // 38
#define PLAYER_TITLE_HAND_OF_ADAL UI64LIT(0x0000008000000000) // 39
#define PLAYER_TITLE_VENGEFUL_GLADIATOR UI64LIT(0x0000010000000000) // 40
+
#define MAX_TITLE_INDEX (3*64) // 3 uint64 fields
+
// used in PLAYER_FIELD_BYTES values
enum PlayerFieldByteFlags
{
@@ -425,12 +495,14 @@ enum PlayerFieldByteFlags
PLAYER_FIELD_BYTE_RELEASE_TIMER = 0x00000008, // Display time till auto release spirit
PLAYER_FIELD_BYTE_NO_RELEASE_WINDOW = 0x00000010 // Display no "release spirit" window at all
};
+
// used in PLAYER_FIELD_BYTES2 values
enum PlayerFieldByte2Flags
{
PLAYER_FIELD_BYTE2_NONE = 0x0000,
PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW = 0x4000
};
+
enum ActivateTaxiReplies
{
ERR_TAXIOK = 0,
@@ -448,6 +520,8 @@ enum ActivateTaxiReplies
ERR_TAXINOTSTANDING = 12
};
+
+
enum MirrorTimerType
{
FATIGUE_TIMER = 0,
@@ -456,6 +530,7 @@ enum MirrorTimerType
};
#define MAX_TIMERS 3
#define DISABLED_MIRROR_TIMER -1
+
// 2^n values
enum PlayerExtraFlags
{
@@ -465,9 +540,11 @@ enum PlayerExtraFlags
PLAYER_EXTRA_TAXICHEAT = 0x0008,
PLAYER_EXTRA_GM_INVISIBLE = 0x0010,
PLAYER_EXTRA_GM_CHAT = 0x0020, // Show GM badge in chat messages
+
// other states
PLAYER_EXTRA_PVP_DEATH = 0x0100 // store PvP death status until corpse creating.
};
+
// 2^n values
enum AtLoginFlags
{
@@ -478,7 +555,9 @@ enum AtLoginFlags
AT_LOGIN_CUSTOMIZE = 0x08,
AT_LOGIN_RESET_PET_TALENTS = 0x10,
};
+
typedef std::map<uint32, QuestStatusData> QuestStatusMap;
+
enum QuestSlotOffsets
{
QUEST_ID_OFFSET = 0,
@@ -486,17 +565,21 @@ enum QuestSlotOffsets
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)
@@ -505,7 +588,9 @@ enum PlayerSlots
PLAYER_SLOT_END = 150,
PLAYER_SLOTS_COUNT = (PLAYER_SLOT_END - PLAYER_SLOT_START)
};
+
#define INVENTORY_SLOT_BAG_0 255
+
enum EquipmentSlots // 19 slots
{
EQUIPMENT_SLOT_START = 0,
@@ -530,42 +615,50 @@ enum EquipmentSlots // 19 slots
EQUIPMENT_SLOT_TABARD = 18,
EQUIPMENT_SLOT_END = 19
};
+
enum InventorySlots // 4 slots
{
INVENTORY_SLOT_BAG_START = 19,
INVENTORY_SLOT_BAG_END = 23
};
+
enum InventoryPackSlots // 16 slots
{
INVENTORY_SLOT_ITEM_START = 23,
INVENTORY_SLOT_ITEM_END = 39
};
+
enum BankItemSlots // 28 slots
{
BANK_SLOT_ITEM_START = 39,
BANK_SLOT_ITEM_END = 67
};
+
enum BankBagSlots // 7 slots
{
BANK_SLOT_BAG_START = 67,
BANK_SLOT_BAG_END = 74
};
+
enum BuyBackSlots // 12 slots
{
// stored in m_buybackitems
BUYBACK_SLOT_START = 74,
BUYBACK_SLOT_END = 86
};
+
enum KeyRingSlots // 32 slots
{
KEYRING_SLOT_START = 86,
KEYRING_SLOT_END = 118
};
+
enum CurrencyTokenSlots // 32 slots
{
CURRENCYTOKEN_SLOT_START = 118,
CURRENCYTOKEN_SLOT_END = 150
};
+
enum EquipmentSetUpdateState
{
EQUIPMENT_SET_UNCHANGED = 0,
@@ -573,6 +666,7 @@ enum EquipmentSetUpdateState
EQUIPMENT_SET_NEW = 2,
EQUIPMENT_SET_DELETED = 3
};
+
struct EquipmentSet
{
EquipmentSet() : Guid(0), state(EQUIPMENT_SET_NEW)
@@ -580,14 +674,18 @@ struct EquipmentSet
for(uint8 i = 0; i < EQUIPMENT_SLOT_END; ++i)
Items[i] = 0;
}
+
uint64 Guid;
std::string Name;
std::string IconName;
uint32 Items[EQUIPMENT_SLOT_END];
EquipmentSetUpdateState state;
};
+
#define MAX_EQUIPMENT_SET_INDEX 10 // client limit
+
typedef std::map<uint32, EquipmentSet> EquipmentSets;
+
struct ItemPosCount
{
ItemPosCount(uint16 _pos, uint32 _count) : pos(_pos), count(_count) {}
@@ -596,12 +694,14 @@ struct ItemPosCount
uint32 count;
};
typedef std::vector<ItemPosCount> ItemPosCountVec;
+
enum TradeSlots
{
TRADE_SLOT_COUNT = 7,
TRADE_SLOT_TRADED_COUNT = 6,
TRADE_SLOT_NONTRADED = 6
};
+
enum TransferAbortReason
{
TRANSFER_ABORT_NONE = 0x00,
@@ -618,6 +718,7 @@ enum TransferAbortReason
TRANSFER_ABORT_NOT_FOUND2 = 0x0C, // 3.1
TRANSFER_ABORT_NOT_FOUND3 = 0x0D, // 3.1
};
+
enum InstanceResetWarningType
{
RAID_INSTANCE_WARNING_HOURS = 1, // WARNING! %s is scheduled to reset in %d hour(s).
@@ -626,19 +727,23 @@ enum InstanceResetWarningType
RAID_INSTANCE_WELCOME = 4, // Welcome to %s. This raid instance is scheduled to reset in %s.
RAID_INSTANCE_EXPIRED = 5
};
+
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,
@@ -647,6 +752,7 @@ enum TeleportToOptions
TELE_TO_NOT_UNSUMMON_PET = 0x08,
TELE_TO_SPELL = 0x10,
};
+
/// Type of environmental damages
enum EnviromentalDamage
{
@@ -658,12 +764,15 @@ enum EnviromentalDamage
DAMAGE_FIRE = 5,
DAMAGE_FALL_TO_VOID = 6 // custom case for fall without durability loss
};
+
enum PlayedTimeIndex
{
PLAYED_TIME_TOTAL = 0,
PLAYED_TIME_LEVEL = 1
};
+
#define MAX_PLAYED_TIME_INDEX 2
+
// used at player loading query list preparing, and later result selection
enum PlayerLoginQueryIndex
{
@@ -694,6 +803,7 @@ enum PlayerLoginQueryIndex
PLAYER_LOGIN_QUERY_LOADACCOUNTDATA = 24,
MAX_PLAYER_LOGIN_QUERY = 25
};
+
enum PlayerDelayedOperations
{
DELAYED_SAVE_PLAYER = 0x01,
@@ -703,9 +813,11 @@ enum PlayerDelayedOperations
DELAYED_BG_TAXI_RESTORE = 0x10, ///< Flag to restore taxi state after teleport from BG
DELAYED_END
};
+
// Player summoning auto-decline time (in secs)
#define MAX_PLAYER_SUMMON_DELAY (2*MINUTE)
#define MAX_MONEY_AMOUNT (0x7FFFFFFF-1)
+
struct InstancePlayerBind
{
InstanceSave *save;
@@ -715,6 +827,7 @@ struct InstancePlayerBind
or when they enter an instance that the group leader is permanently bound to. */
InstancePlayerBind() : save(NULL), perm(false) {}
};
+
struct AccessRequirement
{
uint8 levelMin;
@@ -729,6 +842,7 @@ struct AccessRequirement
uint32 heroicQuest;
std::string heroicQuestFailedText;
};
+
class TRINITY_DLL_SPEC PlayerTaxi
{
public:
@@ -737,6 +851,7 @@ class TRINITY_DLL_SPEC PlayerTaxi
// Nodes
void InitTaxiNodesForLevel(uint32 race, uint32 chrClass, uint32 level);
void LoadTaxiMask(const char* data);
+
bool IsTaximaskNodeKnown(uint32 nodeidx) const
{
uint8 field = uint8((nodeidx - 1) / 32);
@@ -756,9 +871,11 @@ class TRINITY_DLL_SPEC PlayerTaxi
return false;
}
void AppendTaximaskTo(ByteBuffer& data,bool all);
+
// Destinations
bool LoadTaxiDestinationsFromString(const std::string& values, uint32 team);
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(); }
@@ -770,33 +887,44 @@ class TRINITY_DLL_SPEC PlayerTaxi
return GetTaxiDestination();
}
bool empty() const { return m_TaxiDestinations.empty(); }
+
friend std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
private:
TaxiMask m_taximask;
std::deque<uint32> m_TaxiDestinations;
};
+
std::ostringstream& operator<< (std::ostringstream& ss, PlayerTaxi const& taxi);
+
class Player;
+
/// Holder for BattleGround data
struct BGData
{
BGData() : bgInstanceID(0), bgTypeID(BATTLEGROUND_TYPE_NONE), bgAfkReportedCount(0), bgAfkReportedTimer(0),
bgTeam(0), mountSpell(0) { ClearTaxiPath(); }
+
uint32 bgInstanceID; ///< This variable is set to bg->m_InstanceID,
/// when player is teleported to BG - (it is battleground's GUID)
BattleGroundTypeId bgTypeID;
+
std::set<uint32> bgAfkReporter;
uint8 bgAfkReportedCount;
time_t bgAfkReportedTimer;
+
uint32 bgTeam; ///< What side the player will be added to
+
uint32 mountSpell;
uint32 taxiPath[2];
+
WorldLocation joinPos; ///< From where player entered BG
+
void ClearTaxiPath() { taxiPath[0] = taxiPath[1] = 0; }
bool HasTaxiPath() const { return taxiPath[0] && taxiPath[1]; }
};
+
class MANGOS_DLL_SPEC Player : public Unit
{
friend class WorldSession;
@@ -805,18 +933,25 @@ class MANGOS_DLL_SPEC Player : public Unit
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);
void TeleportOutOfMap(Map *oldMap);
+
bool TeleportTo(WorldLocation const &loc, uint32 options = 0)
{
return TeleportTo(loc.GetMapId(), loc.GetPositionX(), loc.GetPositionY(), loc.GetPositionZ(), loc.GetOrientation(), options);
}
+
bool TeleportToBGEntryPoint();
+
void SetSummonPoint(uint32 mapid, float x, float y, float z)
{
m_summon_expire = time(NULL) + MAX_PLAYER_SUMMON_DELAY;
@@ -826,19 +961,27 @@ class MANGOS_DLL_SPEC Player : public Unit
m_summon_z = z;
}
void SummonIfPossible(bool agree);
+
bool Create( uint32 guidlow, const 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 );
+
static bool 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, uint8 reason, uint8 arg = 0);
void SendInstanceResetWarning(uint32 mapid, uint32 difficulty, uint32 time);
+
Creature* GetNPCIfCanInteractWith(uint64 guid, uint32 npcflagmask);
bool CanInteractWithNPCs(bool alive = true) const;
GameObject* GetGameObjectIfCanInteractWith(uint64 guid, GameobjectTypes type) const;
+
bool ToggleAFK();
bool ToggleDND();
bool isAFK() const { return HasFlag(PLAYER_FLAGS,PLAYER_FLAGS_AFK); };
@@ -846,8 +989,11 @@ class MANGOS_DLL_SPEC Player : public Unit
uint8 chatTag() const;
std::string afkMsg;
std::string dndMsg;
+
uint32 GetBarberShopCost(uint8 newhairstyle, uint8 newhaircolor, uint8 newfacialhair);
+
PlayerSocial *GetSocial() { return m_social; }
+
PlayerTaxi m_taxi;
void InitTaxiNodesForLevel() { m_taxi.InitTaxiNodesForLevel(getRace(), getClass(), getLevel()); }
bool ActivateTaxiPathTo(std::vector<uint32> const& nodes, Creature* npc = NULL, uint32 spellid = 0);
@@ -866,16 +1012,21 @@ class MANGOS_DLL_SPEC Player : public Unit
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[MAX_PLAYED_TIME_INDEX];
uint32 GetTotalPlayedTime() { return m_Played_time[PLAYED_TIME_TOTAL]; };
uint32 GetLevelPlayedTime() { return m_Played_time[PLAYED_TIME_LEVEL]; };
+
void setDeathState(DeathState s); // overwrite Unit::setDeathState
+
void InnEnter (int time,uint32 mapid, float x,float y,float z)
{
inn_pos_mapid = mapid;
@@ -884,28 +1035,36 @@ class MANGOS_DLL_SPEC Player : public Unit
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; };
+
Pet* GetPet() const;
Pet* SummonPet(uint32 entry, float x, float y, float z, float ang, PetType petType, uint32 despwtime);
void RemovePet(Pet* pet, PetSaveMode mode, bool returnreagent = false);
uint32 GetPhaseMaskForSpawn() const; // used for proper set phase for DB at GM-mode creature/GO spawn
+
void Say(const std::string& text, const uint32 language);
void Yell(const std::string& text, const uint32 language);
void TextEmote(const std::string& text);
void Whisper(const std::string& text, const uint32 language,uint64 receiver);
void BuildPlayerChat(WorldPacket *data, uint8 msgtype, const std::string& text, uint32 language) const;
+
/*********************************************************/
/*** STORAGE SYSTEM ***/
/*********************************************************/
+
void SetVirtualItemSlot( uint8 i, Item* item);
void SetSheath( SheathState sheathed ); // overwrite Unit version
uint8 FindEquipSlot( ItemPrototype const* proto, uint32 slot, bool swap ) const;
@@ -951,10 +1110,12 @@ class MANGOS_DLL_SPEC Player : public Unit
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, bool swap ) const;
uint8 CanEquipItem( uint8 slot, uint16 &dest, Item *pItem, bool swap, bool not_loading = true ) const;
+
uint8 CanEquipUniqueItem( Item * pItem, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const;
uint8 CanEquipUniqueItem( ItemPrototype const* itemProto, uint8 except_slot = NULL_SLOT, uint32 limit_count = 1 ) const;
uint8 CanUnequipItems( uint32 item, uint32 count ) const;
@@ -972,8 +1133,10 @@ class MANGOS_DLL_SPEC Player : public Unit
bool StoreNewItemInBestSlots(uint32 item_id, uint32 item_count);
void AutoStoreLoot(uint8 bag, uint8 slot, uint32 loot_id, LootStore const& store, bool broadcast = false);
void AutoStoreLoot(uint32 loot_id, LootStore const& store, bool broadcast = false) { AutoStoreLoot(NULL_BAG,NULL_SLOT,loot_id,store,broadcast); }
+
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();
@@ -1023,11 +1186,13 @@ class MANGOS_DLL_SPEC Player : public Unit
}
void SendNewItem( Item *item, uint32 count, bool received, bool created, bool broadcast = false );
bool BuyItemFromVendor(uint64 vendorguid, uint32 item, uint8 count, uint8 bag, 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);
@@ -1043,11 +1208,15 @@ class MANGOS_DLL_SPEC Player : public Unit
void SendItemDurations();
void LoadCorpse();
void LoadPet();
+
uint32 m_stableSlots;
+
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
+
uint32 GetQuestLevel( Quest const* pQuest ) const { return pQuest && pQuest->GetQuestLevel() ? pQuest->GetQuestLevel() : getLevel(); }
+
void PrepareQuestMenu( uint64 guid );
void SendPreparedQuest( uint64 guid );
bool IsActiveQuest( uint32 quest_id ) const;
@@ -1081,8 +1250,10 @@ class MANGOS_DLL_SPEC Player : public Unit
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); }
@@ -1106,6 +1277,7 @@ class MANGOS_DLL_SPEC Player : public Unit
{
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);
}
@@ -1125,6 +1297,7 @@ class MANGOS_DLL_SPEC Player : public Unit
bool HasQuestForGO(int32 GOId) const;
void UpdateForQuestWorldObjects();
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 );
@@ -1133,17 +1306,24 @@ class MANGOS_DLL_SPEC Player : public Unit
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); }
void RemoveTimedQuest( uint32 quest_id ) { m_timedquests.erase(quest_id); }
+
/*********************************************************/
/*** LOAD SYSTEM ***/
/*********************************************************/
+
bool LoadFromDB(uint32 guid, SqlQueryHolder *holder);
bool isBeingLoaded() const { return GetSession()->PlayerLoading();}
+
bool MinimalLoadFromDB(QueryResult *result, uint32 guid);
static bool LoadValuesArrayFromDB(Tokens& data,uint64 guid);
static uint32 GetUInt32ValueFromArray(Tokens const& data, uint16 index);
@@ -1153,9 +1333,11 @@ class MANGOS_DLL_SPEC Player : public Unit
static uint32 GetZoneIdFromDB(uint64 guid);
static uint32 GetLevelFromDB(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();
@@ -1167,8 +1349,10 @@ class MANGOS_DLL_SPEC Player : public Unit
static void SetFloatValueInDB(uint16 index, float value, uint64 guid);
static void Customize(uint64 guid, uint8 gender, uint8 skin, uint8 face, uint8 hairStyle, uint8 hairColor, uint8 facialHair);
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 SendPetSkillWipeConfirm();
@@ -1178,6 +1362,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void RegenerateHealth();
void setRegenTimerCount(uint32 time) {m_regenTimerCount = time;}
void setWeaponChangeTimer(uint32 time) {m_weaponChangeTimer = time;}
+
uint32 GetMoney() { return GetUInt32Value (PLAYER_FIELD_COINAGE); }
void ModifyMoney( int32 d )
{
@@ -1186,6 +1371,7 @@ class MANGOS_DLL_SPEC Player : public Unit
SetMoney (GetMoney() > uint32(-d) ? GetMoney() + d : 0);
else
SetMoney (GetMoney() < uint32(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);
@@ -1196,51 +1382,67 @@ class MANGOS_DLL_SPEC Player : public Unit
MoneyChanged( value );
UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_GOLD_VALUE_OWNED);
}
+
QuestStatusMap& getQuestStatusMap() { return mQuestStatus; };
+
const uint64& GetSelection( ) const { return m_curSelection; }
Unit *GetSelectedUnit() const;
Player *GetSelectedPlayer() const;
void SetSelection(const uint64 &guid) { m_curSelection = guid; SetUInt64Value(UNIT_FIELD_TARGET, guid); }
+
uint8 GetComboPoints() { return m_comboPoints; }
const uint64& GetComboTarget() const { return m_comboTarget; }
+
void AddComboPoints(Unit* target, int8 count, Spell * spell = NULL);
void GainSpellComboPoints(int8 count);
void ClearComboPoints();
void SendComboPoints();
+
void SendMailResult(uint32 mailId, MailResponseType mailAction, MailResponseResult 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 UNORDERED_MAP<uint32, Item*> ItemMap;
+
ItemMap mMitems; //template defined in objectmgr.cpp
+
Item* GetMItem(uint32 id)
{
ItemMap::const_iterator itr = mMitems.find(id);
return itr != mMitems.end() ? itr->second : NULL;
}
+
void AddMItem(Item* it)
{
ASSERT( it );
//assert deleted, because items can be added before loading
mMitems[it->GetGUIDLow()] = it;
}
+
bool RemoveMItem(uint32 id)
{
return mMitems.erase(id) ? true : false;
}
+
void PetSpellInitialize();
void CharmSpellInitialize();
void PossessSpellInitialize();
@@ -1251,6 +1453,7 @@ class MANGOS_DLL_SPEC Player : public Unit
TrainerSpellState GetTrainerSpellState(TrainerSpell const* trainer_spell) const;
bool IsSpellFitByClassAndRace( uint32 spell_id ) const;
bool IsNeedCastPassiveSpellAtLearn(SpellEntry const* spellInfo) const;
+
void SendProficiency(uint8 pr1, uint32 pr2);
void SendInitialSpells();
bool addSpell(uint32 spell_id, bool active, bool learning, bool dependent, bool disabled);
@@ -1276,9 +1479,12 @@ class MANGOS_DLL_SPEC Player : public Unit
void SendTalentsInfoData(bool pet);
void LearnTalent(uint32 talentId, uint32 talentRank);
void LearnPetTalent(uint64 petGuid, uint32 talentId, uint32 talentRank);
+
bool AddTalent(uint32 spell, uint8 spec, bool learning);
bool HasTalent(uint32 spell_id, uint8 spec) const;
+
uint32 CalculateTalentsPoints() const;
+
// Dual Spec
void UpdateSpecCount(uint8 count);
uint32 GetActiveSpec() { return m_activeSpec; }
@@ -1286,21 +1492,26 @@ class MANGOS_DLL_SPEC Player : public Unit
uint8 GetSpecsCount() { return m_specsCount; }
void SetSpecsCount(uint8 count) { m_specsCount = count; }
void ActivateSpec(uint8 spec);
+
void InitGlyphsForLevel();
void SetGlyphSlot(uint8 slot, uint32 slottype) { SetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot, slottype); }
uint32 GetGlyphSlot(uint8 slot) { return GetUInt32Value(PLAYER_FIELD_GLYPH_SLOTS_1 + slot); }
void SetGlyph(uint8 slot, uint32 glyph)
- {
+ {
m_Glyphs[m_activeSpec][slot] = glyph;
- SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph);
+ SetUInt32Value(PLAYER_FIELD_GLYPHS_1 + slot, glyph);
}
uint32 GetGlyph(uint8 slot) { return m_Glyphs[m_activeSpec][slot]; }
+
uint32 GetFreePrimaryProfessionPoints() const { return GetUInt32Value(PLAYER_CHARACTER_POINTS2); }
void SetFreePrimaryProfessions(uint16 profs) { SetUInt32Value(PLAYER_CHARACTER_POINTS2, profs); }
void InitPrimaryProfessions();
+
PlayerSpellMap const& GetSpellMap() const { return m_spells; }
PlayerSpellMap & GetSpellMap() { return m_spells; }
+
SpellCooldowns const& GetSpellCooldownMap() const { return m_spellCooldowns; }
+
void AddSpellMod(SpellModifier* mod, bool apply);
bool IsAffectedBySpellmod(SpellEntry const *spellInfo, SpellModifier *mod, Spell * spell = NULL);
template <class T> T ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell * spell = NULL);
@@ -1308,6 +1519,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void RestoreSpellMods(Spell * spell);
void DropModCharge(SpellModifier * mod, Spell * spell);
void SetSpellModTakingSpell(Spell* spell, bool apply);
+
static uint32 const infinityCooldownDelay = MONTH; // used for set "infinity cooldowns" for spells and check
static uint32 const infinityCooldownDelayCheck = MONTH/2;
bool HasSpellCooldown(uint32 spell_id) const
@@ -1328,6 +1540,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void RemoveSpellCooldown(uint32 spell_id, bool update = false);
void RemoveSpellCategoryCooldown(uint32 cat, bool update = false);
void SendClearCooldown( uint32 spell_id, Unit* target );
+
void RemoveCategoryCooldown(uint32 cat);
void RemoveArenaSpellCooldowns();
void RemoveAllSpellCooldown();
@@ -1335,6 +1548,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void _SaveSpellCooldowns();
void SetLastPotionId(uint32 item_id) { m_lastPotionId = item_id; }
void UpdatePotionCooldown(Spell* spell = NULL);
+
void setResurrectRequestData(uint64 guid, uint32 mapId, float X, float Y, float Z, uint32 health, uint32 mana)
{
m_resurrectGUID = guid;
@@ -1349,6 +1563,7 @@ class MANGOS_DLL_SPEC Player : public Unit
bool isRessurectRequestedBy(uint64 guid) const { return m_resurrectGUID == guid; }
bool isRessurectRequested() const { return m_resurrectGUID != 0; }
void ResurectUsingRequestData();
+
int getCinematic()
{
return m_cinematic;
@@ -1357,10 +1572,12 @@ class MANGOS_DLL_SPEC Player : public Unit
{
m_cinematic = cine;
}
+
ActionButton* addActionButton(uint8 button, uint32 action, uint8 type);
void removeActionButton(uint8 button);
void SendInitialActionButtons() const { SendActionButtons(0); }
void SendActionButtons(uint32 state) const;
+
PvPInfo pvpInfo;
void UpdatePvPState(bool onlyFFA = false);
void SetPvP(bool state)
@@ -1372,8 +1589,10 @@ class MANGOS_DLL_SPEC Player : public Unit
void UpdatePvP(bool state, bool override=false);
void UpdateZone(uint32 newZone,uint32 newArea);
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);
@@ -1384,11 +1603,13 @@ class MANGOS_DLL_SPEC Player : public Unit
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()); }
@@ -1396,6 +1617,7 @@ class MANGOS_DLL_SPEC Player : public Unit
static void RemoveFromGroup(Group* group, uint64 guid);
void RemoveFromGroup() { RemoveFromGroup(GetGroup(),GetGUID()); }
void SendUpdateToOutOfRangeGroupMembers();
+
void SetInGuild(uint32 GuildId) { SetUInt32Value(PLAYER_GUILDID, GuildId); }
void SetRank(uint32 rankId){ SetUInt32Value(PLAYER_GUILDRANK, rankId); }
void SetGuildIdInvited(uint32 GuildId) { m_GuildIdInvited = GuildId; }
@@ -1405,6 +1627,7 @@ class MANGOS_DLL_SPEC Player : public Unit
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)
{
@@ -1415,19 +1638,26 @@ class MANGOS_DLL_SPEC Player : public Unit
void SetArenaTeamIdInvited(uint32 ArenaTeamId) { m_ArenaTeamIdInvited = ArenaTeamId; }
uint32 GetArenaTeamIdInvited() { return m_ArenaTeamIdInvited; }
static void LeaveAllArenaTeams(uint64 guid);
+
void SetDifficulty(uint32 dungeon_difficulty) { m_dungeonDifficulty = dungeon_difficulty; }
uint8 GetDifficulty() { return m_dungeonDifficulty; }
bool IsHeroic() { return m_dungeonDifficulty == DIFFICULTY_HEROIC; }
+
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);
@@ -1440,7 +1670,9 @@ class MANGOS_DLL_SPEC Player : public Unit
void UpdateDamagePhysical(WeaponAttackType attType);
void ApplySpellPowerBonus(int32 amount, bool apply);
void UpdateSpellDamageAndHealingBonus();
+
void CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& min_damage, float& max_damage);
+
void UpdateDefenseBonusesMod();
void ApplyRatingMod(CombatRating cr, int32 value, bool apply);
inline void RecalculateRating(CombatRating cr) { ApplyRatingMod(cr, 0, true);}
@@ -1456,6 +1688,7 @@ class MANGOS_DLL_SPEC Player : public Unit
uint32 GetSpellCritDamageReduction(uint32 damage) const;
uint32 GetDotDamageReduction(uint32 damage) const;
uint32 GetBaseSpellPowerBonus() { return m_baseSpellPower; }
+
float GetExpertiseDodgeOrParryReduction(WeaponAttackType attType) const;
void UpdateBlockPercentage();
void UpdateCritPercentage(WeaponAttackType attType);
@@ -1465,21 +1698,27 @@ class MANGOS_DLL_SPEC Player : public Unit
void UpdateMeleeHitChances();
void UpdateRangedHitChances();
void UpdateSpellHitChances();
+
void UpdateAllSpellCritChances();
void UpdateSpellCritChance(uint32 school);
void UpdateArmorPenetration(int32 amount);
void UpdateExpertise(WeaponAttackType attType);
void ApplyManaRegenBonus(int32 amount, bool apply);
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, bool anim = false ) const;
void SendDelayResponse(const uint32);
void SendLogXPGain(uint32 GivenXP,Unit* victim,uint32 RestXP);
+
// notifiers
void SendAttackSwingCantAttack();
void SendAttackSwingCancelAttack();
@@ -1488,19 +1727,25 @@ class MANGOS_DLL_SPEC Player : public Unit
void SendAttackSwingBadFacingAttack();
void SendAutoRepeatCancel(Unit *target);
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(const Position &pos, bool teleport = false) { return SetPosition(pos.GetPositionX(), pos.GetPositionY(), pos.GetPositionZ(), pos.GetOrientation(), teleport); }
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);
+
void SendTeleportAckMsg();
+
static void DeleteFromDB(uint64 playerguid, uint32 accountId, bool updateRealmChars = true);
+
Corpse *GetCorpse() const;
void SpawnCorpseBones();
void CreateCorpse();
@@ -1510,6 +1755,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void ResurrectPlayer(float restore_percent, 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);
@@ -1517,6 +1763,7 @@ class MANGOS_DLL_SPEC Player : public Unit
void DurabilityPointLossForEquipSlot(EquipmentSlots slot);
uint32 DurabilityRepairAll(bool cost, float discountMod, bool guildBank);
uint32 DurabilityRepair(uint16 pos, bool cost, float discountMod, bool guildBank);
+
void UpdateMirrorTimers();
void StopMirrorTimers()
{
@@ -1524,15 +1771,19 @@ class MANGOS_DLL_SPEC Player : public Unit
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, bool defence);
+
void SetSkill(uint32 id, uint16 currVal, uint16 maxVal);
uint16 GetMaxSkillValue(uint32 skill) const; // max + perm. bonus + temp bonus
uint16 GetPureMaxSkillValue(uint32 skill) const; // max
@@ -1543,6 +1794,7 @@ class MANGOS_DLL_SPEC Player : public Unit
int16 GetSkillTempBonusValue(uint32 skill) const;
bool HasSkill(uint32 skill) const;
void learnSkillRewardedSpells(uint32 id, uint32 value);
+
WorldLocation& GetTeleportDest() { return m_teleport_dest; }
bool IsBeingTeleported() const { return mSemaphoreTeleport_Near || mSemaphoreTeleport_Far; }
bool IsBeingTeleportedNear() const { return mSemaphoreTeleport_Near; }
@@ -1550,25 +1802,32 @@ class MANGOS_DLL_SPEC Player : public Unit
void SetSemaphoreTeleportNear(bool semphsetting) { mSemaphoreTeleport_Near = semphsetting; }
void SetSemaphoreTeleportFar(bool semphsetting) { mSemaphoreTeleport_Far = semphsetting; }
void ProcessDelayedOperations();
+
void CheckExploreSystem(void);
+
static uint32 TeamForRace(uint8 race);
uint32 GetTeam() const { return m_team; }
TeamId GetTeamId() const { return m_team == ALLIANCE ? TEAM_ALLIANCE : TEAM_HORDE; }
static uint32 getFactionForRace(uint8 race);
void setFactionForRace(uint8 race);
+
void InitDisplayIds();
+
bool IsAtGroupRewardDistance(WorldObject const* pRewardSource) const;
bool RewardPlayerAndGroupAtKill(Unit* pVictim);
void RewardPlayerAndGroupAtEvent(uint32 creature_id,WorldObject* pRewardSource);
bool isHonorOrXPTarget(Unit* pVictim);
+
ReputationMgr& GetReputationMgr() { return m_reputationMgr; }
ReputationMgr const& GetReputationMgr() const { return m_reputationMgr; }
ReputationRank GetReputationRank(uint32 faction_id) const;
void RewardReputation(Unit *pVictim, float rate);
void RewardReputation(Quest const *pQuest);
+
void UpdateSkillsForLevel();
void UpdateSkillsToMaxSkillsForLevel(); // for .levelup
void ModifySkillBonus(uint32 skillid,int32 val, bool talent);
+
/*********************************************************/
/*** PVP SYSTEM ***/
/*********************************************************/
@@ -1580,15 +1839,20 @@ class MANGOS_DLL_SPEC Player : public Unit
void ModifyHonorPoints( int32 value );
void ModifyArenaPoints( int32 value );
uint32 GetMaxPersonalArenaRatingRequirement();
+
//End of PvP System
+
inline SpellCooldowns GetSpellCooldowns() const { return m_spellCooldowns; }
+
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);
@@ -1597,6 +1861,7 @@ class MANGOS_DLL_SPEC Player : public Unit
bool CanTitanGrip() const { return m_canTitanGrip ; }
void SetCanTitanGrip(bool value) { m_canTitanGrip = value; }
bool CanTameExoticPets() const { return isGameMaster() || HasAuraType(SPELL_AURA_ALLOW_TAME_PET_TYPE); }
+
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);
@@ -1605,9 +1870,11 @@ class MANGOS_DLL_SPEC Player : public Unit
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, AuraEffect* aura, bool apply);
void _ApplyWeaponDependentAuraDamageMod(Item *item, WeaponAttackType attackType, AuraEffect* aura, bool apply);
+
void _ApplyItemMods(Item *item,uint8 slot,bool apply);
void _RemoveAllItemMods();
void _ApplyAllItemMods();
@@ -1618,35 +1885,45 @@ class MANGOS_DLL_SPEC Player : public Unit
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(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx);
void CastItemUseSpell(Item *item,SpellCastTargets const& targets,uint8 cast_count, uint32 glyphIndex);
void CastItemCombatSpell(Unit *target, WeaponAttackType attType, uint32 procVictim, uint32 procEx, Item *item, ItemPrototype const * proto);
+
void SendEquipmentSetList();
void SetEquipmentSet(uint32 index, EquipmentSet eqset);
void DeleteEquipmentSet(uint64 setGuid);
+
void SendInitWorldStates(uint32 zone, uint32 area);
void SendUpdateWorldState(uint32 Field, uint32 Value);
void SendDirectMessage(WorldPacket *data);
+
void SendAurasForTarget(Unit *target);
+
PlayerMenu* PlayerTalkClass;
std::vector<ItemSetEffect *> ItemSetEff;
+
void SendLoot(uint64 guid, LootType loot_type);
void SendLootRelease( uint64 guid );
void SendNotifyLootItemRemoved(uint8 lootSlot);
void SendNotifyLootMoneyRemoved();
+
/*********************************************************/
/*** BATTLEGROUND SYSTEM ***/
/*********************************************************/
+
bool InBattleGround() const { return m_bgData.bgInstanceID != 0; }
bool InArena() const;
uint32 GetBattleGroundId() const { return m_bgData.bgInstanceID; }
BattleGroundTypeId GetBattleGroundTypeId() const { return m_bgData.bgTypeID; }
BattleGround* GetBattleGround() const;
+
BGQueueIdBasedOnLevel GetBattleGroundQueueIdFromLevel(BattleGroundTypeId bgTypeId) const;
+
bool InBattleGroundQueue() const
{
for (uint8 i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
@@ -1654,6 +1931,7 @@ class MANGOS_DLL_SPEC Player : public Unit
return true;
return false;
}
+
BattleGroundQueueTypeId GetBattleGroundQueueTypeId(uint32 index) const { return m_bgBattleGroundQueueID[index].bgQueueTypeId; }
uint32 GetBattleGroundQueueIndex(BattleGroundQueueTypeId bgQueueTypeId) const
{
@@ -1673,6 +1951,7 @@ class MANGOS_DLL_SPEC Player : public Unit
{
return GetBattleGroundQueueIndex(bgQueueTypeId) < PLAYER_MAX_BATTLEGROUND_QUEUES;
}
+
void SetBattleGroundId(uint32 val, BattleGroundTypeId bgTypeId)
{
m_bgData.bgInstanceID = val;
@@ -1725,41 +2004,53 @@ class MANGOS_DLL_SPEC Player : public Unit
}
WorldLocation const& GetBattleGroundEntryPoint() const { return m_bgData.joinPos; }
void SetBattleGroundEntryPoint();
+
void SetBGTeam(uint32 team) { m_bgData.bgTeam = team; }
uint32 GetBGTeam() const { return m_bgData.bgTeam ? m_bgData.bgTeam : GetTeam(); }
+
void LeaveBattleground(bool teleportToEntryPoint = true);
bool CanJoinToBattleground() const;
bool CanReportAfkDueToLimit();
void ReportedAfkBy(Player* reporter);
void ClearAfkReports() { m_bgData.bgAfkReporter.clear(); }
+
bool GetBGAccessByLevel(BattleGroundTypeId bgTypeId) const;
bool isTotalImmunity();
bool CanUseBattleGroundObject();
bool isTotalImmune();
bool CanCaptureTowerPoint();
+
/*********************************************************/
/*** OUTDOOR PVP SYSTEM ***/
/*********************************************************/
+
OutdoorPvP * GetOutdoorPvP() const;
// returns true if the player is in active state for outdoor pvp objective capturing, false otherwise
bool IsOutdoorPvPActive();
+
/*********************************************************/
/*** REST SYSTEM ***/
/*********************************************************/
+
bool isRested() const { return GetRestTime() >= 10*IN_MILISECONDS; }
uint32 GetXPRestBonus(uint32 xp);
uint32 GetRestTime() const { return m_restTime;};
void SetRestTime(uint32 v) { m_restTime = v;};
+
/*********************************************************/
/*** ENVIROMENTAL SYSTEM ***/
/*********************************************************/
+
uint32 EnvironmentalDamage(EnviromentalDamage type, uint32 damage);
+
/*********************************************************/
/*** FLOOD FILTER SYSTEM ***/
/*********************************************************/
+
void UpdateSpeakTime();
bool CanSpeak() const;
void ChangeSpeakTime(int utime);
+
/*********************************************************/
/*** VARIOUS SYSTEMS ***/
/*********************************************************/
@@ -1772,8 +2063,11 @@ class MANGOS_DLL_SPEC Player : public Unit
m_lastFallZ = z;
}
void HandleFall(MovementInfo const& movementInfo);
+
bool IsKnowHowFlyIn(uint32 mapid, uint32 zone) const;
+
void SetClientControl(Unit* target, uint8 allowMove);
+
void SetMover(Unit* target)
{
m_mover->m_movedPlayer = NULL;
@@ -1785,8 +2079,10 @@ class MANGOS_DLL_SPEC Player : public Unit
WorldObject* GetViewpoint() const;
void StopCastingCharm();
void StopCastingBindSight();
+
uint32 GetSaveTimer() const { return m_nextSave; }
void SetSaveTimer(uint32 timer) { m_nextSave = timer; }
+
// Recall position
uint32 m_recallMap;
float m_recallX;
@@ -1794,44 +2090,61 @@ class MANGOS_DLL_SPEC Player : public Unit
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;
+
WorldLocation GetStartPosition() const;
+
// currently visible objects at player client
typedef std::set<uint64> ClientGUIDs;
ClientGUIDs m_clientGUIDs;
+
bool HaveAtClient(WorldObject const* u) const { return u==this || m_clientGUIDs.find(u->GetGUID())!=m_clientGUIDs.end(); }
+
bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const;
bool IsVisibleInGridForPlayer(Player const* pl) const;
bool IsVisibleGloballyFor(Player* pl) const;
+
void UpdateVisibilityOf(WorldObject* target);
void SendInitialVisiblePackets(Unit* target);
+
template<class T>
void UpdateVisibilityOf(T* target, UpdateData& data, std::set<WorldObject*>& visibleNow);
+
// Stealth detection system
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; }
void RemoveAtLoginFlag(AtLoginFlags f, bool in_db_also = false);
+
LookingForGroup m_lookingForGroup;
+
// Temporarily removed pet cache
uint32 GetTemporaryUnsummonedPetNumber() const { return m_temporaryUnsummonedPetNumber; }
void SetTemporaryUnsummonedPetNumber(uint32 petnumber) { m_temporaryUnsummonedPetNumber = petnumber; }
void UnsummonPetTemporaryIfAny();
void ResummonPetTemporaryUnSummonedIfAny();
bool IsPetNeedBeTemporaryUnsummoned() const { return !IsInWorld() || !isAlive() || IsMounted() /*+in flight*/; }
+
void SendCinematicStart(uint32 CinematicSequenceId);
void SendMovieStart(uint32 MovieId);
+
/*********************************************************/
/*** INSTANCE SYSTEM ***/
/*********************************************************/
+
typedef UNORDERED_MAP< uint32 /*mapId*/, InstancePlayerBind > BoundInstancesMap;
+
void UpdateHomebindTime(uint32 time);
+
uint32 m_HomebindTimer;
bool m_InstanceValid;
// permanent binds and solo binds by difficulty
@@ -1846,12 +2159,15 @@ class MANGOS_DLL_SPEC Player : public Unit
void SendSavedInstances();
static void ConvertInstancesToGroup(Player *player, Group *group = NULL, uint64 player_guid = 0);
bool Satisfy(AccessRequirement const*, uint32 target_map, bool report = false);
+
// last used pet number (for BG's)
uint32 GetLastPetNumber() const { return m_lastpetnumber; }
void SetLastPetNumber(uint32 petnumber) { m_lastpetnumber = petnumber; }
+
/*********************************************************/
/*** GROUP SYSTEM ***/
/*********************************************************/
+
Group * GetGroupInvite() { return m_groupInvite; }
void SetGroupInvite(Group *group) { m_groupInvite = group; }
Group * GetGroup() { return m_group.getTarget(); }
@@ -1872,12 +2188,16 @@ class MANGOS_DLL_SPEC Player : public Unit
GroupReference& GetOriginalGroupRef() { return m_originalGroup; }
uint8 GetOriginalSubGroup() const { return m_originalGroup.getSubGroup(); }
void SetOriginalGroup(Group *group, int8 subgroup = -1);
+
GridReference<Player> &GetGridRef() { return m_gridRef; }
MapReference &GetMapRef() { return m_mapRef; }
+
// Set map to player and add reference
void SetMap(Map * map);
void ResetMap();
+
bool isAllowedToLoot(Creature* creature);
+
DeclinedName const* GetDeclinedNames() const { return m_declinedname; }
uint8 GetRunesState() const { return m_runes->runeState; }
RuneType GetBaseRune(uint8 index) const { return RuneType(m_runes->runes[index].BaseRune); }
@@ -1893,14 +2213,18 @@ class MANGOS_DLL_SPEC Player : public Unit
void ResyncRunes(uint8 count);
void AddRunePower(uint8 index);
void InitRunes();
+
AchievementMgr& GetAchievementMgr() { return m_achievementMgr; }
void UpdateAchievementCriteria(AchievementCriteriaTypes type, uint32 miscvalue1 = 0, uint32 miscvalue2 = 0, Unit *unit = NULL, uint32 time = 0);
void CompletedAchievement(AchievementEntry const* entry);
+
bool HasTitle(uint32 bitIndex);
bool HasTitle(CharTitlesEntry const* title) { return HasTitle(title->bit_index); }
void SetTitle(CharTitlesEntry const* title, bool lost = false);
+
//bool isActiveObject() const { return true; }
bool canSeeSpellClickOn(Creature const* creature) const;
+
uint32 GetChampioningFaction() const { return m_ChampioningFaction; }
void SetChampioningFaction(uint32 faction) { m_ChampioningFaction = faction; }
Spell * m_spellModTakingSpell;
@@ -1909,9 +2233,11 @@ Spell * m_spellModTakingSpell;
uint32 m_regenTimerCount;
float m_powerFraction[MAX_POWERS];
uint32 m_contestedPvPTimer;
+
/*********************************************************/
/*** BATTLEGROUND SYSTEM ***/
/*********************************************************/
+
/*
this is an array of BG queues (BgTypeIDs) in which is player
*/
@@ -1920,18 +2246,24 @@ Spell * m_spellModTakingSpell;
BattleGroundQueueTypeId bgQueueTypeId;
uint32 invitedToInstance;
};
+
BgBattleGroundQueueID_Rec m_bgBattleGroundQueueID[PLAYER_MAX_BATTLEGROUND_QUEUES];
BGData m_bgData;
+
/*********************************************************/
/*** QUEST SYSTEM ***/
/*********************************************************/
+
//We allow only one timed quest active at the same time. Below can then be simple value instead of set.
std::set<uint32> m_timedquests;
+
uint64 m_divider;
uint32 m_ingametime;
+
/*********************************************************/
/*** LOAD SYSTEM ***/
/*********************************************************/
+
void _LoadActions(QueryResult *result, bool startup);
void _LoadAuras(QueryResult *result, uint32 timediff);
void _LoadGlyphAuras();
@@ -1953,9 +2285,11 @@ Spell * m_spellModTakingSpell;
void _LoadBGData(QueryResult* result);
void _LoadGlyphs(QueryResult *result);
void _LoadTalents(QueryResult *result);
+
/*********************************************************/
/*** SAVE SYSTEM ***/
/*********************************************************/
+
void _SaveActions();
void _SaveAuras();
void _SaveInventory();
@@ -1967,8 +2301,10 @@ Spell * m_spellModTakingSpell;
void _SaveBGData();
void _SaveGlyphs();
void _SaveTalents();
+
void _SetCreateBits(UpdateMask *updateMask, Player *target) const;
void _SetUpdateBits(UpdateMask *updateMask, Player *target) const;
+
/*********************************************************/
/*** ENVIRONMENTAL SYSTEM ***/
/*********************************************************/
@@ -1977,71 +2313,100 @@ Spell * m_spellModTakingSpell;
void StopMirrorTimer(MirrorTimerType Type);
void HandleDrowning(uint32 time_diff);
int32 getMaxTimer(MirrorTimerType timer);
+
/*********************************************************/
/*** HONOR SYSTEM ***/
/*********************************************************/
time_t m_lastHonorUpdateTime;
+
void outDebugValues() const;
uint64 m_lootGuid;
+
uint32 m_team;
uint32 m_nextSave;
time_t m_speakTime;
uint32 m_speakCount;
uint32 m_dungeonDifficulty;
+
uint32 m_atLoginFlags;
+
Item* m_items[PLAYER_SLOTS_COUNT];
uint32 m_currentBuybackSlot;
+
std::vector<Item*> m_itemUpdateQueue;
bool m_itemUpdateQueueBlocked;
+
uint32 m_ExtraFlags;
uint64 m_curSelection;
+
uint64 m_comboTarget;
int8 m_comboPoints;
+
QuestStatusMap mQuestStatus;
+
uint32 m_GuildIdInvited;
uint32 m_ArenaTeamIdInvited;
+
PlayerMails m_mail;
PlayerSpellMap m_spells;
PlayerTalentMap *m_talents[MAX_TALENT_SPECS];
uint32 m_lastPotionId; // last used health/mana potion in combat, that block next potion use
+
uint32 m_activeSpec;
uint32 m_specsCount;
+
uint32 m_Glyphs[MAX_TALENT_SPECS][MAX_GLYPH_SLOT_INDEX];
+
ActionButtonList m_actionButtons;
+
float m_auraBaseMod[BASEMOD_END][MOD_END];
int16 m_baseRatingValue[MAX_COMBAT_RATING];
uint16 m_baseSpellPower;
uint16 m_baseFeralAP;
uint16 m_baseManaRegen;
+
SpellModList m_spellMods[MAX_SPELLMOD];
uint32 m_pad;
// Spell * m_spellModTakingSpell; // Spell for which charges are dropped in spell::finish
+
EnchantDurationList m_enchantDuration;
ItemDurationList m_itemDuration;
+
uint64 m_resurrectGUID;
uint32 m_resurrectMap;
float m_resurrectX, m_resurrectY, m_resurrectZ;
uint32 m_resurrectHealth, m_resurrectMana;
+
WorldSession *m_session;
+
typedef std::list<Channel*> JoinedChannelsList;
JoinedChannelsList m_channels;
+
int m_cinematic;
+
Player *pTrader;
bool acceptTrade;
uint16 tradeItems[TRADE_SLOT_COUNT];
uint32 tradeGold;
+
time_t m_nextThinkTime;
+
bool m_DailyQuestChanged;
time_t m_lastDailyQuestTime;
+
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;
@@ -2049,6 +2414,7 @@ Spell * m_spellModTakingSpell;
bool m_canTitanGrip;
uint8 m_swingErrorMsg;
float m_ammoDPS;
+
////////////////////Rest System/////////////////////
int time_inn_enter;
uint32 inn_pos_mapid;
@@ -2058,26 +2424,32 @@ Spell * m_spellModTakingSpell;
float m_rest_bonus;
RestType rest_type;
////////////////////Rest System/////////////////////
+
uint32 m_resetTalentsCost;
time_t m_resetTalentsTime;
uint32 m_usedTalentCount;
uint32 m_questRewardTalentCount;
+
// Social
PlayerSocial *m_social;
+
// Groups
GroupReference m_group;
GroupReference m_originalGroup;
Group *m_groupInvite;
uint32 m_groupUpdateMask;
uint64 m_auraRaidUpdateMask;
+
// last used pet number (for BG's)
uint32 m_lastpetnumber;
+
// Player summoning
time_t m_summon_expire;
uint32 m_summon_mapid;
float m_summon_x;
float m_summon_y;
float m_summon_z;
+
DeclinedName *m_declinedname;
Runes *m_runes;
EquipmentSets m_EquipmentSets;
@@ -2087,47 +2459,63 @@ Spell * m_spellModTakingSpell;
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 );
+
void UpdateKnownCurrencies(uint32 itemId, bool apply);
int32 CalculateReputationGain(uint32 creatureOrQuestLevel, int32 rep, int32 faction, bool for_quest);
void AdjustQuestReqItemCount( Quest const* pQuest, QuestStatusData& questStatusData );
+
bool IsCanDelayTeleport() const { return m_bCanDelayTeleport; }
void SetCanDelayTeleport(bool setting) { m_bCanDelayTeleport = setting; }
bool IsHasDelayedTeleport() const { return m_bHasDelayedTeleport; }
void SetDelayedTeleportFlag(bool setting) { m_bHasDelayedTeleport = setting; }
+
void ScheduleDelayedOperation(uint32 operation)
{
if(operation < DELAYED_END)
m_DelayedOperations |= operation;
}
+
GridReference<Player> m_gridRef;
MapReference m_mapRef;
+
void UpdateCharmedAI();
UnitAI *i_AI;
+
uint32 m_lastFallTime;
float m_lastFallZ;
+
int32 m_MirrorTimer[MAX_TIMERS];
uint8 m_MirrorTimerFlags;
uint8 m_MirrorTimerFlagsLast;
bool m_isInWater;
+
// Current teleport data
WorldLocation m_teleport_dest;
uint32 m_teleport_options;
bool mSemaphoreTeleport_Near;
bool mSemaphoreTeleport_Far;
+
uint32 m_DelayedOperations;
bool m_bCanDelayTeleport;
bool m_bHasDelayedTeleport;
+
uint32 m_DetectInvTimer;
+
// Temporary removed pet cache
uint32 m_temporaryUnsummonedPetNumber;
uint32 m_oldpetspell;
+
AchievementMgr m_achievementMgr;
ReputationMgr m_reputationMgr;
+
SpellCooldowns m_spellCooldowns;
+
uint32 m_ChampioningFaction;
};
+
void AddItemsSetItem(Player*player,Item *item);
void RemoveItemsSetItem(Player*player,ItemPrototype const *proto);
+
// "the bodies of template functions must be made available in a header file"
template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &basevalue, Spell * spell)
{
@@ -2135,15 +2523,19 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas
if (!spellInfo) return 0;
int32 totalpct = 0;
int32 totalflat = 0;
+
// Drop charges for triggering spells instead of triggered ones
if (m_spellModTakingSpell)
spell = m_spellModTakingSpell;
+
for (SpellModList::iterator itr = m_spellMods[op].begin(); itr != m_spellMods[op].end(); ++itr)
{
SpellModifier *mod = *itr;
+
// Charges can be set only for mods with auras
if (!mod->ownerAura)
assert(mod->charges==0);
+
if(!IsAffectedBySpellmod(spellInfo,mod,spell))
continue;
if (mod->type == SPELLMOD_FLAT)
@@ -2153,13 +2545,17 @@ template <class T> T Player::ApplySpellMod(uint32 spellId, SpellModOp op, T &bas
// 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;
}
+
DropModCharge(mod, spell);
}
+
float diff = (float)basevalue*(float)totalpct/100.0f + (float)totalflat;
basevalue = T((float)basevalue + diff);
return T(diff);
diff --git a/src/game/PlayerDump.cpp b/src/game/PlayerDump.cpp
index 5922582dd10..12544c62e41 100644
--- a/src/game/PlayerDump.cpp
+++ b/src/game/PlayerDump.cpp
@@ -17,19 +17,23 @@
* 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 26
+
struct DumpTable
{
char const* name;
DumpTableType type;
};
+
static DumpTable dumpTables[DUMP_TABLE_COUNT] =
{
{ "characters", DTT_CHARACTER },
@@ -59,6 +63,7 @@ static DumpTable dumpTables[DUMP_TABLE_COUNT] =
{ "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)
{
@@ -67,25 +72,32 @@ static bool findtoknth(std::string &str, int n, std::string::size_type &s, std::
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
@@ -97,34 +109,42 @@ bool findnth(std::string &str, int n, std::string::size_type &s, std::string::si
}
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;
@@ -136,37 +156,47 @@ bool changetoknth(std::string &str, int n, const char *with, bool insert = false
str.replace(s, e-s, with);
else
str.insert(s, with);
+
return true;
}
+
uint32 registerNewGuid(uint32 oldGuid, std::map<uint32, uint32> &guidMap, uint32 hiGuid)
{
std::map<uint32, uint32>::const_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<uint32, uint32> &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<uint32, uint32> &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 "";
@@ -177,20 +207,24 @@ std::string CreateDumpString(char const* tableName, QueryResult *result)
{
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;
@@ -198,11 +232,13 @@ std::string PlayerDumpWriter::GenerateWhereStr(char const* field, GUIDs const& g
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 << "','";
@@ -210,6 +246,7 @@ std::string PlayerDumpWriter::GenerateWhereStr(char const* field, GUIDs const& g
wherestr << "')";
return wherestr.str();
}
+
void StoreGUID(QueryResult *result,uint32 field,std::set<uint32>& guids)
{
Field* fields = result->Fetch();
@@ -217,6 +254,7 @@ void StoreGUID(QueryResult *result,uint32 field,std::set<uint32>& guids)
if(guid)
guids.insert(guid);
}
+
void StoreGUID(QueryResult *result,uint32 data,uint32 field, std::set<uint32>& guids)
{
Field* fields = result->Fetch();
@@ -225,11 +263,13 @@ void StoreGUID(QueryResult *result,uint32 data,uint32 field, std::set<uint32>& g
if(guid)
guids.insert(guid);
}
+
// Writing - High-level functions
void PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tableFrom, char const*tableTo, DumpTableType type)
{
GUIDs const* guids = NULL;
char const* fieldname = NULL;
+
switch ( type )
{
case DTT_ITEM: fieldname = "guid"; guids = &items; break;
@@ -241,23 +281,29 @@ void PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tabl
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; // 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;
+
do
{
// collect guids
@@ -277,19 +323,24 @@ void PlayerDumpWriter::DumpTable(std::string& dump, uint32 guid, char const*tabl
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
}
+
std::string PlayerDumpWriter::GetDump(uint32 guid)
{
std::string dump;
+
dump += "IMPORTANT NOTE: This sql queries not created for apply directly, use '.pdump load' command in console or client chat instead.\n";
dump += "IMPORTANT NOTE: NOT APPLY ITS DIRECTLY to character DB or you will DAMAGE and CORRUPT character DB\n\n";
+
// revision check guard
/*
QueryNamedResult* result = CharacterDatabase.QueryNamed("SELECT * FROM character_db_version LIMIT 1");
@@ -305,6 +356,7 @@ std::string PlayerDumpWriter::GetDump(uint32 guid)
break;
}
}
+
if(!reqName.empty())
{
// this will fail at wrong character DB version
@@ -312,6 +364,7 @@ std::string PlayerDumpWriter::GetDump(uint32 guid)
}
else
sLog.outError("Table 'character_db_version' not have revision guard field, revision guard query not added to pdump.");
+
delete result;
}
else
@@ -319,22 +372,29 @@ std::string PlayerDumpWriter::GetDump(uint32 guid)
*/
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..
// TODO: Add a dump level option to skip some non-important tables
+
return dump;
}
+
DumpReturn PlayerDumpWriter::WriteDump(const std::string& file, uint32 guid)
{
FILE *fout = fopen(file.c_str(), "w");
if (!fout)
return DUMP_FILE_OPEN_ERROR;
+
std::string dump = GetDump(guid);
+
fprintf(fout,"%s\n",dump.c_str());
fclose(fout);
return DUMP_SUCCESS;
}
+
// Reading - High-level functions
#define ROLLBACK(DR) {CharacterDatabase.RollbackTransaction(); fclose(fin); return (DR);}
+
DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, std::string name, uint32 guid)
{
// check character count
@@ -346,15 +406,19 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
Field *fields=result->Fetch();
charcount = fields[0].GetUInt8();
delete result;
+
if (charcount >= 10)
return DUMP_TOO_MANY_CHARS;
}
}
+
FILE *fin = fopen(file.c_str(), "r");
if (!fin)
return DUMP_FILE_OPEN_ERROR;
+
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)
@@ -369,9 +433,11 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
}
else
guid = objmgr.m_hiCharGuid;
+
// normalize the name if specified and check if it exists
if (!normalizePlayerName(name))
name = "";
+
if (ObjectMgr::CheckPlayerName(name,true) == CHAR_NAME_SUCCESS)
{
CharacterDatabase.escape_string(name); // for safe, we use name only for sql quearies anyway
@@ -384,18 +450,23 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
}
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<uint32,uint32> items;
std::map<uint32,uint32> mails;
std::map<uint32,uint32> itemTexts;
char buf[32000] = "";
+
typedef std::map<uint32, uint32> PetIds; // old->new petid relation
typedef PetIds::value_type PetIdsPair;
PetIds petids;
+
CharacterDatabase.BeginTransaction();
while(!feof(fin))
{
@@ -404,23 +475,29 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
if(feof(fin)) break;
ROLLBACK(DUMP_FILE_BROKEN);
}
+
std::string line; line.assign(buf);
+
// skip empty strings
size_t nw_pos = line.find_first_not_of(" \t\n\r\7");
if(nw_pos==std::string::npos)
continue;
+
// skip NOTE
if(line.substr(nw_pos,15)=="IMPORTANT NOTE:")
continue;
+
// add required_ check
/*
if(line.substr(nw_pos,41)=="UPDATE character_db_version SET required_")
{
if(!CharacterDatabase.Execute(line.c_str()))
ROLLBACK(DUMP_FILE_BROKEN);
+
continue;
}
*/
+
// determine table name and load type
std::string tn = gettablename(line);
if(tn.empty())
@@ -428,6 +505,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
sLog.outError("LoadPlayerDump: Can't extract table name from line: '%s'!", line.c_str());
ROLLBACK(DUMP_FILE_BROKEN);
}
+
DumpTableType type;
uint8 i;
for(i = 0; i < DUMP_TABLE_COUNT; ++i)
@@ -438,11 +516,13 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
break;
}
}
+
if (i == DUMP_TABLE_COUNT)
{
sLog.outError("LoadPlayerDump: Unknown table: '%s'!", tn.c_str());
ROLLBACK(DUMP_FILE_BROKEN);
}
+
// change the data to server values
switch(type)
{
@@ -450,10 +530,12 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
if(!changenth(line, 1, newguid))
ROLLBACK(DUMP_FILE_BROKEN);
break;
+
case DTT_CHARACTER: // character t.
{
if(!changenth(line, 1, newguid))
ROLLBACK(DUMP_FILE_BROKEN);
+
// guid, data field:guid, items
if(!changenth(line, 2, chraccount))
ROLLBACK(DUMP_FILE_BROKEN);
@@ -470,22 +552,26 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
// 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;
+
if(!changenth(line, 37, "1")) // rename on login: `at_login` field 37 in raw field list
ROLLBACK(DUMP_FILE_BROKEN);
}
}
else if(!changenth(line, 4, name.c_str()))
ROLLBACK(DUMP_FILE_BROKEN);
+
break;
}
case DTT_INVENTORY: // character_inventory t.
{
if(!changenth(line, 1, newguid))
ROLLBACK(DUMP_FILE_BROKEN);
+
// bag, item
if(!changeGuid(line, 2, items, objmgr.m_hiItemGuid, true))
ROLLBACK(DUMP_FILE_BROKEN);
@@ -530,28 +616,36 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
snprintf(newpetid, 20, "%d", objmgr.GeneratePetNumber());
snprintf(lastpetid, 20, "%s", currpetid);
}
+
std::map<uint32, uint32> :: 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(DUMP_FILE_BROKEN);
if(!changenth(line, 3, newguid))
ROLLBACK(DUMP_FILE_BROKEN);
+
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<uint32, uint32> :: const_iterator petids_iter = petids.find(atoi(currpetid));
if(petids_iter == petids.end()) // couldn't find new inserted id
ROLLBACK(DUMP_FILE_BROKEN);
+
snprintf(newpetid, 20, "%d", petids_iter->second);
+
if(!changenth(line, 1, newpetid))
ROLLBACK(DUMP_FILE_BROKEN);
+
break;
}
case DTT_MAIL: // mail
@@ -581,6 +675,7 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
// id
if(!changeGuid(line, 1, itemTexts, objmgr.m_ItemTextId))
ROLLBACK(DUMP_FILE_BROKEN);
+
// add it to cache
uint32 id= atoi(getnth(line,1).c_str());
std::string text = getnth(line,2);
@@ -591,16 +686,22 @@ DumpReturn PlayerDumpReader::LoadDump(const std::string& file, uint32 account, s
sLog.outError("Unknown dump table type: %u",type);
break;
}
+
if(!CharacterDatabase.Execute(line.c_str()))
ROLLBACK(DUMP_FILE_BROKEN);
}
+
CharacterDatabase.CommitTransaction();
+
objmgr.m_hiItemGuid += items.size();
objmgr.m_mailid += mails.size();
objmgr.m_ItemTextId += itemTexts.size();
+
if(incHighest)
++objmgr.m_hiCharGuid;
+
fclose(fin);
+
return DUMP_SUCCESS;
}
diff --git a/src/game/PlayerDump.h b/src/game/PlayerDump.h
index 934244b5d09..e3dfd192efb 100644
--- a/src/game/PlayerDump.h
+++ b/src/game/PlayerDump.h
@@ -17,30 +17,41 @@
* 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 <string>
#include <map>
#include <set>
+
enum DumpTableType
{
DTT_CHARACTER, // // characters
+
DTT_CHAR_TABLE, // // character_achievement, character_achievement_progress,
// 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
};
+
enum DumpReturn
{
DUMP_SUCCESS,
@@ -49,32 +60,40 @@ enum DumpReturn
DUMP_UNEXPECTED_END,
DUMP_FILE_BROKEN,
};
+
class PlayerDump
{
protected:
PlayerDump() {}
};
+
class PlayerDumpWriter : public PlayerDump
{
public:
PlayerDumpWriter() {}
+
std::string GetDump(uint32 guid);
DumpReturn WriteDump(const std::string& file, uint32 guid);
private:
typedef std::set<uint32> GUIDs;
+
void 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() {}
+
DumpReturn LoadDump(const std::string& file, uint32 account, std::string name, uint32 guid);
};
+
#endif
diff --git a/src/game/PointMovementGenerator.cpp b/src/game/PointMovementGenerator.cpp
index 10f8357ef87..23012f7e170 100644
--- a/src/game/PointMovementGenerator.cpp
+++ b/src/game/PointMovementGenerator.cpp
@@ -17,12 +17,14 @@
* 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 "DestinationHolderImp.h"
#include "World.h"
+
//----- Point Movement Generator
template<class T>
void PointMovementGenerator<T>::Initialize(T &unit)
@@ -32,11 +34,13 @@ void PointMovementGenerator<T>::Initialize(T &unit)
// knockback effect has UNIT_STAT_JUMPING set,so if here we disable sentmonstermove there will be creature position sync problem between client and server
i_destinationHolder.SetDestination(traveller,i_x,i_y,i_z, true /* !unit.hasUnitState(UNIT_STAT_JUMPING)*/);
}
+
template<class T>
bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
{
if(!&unit)
return false;
+
if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED))
{
if(unit.hasUnitState(UNIT_STAT_CHARGING))
@@ -44,16 +48,21 @@ bool PointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
else
return true;
}
+
Traveller<T> traveller(unit);
+
i_destinationHolder.UpdateTraveller(traveller, diff);
+
if(i_destinationHolder.HasArrived())
{
unit.clearUnitState(UNIT_STAT_MOVE);
arrived = true;
return false;
}
+
return true;
}
+
template<class T>
void PointMovementGenerator<T>:: Finalize(T &unit)
{
@@ -62,10 +71,12 @@ void PointMovementGenerator<T>:: Finalize(T &unit)
if(arrived) // without this crash!
MovementInform(unit);
}
+
template<class T>
void PointMovementGenerator<T>::MovementInform(T &unit)
{
}
+
template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit)
{
if(id == EVENT_FALL_GROUND)
@@ -75,13 +86,16 @@ template <> void PointMovementGenerator<Creature>::MovementInform(Creature &unit
}
unit.AI()->MovementInform(POINT_MOTION_TYPE, id);
}
+
template void PointMovementGenerator<Player>::Initialize(Player&);
template bool PointMovementGenerator<Player>::Update(Player &, const uint32 &diff);
template void PointMovementGenerator<Player>::MovementInform(Player&);
template void PointMovementGenerator<Player>::Finalize(Player&);
+
template void PointMovementGenerator<Creature>::Initialize(Creature&);
template bool PointMovementGenerator<Creature>::Update(Creature&, const uint32 &diff);
template void PointMovementGenerator<Creature>::Finalize(Creature&);
+
void AssistanceMovementGenerator::Finalize(Unit &unit)
{
((Creature*)&unit)->SetNoCallAssistance(false);
diff --git a/src/game/PointMovementGenerator.h b/src/game/PointMovementGenerator.h
index 1c07d580600..baeb8b26dbd 100644
--- a/src/game/PointMovementGenerator.h
+++ b/src/game/PointMovementGenerator.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_POINTMOVEMENTGENERATOR_H
#define TRINITY_POINTMOVEMENTGENERATOR_H
+
#include "MovementGenerator.h"
#include "DestinationHolder.h"
#include "Traveller.h"
#include "FollowerReference.h"
+
template<class T>
class TRINITY_DLL_SPEC PointMovementGenerator
: public MovementGeneratorMedium< T, PointMovementGenerator<T> >
@@ -30,12 +33,16 @@ class TRINITY_DLL_SPEC 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), arrived(false) {}
+
void Initialize(T &);
void Finalize(T &unit);
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:
uint32 id;
@@ -44,14 +51,17 @@ class TRINITY_DLL_SPEC PointMovementGenerator
DestinationHolder< Traveller<T> > i_destinationHolder;
bool arrived;
};
+
class MANGOS_DLL_SPEC AssistanceMovementGenerator
: public PointMovementGenerator<Creature>
{
public:
AssistanceMovementGenerator(float _x, float _y, float _z) :
PointMovementGenerator<Creature>(0, _x, _y, _z) {}
+
MovementGeneratorType GetMovementGeneratorType() { return ASSISTANCE_MOTION_TYPE; }
void Finalize(Unit &);
};
+
#endif
diff --git a/src/game/PoolHandler.cpp b/src/game/PoolHandler.cpp
index 2eccbb39d41..03b09f9a483 100644
--- a/src/game/PoolHandler.cpp
+++ b/src/game/PoolHandler.cpp
@@ -15,21 +15,26 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "PoolHandler.h"
#include "ObjectMgr.h"
#include "ProgressBar.h"
#include "Log.h"
#include "MapManager.h"
#include "Policies/SingletonImp.h"
+
INSTANTIATE_SINGLETON_1(PoolHandler);
+
////////////////////////////////////////////////////////////
// Methods of template class PoolGroup
+
template <class T>
PoolGroup<T>::PoolGroup()
{
m_SpawnedPoolAmount = 0;
m_LastDespawnedNode = 0;
}
+
// Method to add a gameobject/creature guid to the proper list depending on pool type and chance value
template <class T>
void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries)
@@ -39,6 +44,7 @@ void PoolGroup<T>::AddEntry(PoolObject& poolitem, uint32 maxentries)
else
EqualChanced.push_back(poolitem);
}
+
// Method to check the chances are proper in this object pool
template <class T>
bool PoolGroup<T>::CheckPool(void)
@@ -53,6 +59,7 @@ bool PoolGroup<T>::CheckPool(void)
}
return true;
}
+
// Method that tell if the gameobject, creature or pool is spawned currently
template <class T>
bool PoolGroup<T>::IsSpawnedObject(uint32 guid)
@@ -65,6 +72,7 @@ bool PoolGroup<T>::IsSpawnedObject(uint32 guid)
return EqualChanced[i].spawned;
return false;
}
+
// Method that return a guid of a rolled creature or gameobject
// Note: Copy from loot system because it's very similar and only few things change
template <class T>
@@ -73,6 +81,7 @@ uint32 PoolGroup<T>::RollOne(void)
if (!ExplicitlyChanced.empty()) // First explicitly chanced entries are checked
{
float roll = rand_chance();
+
for (uint32 i=0; i<ExplicitlyChanced.size(); ++i)
{
roll -= ExplicitlyChanced[i].chance;
@@ -82,8 +91,10 @@ uint32 PoolGroup<T>::RollOne(void)
}
if (!EqualChanced.empty())
return EqualChanced[irand(0, EqualChanced.size()-1)].guid;
+
return 0; // None found
}
+
// Main method to despawn a creature or gameobject in a pool
// If no guid is passed, the pool is just removed (event end case)
// If guid is filled, cache will be used and no removal will occur, it just fill the cache
@@ -100,6 +111,7 @@ void PoolGroup<T>::DespawnObject(uint32 guid)
m_LastDespawnedNode = EqualChanced[i].guid;
else
Despawn1Object(EqualChanced[i].guid);
+
EqualChanced[i].spawned = false;
if (m_SpawnedPoolAmount > 0)
--m_SpawnedPoolAmount;
@@ -107,6 +119,7 @@ void PoolGroup<T>::DespawnObject(uint32 guid)
}
}
}
+
// Method that is actualy doing the removal job on one creature
template<>
void PoolGroup<Creature>::Despawn1Object(uint32 guid)
@@ -114,10 +127,12 @@ void PoolGroup<Creature>::Despawn1Object(uint32 guid)
if (CreatureData const* data = objmgr.GetCreatureData(guid))
{
objmgr.RemoveCreatureFromGrid(guid, data);
+
if (Creature* pCreature = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_UNIT), (Creature*)NULL))
pCreature->AddObjectToRemoveList();
}
}
+
// Same on one gameobject
template<>
void PoolGroup<GameObject>::Despawn1Object(uint32 guid)
@@ -125,16 +140,19 @@ void PoolGroup<GameObject>::Despawn1Object(uint32 guid)
if (GameObjectData const* data = objmgr.GetGOData(guid))
{
objmgr.RemoveGameobjectFromGrid(guid, data);
+
if (GameObject* pGameobject = ObjectAccessor::Instance().GetObjectInWorld(MAKE_NEW_GUID(guid, data->id, HIGHGUID_GAMEOBJECT), (GameObject*)NULL))
pGameobject->AddObjectToRemoveList();
}
}
+
// Same on one pool
template<>
void PoolGroup<Pool>::Despawn1Object(uint32 child_pool_id)
{
poolhandler.DespawnPool(child_pool_id);
}
+
// Method for a pool only to remove any found record causing a circular dependency loop
template<>
void PoolGroup<Pool>::RemoveOneRelation(uint16 child_pool_id)
@@ -156,6 +174,7 @@ void PoolGroup<Pool>::RemoveOneRelation(uint16 child_pool_id)
}
}
}
+
// Method that Spawn 1+ creatures or gameobject
// if cache is false (initialization or event start), X creatures are spawned with X <= limit (< if limit higher that the number of creatures in pool)
// if cache is true, this means only one has to be spawned (or respawned if the rolled one is same as cached one)
@@ -167,6 +186,7 @@ void PoolGroup<T>::SpawnObject(uint32 limit, bool cache)
uint32 roll = RollOne();
if (cache && m_LastDespawnedNode != roll)
Despawn1Object(m_LastDespawnedNode);
+
m_LastDespawnedNode = 0;
Spawn1Object(roll);
}
@@ -176,6 +196,7 @@ void PoolGroup<T>::SpawnObject(uint32 limit, bool cache)
for (size_t i=0; i<EqualChanced.size(); ++i)
if (!EqualChanced[i].spawned)
IndexList.push_back(i);
+
while (m_SpawnedPoolAmount < limit && IndexList.size() > 0)
{
uint32 roll = urand(1, IndexList.size()) - 1;
@@ -188,8 +209,10 @@ void PoolGroup<T>::SpawnObject(uint32 limit, bool cache)
}
else
EqualChanced[index].spawned = ReSpawn1Object(EqualChanced[index].guid);
+
if (EqualChanced[index].spawned)
++m_SpawnedPoolAmount; // limited group use the Spawned variable to store the number of actualy spawned creatures
+
std::vector<uint32>::iterator itr = IndexList.begin()+roll;
IndexList.erase(itr);
}
@@ -201,6 +224,7 @@ void PoolGroup<T>::SpawnObject(uint32 limit, bool cache)
EqualChanced[i].spawned = Spawn1Object(EqualChanced[i].guid);
}
}
+
// Method that is actualy doing the spawn job on 1 creature
template <>
bool PoolGroup<Creature>::Spawn1Object(uint32 guid)
@@ -209,6 +233,7 @@ bool PoolGroup<Creature>::Spawn1Object(uint32 guid)
if (data)
{
objmgr.AddCreatureToGrid(guid, data);
+
// Spawn if necessary (loaded grids only)
Map* map = const_cast<Map*>(MapManager::Instance().CreateBaseMap(data->mapid));
// We use spawn coords to spawn
@@ -229,6 +254,7 @@ bool PoolGroup<Creature>::Spawn1Object(uint32 guid)
}
return false;
}
+
// Same for 1 gameobject
template <>
bool PoolGroup<GameObject>::Spawn1Object(uint32 guid)
@@ -259,6 +285,7 @@ bool PoolGroup<GameObject>::Spawn1Object(uint32 guid)
}
return false;
}
+
// Same for 1 pool
template <>
bool PoolGroup<Pool>::Spawn1Object(uint32 child_pool_id)
@@ -266,6 +293,7 @@ bool PoolGroup<Pool>::Spawn1Object(uint32 child_pool_id)
poolhandler.SpawnPool(child_pool_id);
return true;
}
+
// Method that does the respawn job on the specified creature
template <>
bool PoolGroup<Creature>::ReSpawn1Object(uint32 guid)
@@ -279,6 +307,7 @@ bool PoolGroup<Creature>::ReSpawn1Object(uint32 guid)
}
return false;
}
+
// Same for 1 gameobject
template <>
bool PoolGroup<GameObject>::ReSpawn1Object(uint32 guid)
@@ -292,6 +321,7 @@ bool PoolGroup<GameObject>::ReSpawn1Object(uint32 guid)
}
return false;
}
+
// Nothing to do for a child Pool
template <>
bool PoolGroup<Pool>::ReSpawn1Object(uint32 /*guid*/)
@@ -299,12 +329,15 @@ bool PoolGroup<Pool>::ReSpawn1Object(uint32 /*guid*/)
return true;
}
+
////////////////////////////////////////////////////////////
// Methods of class PoolHandler
+
PoolHandler::PoolHandler()
{
m_IsPoolSystemStarted = false;
}
+
void PoolHandler::LoadFromDB()
{
QueryResult *result = WorldDatabase.Query("SELECT MAX(entry) FROM pool_template");
@@ -320,7 +353,9 @@ void PoolHandler::LoadFromDB()
max_pool_id = fields[0].GetUInt16();
delete result;
}
+
mPoolTemplate.resize(max_pool_id + 1);
+
result = WorldDatabase.Query("SELECT entry,max_limit FROM pool_template");
if (!result)
{
@@ -329,43 +364,58 @@ void PoolHandler::LoadFromDB()
sLog.outString();
return;
}
+
uint32 count = 0;
+
barGoLink bar(result->GetRowCount());
do
{
++count;
Field *fields = result->Fetch();
+
bar.step();
+
uint16 pool_id = fields[0].GetUInt16();
+
PoolTemplateData& pPoolTemplate = mPoolTemplate[pool_id];
pPoolTemplate.MaxLimit = fields[1].GetUInt32();
+
} while (result->NextRow());
+
sLog.outString();
sLog.outString( ">> Loaded %u objects pools", count );
delete result;
+
// Creatures
+
mPoolCreatureGroups.resize(max_pool_id + 1);
mCreatureSearchMap.clear();
// 1 2 3
result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_creature");
+
count = 0;
if (!result)
{
barGoLink bar2(1);
bar2.step();
+
sLog.outString();
sLog.outString(">> Loaded %u creatures in pools", count );
}
else
{
+
barGoLink bar2(result->GetRowCount());
do
{
Field *fields = result->Fetch();
+
bar2.step();
+
uint32 guid = fields[0].GetUInt32();
uint16 pool_id = fields[1].GetUInt16();
float chance = fields[2].GetFloat();
+
CreatureData const* data = objmgr.GetCreatureData(guid);
if (!data)
{
@@ -384,39 +434,49 @@ void PoolHandler::LoadFromDB()
}
PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id];
++count;
+
PoolObject plObject = PoolObject(guid, chance);
PoolGroup<Creature>& cregroup = mPoolCreatureGroups[pool_id];
cregroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
SearchPair p(guid, pool_id);
mCreatureSearchMap.insert(p);
+
} while (result->NextRow());
sLog.outString();
sLog.outString( ">> Loaded %u creatures in pools", count );
delete result;
}
+
// Gameobjects
+
mPoolGameobjectGroups.resize(max_pool_id + 1);
mGameobjectSearchMap.clear();
// 1 2 3
result = WorldDatabase.Query("SELECT guid, pool_entry, chance FROM pool_gameobject");
+
count = 0;
if (!result)
{
barGoLink bar2(1);
bar2.step();
+
sLog.outString();
sLog.outString(">> Loaded %u gameobject in pools", count );
}
else
{
+
barGoLink bar2(result->GetRowCount());
do
{
Field *fields = result->Fetch();
+
bar2.step();
+
uint32 guid = fields[0].GetUInt32();
uint16 pool_id = fields[1].GetUInt16();
float chance = fields[2].GetFloat();
+
GameObjectData const* data = objmgr.GetGOData(guid);
if (!data)
{
@@ -442,39 +502,49 @@ void PoolHandler::LoadFromDB()
continue;
}
PoolTemplateData *pPoolTemplate = &mPoolTemplate[pool_id];
+
++count;
+
PoolObject plObject = PoolObject(guid, chance);
PoolGroup<GameObject>& gogroup = mPoolGameobjectGroups[pool_id];
gogroup.AddEntry(plObject, pPoolTemplate->MaxLimit);
SearchPair p(guid, pool_id);
mGameobjectSearchMap.insert(p);
+
} while( result->NextRow() );
sLog.outString();
sLog.outString( ">> Loaded %u gameobject in pools", count );
delete result;
}
+
// Pool of pools
mPoolPoolGroups.resize(max_pool_id + 1);
// 1 2 3
result = WorldDatabase.Query("SELECT pool_id, mother_pool, chance FROM pool_pool");
+
count = 0;
if( !result )
{
barGoLink bar2(1);
bar2.step();
+
sLog.outString();
sLog.outString(">> Loaded %u pools in pools", count );
}
else
{
+
barGoLink bar2( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar2.step();
+
uint16 child_pool_id = fields[0].GetUInt16();
uint16 mother_pool_id = fields[1].GetUInt16();
float chance = fields[2].GetFloat();
+
if (mother_pool_id > max_pool_id)
{
sLog.outErrorDb("`pool_pool` mother_pool id (%i) is out of range compared to max pool id in `pool_template`, skipped.",mother_pool_id);
@@ -496,13 +566,17 @@ void PoolHandler::LoadFromDB()
continue;
}
PoolTemplateData *pPoolTemplateMother = &mPoolTemplate[mother_pool_id];
+
++count;
+
PoolObject plObject = PoolObject(child_pool_id, chance);
PoolGroup<Pool>& plgroup = mPoolPoolGroups[mother_pool_id];
plgroup.AddEntry(plObject, pPoolTemplateMother->MaxLimit);
SearchPair p(child_pool_id, mother_pool_id);
mPoolSearchMap.insert(p);
+
} while( result->NextRow() );
+
// Now check for circular reference
for(uint16 i=0; i<max_pool_id; ++i)
{
@@ -531,6 +605,7 @@ void PoolHandler::LoadFromDB()
delete result;
}
}
+
// The initialize method will spawn all pools not in an event and not in another pool, this is why there is 2 left joins with 2 null checks
void PoolHandler::Initialize()
{
@@ -552,9 +627,11 @@ void PoolHandler::Initialize()
} while (result->NextRow());
delete result;
}
+
sLog.outBasic("Pool handling system initialized, %u pools spawned.", count);
m_IsPoolSystemStarted = true;
}
+
// Call to spawn a pool, if cache if true the method will spawn only if cached entry is different
// If it's same, the gameobject/creature is respawned only (added back to map)
void PoolHandler::SpawnPool(uint16 pool_id, bool cache)
@@ -566,6 +643,7 @@ void PoolHandler::SpawnPool(uint16 pool_id, bool cache)
if (!mPoolCreatureGroups[pool_id].isEmpty())
mPoolCreatureGroups[pool_id].SpawnObject(mPoolTemplate[pool_id].MaxLimit, cache);
}
+
// Call to despawn a pool, all gameobjects/creatures in this pool are removed
void PoolHandler::DespawnPool(uint16 pool_id)
{
@@ -576,23 +654,27 @@ void PoolHandler::DespawnPool(uint16 pool_id)
if (!mPoolCreatureGroups[pool_id].isEmpty())
mPoolCreatureGroups[pool_id].DespawnObject();
}
+
// Call to update the pool when a gameobject/creature part of pool [pool_id] is ready to respawn
// Here we cache only the creature/gameobject whose guid is passed as parameter
// Then the spawn pool call will use this cache to decide
void PoolHandler::UpdatePool(uint16 pool_id, uint32 guid, uint32 type)
{
uint16 motherpoolid = IsPartOfAPool(pool_id, 0);
+
if (motherpoolid)
mPoolPoolGroups[motherpoolid].DespawnObject(pool_id);
else if (type == TYPEID_GAMEOBJECT && !mPoolGameobjectGroups[pool_id].isEmpty())
mPoolGameobjectGroups[pool_id].DespawnObject(guid);
else if (type != TYPEID_GAMEOBJECT && !mPoolCreatureGroups[pool_id].isEmpty())
mPoolCreatureGroups[pool_id].DespawnObject(guid);
+
if (motherpoolid)
SpawnPool(motherpoolid, true);
else
SpawnPool(pool_id, true);
}
+
// Method that tell if the gameobject/creature is part of a pool and return the pool id if yes
uint16 PoolHandler::IsPartOfAPool(uint32 guid, uint32 type)
{
@@ -616,6 +698,7 @@ uint16 PoolHandler::IsPartOfAPool(uint32 guid, uint32 type)
}
return 0;
}
+
// Method that check chance integrity of the creatures and gameobjects in this pool
bool PoolHandler::CheckPool(uint16 pool_id)
{
@@ -624,6 +707,7 @@ bool PoolHandler::CheckPool(uint16 pool_id)
mPoolCreatureGroups[pool_id].CheckPool() &&
mPoolPoolGroups[pool_id].CheckPool();
}
+
// Method that tell if a creature or gameobject in pool_id is spawned currently
bool PoolHandler::IsSpawnedObject(uint16 pool_id, uint32 guid, uint32 type)
{
diff --git a/src/game/PoolHandler.h b/src/game/PoolHandler.h
index ee59f9587e4..10ed000ea25 100644
--- a/src/game/PoolHandler.h
+++ b/src/game/PoolHandler.h
@@ -15,16 +15,20 @@
* 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_POOLHANDLER_H
#define MANGOS_POOLHANDLER_H
+
#include "Platform/Define.h"
#include "Policies/Singleton.h"
#include "Creature.h"
#include "GameObject.h"
+
struct PoolTemplateData
{
uint32 MaxLimit;
};
+
struct PoolObject
{
uint32 guid;
@@ -32,6 +36,7 @@ struct PoolObject
bool spawned;
PoolObject(uint32 _guid, float _chance): guid(_guid), chance(fabs(_chance)), spawned(false) {}
};
+
template <class T>
class PoolGroup
{
@@ -56,9 +61,11 @@ class PoolGroup
uint32 m_LastDespawnedNode ; // Store the guid of the removed creature/gameobject during a pool update
uint32 m_SpawnedPoolAmount; // Used to know the number of spawned objects
};
+
class Pool // for Pool of Pool case
{
};
+
class PoolHandler
{
public:
@@ -72,6 +79,7 @@ class PoolHandler
void DespawnPool(uint16 pool_id);
void UpdatePool(uint16 pool_id, uint32 guid, uint32 type);
void Initialize();
+
protected:
bool m_IsPoolSystemStarted;
uint16 max_pool_id;
@@ -81,6 +89,7 @@ class PoolHandler
typedef std::vector<PoolGroup<Pool> > PoolGroupPoolMap;
typedef std::pair<uint32, uint16> SearchPair;
typedef std::map<uint32, uint16> SearchMap;
+
PoolTemplateDataMap mPoolTemplate;
PoolGroupCreatureMap mPoolCreatureGroups;
PoolGroupGameObjectMap mPoolGameobjectGroups;
@@ -88,6 +97,8 @@ class PoolHandler
SearchMap mCreatureSearchMap;
SearchMap mGameobjectSearchMap;
SearchMap mPoolSearchMap;
+
};
+
#define poolhandler MaNGOS::Singleton<PoolHandler>::Instance()
#endif
diff --git a/src/game/QueryHandler.cpp b/src/game/QueryHandler.cpp
index f4e9540d5ad..ebd5e773c12 100644
--- a/src/game/QueryHandler.cpp
+++ b/src/game/QueryHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -32,10 +33,12 @@
#include "NPCHandler.h"
#include "Pet.h"
#include "MapManager.h"
+
void WorldSession::SendNameQueryOpcode(Player *p)
{
if(!p)
return;
+
// guess size
WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+10) );
data.append(p->GetPackGUID()); // player guid
@@ -53,8 +56,10 @@ void WorldSession::SendNameQueryOpcode(Player *p)
}
else
data << uint8(0); // is not declined
+
SendPacket(&data);
}
+
void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid)
{
CharacterDatabase.AsyncPQuery(&WorldSession::SendNameQueryOpcodeFromDBCallBack, GetAccountId(),
@@ -72,16 +77,19 @@ void WorldSession::SendNameQueryOpcodeFromDB(uint64 guid)
"FROM characters LEFT JOIN character_declinedname ON characters.guid = character_declinedname.guid WHERE characters.guid = '%u'",
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();
@@ -94,6 +102,7 @@ void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32
pGender = fields[3].GetUInt8();
pClass = fields[4].GetUInt8();
}
+
// guess size
WorldPacket data( SMSG_NAME_QUERY_RESPONSE, (8+1+1+1+1+1+1+10) );
data.appendPackGUID(MAKE_NEW_GUID(guid, 0, HIGHGUID_PLAYER));
@@ -103,6 +112,7 @@ void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32
data << uint8(pRace); // race
data << uint8(pGender); // gender
data << uint8(pClass); // class
+
// if the first declined name field (5) is empty, the rest must be too
if(sWorld.getConfig(CONFIG_DECLINED_NAMES_USED) && fields[5].GetCppString() != "")
{
@@ -112,19 +122,25 @@ void WorldSession::SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32
}
else
data << uint8(0); // is not declined
+
session->SendPacket( &data );
delete result;
}
+
void WorldSession::HandleNameQueryOpcode( WorldPacket & recv_data )
{
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 );
@@ -132,18 +148,22 @@ void WorldSession::HandleQueryTimeOpcode( WorldPacket & /*recv_data*/ )
data << (uint32)0;
SendPacket( &data );
}
+
/// Only _static_ data send in this packet !!!
void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data )
{
uint32 entry;
recv_data >> entry;
recv_data.read_skip<uint64>(); // guid
+
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)
{
@@ -187,6 +207,7 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data )
{
uint64 guid;
recv_data >> guid;
+
sLog.outDebug("WORLD: CMSG_CREATURE_QUERY - NO CREATURE INFO! (GUID: %u, ENTRY: %u)",
GUID_LOPART(guid), entry);
WorldPacket data( SMSG_CREATURE_QUERY_RESPONSE, 4 );
@@ -195,21 +216,25 @@ void WorldSession::HandleCreatureQueryOpcode( WorldPacket & recv_data )
sLog.outDebug( "WORLD: Sent SMSG_CREATURE_QUERY_RESPONSE" );
}
}
+
/// Only _static_ data send in this packet !!!
void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data )
{
uint32 entryID;
recv_data >> entryID;
recv_data.read_skip<uint64>(); // guid
+
const GameObjectInfo *info = objmgr.GetGameObjectInfo(entryID);
if(info)
{
std::string Name;
std::string IconName;
std::string CastBarCaption;
+
Name = info->name;
IconName = info->IconName;
CastBarCaption = info->castBarCaption;
+
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -241,8 +266,10 @@ void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data )
}
else
{
+
uint64 guid;
recv_data >> guid;
+
sLog.outDebug( "WORLD: CMSG_GAMEOBJECT_QUERY - Missing gameobject info for (GUID: %u, ENTRY: %u)",
GUID_LOPART(guid), entryID );
WorldPacket data ( SMSG_GAMEOBJECT_QUERY_RESPONSE, 4 );
@@ -251,10 +278,13 @@ void WorldSession::HandleGameObjectQueryOpcode( WorldPacket & recv_data )
sLog.outDebug( "WORLD: Sent SMSG_GAMEOBJECT_QUERY_RESPONSE" );
}
}
+
void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
{
sLog.outDetail("WORLD: Received MSG_CORPSE_QUERY");
+
Corpse *corpse = GetPlayer()->GetCorpse();
+
if(!corpse)
{
WorldPacket data(MSG_CORPSE_QUERY, 1);
@@ -262,11 +292,13 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
SendPacket(&data);
return;
}
+
int32 mapid = corpse->GetMapId();
float x = corpse->GetPositionX();
float y = corpse->GetPositionY();
float z = corpse->GetPositionZ();
int32 corpsemapid = mapid;
+
// if corpse at different map
if(mapid != _player->GetMapId())
{
@@ -286,6 +318,7 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
}
}
}
+
WorldPacket data(MSG_CORPSE_QUERY, 1+(5*4));
data << uint8(1); // corpse found
data << int32(mapid);
@@ -295,17 +328,23 @@ void WorldSession::HandleCorpseQueryOpcode(WorldPacket & /*recv_data*/)
data << int32(corpsemapid);
SendPacket(&data);
}
+
void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data )
{
uint32 textID;
uint64 guid;
+
recv_data >> textID;
sLog.outDetail("WORLD: CMSG_NPC_TEXT_QUERY ID '%u'", textID);
+
recv_data >> guid;
GetPlayer()->SetUInt64Value(UNIT_FIELD_TARGET, guid);
+
GossipText const* pGossip = objmgr.GetGossipText(textID);
+
WorldPacket data( SMSG_NPC_TEXT_UPDATE, 100 ); // guess size
data << textID;
+
if (!pGossip)
{
for(uint32 i = 0; i < 8; ++i)
@@ -330,6 +369,7 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data )
Text_0[i]=pGossip->Options[i].Text_0;
Text_1[i]=pGossip->Options[i].Text_1;
}
+
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -345,18 +385,23 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data )
}
}
}
+
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;
+
for(int j = 0; j < 3; ++j)
{
data << pGossip->Options[i].Emotes[j]._Delay;
@@ -364,20 +409,26 @@ void WorldSession::HandleNpcTextQueryOpcode( WorldPacket & recv_data )
}
}
}
+
SendPacket( &data );
+
sLog.outDebug( "WORLD: Sent SMSG_NPC_TEXT_UPDATE" );
}
+
void WorldSession::HandlePageTextQueryOpcode( WorldPacket & recv_data )
{
uint32 pageID;
+
recv_data >> pageID;
sLog.outDetail("WORLD: Received CMSG_PAGE_TEXT_QUERY for pageID '%u'", pageID);
+
while (pageID)
{
PageText const *pPage = sPageTextStore.LookupEntry<PageText>( pageID );
// guess size
WorldPacket data( SMSG_PAGE_TEXT_QUERY_RESPONSE, 50 );
data << pageID;
+
if (!pPage)
{
data << "Item page missing.";
@@ -387,6 +438,7 @@ void WorldSession::HandlePageTextQueryOpcode( WorldPacket & recv_data )
else
{
std::string Text = pPage->Text;
+
int loc_idx = GetSessionDbLocaleIndex();
if (loc_idx >= 0)
{
@@ -397,11 +449,13 @@ void WorldSession::HandlePageTextQueryOpcode( WorldPacket & recv_data )
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
index 67be373d876..013d6da1f0f 100644
--- a/src/game/QuestDef.cpp
+++ b/src/game/QuestDef.cpp
@@ -17,9 +17,11 @@
* 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();
@@ -57,34 +59,49 @@ Quest::Quest(Field * questRecord)
OfferRewardText = questRecord[32].GetCppString();
RequestItemsText = questRecord[33].GetCppString();
EndText = questRecord[34].GetCppString();
+
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
ObjectiveText[i] = questRecord[35+i].GetCppString();
+
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
ReqItemId[i] = questRecord[39+i].GetUInt32();
+
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
ReqItemCount[i] = questRecord[43+i].GetUInt32();
+
for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
ReqSourceId[i] = questRecord[47+i].GetUInt32();
+
for (int i = 0; i < QUEST_SOURCE_ITEM_IDS_COUNT; ++i)
ReqSourceCount[i] = questRecord[51+i].GetUInt32();
+
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
ReqCreatureOrGOId[i] = questRecord[55+i].GetInt32();
+
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
ReqCreatureOrGOCount[i] = questRecord[59+i].GetUInt32();
+
for (int i = 0; i < QUEST_OBJECTIVES_COUNT; ++i)
ReqSpell[i] = questRecord[63+i].GetUInt32();
+
for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
RewChoiceItemId[i] = questRecord[67+i].GetUInt32();
+
for (int i = 0; i < QUEST_REWARD_CHOICES_COUNT; ++i)
RewChoiceItemCount[i] = questRecord[73+i].GetUInt32();
+
for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
RewItemId[i] = questRecord[79+i].GetUInt32();
+
for (int i = 0; i < QUEST_REWARDS_COUNT; ++i)
RewItemCount[i] = questRecord[83+i].GetUInt32();
+
for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
RewRepFaction[i] = questRecord[87+i].GetUInt32();
+
for (int i = 0; i < QUEST_REPUTATIONS_COUNT; ++i)
RewRepValue[i] = questRecord[92+i].GetInt32();
+
RewHonorableKills = questRecord[97].GetUInt32();
RewOrReqMoney = questRecord[98].GetInt32();
RewMoneyMaxLevel = questRecord[99].GetUInt32();
@@ -96,23 +113,32 @@ Quest::Quest(Field * questRecord)
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();
+
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
DetailsEmoteDelay[i] = questRecord[112+i].GetUInt32();
+
IncompleteEmote = questRecord[116].GetUInt32();
CompleteEmote = questRecord[117].GetUInt32();
+
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
OfferRewardEmote[i] = questRecord[118+i].GetInt32();
+
for (int i = 0; i < QUEST_EMOTE_COUNT; ++i)
OfferRewardEmoteDelay[i] = questRecord[122+i].GetInt32();
+
QuestStartScript = questRecord[126].GetUInt32();
QuestCompleteScript = questRecord[127].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] )
@@ -120,17 +146,20 @@ Quest::Quest(Field * questRecord)
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 )
@@ -154,6 +183,7 @@ uint32 Quest::XPValue( Player *pPlayer ) const
fullxp = RewMoneyMaxLevel / 0.6f;
else if (qLevel == 0)
fullxp = RewMoneyMaxLevel;
+
if( pLevel <= qLevel + 5 )
return (uint32)fullxp;
else if( pLevel == qLevel + 6 )
@@ -170,10 +200,12 @@ uint32 Quest::XPValue( Player *pPlayer ) const
}
return 0;
}
+
int32 Quest::GetRewOrReqMoney() const
{
if(RewOrReqMoney <=0)
return RewOrReqMoney;
+
return int32(RewOrReqMoney * sWorld.getRate(RATE_DROP_MONEY));
}
diff --git a/src/game/QuestDef.h b/src/game/QuestDef.h
index f95e984e204..264acff95b8 100644
--- a/src/game/QuestDef.h
+++ b/src/game/QuestDef.h
@@ -17,15 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_QUEST_H
#define TRINITYCORE_QUEST_H
+
#include "Platform/Define.h"
#include "Database/DatabaseEnv.h"
+
#include <string>
#include <vector>
+
class Player;
+
class ObjectMgr;
+
#define MAX_QUEST_LOG_SIZE 25
+
#define QUEST_OBJECTIVES_COUNT 4
#define QUEST_SOURCE_ITEM_IDS_COUNT 4
#define QUEST_REWARD_CHOICES_COUNT 6
@@ -33,6 +40,7 @@ class ObjectMgr;
#define QUEST_DEPLINK_COUNT 10
#define QUEST_REPUTATIONS_COUNT 5
#define QUEST_EMOTE_COUNT 4
+
enum QuestFailedReasons
{
INVALIDREASON_DONT_HAVE_REQ = 0,
@@ -49,6 +57,7 @@ enum QuestFailedReasons
INVALIDREASON_QUEST_FAILED_CAIS = 27, // You cannot complete quests once you have reached tired time.
INVALIDREASON_DAILY_QUEST_COMPLETED_TODAY = 29 // You have completed that daily quest today.
};
+
enum QuestShareMessages
{
QUEST_PARTY_MSG_SHARING_QUEST = 0,
@@ -63,6 +72,7 @@ enum QuestShareMessages
QUEST_PARTY_MSG_SHARING_TIMER_EXPIRED = 9,
QUEST_PARTY_MSG_NOT_IN_PARTY = 10
};
+
enum __QuestTradeSkill
{
QUEST_TRSKILL_NONE = 0,
@@ -81,6 +91,7 @@ enum __QuestTradeSkill
QUEST_TRSKILL_SKINNING = 13,
QUEST_TRSKILL_JEWELCRAFTING = 14,
};
+
enum QuestStatus
{
QUEST_STATUS_NONE = 0,
@@ -91,6 +102,7 @@ enum QuestStatus
QUEST_STATUS_FAILED = 5,
MAX_QUEST_STATUS
};
+
enum __QuestGiverStatus
{
DIALOG_STATUS_NONE = 0,
@@ -105,6 +117,7 @@ enum __QuestGiverStatus
DIALOG_STATUS_REWARD2 = 9, // no yellow dot on minimap
DIALOG_STATUS_REWARD = 10 // yellow dot on minimap
};
+
enum __QuestFlags
{
// Flags used at server and sent to client
@@ -122,19 +135,23 @@ enum __QuestFlags
QUEST_FLAGS_TBC_RACES = 0x00000800, // Not used currently: Blood elf/Draenei starting zone quests
QUEST_FLAGS_DAILY = 0x00001000, // Used to know quest is Daily one
QUEST_FLAGS_WEEKLY = 0x00008000,
+
// Trinity flags for set SpecialFlags in DB if required but used only at server
QUEST_TRINITY_FLAGS_REPEATABLE = 0x010000, // Set by 1 in SpecialFlags from DB
QUEST_TRINITY_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_TRINITY_FLAGS_DB_ALLOWED = 0xFFFF | QUEST_TRINITY_FLAGS_REPEATABLE | QUEST_TRINITY_FLAGS_EXPLORATION_OR_EVENT,
+
// Trinity flags for internal use only
QUEST_TRINITY_FLAGS_DELIVER = 0x040000, // Internal flag computed only
QUEST_TRINITY_FLAGS_SPEAKTO = 0x080000, // Internal flag computed only
QUEST_TRINITY_FLAGS_KILL_OR_CAST = 0x100000, // Internal flag computed only
QUEST_TRINITY_FLAGS_TIMED = 0x200000, // Internal flag computed only
};
+
struct QuestLocale
{
QuestLocale() { ObjectiveText.resize(QUEST_OBJECTIVES_COUNT); }
+
std::vector<std::string> Title;
std::vector<std::string> Details;
std::vector<std::string> Objectives;
@@ -143,6 +160,7 @@ struct QuestLocale
std::vector<std::string> EndText;
std::vector< std::vector<std::string> > ObjectiveText;
};
+
// This Quest class provides a convenient way to access a few pretotaled (cached) quest details,
// all base quest information, and any utility functions such as generating the amount of
// xp to give
@@ -152,8 +170,10 @@ class Quest
public:
Quest(Field * questRecord);
uint32 XPValue( Player *pPlayer ) const;
+
bool HasFlag( uint32 flag ) const { return ( QuestFlags & flag ) != 0; }
void SetFlag( uint32 flag ) { QuestFlags |= flag; }
+
// table data accessors:
uint32 GetQuestId() const { return QuestId; }
uint32 GetQuestMethod() const { return QuestMethod; }
@@ -208,6 +228,7 @@ class Quest
bool IsAutoComplete() const { return QuestMethod ? false : true; }
uint32 GetFlags() const { return QuestFlags; }
bool IsDaily() const { return QuestFlags & (QUEST_FLAGS_DAILY | QUEST_FLAGS_WEEKLY); }
+
// multiple values
std::string ObjectiveText[QUEST_OBJECTIVES_COUNT];
uint32 ReqItemId[QUEST_OBJECTIVES_COUNT];
@@ -227,20 +248,24 @@ class Quest
uint32 DetailsEmoteDelay[QUEST_EMOTE_COUNT];
uint32 OfferRewardEmote[QUEST_EMOTE_COUNT];
uint32 OfferRewardEmoteDelay[QUEST_EMOTE_COUNT];
+
uint32 GetReqItemsCount() const { return m_reqitemscount; }
uint32 GetReqCreatureOrGOcount() const { return m_reqCreatureOrGOcount; }
uint32 GetRewChoiceItemsCount() const { return m_rewchoiceitemscount; }
uint32 GetRewItemsCount() const { return m_rewitemscount; }
+
typedef std::vector<int32> PrevQuests;
PrevQuests prevQuests;
typedef std::vector<uint32> PrevChainQuests;
PrevChainQuests prevChainQuests;
+
// cached data
private:
uint32 m_reqitemscount;
uint32 m_reqCreatureOrGOcount;
uint32 m_rewchoiceitemscount;
uint32 m_rewitemscount;
+
// table data
protected:
uint32 QuestId;
@@ -293,12 +318,14 @@ class Quest
uint32 QuestStartScript;
uint32 QuestCompleteScript;
};
+
enum QuestUpdateState
{
QUEST_UNCHANGED = 0,
QUEST_CHANGED = 1,
QUEST_NEW = 2
};
+
struct QuestStatusData
{
QuestStatusData()
@@ -308,11 +335,13 @@ struct QuestStatusData
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 ];
};
diff --git a/src/game/QuestHandler.cpp b/src/game/QuestHandler.cpp
index 6aeb6e371ba..a803c49d6fd 100644
--- a/src/game/QuestHandler.cpp
+++ b/src/game/QuestHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -32,18 +33,21 @@
#include "Group.h"
#include "BattleGround.h"
#include "BattleGroundAV.h"
+
void WorldSession::HandleQuestgiverStatusQueryOpcode( WorldPacket & recv_data )
{
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:
@@ -71,14 +75,18 @@ void WorldSession::HandleQuestgiverStatusQueryOpcode( WorldPacket & recv_data )
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 )
{
uint64 guid;
recv_data >> guid;
+
sLog.outDebug ("WORLD: Received CMSG_QUESTGIVER_HELLO npc = %u", GUID_LOPART(guid));
+
Creature *pCreature = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_NONE);
if (!pCreature)
{
@@ -86,26 +94,34 @@ void WorldSession::HandleQuestgiverHelloOpcode( WorldPacket & recv_data )
GUID_LOPART(guid));
return;
}
+
// remove fake death
if(GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
// Stop the npc if moving
pCreature->StopMoving();
+
if(Script->GossipHello( _player, pCreature ) )
return;
+
pCreature->prepareGossipMenu(_player);
pCreature->sendPreparedGossip(_player);
}
+
void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
{
uint64 guid;
uint32 quest;
uint32 unk1;
recv_data >> guid >> quest >> unk1;
+
if(!GetPlayer()->isAlive())
return;
+
sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_ACCEPT_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), quest, unk1 );
+
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))
@@ -116,6 +132,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
_player->SetDivider( 0 );
return;
}
+
Quest const* qInfo = objmgr.GetQuestTemplate(quest);
if ( qInfo )
{
@@ -126,6 +143,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
_player->SetDivider( 0 );
return;
}
+
if( _player->GetDivider() != 0 )
{
Player *pPlayer = ObjectAccessor::FindPlayer( _player->GetDivider() );
@@ -135,11 +153,14 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
_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:
@@ -149,6 +170,7 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
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)
@@ -159,8 +181,10 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
break;
}
}
+
if(destroyItem)
_player->DestroyItem(((Item*)pObject)->GetBagSlot(),((Item*)pObject)->GetSlot(),true);
+
break;
}
case TYPEID_GAMEOBJECT:
@@ -168,13 +192,17 @@ void WorldSession::HandleQuestgiverAcceptQuestOpcode( WorldPacket & recv_data )
break;
}
_player->PlayerTalkClass->CloseGossip();
+
if( qInfo->GetSrcSpell() > 0 )
_player->CastSpell( _player, qInfo->GetSrcSpell(), true);
+
return;
}
}
+
_player->PlayerTalkClass->CloseGossip();
}
+
void WorldSession::HandleQuestgiverQueryQuestOpcode( WorldPacket & recv_data )
{
uint64 guid;
@@ -182,6 +210,7 @@ void WorldSession::HandleQuestgiverQueryQuestOpcode( WorldPacket & recv_data )
uint8 unk1;
recv_data >> guid >> quest >> unk1;
sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUERY_QUEST npc = %u, quest = %u, unk1 = %u", uint32(GUID_LOPART(guid)), quest, unk1 );
+
// 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))
@@ -189,47 +218,58 @@ void WorldSession::HandleQuestgiverQueryQuestOpcode( WorldPacket & recv_data )
_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 )
{
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 )
{
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:
@@ -254,75 +294,101 @@ void WorldSession::HandleQuestgiverChooseRewardOpcode( WorldPacket & recv_data )
_player->PlayerTalkClass->SendQuestGiverOfferReward( pQuest, guid, true );
}
}
+
void WorldSession::HandleQuestgiverRequestRewardOpcode( WorldPacket & recv_data )
{
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 )
{
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)
{
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
+
if (const Quest *pQuest = objmgr.GetQuestTemplate(quest))
{
if (pQuest->HasFlag(QUEST_TRINITY_FLAGS_TIMED))
_player->RemoveTimedQuest(quest);
}
+
_player->TakeQuestSourceItem(quest, true); // remove quest src item from player
_player->SetQuestStatus( quest, QUEST_STATUS_NONE);
}
+
_player->SetQuestSlot(slot, 0);
+
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_QUEST_ABANDONED, 1);
}
}
+
void WorldSession::HandleQuestConfirmAccept(WorldPacket& recv_data)
{
uint32 quest;
recv_data >> quest;
+
sLog.outDebug( "WORLD: Received CMSG_QUEST_CONFIRM_ACCEPT quest = %u",quest );
}
+
void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recv_data)
{
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 )
{
@@ -331,6 +397,7 @@ void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recv_data)
if(BattleGround* bg = GetPlayer()->GetBattleGround())
if(bg->GetTypeID() == BATTLEGROUND_AV)
((BattleGroundAV*)bg)->HandleQuestComplete(quest, GetPlayer());
+
if( _player->GetQuestStatus( quest ) != QUEST_STATUS_COMPLETE )
{
if( pQuest->IsRepeatable() )
@@ -347,15 +414,19 @@ void WorldSession::HandleQuestgiverCompleteQuest(WorldPacket& recv_data)
}
}
}
+
void WorldSession::HandleQuestgiverQuestAutoLaunch(WorldPacket& /*recvPacket*/)
{
sLog.outDebug( "WORLD: Received CMSG_QUESTGIVER_QUEST_AUTOLAUNCH" );
}
+
void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
{
uint32 questId;
recvPacket >> questId;
+
sLog.outDebug("WORLD: Received CMSG_PUSHQUESTTOPARTY quest = %u", questId);
+
if (Quest const *pQuest = objmgr.GetQuestTemplate(questId))
{
if (Group* pGroup = _player->GetGroup())
@@ -363,46 +434,57 @@ void WorldSession::HandlePushQuestToParty(WorldPacket& recvPacket)
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 (!pPlayer->SatisfyQuestStatus(pQuest, false))
{
_player->SendPushToPartyResponse(pPlayer, QUEST_PARTY_MSG_HAVE_QUEST);
continue;
}
+
if (pPlayer->GetQuestStatus(questId) == 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)
{
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() );
@@ -416,11 +498,14 @@ void WorldSession::HandleQuestPushResult(WorldPacket& recvPacket)
}
}
}
+
uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32 defstatus)
{
uint32 result = defstatus;
+
QuestRelations const* qir;
QuestRelations const* qr;
+
switch(questgiver->GetTypeId())
{
case TYPEID_GAMEOBJECT:
@@ -440,12 +525,14 @@ uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32
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)) )
@@ -457,9 +544,11 @@ uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32
}
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;
@@ -467,6 +556,7 @@ uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32
Quest const *pQuest = objmgr.GetQuestTemplate(quest_id);
if ( !pQuest )
continue;
+
QuestStatus status = pPlayer->GetQuestStatus( quest_id );
if ( status == QUEST_STATUS_NONE )
{
@@ -490,21 +580,28 @@ uint32 WorldSession::getDialogStatus(Player *pPlayer, Object* questgiver, uint32
result2 = DIALOG_STATUS_UNAVAILABLE;
}
}
+
if (result2 > result)
result = result2;
}
+
return result;
}
+
void WorldSession::HandleQuestgiverStatusMultipleQuery(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::const_iterator itr = _player->m_clientGUIDs.begin(); itr != _player->m_clientGUIDs.end(); ++itr)
{
uint8 questStatus = DIALOG_STATUS_NONE;
uint8 defstatus = DIALOG_STATUS_NONE;
+
if (IS_CRE_OR_VEH_OR_PET_GUID(*itr))
{
// need also pet quests case support
@@ -516,6 +613,7 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket
questStatus = Script->NPCDialogStatus(_player, questgiver);
if( questStatus > 6 )
questStatus = getDialogStatus(_player, questgiver, defstatus);
+
data << uint64(questgiver->GetGUID());
data << uint8(questStatus);
++count;
@@ -530,11 +628,13 @@ void WorldSession::HandleQuestgiverStatusMultipleQuery(WorldPacket& /*recvPacket
questStatus = Script->GODialogStatus(_player, questgiver);
if( questStatus > 6 )
questStatus = getDialogStatus(_player, questgiver, defstatus);
+
data << uint64(questgiver->GetGUID());
data << uint8(questStatus);
++count;
}
}
+
data.put<uint32>(0, count); // write real count
SendPacket(&data);
}
diff --git a/src/game/RandomMovementGenerator.cpp b/src/game/RandomMovementGenerator.cpp
index 5d6711db13a..f50e6af4709 100644
--- a/src/game/RandomMovementGenerator.cpp
+++ b/src/game/RandomMovementGenerator.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -26,48 +27,63 @@
#include "Map.h"
#include "Util.h"
#include "CreatureGroups.h"
+
#define RUNNING_CHANCE_RANDOMMV 20 //will be "1 / RUNNING_CHANCE_RANDOMMV"
+
template<>
bool
RandomMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const
{
if(i_destinationHolder.HasArrived())
return false;
+
i_destinationHolder.GetDestination(x, y, z);
return true;
}
+
#ifdef MAP_BASED_RAND_GEN
#define rand_norm() creature.rand_norm()
#endif
+
template<>
void
RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
{
float X,Y,Z,z,nx,ny,nz,ori,dist;
+
creature.GetHomePosition(X, Y, Z, ori);
+
z = creature.GetPositionZ();
Map const* map = creature.GetBaseMap();
+
// For 2D/3D system selection
bool is_land_ok = creature.canWalk();
bool is_water_ok = creature.canSwim();
bool is_air_ok = creature.canFly();
+
for(uint32 i = 0;; ++i)
{
+
const float angle = rand_norm()*(M_PI*2);
const float range = rand_norm()*wander_distance;
const float distanceX = range * cos(angle);
const float distanceY = range * sin(angle);
+
nx = X + distanceX;
ny = Y + distanceY;
+
// prevent invalid coordinates generation
Trinity::NormalizeMapCoord(nx);
Trinity::NormalizeMapCoord(ny);
+
dist = (nx - X)*(nx - X) + (ny - Y)*(ny - Y);
+
if(i == 5)
{
nz = Z;
break;
}
+
if (is_air_ok) // 3D system above ground and above water (flying mode)
{
const float distanceZ = rand_norm() * sqrtf(dist)/2; // Limit height change
@@ -81,6 +97,7 @@ RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
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
@@ -95,8 +112,10 @@ RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
}
}
}
+
break;
}
+
Traveller<Creature> traveller(creature);
creature.SetOrientation(creature.GetAngle(nx,ny));
i_destinationHolder.SetDestination(traveller, nx, ny, nz);
@@ -111,33 +130,40 @@ RandomMovementGenerator<Creature>::_setRandomLocation(Creature &creature)
i_nextMoveTime.Reset(urand(500+i_destinationHolder.GetTotalTravelTime(),5000+i_destinationHolder.GetTotalTravelTime()));
creature.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
}
+
//Call for creature group update
if(creature.GetFormation() && creature.GetFormation()->getLeader() == &creature)
{
creature.GetFormation()->LeaderMoveTo(nx, ny, nz);
}
}
+
template<>
void
RandomMovementGenerator<Creature>::Initialize(Creature &creature)
{
if(!creature.isAlive())
return;
+
if(!wander_distance)
wander_distance = creature.GetRespawnRadius();
+
if(irand(0,RUNNING_CHANCE_RANDOMMV) > 0)
creature.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
_setRandomLocation(creature);
}
+
template<>
void
RandomMovementGenerator<Creature>::Reset(Creature &creature)
{
Initialize(creature);
}
+
template<>
void
RandomMovementGenerator<Creature>::Finalize(Creature &creature){}
+
template<>
bool
RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff)
@@ -148,12 +174,17 @@ RandomMovementGenerator<Creature>::Update(Creature &creature, const uint32 &diff
creature.clearUnitState(UNIT_STAT_ROAMING);
return true;
}
+
i_nextMoveTime.Update(diff);
+
if(i_destinationHolder.HasArrived() && !creature.IsStopped() && !creature.canFly())
creature.clearUnitState(UNIT_STAT_ROAMING | UNIT_STAT_MOVE);
+
if(!i_destinationHolder.HasArrived() && creature.IsStopped())
creature.addUnitState(UNIT_STAT_ROAMING);
+
CreatureTraveller traveller(creature);
+
if( i_destinationHolder.UpdateTraveller(traveller, diff, true) )
{
if(i_nextMoveTime.Passed())
diff --git a/src/game/RandomMovementGenerator.h b/src/game/RandomMovementGenerator.h
index 0be58541b3f..20afe23d375 100644
--- a/src/game/RandomMovementGenerator.h
+++ b/src/game/RandomMovementGenerator.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_RANDOMMOTIONGENERATOR_H
#define TRINITY_RANDOMMOTIONGENERATOR_H
+
#include "MovementGenerator.h"
#include "DestinationHolder.h"
#include "Traveller.h"
+
template<class T>
class TRINITY_DLL_SPEC RandomMovementGenerator
: public MovementGeneratorMedium< T, RandomMovementGenerator<T> >
@@ -29,6 +32,7 @@ class TRINITY_DLL_SPEC RandomMovementGenerator
public:
// Wander dist is related on db spawn dist. So what if we wanna set eandom movement on summoned creature?!
RandomMovementGenerator(float spawn_dist = 0.0f) : i_nextMoveTime(0), wander_distance(spawn_dist) {}
+
void _setRandomLocation(T &);
void Initialize(T &);
void Finalize(T &);
@@ -42,6 +46,7 @@ class TRINITY_DLL_SPEC RandomMovementGenerator
MovementGeneratorType GetMovementGeneratorType() { return RANDOM_MOTION_TYPE; }
private:
TimeTrackerSmall i_nextMoveTime;
+
DestinationHolder< Traveller<T> > i_destinationHolder;
float wander_distance;
uint32 i_nextMove;
diff --git a/src/game/ReactorAI.cpp b/src/game/ReactorAI.cpp
index b50aa1825e2..3cece43a7c1 100644
--- a/src/game/ReactorAI.cpp
+++ b/src/game/ReactorAI.cpp
@@ -17,30 +17,37 @@
* 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 "Log.h"
#include "ObjectAccessor.h"
#include "CreatureAIImpl.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::UpdateAI(const uint32 /*time_diff*/)
{
// update i_victimGuid if m_creature->getVictim() !=0 and changed
if(!UpdateVictim())
return;
+
if( m_creature->isAttackReady() )
{
if( m_creature->IsWithinMeleeRange(m_creature->getVictim()))
diff --git a/src/game/ReactorAI.h b/src/game/ReactorAI.h
index a3862ac1258..c20c01154f5 100644
--- a/src/game/ReactorAI.h
+++ b/src/game/ReactorAI.h
@@ -17,15 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_REACTORAI_H
#define TRINITY_REACTORAI_H
+
#include "CreatureAI.h"
+
class Unit;
+
class TRINITY_DLL_DECL ReactorAI : public CreatureAI
{
public:
+
explicit ReactorAI(Creature *c) : CreatureAI(c) {}
+
void MoveInLineOfSight(Unit *);
+
void UpdateAI(const uint32);
static int Permissible(const Creature *);
};
diff --git a/src/game/ReputationMgr.cpp b/src/game/ReputationMgr.cpp
index da1d6750461..35bf2304fe4 100644
--- a/src/game/ReputationMgr.cpp
+++ b/src/game/ReputationMgr.cpp
@@ -15,12 +15,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "ReputationMgr.h"
#include "DBCStores.h"
#include "Player.h"
#include "WorldPacket.h"
#include "World.h"
+
const int32 ReputationMgr::PointsInRank[MAX_REPUTATION_RANK] = {36000, 3000, 3000, 3000, 6000, 12000, 21000, 1000};
+
ReputationRank ReputationMgr::ReputationToRank(int32 standing)
{
int32 limit = Reputation_Cap + 1;
@@ -32,20 +35,25 @@ ReputationRank ReputationMgr::ReputationToRank(int32 standing)
}
return MIN_REPUTATION_RANK;
}
+
int32 ReputationMgr::GetReputation(uint32 faction_id) const
{
FactionEntry const *factionEntry = sFactionStore.LookupEntry(faction_id);
+
if (!factionEntry)
{
sLog.outError("ReputationMgr::GetReputation: Can't get reputation of %s for unknown faction (faction id) #%u.",m_player->GetName(), faction_id);
return 0;
}
+
return GetReputation(factionEntry);
}
+
int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
{
if (!factionEntry)
return 0;
+
uint32 raceMask = m_player->getRaceMask();
uint32 classMask = m_player->getClassMask();
for (int i=0; i < 4; i++)
@@ -58,28 +66,35 @@ int32 ReputationMgr::GetBaseReputation(FactionEntry const* factionEntry) const
)
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 ReputationMgr::GetReputation(FactionEntry const* factionEntry) const
{
// Faction without recorded reputation. Just ignore.
if(!factionEntry)
return 0;
+
if(FactionState const* state = GetState(factionEntry))
return GetBaseReputation(factionEntry) + state->Standing;
+
return 0;
}
+
ReputationRank ReputationMgr::GetRank(FactionEntry const* factionEntry) const
{
int32 reputation = GetReputation(factionEntry);
return ReputationToRank(reputation);
}
+
ReputationRank ReputationMgr::GetBaseRank(FactionEntry const* factionEntry) const
{
int32 reputation = GetBaseReputation(factionEntry);
return ReputationToRank(reputation);
}
+
void ReputationMgr::ApplyForceReaction( uint32 faction_id,ReputationRank rank,bool apply )
{
if(apply)
@@ -87,10 +102,12 @@ void ReputationMgr::ApplyForceReaction( uint32 faction_id,ReputationRank rank,bo
else
m_forcedReactions.erase(faction_id);
}
+
uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) const
{
if (!factionEntry)
return 0;
+
uint32 raceMask = m_player->getRaceMask();
uint32 classMask = m_player->getClassMask();
for (int i=0; i < 4; i++)
@@ -105,6 +122,7 @@ uint32 ReputationMgr::GetDefaultStateFlags(FactionEntry const* factionEntry) con
}
return 0;
}
+
void ReputationMgr::SendForceReactions()
{
WorldPacket data;
@@ -117,6 +135,7 @@ void ReputationMgr::SendForceReactions()
}
m_player->SendDirectMessage(&data);
}
+
void ReputationMgr::SendState(FactionState const* faction) const
{
if(faction->Flags & FACTION_FLAG_VISIBLE) //If faction is visible then update it
@@ -132,11 +151,14 @@ void ReputationMgr::SendState(FactionState const* faction) const
m_player->SendDirectMessage(&data);
}
}
+
void ReputationMgr::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
@@ -145,33 +167,41 @@ void ReputationMgr::SendInitialReputations()
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);
}
+
m_player->SendDirectMessage(&data);
}
+
void ReputationMgr::SendStates() const
{
for(FactionStateList::const_iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
SendState(&(itr->second));
}
+
void ReputationMgr::SendVisible(FactionState const* faction) const
{
if(m_player->GetSession()->PlayerLoading())
return;
+
// make faction visible in reputation list at client
WorldPacket data(SMSG_SET_FACTION_VISIBLE, 4);
data << faction->ReputationListID;
m_player->SendDirectMessage(&data);
}
+
void ReputationMgr::Initialize()
{
m_factions.clear();
@@ -179,9 +209,11 @@ void ReputationMgr::Initialize()
m_honoredFactionCount = 0;
m_reveredFactionCount = 0;
m_exaltedFactionCount = 0;
+
for(unsigned int i = 1; i < sFactionStore.GetNumRows(); i++)
{
FactionEntry const *factionEntry = sFactionStore.LookupEntry(i);
+
if( factionEntry && (factionEntry->reputationListID >= 0))
{
FactionState newFaction;
@@ -190,33 +222,42 @@ void ReputationMgr::Initialize()
newFaction.Standing = 0;
newFaction.Flags = GetDefaultStateFlags(factionEntry);
newFaction.Changed = true;
+
if( newFaction.Flags & FACTION_FLAG_VISIBLE )
++m_visibleFactionCount;
+
UpdateRankCounters(REP_HOSTILE,GetBaseRank(factionEntry));
+
m_factions[newFaction.ReputationListID] = newFaction;
}
}
}
+
bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
{
// Determines whether or not the faction is part of a team or the leader of a team.
bool isTeamMember = false;
+
// Return variable for the function
bool res = false;
+
SimpleFactionsList const* flist = GetFactionTeamList(factionEntry->ID, isTeamMember);
// Determines whether reputation should be sent to team parent or other team members.
int8 extraTarget = (isTeamMember || !flist ? -1 : 0); // 0 = Give equal amount of reputation to anyone in the team (unhandled cases).
+
/* When gaining reputation with some factions, you receive a reputation increase
towards other reputations for that group.
*/
uint32 team = factionEntry->team;
+
int32 sharedStanding = standing; // Here we decide what the amount is to send to the others of the group.
switch(factionEntry->ID)
{
case 1037: // Alliance Vanguard
case 1052: // Horde Expedition
extraTarget = -1; // Make possible to earn rep with this two factions
- break;
+ break;
+
}
switch(team)
{
@@ -229,8 +270,9 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi
case 1052: // Horde Expedition
sharedStanding *= 0.5f; // Half of the reputation earned by any of the four subfactions of this team will
extraTarget = 2; // be added to the main faction. (http://www.wowwiki.com/Alliance_Vanguard)
- break;
+ break;
}
+
FactionEntry const *targetFaction = NULL;
switch(extraTarget)
{
@@ -239,7 +281,7 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi
for (SimpleFactionsList::const_iterator itr = flist->begin(); itr != flist->end(); ++itr)
{
targetFaction = sFactionStore.LookupEntry(*itr);
- ASSERT(targetFaction != NULL);
+ ASSERT(targetFaction != NULL);
res = SetOneFactionReputation(targetFaction, sharedStanding, incremental);
}
return res;
@@ -250,8 +292,9 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi
{
if((*itr) == factionEntry->ID) // Not to self
continue;
+
targetFaction = sFactionStore.LookupEntry(*itr);
- ASSERT(targetFaction != NULL);
+ ASSERT(targetFaction != NULL);
res = SetOneFactionReputation(targetFaction, sharedStanding, incremental);
}
}break;
@@ -266,146 +309,191 @@ bool ReputationMgr::SetReputation(FactionEntry const* factionEntry, int32 standi
return SetOneFactionReputation(factionEntry, standing, incremental);
break;
}
+
return (SetOneFactionReputation(factionEntry, standing, incremental) && res);
}
+
bool ReputationMgr::SetOneFactionReputation(FactionEntry const* factionEntry, int32 standing, bool incremental)
{
FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
if (itr != m_factions.end())
{
int32 BaseRep = GetBaseReputation(factionEntry);
+
if(incremental)
{
// int32 *= float cause one point loss?
standing = floor( (float)standing * sWorld.getRate(RATE_REPUTATION_GAIN) + 0.5 );
standing += itr->second.Standing + BaseRep;
}
+
if (standing > Reputation_Cap)
standing = Reputation_Cap;
else if (standing < Reputation_Bottom)
standing = Reputation_Bottom;
+
ReputationRank old_rank = ReputationToRank(itr->second.Standing + BaseRep);
ReputationRank new_rank = ReputationToRank(standing);
+
itr->second.Standing = standing - BaseRep;
itr->second.Changed = true;
+
SetVisible(&itr->second);
+
if(new_rank <= REP_HOSTILE)
SetAtWar(&itr->second,true);
+
SendState(&itr->second);
+
UpdateRankCounters(old_rank, new_rank);
+
m_player->ReputationChanged(factionEntry);
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KNOWN_FACTIONS, factionEntry->ID);
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REPUTATION, factionEntry->ID);
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_EXALTED_REPUTATION,factionEntry->ID);
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_REVERED_REPUTATION,factionEntry->ID);
m_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_GAIN_HONORED_REPUTATION,factionEntry->ID);
+
return true;
}
return false;
}
+
void ReputationMgr::SetVisible(FactionTemplateEntry const*factionTemplateEntry)
{
if(!factionTemplateEntry->faction)
return;
+
if(FactionEntry const *factionEntry = sFactionStore.LookupEntry(factionTemplateEntry->faction))
// Never show factions of the opposing team
if(!(factionEntry->BaseRepRaceMask[1] & m_player->getRaceMask() && factionEntry->BaseRepValue[1] == Reputation_Bottom) )
SetVisible(factionEntry);
}
+
void ReputationMgr::SetVisible(FactionEntry const *factionEntry)
{
if(factionEntry->reputationListID < 0)
return;
+
FactionStateList::iterator itr = m_factions.find(factionEntry->reputationListID);
if (itr == m_factions.end())
return;
+
SetVisible(&itr->second);
}
+
void ReputationMgr::SetVisible(FactionState* faction)
{
// always invisible or hidden faction can't be make visible
// except if faction has FACTION_FLAG_SPECIAL
if(faction->Flags & (FACTION_FLAG_INVISIBLE_FORCED|FACTION_FLAG_HIDDEN) && !(faction->Flags & FACTION_FLAG_SPECIAL) )
return;
+
// already set
if(faction->Flags & FACTION_FLAG_VISIBLE)
return;
+
faction->Flags |= FACTION_FLAG_VISIBLE;
faction->Changed = true;
+
++m_visibleFactionCount;
+
SendVisible(faction);
}
+
void ReputationMgr::SetAtWar( RepListID repListID, bool on )
{
FactionStateList::iterator itr = m_factions.find(repListID);
if (itr == 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;
+
SetAtWar(&itr->second,on);
}
+
void ReputationMgr::SetAtWar(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 ReputationMgr::SetInactive( RepListID repListID, bool on )
{
FactionStateList::iterator itr = m_factions.find(repListID);
if (itr == m_factions.end())
return;
+
SetInactive(&itr->second,on);
}
+
void ReputationMgr::SetInactive(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 ReputationMgr::LoadFromDB(QueryResult *result)
{
// Set initial reputations (so everything is nifty before DB data load)
Initialize();
+
//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());
+
// update counters
int32 BaseRep = GetBaseReputation(factionEntry);
ReputationRank old_rank = ReputationToRank(BaseRep);
ReputationRank new_rank = ReputationToRank(BaseRep + faction->Standing);
UpdateRankCounters(old_rank,new_rank);
+
uint32 dbFactionFlags = fields[2].GetUInt32();
+
if( dbFactionFlags & FACTION_FLAG_VISIBLE )
SetVisible(faction); // have internal checks for forced invisibility
+
if( dbFactionFlags & FACTION_FLAG_INACTIVE)
SetInactive(faction,true); // have internal checks for visibility requirement
+
if( dbFactionFlags & FACTION_FLAG_AT_WAR ) // DB at war
SetAtWar(faction,true); // have internal checks for FACTION_FLAG_PEACE_FORCED
else // DB not at war
@@ -414,18 +502,22 @@ void ReputationMgr::LoadFromDB(QueryResult *result)
if( faction->Flags & FACTION_FLAG_VISIBLE )
SetAtWar(faction,false); // have internal checks for FACTION_FLAG_PEACE_FORCED
}
+
// set atWar for hostile
if(GetRank(factionEntry) <= REP_HOSTILE)
SetAtWar(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 ReputationMgr::SaveToDB()
{
for(FactionStateList::iterator itr = m_factions.begin(); itr != m_factions.end(); ++itr)
@@ -438,6 +530,7 @@ void ReputationMgr::SaveToDB()
}
}
}
+
void ReputationMgr::UpdateRankCounters( ReputationRank old_rank, ReputationRank new_rank )
{
if(old_rank >= REP_EXALTED)
@@ -446,6 +539,7 @@ void ReputationMgr::UpdateRankCounters( ReputationRank old_rank, ReputationRank
--m_reveredFactionCount;
if(old_rank >= REP_HONORED)
--m_honoredFactionCount;
+
if(new_rank >= REP_EXALTED)
++m_exaltedFactionCount;
if(new_rank >= REP_REVERED)
diff --git a/src/game/ReputationMgr.h b/src/game/ReputationMgr.h
index 99c0d34f6dd..6c78e0fa3b5 100644
--- a/src/game/ReputationMgr.h
+++ b/src/game/ReputationMgr.h
@@ -15,18 +15,22 @@
* 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_REPUTATION_MGR_H
#define __MANGOS_REPUTATION_MGR_H
+
#include "Common.h"
#include "SharedDefines.h"
#include "Language.h"
#include "DBCStructure.h"
#include <map>
+
static uint32 ReputationRankStrIndex[MAX_REPUTATION_RANK] =
{
LANG_REP_HATED, LANG_REP_HOSTILE, LANG_REP_UNFRIENDLY, LANG_REP_NEUTRAL,
LANG_REP_FRIENDLY, LANG_REP_HONORED, LANG_REP_REVERED, LANG_REP_EXALTED
};
+
enum FactionFlags
{
FACTION_FLAG_NONE = 0x00, // no faction flag
@@ -39,6 +43,7 @@ enum FactionFlags
FACTION_FLAG_RIVAL = 0x40, // flag for the two competing outland factions
FACTION_FLAG_SPECIAL = 0x80 // horde and alliance home cities and their northrend allies have this flag
};
+
typedef uint32 RepListID;
struct FactionState
{
@@ -48,48 +53,60 @@ struct FactionState
int32 Standing;
bool Changed;
};
+
typedef std::map<RepListID,FactionState> FactionStateList;
typedef std::map<uint32,ReputationRank> ForcedReactions;
+
class Player;
class QueryResult;
+
class ReputationMgr
{
public: // constructors and global modifiers
explicit ReputationMgr(Player* owner) : m_player(owner),
m_visibleFactionCount(0), m_honoredFactionCount(0), m_reveredFactionCount(0), m_exaltedFactionCount(0) {}
~ReputationMgr() {}
+
void SaveToDB();
void LoadFromDB(QueryResult *result);
public: // statics
static const int32 PointsInRank[MAX_REPUTATION_RANK];
static const int32 Reputation_Cap = 42999;
static const int32 Reputation_Bottom = -42000;
+
static ReputationRank ReputationToRank(int32 standing);
public: // accessors
uint8 GetVisibleFactionCount() const { return m_visibleFactionCount; }
uint8 GetHonoredFactionCount() const { return m_honoredFactionCount; }
uint8 GetReveredFactionCount() const { return m_reveredFactionCount; }
uint8 GetExaltedFactionCount() const { return m_exaltedFactionCount; }
+
FactionStateList const& GetStateList() const { return m_factions; }
+
FactionState const* GetState(FactionEntry const* factionEntry) const
{
return factionEntry->reputationListID >= 0 ? GetState(factionEntry->reputationListID) : NULL;
}
+
FactionState const* GetState(RepListID id) const
{
FactionStateList::const_iterator repItr = m_factions.find (id);
return repItr != m_factions.end() ? &repItr->second : NULL;
}
+
int32 GetReputation(uint32 faction_id) const;
int32 GetReputation(FactionEntry const* factionEntry) const;
int32 GetBaseReputation(FactionEntry const* factionEntry) const;
+
ReputationRank GetRank(FactionEntry const* factionEntry) const;
ReputationRank GetBaseRank(FactionEntry const* factionEntry) const;
+
ReputationRank const* GetForcedRankIfAny(FactionTemplateEntry const* factionTemplateEntry) const
{
ForcedReactions::const_iterator forceItr = m_forcedReactions.find(factionTemplateEntry->faction);
return forceItr != m_forcedReactions.end() ? &forceItr->second : NULL;
}
+
public: // modifiers
bool SetReputation(FactionEntry const* factionEntry, int32 standing)
{
@@ -99,16 +116,20 @@ class ReputationMgr
{
return SetReputation(factionEntry, standing, true);
}
+
void SetVisible(FactionTemplateEntry const* factionTemplateEntry);
void SetVisible(FactionEntry const* factionEntry);
void SetAtWar(RepListID repListID, bool on);
void SetInactive(RepListID repListID, bool on);
+
void ApplyForceReaction(uint32 faction_id,ReputationRank rank,bool apply);
+
public: // senders
void SendInitialReputations();
void SendForceReactions();
void SendState(FactionState const* faction) const;
void SendStates() const;
+
private: // internal helper functions
void Initialize();
uint32 GetDefaultStateFlags(FactionEntry const* factionEntry) const;
@@ -128,4 +149,5 @@ class ReputationMgr
uint8 m_reveredFactionCount :8;
uint8 m_exaltedFactionCount :8;
};
+
#endif
diff --git a/src/game/ScriptCalls.cpp b/src/game/ScriptCalls.cpp
index bc84efd03de..805a5361063 100644
--- a/src/game/ScriptCalls.cpp
+++ b/src/game/ScriptCalls.cpp
@@ -17,15 +17,19 @@
* 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 <dlfcn.h>
#endif
+
#include "Common.h"
#include "Platform/Define.h"
#include "ScriptCalls.h"
#include "World.h"
#include "Config/ConfigEnv.h"
+
ScriptsSet Script=NULL;
+
void UnloadScriptingModule()
{
if(Script)
@@ -37,18 +41,23 @@ void UnloadScriptingModule()
Script = NULL;
}
}
+
bool LoadScriptingModule(char const* libName)
{
ScriptsSet testScript=new _ScriptSet;
+
std::string name = strlen(libName) ? libName : TRINITY_SCRIPT_NAME;
name += TRINITY_SCRIPT_EXT;
+
testScript->hScriptsLib=TRINITY_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 )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"ScriptsInit" ))
||!(testScript->OnLogin =(scriptCallOnLogin )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"OnLogin" ))
||!(testScript->OnLogout =(scriptCallOnLogout )TRINITY_GET_PROC_ADDR(testScript->hScriptsLib,"OnLogout" ))
@@ -100,14 +109,19 @@ bool LoadScriptingModule(char const* libName)
delete testScript;
return false;
}
+
sLog.outString();
sLog.outString( ">>> 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(sConfig.GetFilename().c_str());
+
sWorld.SetScriptsVersion(Script->ScriptsVersion());
+
return true;
}
diff --git a/src/game/ScriptCalls.h b/src/game/ScriptCalls.h
index be7c0ab7a01..5a156c7e83f 100644
--- a/src/game/ScriptCalls.h
+++ b/src/game/ScriptCalls.h
@@ -17,10 +17,13 @@
* 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;
@@ -30,8 +33,10 @@ class Quest;
class SpellCastTargets;
class Map;
class InstanceData;
+
bool LoadScriptingModule(char const* libName = "");
void UnloadScriptingModule();
+
//On Event Handlers
typedef void(TRINITY_IMPORT * scriptCallOnLogin) (Player *pPlayer);
typedef void(TRINITY_IMPORT * scriptCallOnLogout) (Player *pPlayer);
@@ -47,9 +52,11 @@ typedef bool(TRINITY_IMPORT * scriptCallOnItemClick) (Player *pPlayer, Item *pIt
typedef bool(TRINITY_IMPORT * scriptCallOnItemOpen) (Player *pPlayer, Item *pItem);
typedef bool(TRINITY_IMPORT * scriptCallOnGoClick) (Player *pPlayer, GameObject *pGameObject);
typedef void(TRINITY_IMPORT * scriptCallOnCreatureKill) (Player *pPlayer, Creature *pCreature);
+
typedef void(TRINITY_IMPORT * scriptCallScriptsInit) (char const*);
typedef void(TRINITY_IMPORT * scriptCallScriptsFree) ();
typedef char const* (TRINITY_IMPORT * scriptCallScriptsVersion) ();
+
typedef bool(TRINITY_IMPORT * scriptCallGossipHello) (Player *player, Creature *_Creature );
typedef bool(TRINITY_IMPORT * scriptCallQuestAccept) (Player *player, Creature *_Creature, Quest const *);
typedef bool(TRINITY_IMPORT * scriptCallGossipSelect)(Player *player, Creature *_Creature, uint32 sender, uint32 action);
@@ -74,11 +81,13 @@ typedef bool(TRINITY_IMPORT * scriptCallEffectDummyCreature) (Unit *caster, uint
typedef bool(TRINITY_IMPORT * scriptCallEffectDummyItem) (Unit *caster, uint32 spellId, uint32 effIndex, Item *itemTarget);
typedef CreatureAI* (TRINITY_IMPORT * scriptCallGetAI) ( Creature *_Creature );
typedef InstanceData* (TRINITY_IMPORT * scriptCallCreateInstanceData) (Map *map);
+
typedef struct
{
scriptCallScriptsInit ScriptsInit;
scriptCallScriptsFree ScriptsFree;
scriptCallScriptsVersion ScriptsVersion;
+
scriptCallOnLogin OnLogin;
scriptCallOnLogout OnLogout;
scriptCallOnPVPKill OnPVPKill;
@@ -117,8 +126,10 @@ typedef struct
scriptCallEffectDummyItem EffectDummyItem;
scriptCallGetAI GetAI;
scriptCallCreateInstanceData CreateInstanceData;
+
TRINITY_LIBRARY_HANDLE hScriptsLib;
}_ScriptSet,*ScriptsSet;
+
extern ScriptsSet Script;
#endif
diff --git a/src/game/SharedDefines.h b/src/game/SharedDefines.h
index 3fbb97e5a82..0ccab173166 100644
--- a/src/game/SharedDefines.h
+++ b/src/game/SharedDefines.h
@@ -17,16 +17,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_SHAREDDEFINES_H
#define TRINITY_SHAREDDEFINES_H
+
#include "Platform/Define.h"
#include <cassert>
+
enum Gender
{
GENDER_MALE = 0,
GENDER_FEMALE = 1,
GENDER_NONE = 2
};
+
// Race value is index in ChrRaces.dbc
enum Races
{
@@ -52,13 +56,16 @@ enum Races
//RACE_NORTHREND_SKELETON = 20,
//RACE_ICE_TROLL = 21
};
+
// max+1 for player race
#define MAX_RACES 12
+
#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
{
@@ -74,16 +81,22 @@ enum Classes
// CLASS_UNK2 = 10,unused
CLASS_DRUID = 11,
};
+
// max+1 for player class
#define 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)) | \
(1<<(CLASS_DEATH_KNIGHT-1)) )
+
#define CLASSMASK_ALL_CREATURES ((1<<(CLASS_WARRIOR-1)) | (1<<(CLASS_PALADIN-1)) | (1<<(CLASS_ROGUE-1)) | (1<<(CLASS_MAGE-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,
@@ -95,14 +108,17 @@ enum ReputationRank
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,
@@ -111,7 +127,9 @@ enum Stats
STAT_INTELLECT = 3,
STAT_SPIRIT = 4
};
+
#define MAX_STATS 5
+
enum Powers
{
POWER_MANA = 0,
@@ -125,6 +143,7 @@ enum Powers
POWER_ALL = 127, // default for class?
POWER_HEALTH = 0xFFFFFFFE // (-2 as signed value)
};
+
enum SpellSchools
{
SPELL_SCHOOL_NORMAL = 0,
@@ -135,7 +154,9 @@ enum SpellSchools
SPELL_SCHOOL_SHADOW = 5,
SPELL_SCHOOL_ARCANE = 6
};
+
#define MAX_SPELL_SCHOOL 7
+
enum SpellSchoolMask
{
SPELL_SCHOOL_MASK_NONE = 0x00, // not exist
@@ -146,23 +167,29 @@ enum SpellSchoolMask
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 )
};
+
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
@@ -174,12 +201,15 @@ enum ItemQualities
ITEM_QUALITY_ARTIFACT = 6, //LIGHT YELLOW
ITEM_QUALITY_HEIRLOOM = 7
};
+
#define MAX_ITEM_QUALITY 8
+
enum SpellCategory
{
SPELL_CATEGORY_FOOD = 11,
SPELL_CATEGORY_DRINK = 59,
};
+
const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
0xff9d9d9d, //GREY
0xffffffff, //WHITE
@@ -191,9 +221,11 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
0xffe6cc80 //LIGHT YELLOW
};
+
// ***********************************
// Spell Attributes definitions
// ***********************************
+
#define SPELL_ATTR_UNK0 0x00000001 // 0
#define SPELL_ATTR_REQ_AMMO 0x00000002 // 1
#define SPELL_ATTR_ON_NEXT_SWING 0x00000004 // 2 on next swing
@@ -226,6 +258,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#define SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY 0x20000000 // 29 unaffected by invulnerability (hmm possible not...)
#define SPELL_ATTR_BREAKABLE_BY_DAMAGE 0x40000000 // 30
#define SPELL_ATTR_CANT_CANCEL 0x80000000 // 31 positive aura can't be canceled
+
#define SPELL_ATTR_EX_DISMISS_PET 0x00000001 // 0 dismiss pet and not allow to summon new one?
#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 target
@@ -258,6 +291,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#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 ? many triggered spells have this flag
#define SPELL_ATTR_EX2_CANT_REFLECTED 0x00000004 // 2 ? used for detect can or not spell reflected
@@ -290,6 +324,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#define SPELL_ATTR_EX2_CANT_CRIT 0x20000000 // 29 Spell can't crit
#define SPELL_ATTR_EX2_TRIGGERED_CAN_TRIGGER 0x40000000 // 30 spell can trigger even if triggered
#define SPELL_ATTR_EX2_FOOD_BUFF 0x80000000 // 31 Food or Drink Buff (like Well Fed)
+
#define SPELL_ATTR_EX3_UNK0 0x00000001 // 0
#define SPELL_ATTR_EX3_UNK1 0x00000002 // 1
#define SPELL_ATTR_EX3_UNK2 0x00000004 // 2
@@ -322,6 +357,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#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
@@ -354,6 +390,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#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
@@ -386,6 +423,7 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#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_ONLY_IN_ARENA 0x00000002 // 1 only usable in arena
#define SPELL_ATTR_EX6_IGNORE_CASTER_AURAS 0x00000004 // 2
@@ -418,11 +456,13 @@ const uint32 ItemQualityColors[MAX_ITEM_QUALITY] = {
#define SPELL_ATTR_EX6_UNK29 0x20000000 // 29 not set in 3.0.3
#define SPELL_ATTR_EX6_UNK30 0x40000000 // 30 not set in 3.0.3
#define SPELL_ATTR_EX6_UNK31 0x80000000 // 31 not set in 3.0.3
+
#define MIN_TALENT_SPEC 0
#define MAX_TALENT_SPEC 1
#define MIN_TALENT_SPECS 1
#define MAX_TALENT_SPECS 2
#define MAX_GLYPH_SLOT_INDEX 6
+
// Custom values
enum SpellClickUserTypes
{
@@ -432,9 +472,11 @@ enum SpellClickUserTypes
SPELL_CLICK_USER_PARTY = 3,
SPELL_CLICK_USER_MAX = 4
};
+
#define NPC_CLICK_CAST_CASTER_PLAYER 0x01
#define NPC_CLICK_CAST_TARGET_PLAYER 0x02
#define NPC_CLICK_CAST_ORIG_CASTER_OWNER 0x04
+
enum SheathTypes
{
SHEATHETYPE_NONE = 0,
@@ -446,7 +488,9 @@ enum SheathTypes
SHEATHETYPE_HIPWEAPONRIGHT = 6,
SHEATHETYPE_SHIELD = 7
};
+
#define MAX_SHEATHETYPE 8
+
enum CharacterSlot
{
SLOT_HEAD = 0,
@@ -470,6 +514,7 @@ enum CharacterSlot
SLOT_TABARD = 18,
SLOT_EMPTY = 19
};
+
enum Language
{
LANG_UNIVERSAL = 0,
@@ -492,13 +537,16 @@ enum Language
LANG_GOBLIN_BINARY = 38,
LANG_ADDON = 0xFFFFFFFF // used by addons, in 2.4.0 not exit, replaced by messagetype?
};
+
#define LANGUAGES_COUNT 19
+
enum TeamId
{
TEAM_ALLIANCE = 0,
TEAM_HORDE,
TEAM_NEUTRAL,
};
+
enum Team
{
HORDE = 67,
@@ -510,7 +558,9 @@ enum Team
//TEAM_OUTLAND = 980,
TEAM_OTHER = 0, // if ReputationListId > 0 && Flags != FACTION_FLAG_TEAM_HEADER
};
+
const Team TeamId2Team[3] = {ALLIANCE, HORDE, TEAM_OTHER};
+
enum SpellEffects
{
SPELL_EFFECT_INSTAKILL = 1,
@@ -677,6 +727,7 @@ enum SpellEffects
SPELL_EFFECT_TALENT_SPEC_SELECT = 162,
TOTAL_SPELL_EFFECTS = 163
};
+
enum SpellCastResult
{
SPELL_FAILED_AFFECTING_COMBAT = 0x00,
@@ -865,8 +916,10 @@ enum SpellCastResult
SPELL_FAILED_MAX_SOCKETS = 0xB7,
SPELL_FAILED_PET_CAN_RENAME = 0xB8,
SPELL_FAILED_UNKNOWN = 0xB9,
+
SPELL_CAST_OK = 0xFF // custom value, don't must be send to client
};
+
// Spell aura states
enum AuraState
{ // (C) used in caster aura state (T) used in target aura state
@@ -898,8 +951,10 @@ enum AuraState
//AURA_STATE_UNKNOWN22 = 22, // C | not implemented yet (Requires Evasive Charges to use)
AURA_STATE_HEALTH_ABOVE_75_PERCENT = 23, // C |
};
+
#define PER_CASTER_AURA_STATE_MASK ( \
(1<<(AURA_STATE_CONFLAGRATE-1))|(1<<(AURA_STATE_DEADLY_POISON-1)))
+
// Spell mechanics
enum Mechanics
{
@@ -936,6 +991,7 @@ enum Mechanics
MECHANIC_SAPPED = 30,
MECHANIC_ENRAGED = 31
};
+
// Used for spell 42292 Immune Movement Impairment and Loss of Control (0x49967da6)
#define IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK ( \
(1<<MECHANIC_CHARM )|(1<<MECHANIC_DISORIENTED )|(1<<MECHANIC_FEAR )| \
@@ -944,11 +1000,13 @@ enum Mechanics
(1<<MECHANIC_KNOCKOUT)|(1<<MECHANIC_POLYMORPH)|(1<<MECHANIC_BANISH)| \
(1<<MECHANIC_SHACKLE )|(1<<MECHANIC_TURN )|(1<<MECHANIC_HORROR)| \
(1<<MECHANIC_DAZE )|(1<<MECHANIC_SAPPED ) )
+
// Daze and all croud control spells except polymorph are not removed
#define MECHANIC_NOT_REMOVED_BY_SHAPESHIFT ( \
(1<<MECHANIC_CHARM )|(1<<MECHANIC_DISORIENTED)|(1<<MECHANIC_FEAR )|(1<<MECHANIC_PACIFY )| \
(1<<MECHANIC_STUN )|(1<<MECHANIC_FREEZE )|(1<<MECHANIC_BANISH)|(1<<MECHANIC_SHACKLE)| \
(1<<MECHANIC_HORROR)|(1<<MECHANIC_TURN )|(1<<MECHANIC_DAZE )|(1<<MECHANIC_SAPPED ) )
+
// Spell dispell type
enum DispelType
{
@@ -965,7 +1023,9 @@ enum DispelType
DISPEL_ZG_TICKET = 10,
DESPEL_OLD_UNUSED = 11
};
+
#define DISPEL_ALL_MASK ( (1<<DISPEL_MAGIC) | (1<<DISPEL_CURSE) | (1<<DISPEL_DISEASE) | (1<<DISPEL_POISON) )
+
//To all Immune system,if target has immunes,
//some spell that related to ImmuneToDispel or ImmuneToSchool or ImmuneToDamage type can't cast to it,
//some spell_effects that related to ImmuneToEffect<effect>(only this effect in the spell) can't cast to it,
@@ -980,7 +1040,9 @@ enum SpellImmunity
IMMUNITY_MECHANIC = 5, // enum Mechanics
IMMUNITY_ID = 6
};
+
#define MAX_SPELL_IMMUNITY 7
+
enum Targets
{
TARGET_UNIT_CASTER = 1,
@@ -1080,7 +1142,9 @@ enum Targets
TARGET_UNIT_PASSENGER = 97,
TARGET_UNIT_AREA_PATH = 104,
};
+
#define TOTAL_SPELL_TARGETS 105
+
enum SpellMissInfo
{
SPELL_MISS_NONE = 0,
@@ -1096,6 +1160,7 @@ enum SpellMissInfo
SPELL_MISS_ABSORB = 10,
SPELL_MISS_REFLECT = 11
};
+
enum SpellHitType
{
SPELL_HIT_TYPE_UNK1 = 0x00001,
@@ -1105,6 +1170,7 @@ enum SpellHitType
SPELL_HIT_TYPE_UNK5 = 0x00010, // replace caster?
SPELL_HIT_TYPE_UNK6 = 0x00020
};
+
enum SpellDmgClass
{
SPELL_DAMAGE_CLASS_NONE = 0,
@@ -1112,12 +1178,14 @@ enum SpellDmgClass
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,
@@ -1157,8 +1225,11 @@ enum GameobjectTypes
GAMEOBJECT_TYPE_GUILD_BANK = 34,
GAMEOBJECT_TYPE_TRAPDOOR = 35
};
+
#define MAX_GAMEOBJECT_TYPE 36 // 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 = 0x00000001, //disables interaction while animated
@@ -1171,6 +1242,7 @@ enum GameObjectFlags
GO_FLAG_DAMAGED = 0x00000200,
GO_FLAG_DESTROYED = 0x00000400,
};
+
enum TextEmotes
{
TEXTEMOTE_AGREE = 1,
@@ -1346,6 +1418,7 @@ enum TextEmotes
TEXTEMOTE_EYEBROW = 377,
TEXTEMOTE_TOAST = 378
};
+
enum Emote
{
EMOTE_ONESHOT_NONE = 0,
@@ -1514,6 +1587,7 @@ enum Emote
EMOTE_ONESHOT_SPELLCAST_OMNI = 463,
EMOTE_STATE_READYTHROWN = 464
};
+
enum Anim
{
ANIM_STAND = 0x0,
@@ -1661,6 +1735,7 @@ enum Anim
ANIM_SPELL_KNEEL_END = 0x8E,
ANIM_SPRINT = 0x8F,
ANIM_IN_FIGHT = 0x90,
+
ANIM_GAMEOBJ_SPAWN = 145,
ANIM_GAMEOBJ_CLOSE = 146,
ANIM_GAMEOBJ_CLOSED = 147,
@@ -1741,12 +1816,14 @@ enum Anim
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,
@@ -1771,6 +1848,7 @@ enum LockType
LOCKTYPE_INSCRIPTION = 20,
LOCKTYPE_OPEN_FROM_VEHICLE = 21
};
+
enum TrainerType // this is important type for npcs!
{
TRAINER_TYPE_CLASS = 0,
@@ -1778,7 +1856,9 @@ enum TrainerType // this is important
TRAINER_TYPE_TRADESKILLS = 2,
TRAINER_TYPE_PETS = 3
};
+
#define MAX_TRAINER_TYPE 4
+
// CreatureType.dbc
enum CreatureType
{
@@ -1796,9 +1876,11 @@ enum CreatureType
CREATURE_TYPE_NON_COMBAT_PET = 12,
CREATURE_TYPE_GAS_CLOUD = 13
};
+
uint32 const CREATURE_TYPEMASK_DEMON_OR_UNDEAD = (1 << (CREATURE_TYPE_DEMON-1)) | (1 << (CREATURE_TYPE_UNDEAD-1));
uint32 const CREATURE_TYPEMASK_HUMANOID_OR_UNDEAD = (1 << (CREATURE_TYPE_HUMANOID-1)) | (1 << (CREATURE_TYPE_UNDEAD-1));
uint32 const CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL = (1 << (CREATURE_TYPE_MECHANICAL-1)) | (1 << (CREATURE_TYPE_ELEMENTAL-1));
+
// CreatureFamily.dbc
enum CreatureFamily
{
@@ -1844,6 +1926,7 @@ enum CreatureFamily
CREATURE_FAMILY_CORE_HOUND = 45,
CREATURE_FAMILY_SPIRIT_BEAST = 46
};
+
enum CreatureTypeFlags
{
CREATURE_TYPEFLAGS_TAMEABLE = 0x00001, //tameable by any hunter
@@ -1867,6 +1950,7 @@ enum CreatureTypeFlags
CREATURE_TYPEFLAGS_UNK19 = 0x40000, //? Related to veichle/siege weapons?
CREATURE_TYPEFLAGS_UNK20 = 0x80000
};
+
enum CreatureEliteType
{
CREATURE_ELITE_NORMAL = 0,
@@ -1876,6 +1960,7 @@ enum CreatureEliteType
CREATURE_ELITE_RARE = 4,
CREATURE_UNKNOWN = 5 // found in 2.2.3 for 2 mobs
};
+
// values based at Holidays.dbc
enum HolidayIds
{
@@ -1900,6 +1985,7 @@ enum HolidayIds
HOLIDAY_CALL_TO_ARMS_SA = 400,
HOLIDAY_WOTLK_LAUNCH = 406
};
+
// values based at QuestInfo.dbc
enum QuestTypes
{
@@ -1915,6 +2001,7 @@ enum QuestTypes
QUEST_TYPE_RAID_10 = 88,
QUEST_TYPE_RAID_25 = 89
};
+
// values based at QuestSort.dbc
enum QuestSort
{
@@ -1957,6 +2044,7 @@ enum QuestSort
QUEST_SORT_DEATH_KNIGHT = 372,
QUEST_SORT_JEWELCRAFTING = 373
};
+
inline uint8 ClassByQuestSort(int32 QuestSort)
{
switch(QuestSort)
@@ -1974,9 +2062,11 @@ inline uint8 ClassByQuestSort(int32 QuestSort)
}
return 0;
}
+
enum SkillType
{
SKILL_NONE = 0,
+
SKILL_FROST = 6,
SKILL_FIRE = 8,
SKILL_ARMS = 26,
@@ -2128,7 +2218,9 @@ enum SkillType
SKILL_PET_EXOTIC_CORE_HOUND = 787,
SKILL_PET_EXOTIC_SPIRIT_BEAST = 788
};
+
#define MAX_SKILL_TYPE 789
+
inline SkillType SkillByLockType(LockType locktype)
{
switch(locktype)
@@ -2142,6 +2234,7 @@ inline SkillType SkillByLockType(LockType locktype)
}
return SKILL_NONE;
}
+
inline uint32 SkillByQuestSort(int32 QuestSort)
{
switch(QuestSort)
@@ -2160,6 +2253,7 @@ inline uint32 SkillByQuestSort(int32 QuestSort)
}
return 0;
}
+
enum SkillCategory
{
SKILL_CATEGORY_ATTRIBUTES = 5,
@@ -2171,6 +2265,7 @@ enum SkillCategory
SKILL_CATEGORY_PROFESSION = 11, // primary professions
SKILL_CATEGORY_GENERIC = 12
};
+
enum TotemCategory
{
TC_SKINNING_SKIFE_OLD = 1,
@@ -2206,6 +2301,7 @@ enum TotemCategory
TC_RUNED_COBALT_ROD = 189,
TC_RUNED_TITANIUM_ROD = 190
};
+
enum UnitDynFlags
{
UNIT_DYNFLAG_LOOTABLE = 0x0001,
@@ -2216,10 +2312,12 @@ enum UnitDynFlags
UNIT_DYNFLAG_DEAD = 0x0020,
UNIT_DYNFLAG_REFER_A_FRIEND = 0x0040
};
+
enum CorpseDynFlags
{
CORPSE_DYNFLAG_LOOTABLE = 0x0001
};
+
// Passive Spell codes explicit used in code
#define SPELL_ID_GENERIC_LEARN 483
#define SPELL_ID_GENERIC_LEARN_PET 55884 // used for learning mounts and companions
@@ -2239,6 +2337,7 @@ enum CorpseDynFlags
#define SPELL_ID_HONORLESS_TARGET 2479 // Honorless target
#define SPELL_ID_DUEL_BEG 7267 // Beg
#define SPELL_ID_DEATH_GATE 50977 // Death Gate
+
enum WeatherType
{
WEATHER_TYPE_FINE = 0,
@@ -2248,7 +2347,9 @@ enum WeatherType
WEATHER_TYPE_THUNDERS = 86,
WEATHER_TYPE_BLACKRAIN = 90
};
+
#define MAX_WEATHER_TYPE 4
+
enum ChatMsg
{
CHAT_MSG_ADDON = 0xFFFFFFFF,
@@ -2303,7 +2404,9 @@ enum ChatMsg
CHAT_MSG_ACHIEVEMENT = 0x30,
CHAT_MSG_GUILD_ACHIEVEMENT = 0x31
};
+
#define MAX_CHAT_MSG_TYPE 0x32
+
enum ChatLinkColors
{
CHAT_LINK_COLOR_TRADE = 0xffffd000, // orange
@@ -2313,6 +2416,7 @@ enum ChatLinkColors
CHAT_LINK_COLOR_ACHIEVEMENT = 0xffffff00,
CHAT_LINK_COLOR_GLYPH = 0xff66bbff
};
+
// Values from ItemPetFood (power of (value-1) used for compare with CreatureFamilyEntry.petDietMask
enum PetDiet
{
@@ -2325,8 +2429,11 @@ enum PetDiet
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
@@ -2335,6 +2442,7 @@ enum PetDiet
#define GUILD_EVENTLOG_MAX_RECORDS 100
#define GUILD_RANKS_MIN_COUNT 5
#define GUILD_RANKS_MAX_COUNT 10
+
enum AiReaction
{
AI_REACTION_UNK1 = 1,
@@ -2342,6 +2450,7 @@ enum AiReaction
AI_REACTION_UNK3 = 3,
AI_REACTION_UNK4 = 4
};
+
// Diminishing Returns Types
enum DiminishingReturnsType
{
@@ -2349,6 +2458,7 @@ enum DiminishingReturnsType
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
{
@@ -2375,12 +2485,14 @@ enum DiminishingGroup
DIMINISHING_TAUNT,
DIMINISHING_LIMITONLY // Don't Diminish, but limit duration to 10s
};
+
enum DungeonDifficulties
{
DIFFICULTY_NORMAL = 0,
DIFFICULTY_HEROIC = 1,
TOTAL_DIFFICULTIES
};
+
enum SummonCategory
{
SUMMON_CATEGORY_WILD = 0,
@@ -2389,6 +2501,7 @@ enum SummonCategory
SUMMON_CATEGORY_PUPPET = 3,
SUMMON_CATEGORY_VEHICLE = 4,
};
+
enum SummonType
{
SUMMON_TYPE_NONE = 0,
@@ -2404,12 +2517,14 @@ enum SummonType
SUMMON_TYPE_VEHICLE2 = 10,
SUMMON_TYPE_OBJECT = 11,
};
+
enum EventId
{
EVENT_SPELLCLICK = 1001,
EVENT_FALL_GROUND = 1002,
EVENT_CHARGE = 1003,
};
+
enum ResponseCodes
{
RESPONSE_SUCCESS = 0x00,
@@ -2419,11 +2534,13 @@ enum ResponseCodes
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,
@@ -2447,17 +2564,21 @@ enum ResponseCodes
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,
@@ -2473,12 +2594,14 @@ enum ResponseCodes
CHAR_CREATE_EXPANSION_CLASS = 0x3A,
CHAR_CREATE_LEVEL_REQUIREMENT = 0x3B,
CHAR_CREATE_UNIQUE_CLASS_LIMIT = 0x3C,
+
CHAR_DELETE_IN_PROGRESS = 0x3D,
CHAR_DELETE_SUCCESS = 0x3E,
CHAR_DELETE_FAILED = 0x3F,
CHAR_DELETE_FAILED_LOCKED_FOR_TRANSFER = 0x40,
CHAR_DELETE_FAILED_GUILD_LEADER = 0x41,
CHAR_DELETE_FAILED_ARENA_CAPTAIN = 0x42,
+
CHAR_LOGIN_IN_PROGRESS = 0x43,
CHAR_LOGIN_SUCCESS = 0x44,
CHAR_LOGIN_NO_WORLD = 0x45,
@@ -2489,6 +2612,7 @@ enum ResponseCodes
CHAR_LOGIN_NO_CHARACTER = 0x4A,
CHAR_LOGIN_LOCKED_FOR_TRANSFER = 0x4B,
CHAR_LOGIN_LOCKED_BY_BILLING = 0x4C,
+
CHAR_NAME_SUCCESS = 0x4D,
CHAR_NAME_FAILURE = 0x4E,
CHAR_NAME_NO_NAME = 0x4F,
@@ -2507,6 +2631,7 @@ enum ResponseCodes
CHAR_NAME_RUSSIAN_SILENT_CHARACTER_AT_BEGINNING_OR_END = 0x5C,
CHAR_NAME_DECLENSION_DOESNT_MATCH_BASE_NAME = 0x5D
};
+
/// Ban function modes
enum BanMode
{
@@ -2514,6 +2639,7 @@ enum BanMode
BAN_CHARACTER,
BAN_IP
};
+
/// Ban function return codes
enum BanReturn
{
@@ -2521,6 +2647,7 @@ enum BanReturn
BAN_SYNTAX_ERROR,
BAN_NOTFOUND
};
+
// indexes of BattlemasterList.dbc
enum BattleGroundTypeId
{
@@ -2538,6 +2665,7 @@ enum BattleGroundTypeId
BATTLEGROUND_RV = 11
};
#define MAX_BATTLEGROUND_TYPE_ID 12
+
enum MailResponseType
{
MAIL_SEND = 0,
@@ -2547,6 +2675,7 @@ enum MailResponseType
MAIL_DELETED = 4,
MAIL_MADE_PERMANENT = 5
};
+
enum MailResponseResult
{
MAIL_OK = 0,
@@ -2564,6 +2693,7 @@ enum MailResponseResult
MAIL_ERR_MAIL_ATTACHMENT_INVALID = 19,
MAIL_ERR_ITEM_HAS_EXPIRED = 21,
};
+
enum SpellFamilyNames
{
SPELLFAMILY_GENERIC = 0,
@@ -2585,5 +2715,6 @@ enum SpellFamilyNames
// 16 - unused
SPELLFAMILY_PET = 17
};
+
#endif
diff --git a/src/game/SkillDiscovery.cpp b/src/game/SkillDiscovery.cpp
index d30d25e2355..b1e16897b18 100644
--- a/src/game/SkillDiscovery.cpp
+++ b/src/game/SkillDiscovery.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -27,48 +28,64 @@
#include "SpellMgr.h"
#include "Player.h"
#include <map>
+
struct SkillDiscoveryEntry
{
uint32 spellId; // discavered spell
uint32 reqSkillValue; // skill level limitation
float chance; // chance
+
SkillDiscoveryEntry()
: spellId(0), reqSkillValue(0), chance(0) {}
+
SkillDiscoveryEntry(uint32 _spellId, uint32 req_skill_val, float _chance)
: spellId(_spellId), reqSkillValue(req_skill_val), chance(_chance) {}
};
+
typedef std::list<SkillDiscoveryEntry> SkillDiscoveryList;
typedef UNORDERED_MAP<int32, SkillDiscoveryList> SkillDiscoveryMap;
+
static SkillDiscoveryMap SkillDiscoveryStore;
+
void LoadSkillDiscoveryTable()
{
+
SkillDiscoveryStore.clear(); // need for reload
+
uint32 count = 0;
+
// 0 1 2 3
QueryResult *result = WorldDatabase.Query("SELECT spellId, reqSpell, reqSkillValue, chance FROM skill_discovery_template");
+
if (!result)
{
sLog.outString();
sLog.outString( ">> Loaded 0 skill discovery definitions. DB table `skill_discovery_template` is empty." );
return;
}
+
barGoLink bar(result->GetRowCount());
+
std::ostringstream ssNonDiscoverableEntries;
std::set<uint32> reportedReqSpells;
+
do
{
Field *fields = result->Fetch();
bar.step();
+
uint32 spellId = fields[0].GetUInt32();
int32 reqSkillOrSpell = fields[1].GetInt32();
uint32 reqSkillValue = fields[2].GetInt32();
float chance = fields[3].GetFloat();
+
if (chance <= 0) // chance
{
ssNonDiscoverableEntries << "spellId = " << spellId << " reqSkillOrSpell = " << reqSkillOrSpell
<< " reqSkillValue = " << reqSkillValue << " chance = " << chance << "(chance problem)\n";
continue;
}
+
if (reqSkillOrSpell > 0) // spell case
{
SpellEntry const* reqSpellEntry = sSpellStore.LookupEntry(reqSkillOrSpell);
@@ -81,6 +98,7 @@ void LoadSkillDiscoveryTable()
}
continue;
}
+
// mechanic discovery
if (reqSpellEntry->Mechanic != MECHANIC_DISCOVERY &&
// explicit discovery ability
@@ -95,16 +113,19 @@ void LoadSkillDiscoveryTable()
}
continue;
}
+
SkillDiscoveryStore[reqSkillOrSpell].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) );
}
else if (reqSkillOrSpell == 0) // skill case
{
SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellId);
+
if (bounds.first==bounds.second)
{
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 = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
SkillDiscoveryStore[-int32(_spell_idx->second->skillId)].push_back( SkillDiscoveryEntry(spellId, reqSkillValue, chance) );
}
@@ -113,26 +134,33 @@ void LoadSkillDiscoveryTable()
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());
+
// report about empty data for explicit discovery spells
for(uint32 spell_id = 1; spell_id < sSpellStore.GetNumRows(); ++spell_id)
{
SpellEntry const* spellEntry = sSpellStore.LookupEntry(spell_id);
if (!spellEntry)
continue;
+
// skip not explicit discovery spells
if (!IsExplicitDiscoverySpell(spellEntry))
continue;
+
if (SkillDiscoveryStore.find(spell_id)==SkillDiscoveryStore.end())
sLog.outErrorDb("Spell (ID: %u) is 100%% chance random discovery ability but not have data in `skill_discovery_template` table",spell_id);
}
}
+
uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
{
// explicit discovery spell chances (always success if case exist)
@@ -140,32 +168,43 @@ uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player)
SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(spellId);
if (tab == SkillDiscoveryStore.end())
return 0;
+
SkillLineAbilityMapBounds bounds = spellmgr.GetSkillLineAbilityMapBounds(spellId);
uint32 skillvalue = bounds.first != bounds.second ? player->GetSkillValue(bounds.first->second->skillId) : 0;
+
float full_chance = 0;
for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
if (item_iter->reqSkillValue <= skillvalue)
if (!player->HasSpell(item_iter->spellId))
full_chance += item_iter->chance;
+
float rate = full_chance / 100.0f;
float roll = rand_chance() * rate; // roll now in range 0..full_chance
+
for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
{
if (item_iter->reqSkillValue > skillvalue)
continue;
+
if (player->HasSpell(item_iter->spellId))
continue;
+
if (item_iter->chance > roll)
return item_iter->spellId;
+
roll -= item_iter->chance;
}
+
return 0;
}
+
uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player)
{
uint32 skillvalue = skillId ? player->GetSkillValue(skillId) : 0;
+
// check spell case
SkillDiscoveryMap::const_iterator tab = SkillDiscoveryStore.find(spellId);
+
if (tab != SkillDiscoveryStore.end())
{
for(SkillDiscoveryList::const_iterator item_iter = tab->second.begin(); item_iter != tab->second.end(); ++item_iter)
@@ -175,10 +214,13 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player)
!player->HasSpell(item_iter->spellId))
return item_iter->spellId;
}
+
return 0;
}
+
if (!skillId)
return 0;
+
// check skill line case
tab = SkillDiscoveryStore.find(-(int32)skillId);
if (tab != SkillDiscoveryStore.end())
@@ -190,8 +232,10 @@ uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player)
!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
index c63ac1503be..9981eb133f6 100644
--- a/src/game/SkillDiscovery.h
+++ b/src/game/SkillDiscovery.h
@@ -17,10 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_SKILLDISCOVERY_H
#define TRINITY_SKILLDISCOVERY_H
+
#include "Common.h"
+
class Player;
+
void LoadSkillDiscoveryTable();
uint32 GetSkillDiscoverySpell(uint32 skillId, uint32 spellId, Player* player);
uint32 GetExplicitDiscoverySpell(uint32 spellId, Player* player);
diff --git a/src/game/SkillExtraItems.cpp b/src/game/SkillExtraItems.cpp
index d5d1eb54c8d..f0061f97c00 100644
--- a/src/game/SkillExtraItems.cpp
+++ b/src/game/SkillExtraItems.cpp
@@ -17,14 +17,17 @@
* 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 <map>
+
// 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
@@ -35,59 +38,78 @@ struct SkillExtraItemEntry
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<uint32,SkillExtraItemEntry> 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.Query("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 );
}
@@ -97,22 +119,28 @@ void LoadSkillExtraItemTable()
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
index f1e39d1b982..3a44619a862 100644
--- a/src/game/SkillExtraItems.h
+++ b/src/game/SkillExtraItems.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_SKILL_EXTRA_ITEMS_H
#define TRINITY_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
diff --git a/src/game/SkillHandler.cpp b/src/game/SkillHandler.cpp
index 949cb33a656..e8ee75ab86b 100644
--- a/src/game/SkillHandler.cpp
+++ b/src/game/SkillHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -26,40 +27,52 @@
#include "WorldSession.h"
#include "ObjectAccessor.h"
#include "UpdateMask.h"
+
void WorldSession::HandleLearnTalentOpcode( WorldPacket & recv_data )
{
uint32 talent_id, requested_rank;
recv_data >> talent_id >> requested_rank;
+
_player->LearnTalent(talent_id, requested_rank);
_player->SendTalentsInfoData(false);
}
+
void WorldSession::HandleLearnPreviewTalents(WorldPacket& recvPacket)
{
sLog.outDebug("CMSG_LEARN_PREVIEW_TALENTS");
+
uint32 talentsCount;
recvPacket >> talentsCount;
+
uint32 talentId, talentRank;
+
for(uint32 i = 0; i < talentsCount; ++i)
{
recvPacket >> talentId >> talentRank;
+
_player->LearnTalent(talentId, talentRank);
}
+
_player->SendTalentsInfoData(false);
}
+
void WorldSession::HandleTalentWipeConfirmOpcode( WorldPacket & recv_data )
{
sLog.outDetail("MSG_TALENT_WIPE_CONFIRM");
uint64 guid;
recv_data >> guid;
+
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid,UNIT_NPC_FLAG_TRAINER);
if (!unit)
{
sLog.outDebug( "WORLD: HandleTalentWipeConfirmOpcode - 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()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
if(!(_player->resetTalents()))
{
WorldPacket data( MSG_TALENT_WIPE_CONFIRM, 8+4); //you have not any talent
@@ -68,9 +81,11 @@ void WorldSession::HandleTalentWipeConfirmOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
_player->SendTalentsInfoData(false);
unit->CastSpell(_player, 14867, true); //spell: "Untalent Visual Effect"
}
+
void WorldSession::HandleUnlearnSkillOpcode(WorldPacket & recv_data)
{
uint32 skill_id;
diff --git a/src/game/SocialMgr.cpp b/src/game/SocialMgr.cpp
index 6d016062e1b..2331b7ad839 100644
--- a/src/game/SocialMgr.cpp
+++ b/src/game/SocialMgr.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -26,15 +27,19 @@
#include "ObjectMgr.h"
#include "World.h"
#include "Util.h"
+
INSTANTIATE_SINGLETON_1( SocialMgr );
+
PlayerSocial::PlayerSocial()
{
m_playerGUID = 0;
}
+
PlayerSocial::~PlayerSocial()
{
m_playerSocialMap.clear();
}
+
uint32 PlayerSocial::GetNumberOfSocialsWithFlag(SocialFlag flag)
{
uint32 counter = 0;
@@ -45,6 +50,7 @@ uint32 PlayerSocial::GetNumberOfSocialsWithFlag(SocialFlag flag)
}
return counter;
}
+
bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore)
{
// check client limits
@@ -58,9 +64,11 @@ bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore)
if(GetNumberOfSocialsWithFlag(SOCIAL_FLAG_FRIEND) >= SOCIALMGR_FRIEND_LIMIT)
return false;
}
+
uint32 flag = SOCIAL_FLAG_FRIEND;
if(ignore)
flag = SOCIAL_FLAG_IGNORED;
+
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(friend_guid);
if(itr != m_playerSocialMap.end())
{
@@ -76,14 +84,17 @@ bool PlayerSocial::AddToSocialList(uint32 friend_guid, bool ignore)
}
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)
{
@@ -95,28 +106,36 @@ void PlayerSocial::RemoveFromSocialList(uint32 friend_guid, bool ignore)
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::const_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
@@ -131,9 +150,11 @@ void PlayerSocial::SendSocialList()
}
}
}
+
plr->GetSession()->SendPacket(&data);
sLog.outDebug("WORLD: Sent SMSG_CONTACT_LIST");
}
+
bool PlayerSocial::HasFriend(uint32 friend_guid)
{
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(friend_guid);
@@ -141,6 +162,7 @@ bool PlayerSocial::HasFriend(uint32 friend_guid)
return itr->second.Flags & SOCIAL_FLAG_FRIEND;
return false;
}
+
bool PlayerSocial::HasIgnore(uint32 ignore_guid)
{
PlayerSocialMap::const_iterator itr = m_playerSocialMap.find(ignore_guid);
@@ -148,30 +170,40 @@ bool PlayerSocial::HasIgnore(uint32 ignore_guid)
return itr->second.Flags & SOCIAL_FLAG_IGNORED;
return false;
}
+
SocialMgr::SocialMgr()
{
+
}
+
SocialMgr::~SocialMgr()
{
+
}
+
void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo)
{
if(!player)
return;
+
friendInfo.Status = FRIEND_STATUS_OFFLINE;
friendInfo.Area = 0;
friendInfo.Level = 0;
friendInfo.Class = 0;
+
Player *pFriend = ObjectAccessor::FindPlayer(friendGUID);
if(!pFriend)
return;
+
uint32 team = player->GetTeam();
AccountTypes security = player->GetSession()->GetSecurity();
bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
AccountTypes gmLevelInWhoList = AccountTypes (sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST));
+
PlayerSocialMap::iterator itr = player->GetSocial()->m_playerSocialMap.find(friendGUID);
if(itr != player->GetSocial()->m_playerSocialMap.end())
friendInfo.Note = itr->second.Note;
+
// PLAYER see his team only and PLAYER can't see MODERATOR, GAME MASTER, ADMINISTRATOR characters
// MODERATOR, GAME MASTER, ADMINISTRATOR can see all
if (pFriend && pFriend->GetName() &&
@@ -189,15 +221,18 @@ void SocialMgr::GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &fri
friendInfo.Class = pFriend->getClass();
}
}
+
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, bool broadcast)
{
FriendInfo fi;
+
WorldPacket data;
MakeFriendStatusPacket(result, friend_guid, &data);
GetFriendInfo(player, friend_guid, fi);
@@ -210,6 +245,7 @@ void SocialMgr::SendFriendStatus(Player *player, FriendsResult result, uint32 fr
default:
break;
}
+
switch(result)
{
case FRIEND_ADDED_ONLINE:
@@ -222,26 +258,31 @@ void SocialMgr::SendFriendStatus(Player *player, FriendsResult result, uint32 fr
default:
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();
AccountTypes security = player->GetSession()->GetSecurity();
uint32 guid = player->GetGUIDLow();
AccountTypes gmLevelInWhoList = AccountTypes(sWorld.getConfig(CONFIG_GM_LEVEL_IN_WHO_LIST));
bool allowTwoSideWhoList = sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_WHO_LIST);
+
for(SocialMap::const_iterator itr = m_socialMap.begin(); itr != m_socialMap.end(); ++itr)
{
PlayerSocialMap::const_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() &&
@@ -254,22 +295,29 @@ void SocialMgr::BroadcastToFriendListers(Player *player, WorldPacket *packet)
}
}
}
+
PlayerSocial *SocialMgr::LoadFromDB(QueryResult *result, uint32 guid)
{
PlayerSocial *social = &m_socialMap[guid];
social->SetPlayerGUID(guid);
+
if(!result)
return social;
+
uint32 friend_guid = 0;
uint32 flags = 0;
std::string note = "";
+
do
{
Field *fields = result->Fetch();
+
friend_guid = fields[0].GetUInt32();
flags = fields[1].GetUInt32();
note = fields[2].GetCppString();
+
social->m_playerSocialMap[friend_guid] = FriendInfo(flags, note);
+
// client limit
if(social->m_playerSocialMap.size() >= 50)
break;
diff --git a/src/game/SocialMgr.h b/src/game/SocialMgr.h
index 736667c3e0b..1ca04d69707 100644
--- a/src/game/SocialMgr.h
+++ b/src/game/SocialMgr.h
@@ -17,15 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef __TRINITY_SOCIALMGR_H
#define __TRINITY_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,
@@ -34,12 +38,14 @@ enum FriendStatus
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;
@@ -48,6 +54,7 @@ struct FriendInfo
uint32 Level;
uint32 Class;
std::string Note;
+
FriendInfo()
{
Status = FRIEND_STATUS_OFFLINE;
@@ -57,6 +64,7 @@ struct FriendInfo
Class = 0;
Note = "";
}
+
FriendInfo(uint32 flags, const std::string& note)
{
Status = FRIEND_STATUS_OFFLINE;
@@ -67,8 +75,10 @@ struct FriendInfo
Note = note;
}
};
+
typedef std::map<uint32, FriendInfo> PlayerSocialMap;
typedef std::map<uint32, PlayerSocial> SocialMap;
+
/// Results of friend related commands
enum FriendsResult
{
@@ -100,8 +110,10 @@ enum FriendsResult
FRIEND_UNK7 = 0x19, // no message at client
FRIEND_UNKNOWN = 0x1A // Unknown friend response from server
};
+
#define SOCIALMGR_FRIEND_LIMIT 50
#define SOCIALMGR_IGNORE_LIMIT 25
+
class PlayerSocial
{
friend class SocialMgr;
@@ -124,6 +136,7 @@ class PlayerSocial
PlayerSocialMap m_playerSocialMap;
uint32 m_playerGUID;
};
+
class SocialMgr
{
public:
@@ -131,6 +144,7 @@ class SocialMgr
~SocialMgr();
// Misc
void RemovePlayerSocial(uint32 guid) { m_socialMap.erase(guid); }
+
void GetFriendInfo(Player *player, uint32 friendGUID, FriendInfo &friendInfo);
// Packet management
void MakeFriendStatusPacket(FriendsResult result, uint32 friend_guid, WorldPacket *data);
@@ -141,6 +155,7 @@ class SocialMgr
private:
SocialMap m_socialMap;
};
+
#define sSocialMgr Trinity::Singleton<SocialMgr>::Instance()
#endif
diff --git a/src/game/Spell.cpp b/src/game/Spell.cpp
index 9690f131427..cb0236967ab 100644
--- a/src/game/Spell.cpp
+++ b/src/game/Spell.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -49,15 +50,20 @@
#include "TemporarySummon.h"
#include "Vehicle.h"
#include "ScriptCalls.h"
+
#define SPELL_CHANNEL_UPDATE_INTERVAL (1 * IN_MILISECONDS)
+
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;
}
+
class PrioritizeManaUnitWraper
{
public:
@@ -72,6 +78,7 @@ class PrioritizeManaUnitWraper
Unit* i_unit;
uint32 i_percent;
};
+
struct PrioritizeMana
{
int operator()( PrioritizeManaUnitWraper const& x, PrioritizeManaUnitWraper const& y ) const
@@ -79,7 +86,9 @@ struct PrioritizeMana
return x.getPercent() > y.getPercent();
}
};
+
typedef std::priority_queue<PrioritizeManaUnitWraper, std::vector<PrioritizeManaUnitWraper>, PrioritizeMana> PrioritizeManaUnitQueue;
+
class PrioritizeHealthUnitWraper
{
public:
@@ -93,6 +102,7 @@ private:
Unit* i_unit;
uint32 i_percent;
};
+
struct PrioritizeHealth
{
int operator()( PrioritizeHealthUnitWraper const& x, PrioritizeHealthUnitWraper const& y ) const
@@ -100,37 +110,46 @@ struct PrioritizeHealth
return x.getPercent() > y.getPercent();
}
};
+
typedef std::priority_queue<PrioritizeHealthUnitWraper, std::vector<PrioritizeHealthUnitWraper>, PrioritizeHealth> PrioritizeHealthUnitQueue;
+
SpellCastTargets::SpellCastTargets() : m_elevation(0), m_speed(0)
{
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_srcPos.Relocate(0,0,0,0);
m_strTarget = "";
m_targetMask = 0;
}
+
SpellCastTargets::~SpellCastTargets()
{
}
+
void SpellCastTargets::setUnitTarget(Unit *target)
{
if (!target)
return;
+
m_unitTarget = target;
m_unitTargetGUID = target->GetGUID();
m_targetMask |= TARGET_FLAG_UNIT;
}
+
void SpellCastTargets::setSrc(float x, float y, float z)
{
m_srcPos.Relocate(x, y, z);
m_targetMask |= TARGET_FLAG_SOURCE_LOCATION;
}
+
void SpellCastTargets::setSrc(Position *pos)
{
if(pos)
@@ -139,6 +158,7 @@ void SpellCastTargets::setSrc(Position *pos)
m_targetMask |= TARGET_FLAG_SOURCE_LOCATION;
}
}
+
void SpellCastTargets::setDst(float x, float y, float z, uint32 mapId)
{
m_dstPos.Relocate(x, y, z);
@@ -146,6 +166,7 @@ void SpellCastTargets::setDst(float x, float y, float z, uint32 mapId)
if(mapId != MAPID_INVALID)
m_dstPos.m_mapId = mapId;
}
+
void SpellCastTargets::setDst(Position *pos)
{
if(pos)
@@ -154,31 +175,37 @@ void SpellCastTargets::setDst(Position *pos)
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 ? caster->GetMap()->GetGameObject(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)
{
@@ -195,47 +222,61 @@ void SpellCastTargets::Update(Unit* caster)
m_itemTargetEntry = m_itemTarget->GetEntry();
}
}
+
bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
{
if(data->rpos() + 4 > data->size())
return false;
+
//data->hexlike();
+
*data >> m_targetMask;
//sLog.outDebug("Spell read, target mask = %u", m_targetMask);
+
if(m_targetMask == TARGET_FLAG_SELF)
return true;
+
// TARGET_FLAG_UNK2 is used for non-combat pets, maybe other?
if( m_targetMask & ( TARGET_FLAG_UNIT | TARGET_FLAG_UNK2 ))
if(!data->readPackGUID(m_unitTargetGUID))
return false;
+
if( m_targetMask & ( TARGET_FLAG_OBJECT ))
if(!data->readPackGUID(m_GOTargetGUID))
return false;
+
if(( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM )) && caster->GetTypeId() == TYPEID_PLAYER)
if(!data->readPackGUID(m_itemTargetGUID))
return false;
+
if( m_targetMask & (TARGET_FLAG_CORPSE | TARGET_FLAG_PVP_CORPSE ) )
if(!data->readPackGUID(m_CorpseTargetGUID))
return false;
+
if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
{
if(data->rpos() + 4 + 4 + 4 > data->size())
return false;
+
*data >> m_srcPos.m_positionX >> m_srcPos.m_positionY >> m_srcPos.m_positionZ;
if(!m_srcPos.IsPositionValid())
return false;
}
else
m_srcPos.Relocate(caster);
+
if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
{
if(data->rpos() + 1 + 4 + 4 + 4 > data->size())
return false;
+
if(!data->readPackGUID(m_unitTargetGUID))
return false;
+
*data >> m_dstPos.m_positionX >> m_dstPos.m_positionY >> m_dstPos.m_positionZ;
if(!m_dstPos.IsPositionValid())
return false;
+
if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
{
if(data->rpos() + 4 + 4 <= data->size())
@@ -250,20 +291,25 @@ bool SpellCastTargets::read ( WorldPacket * data, Unit *caster )
}
else
m_dstPos.Relocate(caster);
+
if( m_targetMask & TARGET_FLAG_STRING )
{
if(data->rpos() + 1 > data->size())
return false;
+
*data >> m_strTarget;
}
+
// find real units/GOs
Update(caster);
return true;
}
+
void SpellCastTargets::write ( WorldPacket * data )
{
*data << uint32(m_targetMask);
//sLog.outDebug("Spell write, target mask = %u", 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)
@@ -285,6 +331,7 @@ void SpellCastTargets::write ( WorldPacket * data )
else
*data << uint8(0);
}
+
if( m_targetMask & ( TARGET_FLAG_ITEM | TARGET_FLAG_TRADE_ITEM ) )
{
if(m_itemTarget)
@@ -292,19 +339,24 @@ void SpellCastTargets::write ( WorldPacket * data )
else
*data << uint8(0);
}
+
if( m_targetMask & TARGET_FLAG_SOURCE_LOCATION )
*data << m_srcPos.m_positionX << m_srcPos.m_positionY << m_srcPos.m_positionZ;
+
if( m_targetMask & TARGET_FLAG_DEST_LOCATION )
{
if(m_unitTarget)
data->append(m_unitTarget->GetPackGUID());
else
*data << uint8(0);
+
*data << m_dstPos.m_positionX << m_dstPos.m_positionY << m_dstPos.m_positionZ;
}
+
if( m_targetMask & TARGET_FLAG_STRING )
*data << m_strTarget;
}
+
Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID, Spell** triggeringContainer, bool skipCheck )
: m_spellInfo(info), m_spellValue(new SpellValue(m_spellInfo))
, m_caster(Caster)
@@ -319,10 +371,13 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
m_comboPointGain = 0;
m_delayStart = 0;
m_delayAtDamageCount = 0;
+
m_canTrigger=true;
+
m_applyMultiplierMask = 0;
m_effectMask = 0;
m_auraScaleMask = 0;
+
// Get data for type of attack
switch (m_spellInfo->DmgClass)
{
@@ -343,7 +398,9 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
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
@@ -355,10 +412,12 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
}
// Set health leech amount to zero
m_healthLeech = 0;
+
if(originalCasterGUID)
m_originalCasterGUID = originalCasterGUID;
else
m_originalCasterGUID = m_caster->GetGUID();
+
if(m_originalCasterGUID == m_caster->GetGUID())
m_originalCaster = m_caster;
else
@@ -366,13 +425,17 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
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_spellValue->EffectBasePoints[i];
+
m_spellState = SPELL_STATE_NULL;
+
m_TriggerSpells.clear();
m_IsTriggeredSpell = triggered;
//m_AreaAura = false;
m_CastItem = NULL;
+
unitTarget = NULL;
itemTarget = NULL;
gameObjTarget = NULL;
@@ -383,61 +446,78 @@ Spell::Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 origi
m_triggeredByAuraSpell = NULL;
m_spellAura = NULL;
m_spellDynObj = NULL;
+
//Auto Shot & Shoot (wand)
m_autoRepeat = IsAutoRepeatRangedSpell(m_spellInfo);
+
m_runesState = 0;
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 prepare
+
m_needAliveTargetMask = 0;
+
// determine reflection
m_canReflect = false;
+
if(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && !IsAreaOfEffectSpell(m_spellInfo) && !(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_CANT_REFLECTED))
{
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 & SPELL_ATTR_EX_NEGATIVE) ? true : false;
+
if(m_canReflect)
continue;
else
break;
}
}
+
CleanupTargetList();
}
+
Spell::~Spell()
{
if(m_caster && m_caster->GetTypeId() == TYPEID_PLAYER)
assert(((Player*)m_caster)->m_spellModTakingSpell != this);
delete m_spellValue;
}
+
template<typename T>
WorldObject* Spell::FindCorpseUsing()
{
// non-standard target selection
float max_range = GetSpellMaxRange(m_spellInfo, false);
+
CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate();
+
WorldObject* result = NULL;
+
T u_check(m_caster, max_range);
MaNGOS::WorldObjectSearcher<T> searcher(m_caster, result, u_check);
+
TypeContainerVisitor<MaNGOS::WorldObjectSearcher<T>, GridTypeMapContainer > grid_searcher(searcher);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_searcher, *m_caster->GetMap(), *m_caster, max_range);
+
if (!result)
{
TypeContainerVisitor<MaNGOS::WorldObjectSearcher<T>, WorldTypeMapContainer > world_searcher(searcher);
cell_lock->Visit(cell_lock, world_searcher, *m_caster->GetMap(), *m_caster, max_range);
}
+
return result;
}
+
void Spell::SelectSpellTargets()
{
for(uint32 i = 0; i < 3; ++i)
@@ -446,16 +526,21 @@ void Spell::SelectSpellTargets()
// Also some spells use not used effect targets for store targets for dummy effect in triggered spells
if(!m_spellInfo->Effect[i])
continue;
+
uint32 effectTargetType = EffectTargetType[m_spellInfo->Effect[i]];
+
// is it possible that areaaura is not applied to caster?
if(effectTargetType == SPELL_REQUIRE_NONE)
continue;
+
uint32 targetA = m_spellInfo->EffectImplicitTargetA[i];
uint32 targetB = m_spellInfo->EffectImplicitTargetB[i];
+
if(targetA)
SelectEffectTargets(i, targetA);
if(targetB) // In very rare case !A && B
SelectEffectTargets(i, targetB);
+
if(effectTargetType != SPELL_REQUIRE_UNIT)
{
if(effectTargetType == SPELL_REQUIRE_CASTER)
@@ -467,11 +552,13 @@ void Spell::SelectSpellTargets()
}
continue;
}
+
if(/*tmpUnitMap.empty() && */m_spellInfo->Targets & TARGET_FLAG_CASTER)
{
AddUnitTarget(m_caster, i);
continue;
}
+
if(!targetA && !targetB)
{
if(!GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex)))
@@ -479,6 +566,7 @@ void Spell::SelectSpellTargets()
AddUnitTarget(m_caster, i);
continue;
}
+
// add here custom effects that need default target.
// FOR EVERY TARGET TYPE THERE IS A DIFFERENT FILL!!
switch(m_spellInfo->Effect[i])
@@ -490,6 +578,7 @@ void Spell::SelectSpellTargets()
case 20577: // Cannibalize
{
WorldObject* result = FindCorpseUsing<MaNGOS::CannibalizeObjectCheck> ();
+
if(result)
{
switch(result->GetTypeId())
@@ -657,6 +746,7 @@ void Spell::SelectSpellTargets()
}
}
}
+
if(m_targets.HasDst())
{
if(m_targets.HasTraj())
@@ -672,6 +762,7 @@ void Spell::SelectSpellTargets()
}
}
}
+
void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura)
{
//==========================================================================================
@@ -679,6 +770,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura)
// can spell trigger another or not ( m_canTrigger )
// Create base triggers flags for Attacker and Victim ( m_procAttacker, m_procVictim and m_procEx)
//==========================================================================================
+
m_procVictim = m_procAttacker = 0;
// Get data for type of attack and fill base info for trigger
switch (m_spellInfo->DmgClass)
@@ -701,7 +793,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura)
}
break;
default:
- if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON &&
+ if (m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON &&
m_spellInfo->EquippedItemSubClassMask & (1<<ITEM_SUBCLASS_WEAPON_WAND)
&& m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG) // Wands auto attack
{
@@ -712,6 +804,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura)
// Because spell positivity is dependant on target
}
m_procEx= PROC_EX_NONE;
+
// Hunter traps spells (for Entrapment trigger)
// Gives your Immolation Trap, Frost Trap, Explosive Trap, and Snake Trap ....
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && (m_spellInfo->SpellFamilyFlags[1] & 0x00002000 || m_spellInfo->SpellFamilyFlags[0] & 0x1C))
@@ -729,10 +822,11 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura)
{
m_canTrigger=false;
}
+
// Ranged autorepeat attack is set as triggered spell - ignore it
if (!(m_procAttacker & PROC_FLAG_SUCCESSFUL_RANGED_HIT))
{
- if (m_IsTriggeredSpell &&
+ if (m_IsTriggeredSpell &&
(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_TRIGGERED_CAN_TRIGGER ||
m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_TRIGGERED_CAN_TRIGGER_2))
m_procEx |= PROC_EX_INTERNAL_CANT_PROC;
@@ -745,6 +839,7 @@ void Spell::prepareDataForTriggerSystem(AuraEffect * triggeredByAura)
m_procEx |= PROC_EX_INTERNAL_REQ_FAMILY;
}
}
+
void Spell::CleanupTargetList()
{
m_UniqueTargetInfo.clear();
@@ -752,15 +847,20 @@ void Spell::CleanupTargetList()
m_UniqueItemInfo.clear();
m_delayMoment = 0;
}
+
void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
{
if( m_spellInfo->Effect[effIndex] == 0 )
return;
+
if(!CheckTarget(pVictim, effIndex))
return;
+
// Check for effect immune skip if immuned
bool immuned = pVictim->IsImmunedToSpellEffect(m_spellInfo, effIndex);
+
uint64 targetGUID = pVictim->GetGUID();
+
// Lookup target in already in list
for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
@@ -778,7 +878,9 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
return;
}
}
+
// This is new target calculate data for him
+
// Get spell hit result on target
TargetInfo target;
target.targetGUID = targetGUID; // Store target GUID
@@ -794,6 +896,7 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
if ((pVictim->getLevel() + 10) >= auraSpell->spellLevel)
target.scaleAura = true;
}
+
// Calculate hit result
if(m_originalCaster)
{
@@ -803,46 +906,57 @@ void Spell::AddUnitTarget(Unit* pVictim, uint32 effIndex)
}
else
target.missCondition = SPELL_MISS_EVADE; //SPELL_MISS_NONE;
+
// Spell have speed - need calculate incoming time
if (m_spellInfo->speed > 0.0f)
{
// calculate spell incoming interval
// TODO: this is a hack
float dist = m_caster->GetDistance(pVictim->GetPositionX(), pVictim->GetPositionY(), pVictim->GetPositionZ());
+
if (dist < 5.0f) dist = 5.0f;
target.timeDelay = (uint64) floor(dist / m_spellInfo->speed * 1000.0f);
+
// Calculate minimum incoming time
if (m_delayMoment == 0 || m_delayMoment>target.timeDelay)
m_delayMoment = target.timeDelay;
}
else
target.timeDelay = 0LL;
+
// If target reflect spell back to caster
if (target.missCondition == SPELL_MISS_REFLECT)
{
// Calculate reflected spell result on caster
target.reflectResult = m_caster->SpellHitResult(m_caster, m_spellInfo, m_canReflect);
+
if (target.reflectResult == SPELL_MISS_REFLECT) // Impossible reflect again, so simply deflect spell
target.reflectResult = SPELL_MISS_PARRY;
+
// Increase time interval for reflected spells by 1.5
target.timeDelay += target.timeDelay >> 1;
}
else
target.reflectResult = SPELL_MISS_NONE;
+
// Add target to list
m_UniqueTargetInfo.push_back(target);
}
+
void Spell::AddUnitTarget(uint64 unitGUID, uint32 effIndex)
{
Unit* unit = m_caster->GetGUID() == unitGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, unitGUID);
if (unit)
AddUnitTarget(unit, effIndex);
}
+
void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
{
if( m_spellInfo->Effect[effIndex] == 0 )
return;
+
uint64 targetGUID = pVictim->GetGUID();
+
// Lookup target in already in list
for(std::list<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit)
{
@@ -852,11 +966,14 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
return;
}
}
+
// This is new target calculate data for him
+
GOTargetInfo target;
target.targetGUID = targetGUID;
target.effectMask = 1 << effIndex;
target.processed = false; // Effects not apply on target
+
// Spell have speed - need calculate incoming time
if (m_spellInfo->speed > 0.0f)
{
@@ -869,19 +986,23 @@ void Spell::AddGOTarget(GameObject* pVictim, uint32 effIndex)
}
else
target.timeDelay = 0LL;
+
// Add target to list
m_UniqueGOTargetInfo.push_back(target);
}
+
void Spell::AddGOTarget(uint64 goGUID, uint32 effIndex)
{
GameObject* go = m_caster->GetMap()->GetGameObject(goGUID);
if (go)
AddGOTarget(go, effIndex);
}
+
void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
{
if( m_spellInfo->Effect[effIndex] == 0 )
return;
+
// Lookup target in already in list
for(std::list<ItemTargetInfo>::iterator ihit = m_UniqueItemInfo.begin(); ihit != m_UniqueItemInfo.end(); ++ihit)
{
@@ -891,45 +1012,60 @@ void Spell::AddItemTarget(Item* pitem, uint32 effIndex)
return;
}
}
+
// This is new target add data
+
ItemTargetInfo target;
target.item = pitem;
target.effectMask = 1 << effIndex;
m_UniqueItemInfo.push_back(target);
}
+
void Spell::DoAllEffectOnTarget(TargetInfo *target)
{
- if (!target || target->processed) // Check 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;
+
Unit* unit = m_caster->GetGUID()==target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster,target->targetGUID);
if (!unit)
return;
+
if(unit->isAlive() != target->alive)
return;
+
// Get original caster (if exist) and calculate damage/healing from him data
Unit *caster = m_originalCaster ? m_originalCaster : m_caster;
+
// Skip if m_originalCaster not avaiable
if (!caster)
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;
+
// Reset damage/healing counter
m_damage = target->damage;
m_healing = -target->damage;
+
// Fill base trigger info
uint32 procAttacker = m_procAttacker;
uint32 procVictim = m_procVictim;
uint32 procEx = m_procEx;
+
m_spellAura = NULL; // Set aura to null for every target-make sure that pointer is not used for unit without aura applied
+
//Spells with this flag cannot trigger if effect is casted on self
// Slice and Dice, relentless strikes, eviscerate
bool canEffectTrigger = m_canTrigger && (m_spellInfo->AttributesEx4 & (SPELL_ATTR_EX4_CANT_PROC_FROM_SELFCAST) ? m_caster!=unitTarget : true);
Unit * spellHitTarget = NULL;
+
if (missInfo==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
spellHitTarget = unit;
else if (missInfo == SPELL_MISS_REFLECT) // In case spell reflect from target, do all effect on caster (if hit)
@@ -941,6 +1077,7 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
((Creature*)m_caster)->LowerPlayerDamageReq(target->damage);
}
}
+
if(spellHitTarget)
{
SpellMissInfo missInfo = DoSpellHitOnUnit(spellHitTarget, mask, target->scaleAura);
@@ -952,10 +1089,12 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
spellHitTarget = NULL;
}
}
+
// Do not take combo points on dodge
if (m_needComboPoints && m_targets.getUnitTargetGUID() == target->targetGUID)
if( missInfo != SPELL_MISS_NONE && missInfo != SPELL_MISS_MISS)
m_needComboPoints = false;
+
// Trigger info was not filled in spell::preparedatafortriggersystem - we do it now
if (canEffectTrigger && !procAttacker && !procVictim)
{
@@ -1013,11 +1152,14 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
}
else
procEx |= PROC_EX_NORMAL_HIT;
+
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT)
caster->ProcDamageAndSpell(unitTarget, procAttacker, procVictim, procEx, addhealth, m_attackType, m_spellInfo, m_triggeredByAuraSpell);
+
if (m_spellAura)
m_spellAura->SetProcDamage(addhealth);
+
int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo, crit);
unitTarget->getHostilRefManager().threatAssist(caster, float(gain) * 0.5f, m_spellInfo);
}
@@ -1026,13 +1168,17 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
{
// Fill base damage struct (unitTarget - is real spell target)
SpellNonMeleeDamage damageInfo(caster, unitTarget, m_spellInfo->Id, m_spellSchoolMask);
+
// Add bonuses and fill damageInfo struct
caster->CalculateSpellDamageTaken(&damageInfo, m_damage, m_spellInfo, m_attackType, target->crit);
caster->DealDamageMods(damageInfo.target,damageInfo.damage,&damageInfo.absorb);
+
// Send log damage message to client
caster->SendSpellNonMeleeDamageLog(&damageInfo);
+
procEx |= createProcExtendMask(&damageInfo, missInfo);
procVictim |= PROC_FLAG_TAKEN_ANY_DAMAGE;
+
// Do triggers for unit (reflect triggers passed on hit phase for correct drop charge)
if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT)
{
@@ -1041,9 +1187,11 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE || m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_RANGED))
((Player *)caster)->CastItemCombatSpell(unitTarget, m_attackType, procVictim, procEx);
}
+
if (m_spellAura)
m_spellAura->SetProcDamage(damageInfo.damage);
caster->DealSpellDamage(&damageInfo, true);
+
}
// Passive spell hits/misses or active spells only misses (only triggers)
else
@@ -1055,13 +1203,16 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
if (canEffectTrigger && missInfo != SPELL_MISS_REFLECT)
caster->ProcDamageAndSpell(unit, procAttacker, procVictim, procEx, 0, m_attackType, m_spellInfo, m_triggeredByAuraSpell);
}
+
if(m_caster && !m_caster->IsFriendlyTo(unit) && !IsPositiveSpell(m_spellInfo->Id))
{
m_caster->CombatStart(unit, !(m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_NO_INITIAL_AGGRO));
+
if(m_customAttr & SPELL_ATTR_CU_AURA_CC)
if(!unit->IsStandState())
unit->SetStandState(UNIT_STAND_STATE_STAND);
}
+
if(spellHitTarget)
{
//AI functions
@@ -1069,40 +1220,50 @@ void Spell::DoAllEffectOnTarget(TargetInfo *target)
{
if(((Creature*)spellHitTarget)->IsAIEnabled)
((Creature*)spellHitTarget)->AI()->SpellHit(m_caster, m_spellInfo);
+
// cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
// ignore pets or autorepeat/melee casts for speed (not exist quest for spells (hm... )
if(m_originalCaster && m_originalCaster->IsControlledByPlayer() && !((Creature*)spellHitTarget)->isPet() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive())
if(Player* p = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself())
p->CastedCreatureOrGO(spellHitTarget->GetEntry(),spellHitTarget->GetGUID(),m_spellInfo->Id);
}
+
if(m_caster && m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->IsAIEnabled)
((Creature*)m_caster)->AI()->SpellHitTarget(spellHitTarget, m_spellInfo);
+
// Needs to be called after dealing damage/healing to not remove breaking on damage auras
DoTriggersOnSpellHit(spellHitTarget);
+
// if target is fallged for pvp also flag caster if a player
if(unit->IsPvP())
{
if (m_caster->GetTypeId() == TYPEID_PLAYER)
((Player*)m_caster)->UpdatePvP(true);
}
+
}
}
+
SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool scaleAura)
{
if(!unit || !effectMask)
return SPELL_MISS_EVADE;
+
// Recheck immune (only for delayed spells)
if(m_spellInfo->speed && (unit->IsImmunedToDamage(m_spellInfo) || unit->IsImmunedToSpell(m_spellInfo)))
return SPELL_MISS_IMMUNE;
+
if (unit->GetTypeId() == TYPEID_PLAYER)
{
((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET, m_spellInfo->Id);
((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_BE_SPELL_TARGET2, m_spellInfo->Id);
}
+
if(m_caster->GetTypeId() == TYPEID_PLAYER)
{
((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL2, m_spellInfo->Id, 0, unit);
}
+
if( m_caster != unit )
{
// Recheck UNIT_FLAG_NON_ATTACKABLE for delayed spells
@@ -1112,6 +1273,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
{
return SPELL_MISS_EVADE;
}
+
if( !m_caster->IsFriendlyTo(unit) )
{
// reset damage to 0 if target has Invisibility or Vanish aura (_only_ vanish, not stealth) and isn't visible for caster
@@ -1126,6 +1288,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
// return SPELL_MISS_EVADE;
return SPELL_MISS_MISS; // miss = do not send anything here
}
+
unit->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_HITBYSPELL);
//TODO: This is a hack. But we do not know what types of stealth should be interrupted by CC
if((m_customAttr & SPELL_ATTR_CU_AURA_CC) && unit->IsControlledByPlayer())
@@ -1139,6 +1302,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
{
return SPELL_MISS_EVADE;
}
+
// assisting case, healing and resurrection
if(unit->hasUnitState(UNIT_STAT_ATTACK_PLAYER))
{
@@ -1153,6 +1317,7 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
}
}
}
+
// Get Data Needed for Diminishing Returns, some effects may have multiple auras, so this must be done on spell hit, not aura add
if(m_diminishGroup = GetDiminishingReturnsGroupForSpell(m_spellInfo,m_triggeredByAuraSpell))
{
@@ -1162,10 +1327,12 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
if((type == DRTYPE_PLAYER && (unit->GetTypeId() == TYPEID_PLAYER || ((Creature*)unit)->isPet() || ((Creature*)unit)->isPossessedByPlayer())) || type == DRTYPE_ALL)
unit->IncrDiminishing(m_diminishGroup);
}
+
uint8 aura_effmask = 0;
for (uint8 i = 0; i < 3; ++i)
if (effectMask & (1<<i) && (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || IsAreaAuraEffect(m_spellInfo->Effect[i])))
aura_effmask |= 1<<i;
+
if (aura_effmask)
{
// Select rank for aura with level requirements only in specific cases
@@ -1188,9 +1355,11 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
basePoints[i] = m_currentBasePoints[i];
}
}
+
if(m_originalCaster)
{
Aura *Aur = new Aura(aurSpellInfo, aura_effmask, unit, m_caster, m_originalCaster, basePoints, m_CastItem);
+
if (!Aur->IsAreaAura())
{
// Now Reduce spell duration using data received at spell hit
@@ -1198,15 +1367,19 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
int32 limitduration = GetDiminishingReturnsLimitDuration(m_diminishGroup,aurSpellInfo);
unitTarget->ApplyDiminishingToDuration(m_diminishGroup, duration, m_originalCaster, m_diminishLevel,limitduration);
Aur->setDiminishGroup(m_diminishGroup);
+
duration = m_originalCaster->ModSpellDuration(aurSpellInfo, unit, duration, Aur->IsPositive());
+
//mod duration of channeled aura by spell haste
if (IsChanneledSpell(m_spellInfo))
m_originalCaster->ModSpellCastTime(aurSpellInfo, duration, this);
+
if(duration != Aur->GetAuraMaxDuration())
{
Aur->SetAuraMaxDuration(duration);
Aur->SetAuraDuration(duration);
}
+
// Prayer of Mending (jump animation), we need formal caster instead original for correct animation
if( aurSpellInfo->SpellFamilyName == SPELLFAMILY_PRIEST)
{
@@ -1217,15 +1390,19 @@ SpellMissInfo Spell::DoSpellHitOnUnit(Unit *unit, const uint32 effectMask, bool
// Set aura only when successfully applied
if (unit->AddAura(Aur, false))
m_spellAura = Aur;
+
}
}
+
for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber)
{
if (effectMask & (1<<effectNumber))
HandleEffects(unit,NULL,NULL,effectNumber);
}
+
return SPELL_MISS_NONE;
}
+
void Spell::DoTriggersOnSpellHit(Unit *unit)
{
// Apply additional spell effects to target
@@ -1243,6 +1420,7 @@ void Spell::DoTriggersOnSpellHit(Unit *unit)
else
m_caster->CastSpell(unit,m_preCastSpell, true, m_CastItem);
}
+
// spells with this flag can trigger only if not selfcast (eviscerate for example)
if (m_ChanceTriggerSpells.size() && (!((m_spellInfo->AttributesEx4 & SPELL_ATTR_EX4_CANT_PROC_FROM_SELFCAST) && unit==m_caster)))
{
@@ -1272,6 +1450,7 @@ void Spell::DoTriggersOnSpellHit(Unit *unit)
}
}
}
+
if(m_customAttr & SPELL_ATTR_CU_LINK_HIT)
{
if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(m_spellInfo->Id + SPELL_LINK_HIT))
@@ -1282,20 +1461,25 @@ void Spell::DoTriggersOnSpellHit(Unit *unit)
unit->CastSpell(unit, *i, true, 0, 0, m_caster->GetGUID());
}
}
+
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 = m_caster->GetMap()->GetGameObject(target->targetGUID);
if(!go)
return;
+
for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber)
if (effectMask & (1 << effectNumber))
HandleEffects(NULL, NULL, go, effectNumber);
+
// cast at creature (or GO) quest objectives update at successful cast finished (+channel finished)
// ignore autorepeat/melee casts for speed (not exist quest for spells (hm... )
if(m_originalCaster && m_originalCaster->IsControlledByPlayer() && !IsAutoRepeat() && !IsNextMeleeSwingSpell() && !IsChannelActive() )
@@ -1304,26 +1488,32 @@ void Spell::DoAllEffectOnTarget(GOTargetInfo *target)
p->CastedCreatureOrGO(go->GetEntry(),go->GetGUID(),m_spellInfo->Id);
}
}
+
void Spell::DoAllEffectOnTarget(ItemTargetInfo *target)
{
uint32 effectMask = target->effectMask;
if(!target->item || !effectMask)
return;
+
for(uint32 effectNumber = 0; effectNumber < 3; ++effectNumber)
if (effectMask & (1 << effectNumber))
HandleEffects(NULL, target->item, NULL, effectNumber);
}
+
bool Spell::UpdateChanneledTargetList()
{
// Not need check return true
if (m_needAliveTargetMask == 0)
return true;
+
uint8 needAliveTargetMask = m_needAliveTargetMask;
uint8 needAuraMask = 0;
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (m_spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
needAuraMask |= 1<<i;
+
needAuraMask &= needAliveTargetMask;
+
float range;
if(needAuraMask)
{
@@ -1331,11 +1521,13 @@ bool Spell::UpdateChanneledTargetList()
if(Player * modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
}
+
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
if( ihit->missCondition == SPELL_MISS_NONE && (needAliveTargetMask & ihit->effectMask) )
{
Unit *unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID);
+
if (unit && unit->isAlive())
{
if (needAuraMask & ihit->effectMask)
@@ -1352,13 +1544,16 @@ bool Spell::UpdateChanneledTargetList()
else // aura is dispelled
continue;
}
+
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)
@@ -1372,6 +1567,7 @@ struct ChainHealingOrder : public std::binary_function<const Unit*, const Unit*,
{
return (ChainHealingHash(_Left) < ChainHealingHash(_Right));
}
+
int32 ChainHealingHash(Unit const* Target) const
{
/*if (Target == MainTarget)
@@ -1388,6 +1584,7 @@ struct ChainHealingOrder : public std::binary_function<const Unit*, const Unit*,
return 40000 - Target->GetMaxHealth() + Target->GetHealth();
}
};
+
// Helper for targets nearest to the spell target
// The spell target is always first unless there is a target at _completely_ the same position (unbelievable case)
struct TargetDistanceOrder : public std::binary_function<const Unit, const Unit, bool>
@@ -1400,11 +1597,13 @@ struct TargetDistanceOrder : public std::binary_function<const Unit, const Unit,
return MainTarget->GetDistanceOrder(_Left,_Right);
}
};
+
void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uint32 num, SpellTargets TargetType)
{
Unit *cur = m_targets.getUnitTarget();
if(!cur)
return;
+
// Get spell max affected targets
/*uint32 unMaxTargets = m_spellInfo->MaxAffectedTargets;
Unit::AuraList const& mod = m_caster->GetAurasByType(SPELL_AURA_MOD_MAX_AFFECTED_TARGETS);
@@ -1414,9 +1613,11 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin
continue;
unMaxTargets+=(*m)->GetAmount();
}*/
+
//FIXME: This very like horrible hack and wrong for most spells
if(m_spellInfo->DmgClass != SPELL_DAMAGE_CLASS_MELEE)
max_range += num * CHAIN_SPELL_JUMP_RADIUS;
+
std::list<Unit*> tempUnitMap;
if(TargetType == SPELL_TARGETS_CHAINHEAL)
{
@@ -1428,13 +1629,17 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin
else
SearchAreaTarget(tempUnitMap, max_range, PUSH_CHAIN, TargetType);
tempUnitMap.remove(cur);
+
while(num)
{
TagUnitMap.push_back(cur);
--num;
+
if(tempUnitMap.empty())
break;
+
std::list<Unit*>::iterator next;
+
if(TargetType == SPELL_TARGETS_CHAINHEAL)
{
next = tempUnitMap.begin();
@@ -1450,6 +1655,7 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin
{
tempUnitMap.sort(TargetDistanceOrder(cur));
next = tempUnitMap.begin();
+
if(cur->GetDistance(*next) > CHAIN_SPELL_JUMP_RADIUS)
break;
while(m_spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MELEE
@@ -1462,10 +1668,12 @@ void Spell::SearchChainTarget(std::list<Unit*> &TagUnitMap, float max_range, uin
return;
}
}
+
cur = *next;
tempUnitMap.erase(next);
}
}
+
void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, SpellNotifyPushType type, SpellTargets TargetType, uint32 entry)
{
Position *pos;
@@ -1494,15 +1702,18 @@ void Spell::SearchAreaTarget(std::list<Unit*> &TagUnitMap, float radius, SpellNo
pos = m_caster;
break;
}
+
Trinity::SpellNotifierCreatureAndPlayer notifier(m_caster, TagUnitMap, radius, type, TargetType, pos, entry);
if((m_spellInfo->AttributesEx3 & SPELL_ATTR_EX3_PLAYERS_ONLY)
|| TargetType == SPELL_TARGETS_ENTRY && !entry)
m_caster->GetMap()->VisitWorld(pos->m_positionX, pos->m_positionY, radius, notifier);
else
m_caster->GetMap()->VisitAll(pos->m_positionX, pos->m_positionY, radius, notifier);
+
if(m_customAttr & SPELL_ATTR_CU_EXCLUDE_SELF)
TagUnitMap.remove(m_caster);
}
+
WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType)
{
switch(TargetType)
@@ -1518,8 +1729,10 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType)
else
return SearchNearbyTarget(range, SPELL_TARGETS_ENEMY);
}
+
Creature* creatureScriptTarget = NULL;
GameObject* goScriptTarget = NULL;
+
for(SpellScriptTarget::const_iterator i_spellST = bounds.first; i_spellST != bounds.second; ++i_spellST)
{
switch(i_spellST->second.type)
@@ -1569,6 +1782,7 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType)
break;
}
}
+
if(creatureScriptTarget)
return creatureScriptTarget;
else
@@ -1593,12 +1807,14 @@ WorldObject* Spell::SearchNearbyTarget(float range, SpellTargets TargetType)
}
}
}
+
void Spell::SelectEffectTargets(uint32 i, uint32 cur)
{
SpellNotifyPushType pushType = PUSH_NONE;
Player *modOwner = NULL;
if(m_originalCaster)
modOwner = m_originalCaster->GetSpellModOwner();
+
switch(SpellTargetType[cur])
{
case TARGET_TYPE_UNIT_CASTER:
@@ -1643,6 +1859,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
break;
}
+
case TARGET_TYPE_UNIT_TARGET:
{
Unit *target = m_targets.getUnitTarget();
@@ -1651,6 +1868,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
sLog.outError("SPELL: no unit target for spell ID %u", m_spellInfo->Id);
break;
}
+
switch(cur)
{
case TARGET_UNIT_TARGET_ENEMY:
@@ -1682,10 +1900,12 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
break;
}
+
case TARGET_TYPE_UNIT_NEARBY:
{
WorldObject *target = NULL;
float range;
+
switch(cur)
{
case TARGET_UNIT_NEARBY_ENEMY:
@@ -1706,6 +1926,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY);
break;
}
+
if(!target)
return;
else if(target->GetTypeId() == TYPEID_GAMEOBJECT)
@@ -1713,17 +1934,22 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
else
{
pushType = PUSH_CHAIN;
+
if(m_targets.getUnitTarget() != target)
m_targets.setUnitTarget((Unit*)target);
}
+
break;
}
+
case TARGET_TYPE_AREA_SRC:
pushType = PUSH_SRC_CENTER;
break;
+
case TARGET_TYPE_AREA_DST:
pushType = PUSH_DST_CENTER;
break;
+
case TARGET_TYPE_AREA_CONE:
if(m_customAttr & SPELL_ATTR_CU_CONE_BACK)
pushType = PUSH_IN_BACK;
@@ -1732,6 +1958,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
else
pushType = PUSH_IN_FRONT;
break;
+
case TARGET_TYPE_DEST_CASTER: //4+8+2
{
if(cur == TARGET_SRC_CASTER)
@@ -1744,13 +1971,16 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
m_targets.setDst(m_caster);
break;
}
+
float angle, dist;
+
float objSize = m_caster->GetObjectSize();
dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if(dist < objSize)
dist = objSize;
else if(cur == TARGET_DEST_CASTER_RANDOM)
dist = objSize + (dist - objSize) * rand_norm();
+
switch(cur)
{
case TARGET_DEST_CASTER_FRONT_LEFT: angle = -M_PI/4; break;
@@ -1765,11 +1995,13 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
case TARGET_DEST_CASTER_LEFT: angle = -M_PI/2; break;
default: angle = rand_norm()*2*M_PI; break;
}
+
Position pos;
m_caster->GetNearPosition(pos, dist, angle);
m_targets.setDst(&pos); // also flag
break;
}
+
case TARGET_TYPE_DEST_TARGET: //2+8+2
{
Unit *target = m_targets.getUnitTarget();
@@ -1778,18 +2010,22 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
sLog.outError("SPELL: no unit target for spell ID %u\n", m_spellInfo->Id);
break;
}
+
if(cur == TARGET_DST_TARGET_ENEMY || cur == TARGET_DEST_TARGET_ANY)
{
m_targets.setDst(target);
break;
}
+
float angle, dist;
+
float objSize = target->GetObjectSize();
dist = target->GetSpellRadiusForTarget(target, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if(dist < objSize)
dist = objSize;
else if(cur == TARGET_DEST_CASTER_RANDOM)
dist = objSize + (dist - objSize) * rand_norm();
+
switch(cur)
{
case TARGET_DEST_TARGET_FRONT: angle = 0.0f; break;
@@ -1802,11 +2038,13 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
case TARGET_DEST_TARGET_FRONT_RIGHT:angle = M_PI/4; break;
default: angle = rand_norm()*2*M_PI; break;
}
+
Position pos;
target->GetNearPosition(pos, dist, angle);
m_targets.setDst(&pos);
break;
}
+
case TARGET_TYPE_DEST_DEST: //5+8+1
{
if(!m_targets.HasDst())
@@ -1814,6 +2052,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
sLog.outError("SPELL: no destination for spell ID %u\n", m_spellInfo->Id);
break;
}
+
float angle;
switch(cur)
{
@@ -1835,14 +2074,17 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
case TARGET_DEST_DEST_FRONT_RIGHT:angle = M_PI/4; break;
default: angle = rand_norm()*2*M_PI; break;
}
+
float dist;
dist = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if (cur == TARGET_DEST_DEST_RANDOM)
dist *= rand_norm();
+
// must has dst, no need to set flag
m_caster->MovePosition(m_targets.m_dstPos, dist, angle);
break;
}
+
case TARGET_TYPE_DEST_SPECIAL:
{
switch(cur)
@@ -1875,6 +2117,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
{
float range = GetSpellMaxRange(m_spellInfo, IsPositiveSpell(m_spellInfo->Id));
if(modOwner) modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, range, this);
+
if(WorldObject *target = SearchNearbyTarget(range, SPELL_TARGETS_ENTRY))
m_targets.setDst(target);
break;
@@ -1882,6 +2125,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
break;
}
+
case TARGET_TYPE_CHANNEL:
{
if(!m_originalCaster || !m_originalCaster->GetCurrentSpell(CURRENT_CHANNELED_SPELL))
@@ -1889,6 +2133,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
sLog.outError( "SPELL: no current channeled spell for spell ID %u", m_spellInfo->Id );
break;
}
+
switch(cur)
{
case TARGET_UNIT_CHANNEL:
@@ -1909,6 +2154,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
break;
}
+
default:
{
switch(cur)
@@ -1931,6 +2177,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
break;
}
}
+
if(pushType == PUSH_CHAIN) // Chain
{
Unit *target = m_targets.getUnitTarget();
@@ -1939,17 +2186,21 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
sLog.outError("SPELL: no chain unit target for spell ID %u", m_spellInfo->Id);
return;
}
+
//Chain: 2, 6, 22, 25, 45, 77
uint32 maxTargets = m_spellInfo->EffectChainTarget[i];
if(modOwner)
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_JUMP_TARGETS, maxTargets, this);
+
if(maxTargets > 1)
{
//otherwise, this multiplier is used for something else
m_damageMultipliers[i] = 1.0f;
m_applyMultiplierMask |= 1 << i;
+
float range;
std::list<Unit*> unitList;
+
switch(cur)
{
case TARGET_UNIT_NEARBY_ENEMY:
@@ -1968,6 +2219,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
SearchChainTarget(unitList, range, maxTargets, SPELL_TARGETS_CHAINHEAL);
break;
}
+
for(std::list<Unit*>::iterator itr = unitList.begin(); itr != unitList.end(); ++itr)
AddUnitTarget(*itr, i);
}
@@ -1979,6 +2231,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
// Dummy, just for client
if(EffectTargetType[m_spellInfo->Effect[i]] != SPELL_REQUIRE_UNIT)
return;
+
float radius;
SpellTargets targetType;
switch(cur)
@@ -2008,9 +2261,11 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
targetType = SPELL_TARGETS_NONE;
break;
}
+
if(modOwner)
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius, this);
radius *= m_spellValue->RadiusMod;
+
std::list<Unit*> unitList;
if(targetType == SPELL_TARGETS_ENTRY)
{
@@ -2044,14 +2299,16 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
// Search for ghoul if our ghoul or dead body not valid unit target
if (!(m_targets.getUnitTarget() && (m_targets.getUnitTarget()->GetEntry() == 26125 && m_targets.getUnitTarget()->GetOwnerGUID() == m_caster->GetGUID()
|| (m_targets.getUnitTarget()->getDeathState() == CORPSE
- && m_targets.getUnitTarget()->GetDisplayId() == m_targets.getUnitTarget()->GetNativeDisplayId()
+ && m_targets.getUnitTarget()->GetDisplayId() == m_targets.getUnitTarget()->GetNativeDisplayId()
&& m_targets.getUnitTarget()->GetTypeId()== TYPEID_UNIT
&& !((Creature*)m_targets.getUnitTarget())->isDeadByDefault()
&& !(m_targets.getUnitTarget()->GetCreatureTypeMask() & CREATURE_TYPEMASK_MECHANICAL_OR_ELEMENTAL))
&& m_targets.getUnitTarget()->GetDisplayId() == m_targets.getUnitTarget()->GetNativeDisplayId())))
{
CleanupTargetList();
+
WorldObject* result = FindCorpseUsing <Trinity::ExplodeCorpseObjectCheck> ();
+
if(result)
{
switch(result->GetTypeId())
@@ -2071,8 +2328,10 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
}
break;
+
default:
sLog.outDebug("Spell (ID: %u) (caster Entry: %u) does not have record in `spell_script_target`", m_spellInfo->Id, m_caster->GetEntry());
+
if(m_spellInfo->Effect[i] == SPELL_EFFECT_TELEPORT_UNITS)
SearchAreaTarget(unitList, radius, pushType, SPELL_TARGETS_ENTRY, 0);
else if(IsPositiveEffect(m_spellInfo->Id, i))
@@ -2091,7 +2350,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
else if (i_spellST->second.type == SPELL_TARGET_TYPE_CONTROLLED)
{
for(Unit::ControlList::iterator itr = m_caster->m_Controlled.begin(); itr != m_caster->m_Controlled.end(); ++itr)
- if ((*itr)->GetEntry() == i_spellST->second.targetEntry &&
+ if ((*itr)->GetEntry() == i_spellST->second.targetEntry &&
/*(*itr)->IsWithinDistInMap(m_caster, radius)*/ (*itr)->IsInMap(m_caster)) // For 60243 and 52173 need skip radius check or use range (no radius entry for effect)
unitList.push_back(*itr);
}
@@ -2126,6 +2385,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
else
break;
}
+
Trinity::GameObjectInRangeCheck check(x, y, z, radius + 50);
std::list<GameObject*> goList;
Trinity::GameObjectListSearcher<Trinity::GameObjectInRangeCheck> searcher(m_caster, goList, check);
@@ -2147,12 +2407,14 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
{
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() &&
@@ -2168,6 +2430,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
}
}
+
if(!unitList.empty())
{
if(uint32 maxTargets = m_spellValue->MaxAffectedTargets)
@@ -2176,6 +2439,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j)
if((*j)->isAffectedOnSpell(m_spellInfo))
maxTargets += (*j)->GetAmount();
+
if(m_spellInfo->Id == 5246) //Intimidating Shout
unitList.remove(m_targets.getUnitTarget());
Trinity::RandomResizeList(unitList, m_spellValue->MaxAffectedTargets);
@@ -2203,6 +2467,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
manaUsers.push(WTarget);
}
}
+
unitList.clear();
while(!manaUsers.empty() && unitList.size()<10)
{
@@ -2220,6 +2485,7 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
PrioritizeHealthUnitWraper WTarget(*itr);
healedMembers.push(WTarget);
}
+
unitList.clear();
while(!healedMembers.empty() && unitList.size()<1)
{
@@ -2242,10 +2508,13 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
healedMembers.push(WTarget);
}
}
+
unitList.clear();
uint32 maxsize = 5;
+
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x04000000)
maxsize += m_caster->HasAura(62970) ? 1 : 0;
+
while(!healedMembers.empty() && unitList.size()<maxsize)
{
unitList.push_back(healedMembers.top().getUnit());
@@ -2286,13 +2555,16 @@ void Spell::SelectEffectTargets(uint32 i, uint32 cur)
}
}
}
+
void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura)
{
if(m_CastItem)
m_castItemGUID = m_CastItem->GetGUID();
else
m_castItemGUID = 0;
+
m_targets = *targets;
+
if(!m_targets.getUnitTargetGUID() && m_spellInfo->Targets & TARGET_FLAG_UNIT)
{
Unit *target = NULL;
@@ -2300,6 +2572,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
target = m_caster->getVictim();
else
target = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
+
if(target && IsValidSingleTargetSpell(target))
m_targets.setUnitTarget(target);
else
@@ -2309,6 +2582,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
return;
}
}
+
// Fill aura scaling information
if (m_caster->IsControlledByPlayer() && !IsPassiveSpell(m_spellInfo->Id) && m_spellInfo->spellLevel && !IsChanneledSpell(m_spellInfo) && !m_IsTriggeredSpell)
{
@@ -2329,12 +2603,16 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
}
}
}
+
m_spellState = SPELL_STATE_PREPARING;
+
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, true) && m_cast_count)
{
@@ -2342,6 +2620,7 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
finish(false);
return;
}
+
if(m_caster->GetTypeId() == TYPEID_PLAYER)
{
if(objmgr.IsPlayerSpellDisabled(m_spellInfo->Id))
@@ -2368,12 +2647,14 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
return;
}
}
+
if (m_caster->GetTypeId()==TYPEID_PLAYER)
((Player*)m_caster)->SetSpellModTakingSpell(this, true);
// Fill cost data (not use power for item casts
m_powerCost = m_CastItem ? 0 : CalculatePowerCost(m_spellInfo, m_caster, m_spellSchoolMask);
if (m_caster->GetTypeId()==TYPEID_PLAYER)
((Player*)m_caster)->SetSpellModTakingSpell(this, false);
+
SpellCastResult result = CheckCast(true);
if(result != SPELL_CAST_OK && !IsAutoRepeat()) //always cast autorepeat dummy for triggering
{
@@ -2383,24 +2664,31 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
triggeredByAura->GetParentAura()->SetAuraDuration(0);
}
SendCastResult(result);
+
finish(false);
return;
}
+
// Prepare data for triggers
prepareDataForTriggerSystem(triggeredByAura);
+
// Set combo point requirement
if (m_IsTriggeredSpell || m_CastItem || !m_caster->m_movedPlayer)
m_needComboPoints = false;
+
// calculate cast time (calculated after first CheckCast check to prevent charge counting for first CheckCast fail)
m_casttime = GetSpellCastTime(m_spellInfo, this);
//m_caster->ModSpellCastTime(m_spellInfo, m_casttime, this);
+
// set timer base at cast time
ReSetTimer();
+
sLog.outDebug("Spell::prepare: spell id %u source %u caster %d triggered %u mask %u", m_spellInfo->Id, m_caster->GetEntry(), m_originalCaster ? m_originalCaster->GetEntry() : -1, m_IsTriggeredSpell ? 1 : 0, m_targets.m_targetMask);
//if(m_targets.getUnitTarget())
// sLog.outError("Spell::prepare: unit target %u", m_targets.getUnitTarget()->GetEntry());
//if(m_targets.HasDst())
// sLog.outError("Spell::prepare: pos target %f %f %f", m_targets.m_dstPos.m_positionX, m_targets.m_dstPos.m_positionY, m_targets.m_dstPos.m_positionZ);
+
//Containers for channeled spells have to be set
//TODO:Apply this to all casted spells if needed
// Why check duration? 29350: channelled triggers channelled
@@ -2422,23 +2710,29 @@ void Spell::prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura
}
}
}
+
m_caster->SetCurrentCastedSpell( this );
SendSpellStart();
+
if(!m_casttime && !m_spellInfo->StartRecoveryTime
&& !m_castItemGUID //item: first cast may destroy item and second cast causes crash
&& GetCurrentContainer() == CURRENT_GENERIC_SPELL)
cast(true);
}
}
+
void Spell::cancel()
{
if(m_spellState == SPELL_STATE_FINISHED)
return;
+
SetReferencedFromCurrent(false);
if(m_selfContainer && *m_selfContainer == this)
*m_selfContainer = NULL;
+
uint32 oldState = m_spellState;
m_spellState = SPELL_STATE_FINISHED;
+
m_autoRepeat = false;
switch (oldState)
{
@@ -2448,6 +2742,7 @@ void Spell::cancel()
SendInterrupted(0);
SendCastResult(SPELL_FAILED_INTERRUPTED);
} break;
+
case SPELL_STATE_CASTING:
{
for(std::list<TargetInfo>::iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
@@ -2455,32 +2750,41 @@ void Spell::cancel()
if(Unit* unit = m_caster->GetGUID() == ihit->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, ihit->targetGUID))
if(unit->isAlive())
unit->RemoveAurasDueToSpell(m_spellInfo->Id, m_originalCasterGUID, AURA_REMOVE_BY_CANCEL);
+
SendChannelUpdate(0);
SendInterrupted(0);
SendCastResult(SPELL_FAILED_INTERRUPTED);
+
// spell is canceled-take mods and clear list
if (m_caster->GetTypeId() == TYPEID_PLAYER)
((Player*)m_caster)->RemoveSpellMods(this);
+
m_appliedMods.clear();
} break;
+
default:
{
} break;
}
+
m_caster->RemoveDynObject(m_spellInfo->Id);
m_caster->RemoveGameObject(m_spellInfo->Id,true);
+
//set state back so finish will be processed
m_spellState = oldState;
+
finish(false);
}
+
void Spell::cast(bool skipCheck)
{
// update pointers base at GUIDs to prevent access to non-existed already object
UpdatePointers();
+
if(Unit *target = m_targets.getUnitTarget())
{
// three check: prepare, cast (m_casttime > 0), hit (delayed)
- if(m_casttime && target->isAlive()
+ if(m_casttime && target->isAlive()
&& (target->m_invisibilityMask || m_caster->m_invisibilityMask
|| target->GetVisibility() == VISIBILITY_GROUP_STEALTH)
&& !target->IsFriendlyTo(m_caster) && !m_caster->canSeeOrDetect(target, true))
@@ -2500,9 +2804,12 @@ void Spell::cast(bool skipCheck)
return;
}
}
+
SetExecutedCurrently(true);
+
if(m_caster->GetTypeId() != TYPEID_PLAYER && m_targets.getUnitTarget() && m_targets.getUnitTarget() != m_caster)
m_caster->SetInFront(m_targets.getUnitTarget());
+
// Should this be done for original caster?
if (m_caster->GetTypeId()==TYPEID_PLAYER)
{
@@ -2510,6 +2817,7 @@ void Spell::cast(bool skipCheck)
// if not successfully casted, will be remove in finish(false)
((Player*)m_caster)->SetSpellModTakingSpell(this, true);
}
+
// triggered cast called from Spell::prepare where it was already checked
if(!m_IsTriggeredSpell || !skipCheck)
{
@@ -2531,7 +2839,9 @@ void Spell::cast(bool skipCheck)
return;
}
}
+
SelectSpellTargets();
+
// Spell may be finished after target map check
if(m_spellState == SPELL_STATE_FINISHED)
{
@@ -2548,6 +2858,7 @@ void Spell::cast(bool skipCheck)
SetExecutedCurrently(false);
return;
}
+
if(m_spellInfo->SpellFamilyName)
{
if (m_spellInfo->excludeCasterAuraSpell && !IsPositiveSpell(m_spellInfo->excludeCasterAuraSpell))
@@ -2567,18 +2878,22 @@ void Spell::cast(bool skipCheck)
// 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();
+
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
if (!m_IsTriggeredSpell && m_CastItem)
((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_ITEM, m_CastItem->GetEntry());
+
((Player*)m_caster)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_CAST_SPELL, m_spellInfo->Id);
}
+
if(!m_IsTriggeredSpell)
{
// Powers have to be taken before SendSpellGo
TakePower();
TakeReagents(); // we must remove reagents before HandleEffects to allow place crafted item in same slot
}
+
// are there any spells need to be triggered after hit?
// handle SPELL_AURA_ADD_TARGET_TRIGGER auras
Unit::AuraEffectList const& targetTriggers = m_caster->GetAurasByType(SPELL_AURA_ADD_TARGET_TRIGGER);
@@ -2595,12 +2910,15 @@ void Spell::cast(bool skipCheck)
m_ChanceTriggerSpells.push_back(std::make_pair(spellInfo, chance * (*i)->GetParentAura()->GetStackAmount()));
}
}
+
if(m_customAttr & SPELL_ATTR_CU_DIRECT_DAMAGE)
CalculateDamageDoneForAllTargets();
+
// CAST SPELL
SendSpellCooldown();
//SendCastResult(castResult);
SendSpellGo(); // we must send smsg_spell_go packet before m_castItem delete in TakeCastItem()...
+
if(m_customAttr & SPELL_ATTR_CU_CHARGE)
{
for(uint32 i = 0; i < 3; ++i)
@@ -2617,16 +2935,19 @@ void Spell::cast(bool skipCheck)
}
}
}
+
// Okay, everything is prepared. Now we need to distinguish between immediate and evented delayed spells
if (m_spellInfo->speed > 0.0f && !IsChanneledSpell(m_spellInfo))
{
// 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);
+
if(m_caster->hasUnitState(UNIT_STAT_CASTING) && !m_caster->IsNonMeleeSpellCasted(false, false, true))
m_caster->clearUnitState(UNIT_STAT_CASTING);
}
@@ -2635,6 +2956,7 @@ void Spell::cast(bool skipCheck)
// Immediate spell, no big deal
handle_immediate();
}
+
if(m_customAttr & SPELL_ATTR_CU_LINK_CAST)
{
if(const std::vector<int32> *spell_triggered = spellmgr.GetSpellLinked(m_spellInfo->Id))
@@ -2644,10 +2966,13 @@ void Spell::cast(bool skipCheck)
else
m_caster->CastSpell(m_targets.getUnitTarget() ? m_targets.getUnitTarget() : m_caster, *i, true);
}
+
if (m_caster->GetTypeId()==TYPEID_PLAYER)
((Player*)m_caster)->SetSpellModTakingSpell(this, false);
+
SetExecutedCurrently(false);
}
+
void Spell::handle_immediate()
{
// start channeling if applicable
@@ -2666,31 +2991,43 @@ void Spell::handle_immediate()
SendChannelStart(duration);
}
}
+
// process immediate effects (items, ground, etc.) also initialize some variables
_handle_immediate_phase();
+
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
DoAllEffectOnTarget(&(*ihit));
+
for(std::list<GOTargetInfo>::iterator ihit= m_UniqueGOTargetInfo.begin();ihit != m_UniqueGOTargetInfo.end();++ihit)
DoAllEffectOnTarget(&(*ihit));
+
// spell is finished, perform some last features of the spell here
_handle_finish_phase();
+
// Remove used for cast item if need (it can be already NULL after TakeReagents call
TakeCastItem();
+
if(m_spellState != SPELL_STATE_CASTING)
finish(true); // successfully finish spell cast (not last in case autorepeat or channel spell)
}
+
uint64 Spell::handle_delayed(uint64 t_offset)
{
UpdatePointers();
+
if (m_caster->GetTypeId()==TYPEID_PLAYER)
((Player*)m_caster)->SetSpellModTakingSpell(this, true);
+
uint64 next_time = 0;
+
if (!m_immediateHandled)
{
_handle_immediate_phase();
m_immediateHandled = true;
}
+
bool single_missile = (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION);
+
// now recheck units targeting correctness (need before any effects apply to prevent adding immunity at first effect not allow apply second spell effect and similar cases)
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end();++ihit)
{
@@ -2702,6 +3039,7 @@ uint64 Spell::handle_delayed(uint64 t_offset)
next_time = ihit->timeDelay;
}
}
+
// now recheck gameobject targeting correctness
for(std::list<GOTargetInfo>::iterator ighit= m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end();++ighit)
{
@@ -2713,14 +3051,18 @@ uint64 Spell::handle_delayed(uint64 t_offset)
next_time = ighit->timeDelay;
}
}
+
if (m_caster->GetTypeId()==TYPEID_PLAYER)
((Player*)m_caster)->SetSpellModTakingSpell(this, false);
+
// 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;
}
@@ -2730,31 +3072,38 @@ uint64 Spell::handle_delayed(uint64 t_offset)
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;
}
+
// initialize Diminishing Returns Data
m_diminishLevel = DIMINISHING_LEVEL_1;
m_diminishGroup = DIMINISHING_NONE;
+
// process items
for(std::list<ItemTargetInfo>::iterator ihit= m_UniqueItemInfo.begin();ihit != m_UniqueItemInfo.end();++ihit)
DoAllEffectOnTarget(&(*ihit));
+
if(!m_originalCaster)
return;
uint8 oldEffMask = m_effectMask;
@@ -2807,6 +3156,7 @@ void Spell::_handle_immediate_phase()
m_originalCaster->ProcDamageAndSpell(0, procAttacker, 0, m_procEx | PROC_EX_NORMAL_HIT, 0, BASE_ATTACK, m_spellInfo, m_triggeredByAuraSpell);
}
}
+
void Spell::_handle_finish_phase()
{
if(m_caster->m_movedPlayer)
@@ -2814,19 +3164,24 @@ void Spell::_handle_finish_phase()
// Take for real after all targets are processed
if (m_needComboPoints)
m_caster->m_movedPlayer->ClearComboPoints();
+
// Real add combo points from effects
if (m_comboPointGain)
m_caster->m_movedPlayer->GainSpellComboPoints(m_comboPointGain);
}
+
// spell log
if(m_needSpellLog)
SendLogExecute();
}
+
void Spell::SendSpellCooldown()
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player* _player = (Player*)m_caster;
+
// mana/health/etc potions, disabled by client (until combat out as declarate)
if (m_CastItem && m_CastItem->IsPotion())
{
@@ -2834,21 +3189,26 @@ void Spell::SendSpellCooldown()
_player->SetLastPotionId(m_CastItem->GetEntry());
return;
}
+
// have infinity cooldown but set at aura apply // do not set cooldown for triggered spells (needed by reincarnation)
if(m_spellInfo->Attributes & SPELL_ATTR_DISABLED_WHILE_ACTIVE || m_IsTriggeredSpell)
return;
+
_player->AddSpellAndCategoryCooldowns(m_spellInfo,m_CastItem ? m_CastItem->GetEntry() : 0, this);
}
+
void Spell::update(uint32 difftime)
{
// update pointers based at it's GUIDs
UpdatePointers();
+
if(m_targets.getUnitTargetGUID() && !m_targets.getUnitTarget())
{
sLog.outDebug("Spell %u is cancelled due to removal of target.", m_spellInfo->Id);
cancel();
return;
}
+
// check if the player caster has moved before the spell finished
if ((m_caster->GetTypeId() == TYPEID_PLAYER && m_timer != 0) &&
m_caster->isMoving() && (m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_MOVEMENT) &&
@@ -2858,6 +3218,7 @@ void Spell::update(uint32 difftime)
if(!IsNextMeleeSwingSpell() && !IsAutoRepeat() && !m_IsTriggeredSpell)
cancel();
}
+
switch(m_spellState)
{
case SPELL_STATE_PREPARING:
@@ -2869,6 +3230,7 @@ void Spell::update(uint32 difftime)
else
m_timer -= difftime;
}
+
if(m_timer == 0 && !IsNextMeleeSwingSpell() && !IsAutoRepeat())
cast(m_spellInfo->CastingTimeIndex == 1);
} break;
@@ -2883,14 +3245,17 @@ void Spell::update(uint32 difftime)
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... )
@@ -2903,21 +3268,27 @@ void Spell::update(uint32 difftime)
TargetInfo* target = &*ihit;
if(!IS_CRE_OR_VEH_GUID(target->targetGUID))
continue;
+
Unit* unit = m_caster->GetGUID() == target->targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target->targetGUID);
if (unit == NULL)
continue;
+
p->CastedCreatureOrGO(unit->GetEntry(), unit->GetGUID(), m_spellInfo->Id);
}
+
for(std::list<GOTargetInfo>::iterator ihit = m_UniqueGOTargetInfo.begin(); ihit != m_UniqueGOTargetInfo.end(); ++ihit)
{
GOTargetInfo* target = &*ihit;
+
GameObject* go = m_caster->GetMap()->GetGameObject(target->targetGUID);
if(!go)
continue;
+
p->CastedCreatureOrGO(go->GetEntry(), go->GetGUID(), m_spellInfo->Id);
}
}
}
+
finish();
}
} break;
@@ -2926,17 +3297,22 @@ void Spell::update(uint32 difftime)
}break;
}
}
+
void Spell::finish(bool ok)
{
if(!m_caster)
return;
+
if(m_spellState == SPELL_STATE_FINISHED)
return;
m_spellState = SPELL_STATE_FINISHED;
+
if(IsChanneledSpell(m_spellInfo))
m_caster->UpdateInterruptMask();
+
if(m_caster->hasUnitState(UNIT_STAT_CASTING) && !m_caster->IsNonMeleeSpellCasted(false, false, true))
m_caster->clearUnitState(UNIT_STAT_CASTING);
+
// Unsummon summon as possessed creatures on spell cancel
if(IsChanneledSpell(m_spellInfo) && m_caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -2946,8 +3322,10 @@ void Spell::finish(bool ok)
&& charm->GetUInt32Value(UNIT_CREATED_BY_SPELL) == m_spellInfo->Id)
((Puppet*)charm)->UnSummon();
}
+
if(!ok)
return;
+
if (m_caster->GetTypeId()==TYPEID_UNIT && ((Creature*)m_caster)->isSummon())
{
// Unsummon statue
@@ -2960,12 +3338,15 @@ void Spell::finish(bool ok)
return;
}
}
+
// Okay to remove extra attacks
if(IsSpellHaveEffect(m_spellInfo, SPELL_EFFECT_ADD_EXTRA_ATTACKS))
m_caster->m_extraAttacks = 0;
+
// Heal caster for all health leech from all targets
if (m_healthLeech)
m_caster->DealHeal(m_caster, uint32(m_healthLeech), m_spellInfo);
+
if (IsMeleeAttackResetSpell())
{
m_caster->resetAttackTimer(BASE_ATTACK);
@@ -2974,19 +3355,23 @@ void Spell::finish(bool ok)
if(!(m_spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_RESET_AUTOSHOT))
m_caster->resetAttackTimer(RANGED_ATTACK);
}
+
// potions disabled by client, send event "not in combat" if need
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
if (!m_triggeredByAuraSpell)
((Player*)m_caster)->UpdatePotionCooldown(this);
+
// triggered spell pointer can be not set in some cases
// this is needed for proper apply of triggered spell mods
((Player*)m_caster)->SetSpellModTakingSpell(this, true);
}
+
// call triggered spell only at successful cast (after clear combo points -> for add some if need)
// I assume what he means is that some triggered spells may add combo points
if(!m_TriggerSpells.empty())
TriggerSpell();
+
// Take mods after trigger spell (needed for 14177 to affect 48664)
// mods are taken only on succesfull cast and independantly from targets of the spell
if (m_caster->GetTypeId() == TYPEID_PLAYER)
@@ -2994,24 +3379,31 @@ void Spell::finish(bool ok)
((Player*)m_caster)->RemoveSpellMods(this);
((Player*)m_caster)->SetSpellModTakingSpell(this, false);
}
+
// Stop Attack for some spells
if( m_spellInfo->Attributes & SPELL_ATTR_STOP_ATTACK_TARGET )
m_caster->AttackStop();
}
+
void Spell::SendCastResult(SpellCastResult result)
{
if(result == SPELL_CAST_OK)
return;
+
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
if(((Player*)m_caster)->GetSession()->PlayerLoading()) // don't send cast results at loading time
return;
+
SendCastResult((Player*)m_caster,m_spellInfo,m_cast_count,result);
}
+
void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result)
{
if(result == SPELL_CAST_OK)
return;
+
WorldPacket data(SMSG_CAST_FAILED, (4+1+1));
data << uint8(cast_count); // single cast or multi 2.3 (0/1)
data << uint32(spellInfo->Id);
@@ -3063,11 +3455,14 @@ void Spell::SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 ca
}
caster->GetSession()->SendPacket(&data);
}
+
void Spell::SendSpellStart()
{
if(!IsNeedSendToClient())
return;
+
//sLog.outDebug("Sending SMSG_SPELL_START id=%u", m_spellInfo->Id);
+
uint32 castFlags = CAST_FLAG_UNKNOWN1;
if(m_spellInfo->Attributes & SPELL_ATTR_REQ_AMMO)
castFlags |= CAST_FLAG_AMMO;
@@ -3075,60 +3470,80 @@ void Spell::SendSpellStart()
(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isPet()))
&& m_spellInfo->powerType != POWER_HEALTH )
castFlags |= CAST_FLAG_POWER_LEFT_SELF;
+
if(m_spellInfo->runeCostID && m_spellInfo->powerType == POWER_RUNE)
castFlags |= CAST_FLAG_UNKNOWN10;
+
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 << uint8(m_cast_count); // pending spell cast?
data << uint32(m_spellInfo->Id); // spellId
data << uint32(castFlags); // cast flags
data << uint32(m_timer); // delay?
+
m_targets.write(&data);
+
if(castFlags & CAST_FLAG_POWER_LEFT_SELF)
data << uint32(m_caster->GetPower((Powers)m_spellInfo->powerType));
+
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);
+
uint32 castFlags = CAST_FLAG_UNKNOWN3;
+
// triggered spells with spell visual != 0
if((m_IsTriggeredSpell && !IsAutoRepeatRangedSpell(m_spellInfo)) || m_triggeredByAuraSpell)
- castFlags |= CAST_FLAG_UNKNOWN0;
+ castFlags |= CAST_FLAG_UNKNOWN0;
+
if(m_spellInfo->Attributes & SPELL_ATTR_REQ_AMMO)
castFlags |= CAST_FLAG_AMMO; // arrows/bullets visual
if ((m_caster->GetTypeId() == TYPEID_PLAYER ||
(m_caster->GetTypeId() == TYPEID_UNIT && ((Creature*)m_caster)->isPet()))
&& m_spellInfo->powerType != POWER_HEALTH )
castFlags |= CAST_FLAG_POWER_LEFT_SELF; // should only be sent to self, but the current messaging doesn't make that possible
+
if((m_caster->GetTypeId() == TYPEID_PLAYER) && (m_caster->getClass() == CLASS_DEATH_KNIGHT) && m_spellInfo->runeCostID && m_spellInfo->powerType == POWER_RUNE)
{
castFlags |= CAST_FLAG_UNKNOWN10; // same as in SMSG_SPELL_START
castFlags |= CAST_FLAG_UNKNOWN7; // rune cooldowns list
}
+
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 << uint8(m_cast_count); // pending spell cast?
data << uint32(m_spellInfo->Id); // spellId
data << uint32(castFlags); // cast flags
data << uint32(getMSTime()); // timestamp
+
WriteSpellGoTargets(&data);
+
m_targets.write(&data);
+
if(castFlags & CAST_FLAG_POWER_LEFT_SELF)
data << uint32(m_caster->GetPower((Powers)m_spellInfo->powerType));
+
if ( castFlags & CAST_FLAG_UNKNOWN7 ) // rune cooldowns list
{
uint8 v1 = m_runesState;
@@ -3143,28 +3558,35 @@ void Spell::SendSpellGo()
data << uint8(0); // some unknown byte (time?)
}
}
+
if ( castFlags & CAST_FLAG_UNKNOWN4 ) // unknown wotlk
{
data << float(0);
data << uint32(0);
}
+
if ( castFlags & CAST_FLAG_AMMO )
WriteAmmoToPacket(&data);
+
if ( castFlags & CAST_FLAG_UNKNOWN5 ) // unknown wotlk
{
data << uint32(0);
data << uint32(0);
}
+
if ( m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION )
{
data << uint8(0);
}
+
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 );
@@ -3219,6 +3641,7 @@ void Spell::WriteAmmoToPacket( WorldPacket * data )
ammoInventoryType = INVTYPE_AMMO;
break;
}
+
if(ammoDisplayID)
break;
}
@@ -3226,9 +3649,11 @@ void Spell::WriteAmmoToPacket( WorldPacket * data )
}
}
}
+
*data << uint32(ammoDisplayID);
*data << uint32(ammoInventoryType);
}
+
void Spell::WriteSpellGoTargets( WorldPacket * data )
{
// This function also fill data for channeled spells:
@@ -3248,6 +3673,7 @@ void Spell::WriteSpellGoTargets( WorldPacket * data )
else
++miss;
}
+
*data << (uint8)hit;
for(std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
@@ -3257,8 +3683,10 @@ void Spell::WriteSpellGoTargets( WorldPacket * data )
m_needAliveTargetMask |=ihit->effectMask;
}
}
+
for(std::list<GOTargetInfo>::const_iterator ighit = m_UniqueGOTargetInfo.begin(); ighit != m_UniqueGOTargetInfo.end(); ++ighit)
*data << uint64(ighit->targetGUID); // Always hits
+
*data << (uint8)miss;
for(std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
@@ -3274,14 +3702,18 @@ void Spell::WriteSpellGoTargets( WorldPacket * data )
if(!IsChanneledSpell(m_spellInfo))
m_needAliveTargetMask = 0;
}
+
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?)
@@ -3308,7 +3740,7 @@ void Spell::SendLogExecute()
data.append(unit->GetPackGUID());
else
data << uint8(0);
- data << uint32(m_caster->m_extraAttacks);
+ data << uint32(m_caster->m_extraAttacks);
break;
case SPELL_EFFECT_INTERRUPT_CAST:
if(Unit *unit = m_targets.getUnitTarget())
@@ -3375,8 +3807,10 @@ void Spell::SendLogExecute()
}
}
}
+
m_caster->SendMessageToSet(&data, true);
}
+
void Spell::SendInterrupted(uint8 result)
{
WorldPacket data(SMSG_SPELL_FAILURE, (8+4+1));
@@ -3385,6 +3819,7 @@ void Spell::SendInterrupted(uint8 result)
data << uint32(m_spellInfo->Id);
data << uint8(result);
m_caster->SendMessageToSet(&data, true);
+
data.Initialize(SMSG_SPELL_FAILED_OTHER, (8+4));
data.append(m_caster->GetPackGUID());
data << uint8(m_cast_count);
@@ -3392,6 +3827,7 @@ void Spell::SendInterrupted(uint8 result)
data << uint8(result);
m_caster->SendMessageToSet(&data, true);
}
+
void Spell::SendChannelUpdate(uint32 time)
{
if(time == 0)
@@ -3399,16 +3835,21 @@ void Spell::SendChannelUpdate(uint32 time)
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 << uint32(time);
+
m_caster->SendMessageToSet(&data, true);
}
+
void Spell::SendChannelStart(uint32 duration)
{
WorldObject* target = NULL;
+
// select first not resisted target from target list for _0_ effect
if(!m_UniqueTargetInfo.empty())
{
@@ -3432,46 +3873,60 @@ void Spell::SendChannelStart(uint32 duration)
}
}
}
+
WorldPacket data( MSG_CHANNEL_START, (8+4+4) );
data.append(m_caster->GetPackGUID());
data << uint32(m_spellInfo->Id);
data << uint32(duration);
+
m_caster->SendMessageToSet(&data, true);
+
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)
{
// Both players and NPCs can resurrect using spells - have a look at creature 28487 for example
// However, the packet structure differs slightly
+
const char* sentName = m_caster->GetTypeId() == TYPEID_PLAYER ? "" : m_caster->GetNameForLocaleIdx(target->GetSession()->GetSessionDbLocaleIndex());
+
WorldPacket data(SMSG_RESURRECT_REQUEST, (8+4+strlen(sentName)+1+1+1));
data << uint64(m_caster->GetGUID());
data << uint32(strlen(sentName) + 1);
+
data << sentName;
data << uint8(0);
+
data << uint8(m_caster->GetTypeId() == TYPEID_PLAYER ? 0 : 1);
target->GetSession()->SendPacket(&data);
}
+
void Spell::SendPlaySpellVisual(uint32 SpellID)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
WorldPacket data(SMSG_PLAY_SPELL_VISUAL, 8 + 4);
data << uint64(m_caster->GetGUID());
data << uint32(SpellID); // spell visual id?
((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
@@ -3479,8 +3934,10 @@ void Spell::TakeCastItem()
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 < MAX_ITEM_PROTO_SPELLS; ++i)
{
if (proto->Spells[i].SpellId)
@@ -3490,7 +3947,9 @@ void Spell::TakeCastItem()
{
if (proto->Spells[i].SpellCharges < 0)
expendable = true;
+
int32 charges = m_CastItem->GetSpellCharges(i);
+
// item has charges left
if (charges)
{
@@ -3499,25 +3958,31 @@ void Spell::TakeCastItem()
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;
+
bool hit = true;
if(m_caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -3537,41 +4002,51 @@ void Spell::TakePower()
break;
}
}
+
Powers powerType = Powers(m_spellInfo->powerType);
+
if(powerType == POWER_RUNE)
{
TakeRunePower();
return;
}
+
if (!m_powerCost)
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;
}
+
if(hit)
m_caster->ModifyPower(powerType, -m_powerCost);
else
m_caster->ModifyPower(powerType, -irand(0, m_powerCost/4));
+
// Set the five second timer
if (powerType == POWER_MANA && m_powerCost > 0)
m_caster->SetLastManaUse(getMSTime());
}
+
void Spell::TakeAmmo()
{
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)
@@ -3590,54 +4065,77 @@ void Spell::TakeAmmo()
((Player*)m_caster)->DestroyItemCount(ammo, 1, true);
}
}
+
SpellCastResult Spell::CheckRuneCost(uint32 runeCostID)
{
if(m_spellInfo->powerType != POWER_RUNE || !runeCostID)
return SPELL_CAST_OK;
+
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_CAST_OK;
+
Player *plr = (Player*)m_caster;
+
if(plr->getClass() != CLASS_DEATH_KNIGHT)
return SPELL_CAST_OK;
+
SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(runeCostID);
+
if(!src)
return SPELL_CAST_OK;
+
if(src->NoRuneCost())
return SPELL_CAST_OK;
+
int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death
+
for(uint32 i = 0; i < RUNE_DEATH; ++i)
runeCost[i] = src->RuneCost[i];
+
runeCost[RUNE_DEATH] = MAX_RUNES; // calculated later
+
for(uint32 i = 0; i < MAX_RUNES; ++i)
{
RuneType rune = plr->GetCurrentRune(i);
if((plr->GetRuneCooldown(i) == 0) && (runeCost[rune] > 0))
runeCost[rune]--;
}
+
for(uint32 i = 0; i < RUNE_DEATH; ++i)
if(runeCost[i] > 0)
runeCost[RUNE_DEATH] += runeCost[i];
+
if(runeCost[RUNE_DEATH] > MAX_RUNES)
return SPELL_FAILED_NO_POWER; // not sure if result code is correct
+
return SPELL_CAST_OK;
}
+
void Spell::TakeRunePower()
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *plr = (Player*)m_caster;
+
if(plr->getClass() != CLASS_DEATH_KNIGHT)
return;
+
SpellRuneCostEntry const *src = sSpellRuneCostStore.LookupEntry(m_spellInfo->runeCostID);
+
if(!src || (src->NoRuneCost() && src->NoRunicPowerGain()))
return;
m_runesState = plr->GetRunesState(); // store previous state
+
int32 runeCost[NUM_RUNE_TYPES]; // blood, frost, unholy, death
+
for(uint32 i = 0; i < RUNE_DEATH; ++i)
{
runeCost[i] = src->RuneCost[i];
}
+
runeCost[RUNE_DEATH] = 0; // calculated later
+
for(uint32 i = 0; i < MAX_RUNES; ++i)
{
RuneType rune = plr->GetCurrentRune(i);
@@ -3648,7 +4146,9 @@ void Spell::TakeRunePower()
runeCost[rune]--;
}
}
+
runeCost[RUNE_DEATH] = runeCost[RUNE_BLOOD] + runeCost[RUNE_UNHOLY] + runeCost[RUNE_FROST];
+
if(runeCost[RUNE_DEATH] > 0)
{
for(uint32 i = 0; i < MAX_RUNES; ++i)
@@ -3695,43 +4195,53 @@ void Spell::TakeRunePower()
}
// Blood of the North
// Reaping
- else if ((*itr)->GetSpellProto()->SpellIconID != 3041 &&
+ else if ((*itr)->GetSpellProto()->SpellIconID != 3041 &&
(*itr)->GetSpellProto()->SpellIconID != 22)
continue;
+
// Remove rune of aura if avalible
if ((*itr)->GetAmount() & (1<<i))
(*itr)->SetAmount((*itr)->GetAmount() & ~(1<<i));
break;
}
}
+
if(runeCost[RUNE_DEATH] == 0)
break;
}
}
}
+
// you can gain some runic power when use runes
float rp = src->runePowerGain;
rp *= sWorld.getRate(RATE_POWER_RUNICPOWER_INCOME);
plr->ModifyPower(POWER_RUNIC_POWER, (int32)rp);
}
+
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;
+
// do not take reagents for these item casts
if (m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAGS_TRIGGERED_CAST)
return;
+
Player* p_caster = (Player*)m_caster;
if (p_caster->CanNoReagentCast(m_spellInfo))
return;
+
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)
{
@@ -3748,47 +4258,65 @@ void Spell::TakeReagents()
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;
+
uint16 threat = spellmgr.GetSpellThreat(spellId);
+
if(!threat)
return;
+
m_targets.getUnitTarget()->AddThreat(m_caster, float(threat));
+
DEBUG_LOG("Spell %u, rank %u, added an additional %i threat", spellId, spellmgr.GetSpellRank(spellId), threat);
}
+
void Spell::HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i)
{
+
if(!Script->OnSpellCast(pUnitTarget,pItemTarget,pGOTarget,i,m_spellInfo))
return;
+
//effect has been handled, skip it
if(m_effectMask & (1<<i))
return;
+
unitTarget = pUnitTarget;
itemTarget = pItemTarget;
gameObjTarget = pGOTarget;
+
uint8 eff = m_spellInfo->Effect[i];
+
sLog.outDebug( "Spell: %u Effect : %u", m_spellInfo->Id, eff);
+
//we do not need DamageMultiplier here.
damage = CalculateDamage(i, NULL);
+
if(eff<TOTAL_SPELL_EFFECTS)
{
//sLog.outDebug( "WORLD: Spell FX %d < TOTAL_SPELL_EFFECTS ", eff);
(this->*SpellEffects[eff])(i);
}
}
+
void Spell::TriggerSpell()
{
for(TriggerSpells::iterator si=m_TriggerSpells.begin(); si!=m_TriggerSpells.end(); ++si)
@@ -3797,6 +4325,7 @@ void Spell::TriggerSpell()
spell->prepare(&m_targets); // use original spell original targets
}
}
+
SpellCastResult Spell::CheckCast(bool strict)
{
// check cooldowns to prevent cheating
@@ -3805,6 +4334,7 @@ SpellCastResult Spell::CheckCast(bool strict)
//can cast triggered (by aura only?) spells while have this flag
if (!m_IsTriggeredSpell && ((Player*)m_caster)->HasFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY))
return SPELL_FAILED_SPELL_IN_PROGRESS;
+
if (((Player*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
{
if(m_triggeredByAuraSpell)
@@ -3813,11 +4343,13 @@ SpellCastResult Spell::CheckCast(bool strict)
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)
@@ -3838,10 +4370,12 @@ SpellCastResult Spell::CheckCast(bool strict)
SpellCastResult shapeError = GetErrorAtShapeshiftedCast(m_spellInfo, m_caster->m_form);
if(shapeError != SPELL_CAST_OK)
return shapeError;
+
if ((m_spellInfo->Attributes & SPELL_ATTR_ONLY_STEALTHED) && !(m_caster->HasStealthAura()))
return SPELL_FAILED_ONLY_STEALTHED;
}
}
+
bool reqCombat=true;
Unit::AuraEffectList const& stateAuras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_IGNORE_AURASTATE);
for(Unit::AuraEffectList::const_iterator j = stateAuras.begin();j != stateAuras.end(); ++j)
@@ -3855,7 +4389,8 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
}
- // caster state requirements
+
+ // caster state requirements
// not for triggered spells (needed by execute)
if (!m_IsTriggeredSpell)
{
@@ -3863,14 +4398,17 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_CASTER_AURASTATE;
if(m_spellInfo->CasterAuraStateNot && m_caster->HasAuraState(AuraState(m_spellInfo->CasterAuraStateNot), m_spellInfo, m_caster))
return SPELL_FAILED_CASTER_AURASTATE;
+
// Note: spell 62473 requres casterAuraSpell = triggering spell
if(m_spellInfo->casterAuraSpell && !m_caster->HasAura(m_spellInfo->casterAuraSpell))
return SPELL_FAILED_CASTER_AURASTATE;
if(m_spellInfo->excludeCasterAuraSpell && m_caster->HasAura(m_spellInfo->excludeCasterAuraSpell))
return SPELL_FAILED_CASTER_AURASTATE;
+
if(reqCombat && m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo))
return SPELL_FAILED_AFFECTING_COMBAT;
}
+
// 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() )
@@ -3880,31 +4418,41 @@ SpellCastResult Spell::CheckCast(bool strict)
(IsAutoRepeat() || (m_spellInfo->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED) != 0) )
return SPELL_FAILED_MOVING;
}
+
if(Unit *target = m_targets.getUnitTarget())
{
// target state requirements (not allowed state), apply to self also
if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraStateNot && target->HasAuraState(AuraState(m_spellInfo->TargetAuraStateNot), m_spellInfo, m_caster))
return SPELL_FAILED_TARGET_AURASTATE;
+
if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell))
return SPELL_FAILED_TARGET_AURASTATE;
+
if(m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell))
return SPELL_FAILED_TARGET_AURASTATE;
+
if(!m_IsTriggeredSpell && target == m_caster && m_spellInfo->AttributesEx & SPELL_ATTR_EX_CANT_TARGET_SELF)
return SPELL_FAILED_BAD_TARGETS;
+
bool non_caster_target = target != m_caster && !spellmgr.IsSpellWithCasterSourceTargetsOnly(m_spellInfo);
+
if(non_caster_target)
{
// target state requirements (apply to non-self only), to allow cast affects to self like Dirty Deeds
if(!m_IsTriggeredSpell && m_spellInfo->TargetAuraState && !target->HasAuraState(AuraState(m_spellInfo->TargetAuraState), m_spellInfo, m_caster))
return SPELL_FAILED_TARGET_AURASTATE;
+
// Not allow casting on flying player
if (target->hasUnitState(UNIT_STAT_UNATTACKABLE))
return SPELL_FAILED_BAD_TARGETS;
+
if(!m_IsTriggeredSpell && VMAP::VMapFactory::checkSpellForLoS(m_spellInfo->Id) && !m_caster->IsWithinLOSInMap(target))
return SPELL_FAILED_LINE_OF_SIGHT;
+
}
else if (m_caster == target)
{
+
if (m_caster->GetTypeId() == TYPEID_PLAYER) // Target - is player caster
{
// Additional check for some spells
@@ -3920,6 +4468,7 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
}
+
// check pet presents
for(int j = 0; j < 3; ++j)
{
@@ -3936,6 +4485,7 @@ SpellCastResult Spell::CheckCast(bool strict)
break;
}
}
+
//check creature type
//ignore self casts (including area casts when caster selected as target)
if(non_caster_target)
@@ -3948,6 +4498,7 @@ SpellCastResult Spell::CheckCast(bool strict)
return SPELL_FAILED_BAD_TARGETS;
}
}
+
// who can give me an example to show what is the use of this
// even if we need check, check by effect rather than whole spell, otherwise 57108,57143 are broken
/*
@@ -3969,9 +4520,11 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
*/
+
if(IsPositiveSpell(m_spellInfo->Id))
if(target->IsImmunedToSpell(m_spellInfo))
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)
//Exclusion for Pounce: Facing Limitation was removed in 2.0.1, but it still uses the same, old Ex-Flags
@@ -3982,20 +4535,24 @@ SpellCastResult Spell::CheckCast(bool strict)
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 (non_caster_target && (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
@@ -4004,16 +4561,19 @@ SpellCastResult Spell::CheckCast(bool strict)
if(MapEntry const* mapEntry = sMapStore.LookupEntry(m_caster->GetMapId()))
if(mapEntry->IsBattleArena())
return SPELL_FAILED_NOT_IN_ARENA;
+
// zone check
if(m_caster->GetTypeId() == TYPEID_UNIT || !((Player*)m_caster)->isGameMaster())
{
uint32 zone, area;
m_caster->GetZoneAndAreaId(zone,area);
+
SpellCastResult locRes= spellmgr.GetSpellAllowedInLocationError(m_spellInfo,m_caster->GetMapId(),zone,area,
m_caster->GetTypeId()==TYPEID_PLAYER ? ((Player*)m_caster) : NULL);
if(locRes != SPELL_CAST_OK)
return locRes;
}
+
// 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))
@@ -4023,6 +4583,7 @@ SpellCastResult Spell::CheckCast(bool strict)
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))
{
@@ -4030,6 +4591,7 @@ SpellCastResult Spell::CheckCast(bool strict)
if(castResult != SPELL_CAST_OK)
return castResult;
}
+
/*//ImpliciteTargetA-B = 38, If fact there is 0 Spell with ImpliciteTargetB=38
if(m_UniqueTargetInfo.empty()) // skip second CheckCast apply (for delayed spells for example)
{
@@ -4044,10 +4606,13 @@ SpellCastResult Spell::CheckCast(bool strict)
SpellScriptTarget::const_iterator upper = spellmgr.GetEndSpellScriptTarget(m_spellInfo->Id);
if(lower==upper)
sLog.outErrorDb("Spell (ID: %u) has effect EffectImplicitTargetA/EffectImplicitTargetB = TARGET_UNIT_NEARBY_ENTRY or TARGET_DST_NEARBY_ENTRY, 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 = bounds.first; i_spellST != bounds.second; ++i_spellST)
{
switch(i_spellST->second.type)
@@ -4055,16 +4620,20 @@ SpellCastResult Spell::CheckCast(bool strict)
case SPELL_TARGET_TYPE_GAMEOBJECT:
{
GameObject* p_GameObject = NULL;
+
if (i_spellST->second.targetEntry)
{
CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::NearestGameObjectEntryInObjectRangeCheck go_check(*m_caster,i_spellST->second.targetEntry,range);
MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck> checker(m_caster, p_GameObject,go_check);
+
TypeContainerVisitor<MaNGOS::GameObjectLastSearcher<MaNGOS::NearestGameObjectEntryInObjectRangeCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, object_checker, *m_caster->GetMap());
+
if (p_GameObject)
{
// remember found target and range, next attempt will find more near target with another entry
@@ -4090,15 +4659,20 @@ SpellCastResult Spell::CheckCast(bool strict)
default:
{
Creature *p_Creature = NULL;
+
CellPair p(MaNGOS::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
cell.SetNoCreate(); // Really don't know what is that???
+
MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck u_check(*m_caster,i_spellST->second.targetEntry,i_spellST->second.type!=SPELL_TARGET_TYPE_DEAD,range);
MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck> searcher(m_caster, p_Creature, u_check);
+
TypeContainerVisitor<MaNGOS::CreatureLastSearcher<MaNGOS::NearestCreatureEntryWithLiveStateInObjectRangeCheck>, GridTypeMapContainer > grid_creature_searcher(searcher);
+
CellLock<GridReadGuard> cell_lock(cell, p);
cell_lock->Visit(cell_lock, grid_creature_searcher, *m_caster->GetMap(), *m_caster, range);
+
if(p_Creature )
{
creatureScriptTarget = p_Creature;
@@ -4109,6 +4683,7 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
}
+
if(creatureScriptTarget)
{
// store coordinates for TARGET_DST_NEARBY_ENTRY
@@ -4116,6 +4691,7 @@ SpellCastResult Spell::CheckCast(bool strict)
m_spellInfo->EffectImplicitTargetB[j] == TARGET_DST_NEARBY_ENTRY )
{
m_targets.setDst(creatureScriptTarget->GetPositionX(),creatureScriptTarget->GetPositionY(),creatureScriptTarget->GetPositionZ());
+
if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_DST_NEARBY_ENTRY && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
AddUnitTarget(creatureScriptTarget, j);
}
@@ -4130,6 +4706,7 @@ SpellCastResult Spell::CheckCast(bool strict)
m_spellInfo->EffectImplicitTargetB[j] == TARGET_DST_NEARBY_ENTRY )
{
m_targets.setDst(goScriptTarget->GetPositionX(),goScriptTarget->GetPositionY(),goScriptTarget->GetPositionZ());
+
if(m_spellInfo->EffectImplicitTargetA[j] == TARGET_DST_NEARBY_ENTRY && m_spellInfo->EffectImplicitTargetB[j] == 0 && m_spellInfo->Effect[j]!=SPELL_EFFECT_PERSISTENT_AREA_AURA)
AddGOTarget(goScriptTarget, j);
}
@@ -4149,18 +4726,22 @@ SpellCastResult Spell::CheckCast(bool strict)
}
}
}*/
+
if(!m_IsTriggeredSpell)
{
SpellCastResult castResult = CheckRange(strict);
if(castResult != SPELL_CAST_OK)
return castResult;
+
castResult = CheckPower();
if(castResult != SPELL_CAST_OK)
return castResult;
+
castResult = CheckCasterAuras();
if(castResult != SPELL_CAST_OK)
return castResult;
}
+
for (int i = 0; i < 3; i++)
{
// for effects of spells that have only one target
@@ -4203,30 +4784,42 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_BAD_TARGETS;
+
if(m_spellInfo->EffectImplicitTargetA[i] != TARGET_UNIT_PET)
break;
+
Pet* pet = ((Player*)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(m_spellInfo->spellLevel > pet->getLevel())
return SPELL_FAILED_LOWLEVEL;
+
break;
}
case SPELL_EFFECT_LEARN_PET_SPELL:
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_BAD_TARGETS;
+
Pet* pet = ((Player*)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(m_spellInfo->spellLevel > pet->getLevel())
return SPELL_FAILED_LOWLEVEL;
+
break;
}
case SPELL_EFFECT_APPLY_GLYPH:
@@ -4241,18 +4834,25 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_BAD_TARGETS;
+
Item* foodItem = m_targets.getItemTarget();
if(!foodItem)
return SPELL_FAILED_BAD_TARGETS;
+
Pet* pet = ((Player*)m_caster)->GetPet();
+
if(!pet)
return SPELL_FAILED_NO_PET;
+
if(!pet->HaveInDiet(foodItem->GetProto()))
return SPELL_FAILED_WRONG_PET_FOOD;
+
if(!pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel))
return SPELL_FAILED_FOOD_LOWLEVEL;
+
if(m_caster->isInCombat() || pet->isInCombat())
return SPELL_FAILED_AFFECTING_COMBAT;
+
break;
}
case SPELL_EFFECT_POWER_BURN:
@@ -4281,24 +4881,30 @@ SpellCastResult Spell::CheckCast(bool strict)
{
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 = creature->GetCreatureInfo()->GetRequiredLootSkill();
+
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:
@@ -4306,6 +4912,7 @@ SpellCastResult Spell::CheckCast(bool strict)
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()
@@ -4313,10 +4920,12 @@ SpellCastResult Spell::CheckCast(bool strict)
|| m_spellInfo->EffectImplicitTargetA[i] == TARGET_GAMEOBJECT_ITEM && !m_targets.getGOTarget() &&
(!m_targets.getItemTarget() || !m_targets.getItemTarget()->GetProto()->LockID || m_targets.getItemTarget()->GetOwner() != m_caster ) )
return SPELL_FAILED_BAD_TARGETS;
+
// In BattleGround players can use only flags and banners
if( ((Player*)m_caster)->InBattleGround() &&
!((Player*)m_caster)->CanUseBattleGroundObject() )
return SPELL_FAILED_TRY_AGAIN;
+
// get the lock entry
uint32 lockId = 0;
if (GameObject* go = m_targets.getGOTarget())
@@ -4327,18 +4936,22 @@ SpellCastResult Spell::CheckCast(bool strict)
}
else if(Item* itm = m_targets.getItemTarget())
lockId = itm->GetProto()->LockID;
+
SkillType skillId = SKILL_NONE;
int32 reqSkillValue = 0;
int32 skillValue = 0;
+
// check lock compatibility
SpellCastResult res = CanOpenLock(i, lockId, skillId, reqSkillValue, skillValue);
if(res != SPELL_CAST_OK)
return res;
+
// chance for fail at orange mining/herb/LockPicking gathering attempt
// second check prevent fail at rechecks
if(skillId != SKILL_NONE && (!m_selfContainer || ((*m_selfContainer) != this)))
{
bool canFailAtMax = skillId != SKILL_HERBALISM && skillId != SKILL_MINING;
+
// chance for failure in orange gather / lockpick (gathering skill can't fail at maxskill)
if((canFailAtMax || skillValue < sWorld.GetConfigMaxSkillValue()) && reqSkillValue > irand(skillValue - 25, skillValue + 37))
return SPELL_FAILED_TRY_AGAIN;
@@ -4350,8 +4963,10 @@ SpellCastResult Spell::CheckCast(bool strict)
Creature *pet = m_caster->GetGuardianPet();
if(!pet)
return SPELL_FAILED_NO_PET;
+
if(pet->isAlive())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
break;
}
// This is generic summon effect
@@ -4377,8 +4992,10 @@ SpellCastResult Spell::CheckCast(bool strict)
{
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:
@@ -4394,8 +5011,10 @@ SpellCastResult Spell::CheckCast(bool strict)
else
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
}
+
if(m_caster->GetCharmGUID())
return SPELL_FAILED_ALREADY_HAVE_CHARM;
+
break;
}
case SPELL_EFFECT_SUMMON_PLAYER:
@@ -4404,9 +5023,11 @@ SpellCastResult Spell::CheckCast(bool strict)
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() )
{
@@ -4437,6 +5058,7 @@ SpellCastResult Spell::CheckCast(bool strict)
default:break;
}
}
+
for (int i = 0; i < 3; i++)
{
switch(m_spellInfo->EffectApplyAuraName[i])
@@ -4454,18 +5076,25 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_BAD_TARGETS;
+
if (!m_targets.getUnitTarget() || m_targets.getUnitTarget()->GetTypeId() == TYPEID_PLAYER)
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
Creature* target = (Creature*)m_targets.getUnitTarget();
+
if (target->getLevel() > m_caster->getLevel())
return SPELL_FAILED_HIGHLEVEL;
+
// use SMSG_PET_TAME_FAILURE?
if (!target->GetCreatureInfo()->isTameable (((Player*)m_caster)->CanTameExoticPets()))
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;
}
default:
@@ -4477,9 +5106,11 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_FAILED_NO_PET;
+
Pet *pet = ((Player*)m_caster)->GetPet();
if(!pet)
return SPELL_FAILED_NO_PET;
+
if(pet->GetCharmerGUID())
return SPELL_FAILED_CHARMED;
break;
@@ -4490,50 +5121,62 @@ SpellCastResult Spell::CheckCast(bool strict)
{
if(m_caster->GetCharmerGUID())
return SPELL_FAILED_CHARMED;
+
if(m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_CHARM
|| m_spellInfo->EffectApplyAuraName[i] == SPELL_AURA_MOD_POSSESS)
{
if(m_caster->GetPetGUID())
return SPELL_FAILED_ALREADY_HAVE_SUMMON;
+
if(m_caster->GetCharmGUID())
return SPELL_FAILED_ALREADY_HAVE_CHARM;
}
+
if(Unit *target = m_targets.getUnitTarget())
{
if(target->GetTypeId() == TYPEID_UNIT && ((Creature*)target)->IsVehicle())
return SPELL_FAILED_BAD_IMPLICIT_TARGETS;
+
if(target->GetCharmerGUID())
return SPELL_FAILED_CHARMED;
+
int32 damage = CalculateDamage(i, target);
if(damage && int32(target->getLevel()) > damage)
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->AreaGroupId)
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 || form == FORM_METAMORPHOSIS )
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_FLY:
@@ -4552,33 +5195,42 @@ SpellCastResult Spell::CheckCast(bool strict)
{
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 SPELL_CAST_OK;
}
+
SpellCastResult Spell::CheckPetCast(Unit* target)
{
if(!m_caster->isAlive() && !(m_spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD))
return SPELL_FAILED_CASTER_DEAD;
+
if(m_caster->hasUnitState(UNIT_STAT_CASTING) && !m_IsTriggeredSpell) //prevent spellcast interruption by another spellcast
return SPELL_FAILED_SPELL_IN_PROGRESS;
if(m_caster->isInCombat() && IsNonCombatSpell(m_spellInfo))
return SPELL_FAILED_AFFECTING_COMBAT;
+
//dead owner (pets still alive when owners ressed?)
if(Unit *owner = m_caster->GetCharmerOrOwner())
if(!owner->isAlive())
return SPELL_FAILED_CASTER_DEAD;
+
if(!target && m_targets.getUnitTarget())
target = m_targets.getUnitTarget();
+
for(uint32 i = 0; i < 3; ++i)
{
if(SpellTargetType[m_spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_UNIT_TARGET
@@ -4590,27 +5242,34 @@ SpellCastResult Spell::CheckPetCast(Unit* target)
break;
}
}
+
Unit* _target = m_targets.getUnitTarget();
+
if(_target) //for target dead/target not valid
{
if(!_target->isAlive())
return SPELL_FAILED_BAD_TARGETS;
+
if(!IsValidSingleTargetSpell(_target))
return SPELL_FAILED_BAD_TARGETS;
}
//cooldown
if(((Creature*)m_caster)->HasSpellCooldown(m_spellInfo->Id))
return SPELL_FAILED_NOT_READY;
+
return CheckCast(true);
}
+
SpellCastResult Spell::CheckCasterAuras() const
{
// spells totally immuned to caster auras ( wsg flag drop, give marks etc)
if(m_spellInfo->AttributesEx6& SPELL_ATTR_EX6_IGNORE_CASTER_AURAS)
return SPELL_CAST_OK;
+
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 )
@@ -4628,6 +5287,7 @@ SpellCastResult Spell::CheckCasterAuras() const
if(m_spellInfo->Id==42292 || m_spellInfo->Id==59752)
mechanic_immune = IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
}
+
// Check whether the cast should be prevented by any state you might have.
SpellCastResult prevented_reason = SPELL_CAST_OK;
// Have to check if there is a stun aura. Otherwise will have problems with ghost aura apply while logging out
@@ -4642,6 +5302,7 @@ SpellCastResult Spell::CheckCasterAuras() const
prevented_reason = SPELL_FAILED_SILENCED;
else if(unitflag & UNIT_FLAG_PACIFIED && m_spellInfo->PreventionType == SPELL_PREVENTION_TYPE_PACIFY)
prevented_reason = SPELL_FAILED_PACIFIED;
+
// Attr must make flag drop spell totally immune from all effects
if(prevented_reason != SPELL_CAST_OK)
{
@@ -4659,6 +5320,7 @@ SpellCastResult Spell::CheckCasterAuras() const
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.
for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i)
@@ -4700,9 +5362,11 @@ SpellCastResult Spell::CheckCasterAuras() const
}
return SPELL_CAST_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)
@@ -4725,7 +5389,9 @@ bool Spell::CanAutoCast(Unit* target)
return false;
}
}
+
SpellCastResult result = CheckPetCast(target);
+
if(result == SPELL_CAST_OK || result == SPELL_FAILED_UNIT_NOT_INFRONT)
{
SelectSpellTargets();
@@ -4736,24 +5402,31 @@ bool Spell::CanAutoCast(Unit* target)
}
return false; //target invalid
}
+
SpellCastResult Spell::CheckRange(bool strict)
{
//float range_mod;
+
// self cast doesn't need range checking -- also for Starshards fix
if (m_spellInfo->rangeIndex == 1)
return SPELL_CAST_OK;
+
// i do not know why we need this
/*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);
+
Unit *target = m_targets.getUnitTarget();
float max_range = m_caster->GetSpellMaxRangeForTarget(target, srange); // + range_mod;
float min_range = m_caster->GetSpellMinRangeForTarget(target, srange);
uint32 range_type = GetSpellRangeType(srange);
+
if(Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RANGE, max_range, this);
+
if(target && target != m_caster)
{
if(range_type == SPELL_RANGE_MELEE)
@@ -4764,6 +5437,7 @@ SpellCastResult Spell::CheckRange(bool strict)
}
else if(!m_caster->IsWithinCombatRange(target, max_range))
return SPELL_FAILED_OUT_OF_RANGE; //0x5A;
+
if(range_type == SPELL_RANGE_RANGED)
{
if(m_caster->IsWithinMeleeRange(target))
@@ -4771,10 +5445,12 @@ SpellCastResult Spell::CheckRange(bool strict)
}
else if(min_range && m_caster->IsWithinCombatRange(target, min_range)) // skip this check if min_range = 0
return SPELL_FAILED_TOO_CLOSE;
+
if( m_caster->GetTypeId() == TYPEID_PLAYER &&
(m_spellInfo->FacingCasterFlags & SPELL_FACING_FLAG_INFRONT) && !m_caster->HasInArc( M_PI, target ) )
return SPELL_FAILED_UNIT_NOT_INFRONT;
}
+
if(m_targets.HasDst() && !m_targets.HasTraj())
{
if(!m_caster->IsWithinDist3d(&m_targets.m_dstPos, max_range))
@@ -4782,13 +5458,16 @@ SpellCastResult Spell::CheckRange(bool strict)
if(min_range && m_caster->IsWithinDist3d(&m_targets.m_dstPos, min_range))
return SPELL_FAILED_TOO_CLOSE;
}
+
return SPELL_CAST_OK;
}
+
SpellCastResult Spell::CheckPower()
{
// item cast not used power
if(m_CastItem)
return SPELL_CAST_OK;
+
// health as power used - need check health amount
if(m_spellInfo->powerType == POWER_HEALTH)
{
@@ -4802,6 +5481,7 @@ SpellCastResult Spell::CheckPower()
sLog.outError("Spell::CheckPower: Unknown power type '%d'", m_spellInfo->powerType);
return SPELL_FAILED_UNKNOWN;
}
+
//check rune cost only if a spell has PowerType == POWER_RUNE
if(m_spellInfo->powerType == POWER_RUNE)
{
@@ -4809,6 +5489,7 @@ SpellCastResult Spell::CheckPower()
if(failReason != SPELL_CAST_OK)
return failReason;
}
+
// Check power amount
Powers powerType = Powers(m_spellInfo->powerType);
if(m_caster->GetPower(powerType) < m_powerCost)
@@ -4816,11 +5497,14 @@ SpellCastResult Spell::CheckPower()
else
return SPELL_CAST_OK;
}
+
SpellCastResult Spell::CheckItems()
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return SPELL_CAST_OK;
+
Player* p_caster = (Player*)m_caster;
+
if(!m_CastItem)
{
if(m_castItemGUID)
@@ -4831,13 +5515,16 @@ SpellCastResult Spell::CheckItems()
uint32 itemid = m_CastItem->GetEntry();
if( !p_caster->HasItemCount(itemid, 1) )
return SPELL_FAILED_ITEM_NOT_READY;
+
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;
+
// consumable cast item checks
if (proto->Class == ITEM_CLASS_CONSUMABLE && m_targets.getUnitTarget())
{
@@ -4848,6 +5535,7 @@ SpellCastResult Spell::CheckItems()
// skip check, pet not required like checks, and for TARGET_UNIT_PET m_targets.getUnitTarget() is not the real target but the caster
if (m_spellInfo->EffectImplicitTargetA[i] == TARGET_UNIT_PET)
continue;
+
if (m_spellInfo->Effect[i] == SPELL_EFFECT_HEAL)
{
if (m_targets.getUnitTarget()->GetHealth() == m_targets.getUnitTarget()->GetMaxHealth())
@@ -4861,6 +5549,7 @@ SpellCastResult Spell::CheckItems()
break;
}
}
+
// Mana Potion, Rage Potion, Thistle Tea(Rogue), ...
if (m_spellInfo->Effect[i] == SPELL_EFFECT_ENERGIZE)
{
@@ -4869,6 +5558,7 @@ SpellCastResult Spell::CheckItems()
failReason = SPELL_FAILED_ALREADY_AT_FULL_POWER;
continue;
}
+
Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
if (m_targets.getUnitTarget()->GetPower(power) == m_targets.getUnitTarget()->GetMaxPower(power))
{
@@ -4886,13 +5576,16 @@ SpellCastResult Spell::CheckItems()
return failReason;
}
}
+
// check target item
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;
}
@@ -4902,23 +5595,29 @@ SpellCastResult Spell::CheckItems()
if(m_caster->GetTypeId() == TYPEID_PLAYER && !((Player*)m_caster)->HasItemFitToSpellReqirements(m_spellInfo))
return SPELL_FAILED_EQUIPPED_ITEM_CLASS;
}
+
// check spell focus object
if(m_spellInfo->RequiresSpellFocus)
{
CellPair p(Trinity::ComputeCellPair(m_caster->GetPositionX(), m_caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
GameObject* ok = NULL;
MaNGOS::GameObjectFocusCheck go_check(m_caster,m_spellInfo->RequiresSpellFocus);
MaNGOS::GameObjectSearcher<MaNGOS::GameObjectFocusCheck> checker(m_caster, ok, go_check);
+
TypeContainerVisitor<Trinity::GameObjectSearcher<Trinity::GameObjectFocusCheck>, GridTypeMapContainer > object_checker(checker);
CellLock<GridReadGuard> cell_lock(cell, p);
Map& map = *m_caster->GetMap();
cell_lock->Visit(cell_lock, object_checker, map, *m_caster, map.GetVisibilityDistance());
+
if(!ok)
return SPELL_FAILED_REQUIRES_SPELL_FOCUS;
+
focusObject = ok; // game object found in range
}
+
// do not take reagents for these item casts
if (!(m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAGS_TRIGGERED_CAST))
{
@@ -4929,8 +5628,10 @@ SpellCastResult Spell::CheckItems()
{
if(m_spellInfo->Reagent[i] <= 0)
continue;
+
uint32 itemid = m_spellInfo->Reagent[i];
uint32 itemcount = m_spellInfo->ReagentCount[i];
+
// if CastItem is also spell reagent
if( m_CastItem && m_CastItem->GetEntry() == itemid )
{
@@ -4952,6 +5653,7 @@ SpellCastResult Spell::CheckItems()
return SPELL_FAILED_ITEM_NOT_READY; //0x54
}
}
+
// check totem-item requirements (items presence in inventory)
uint32 totems = 2;
for(int i = 0; i < 2 ; ++i)
@@ -4968,6 +5670,7 @@ SpellCastResult Spell::CheckItems()
}
if(totems != 0)
return SPELL_FAILED_TOTEMS; //0x7C
+
// Check items for TotemCategory (items presence in inventory)
uint32 TotemCategory = 2;
for(int i= 0; i < 2; ++i)
@@ -4986,6 +5689,7 @@ SpellCastResult Spell::CheckItems()
if(TotemCategory != 0)
return SPELL_FAILED_TOTEM_CATEGORY; //0x7B
}
+
// special checks for spell effects
for(int i = 0; i < 3; i++)
{
@@ -5006,7 +5710,7 @@ SpellCastResult Spell::CheckItems()
break;
}
case SPELL_EFFECT_ENCHANT_ITEM:
- if(m_spellInfo->EffectItemType[i] && m_targets.getItemTarget()
+ if(m_spellInfo->EffectItemType[i] && m_targets.getItemTarget()
&& (m_targets.getItemTarget()->IsWeaponVellum() || m_targets.getItemTarget()->IsArmorVellum()))
{
// cannot enchant vellum for other player
@@ -5028,6 +5732,7 @@ SpellCastResult Spell::CheckItems()
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
@@ -5066,12 +5771,15 @@ SpellCastResult Spell::CheckItems()
{
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 against the item disenchanting requirements
uint32 item_disenchantskilllevel = itemProto->RequiredDisenchantSkill;
@@ -5104,8 +5812,10 @@ SpellCastResult Spell::CheckItems()
//make sure the player has the required ores in inventory
if(m_targets.getItemTarget()->GetCount() < 5)
return SPELL_FAILED_NEED_MORE_ITEMS;
+
if(!LootTemplates_Prospecting.HaveLootFor(m_targets.getItemTargetEntry()))
return SPELL_FAILED_CANT_BE_PROSPECTED;
+
break;
}
case SPELL_EFFECT_MILLING:
@@ -5125,8 +5835,10 @@ SpellCastResult Spell::CheckItems()
//make sure the player has the required herbs in inventory
if(m_targets.getItemTarget()->GetCount() < 5)
return SPELL_FAILED_NEED_MORE_ITEMS;
+
if(!LootTemplates_Milling.HaveLootFor(m_targets.getItemTargetEntry()))
return SPELL_FAILED_CANT_BE_MILLED;
+
break;
}
case SPELL_EFFECT_WEAPON_DAMAGE:
@@ -5138,6 +5850,7 @@ SpellCastResult Spell::CheckItems()
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:
@@ -5156,13 +5869,17 @@ SpellCastResult Spell::CheckItems()
// Requires No Ammo
if(m_caster->HasAura(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)
{
@@ -5178,6 +5895,7 @@ SpellCastResult Spell::CheckItems()
default:
return SPELL_FAILED_NO_AMMO;
}
+
if( !((Player*)m_caster)->HasItemCount( ammo, 1 ) )
return SPELL_FAILED_NO_AMMO;
}; break;
@@ -5191,6 +5909,7 @@ SpellCastResult Spell::CheckItems()
default:break;
}
}
+
// check weapon presence in slots for main/offhand weapons
if(m_spellInfo->EquippedItemClass >=0)
{
@@ -5198,38 +5917,49 @@ SpellCastResult Spell::CheckItems()
if(m_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 m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+
// skip spell if weapon not fit to triggered spell
if(!item->IsFitToSpellRequirements(m_spellInfo))
return m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS;
}
+
// offhand hand weapon required
if(m_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 m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS;
+
// skip spell if weapon not fit to triggered spell
if(!item->IsFitToSpellRequirements(m_spellInfo))
return m_IsTriggeredSpell? SPELL_FAILED_DONT_REPORT : SPELL_FAILED_EQUIPPED_ITEM_CLASS;
}
}
+
return SPELL_CAST_OK;
}
+
void Spell::Delayed() // only called in DealDamage()
{
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
+
if(isDelayableNoMore()) // Spells may only be delayed twice
return;
+
// spells not loosing casting time ( slam, dynamites, bombs.. )
//if(!(m_spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_DAMAGE))
// return;
+
//check pushback reduce
int32 delaytime = 500; // spellcasting delay is normally 500ms
int32 delayReduce = 100; // must be initialized to 100 for percent modifiers
@@ -5237,7 +5967,9 @@ void Spell::Delayed() // only called in DealDamage()
delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100;
if(delayReduce >= 100)
return;
+
delaytime = delaytime * (100 - delayReduce) / 100;
+
if(int32(m_timer) + delaytime > m_casttime)
{
delaytime = m_casttime - m_timer;
@@ -5245,18 +5977,24 @@ void Spell::Delayed() // only called in DealDamage()
}
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;
+
if(isDelayableNoMore()) // Spells may only be delayed twice
return;
+
//check pushback reduce
int32 delaytime = GetSpellDuration(m_spellInfo) * 25 / 100; // channeling delay is normally 25% of its time per hit
int32 delayReduce = 100; // must be initialized to 100 for percent modifiers
@@ -5264,7 +6002,9 @@ void Spell::DelayedChannel()
delayReduce += m_caster->GetTotalAuraModifier(SPELL_AURA_REDUCE_PUSHBACK) - 100;
if(delayReduce >= 100)
return;
+
delaytime = delaytime * (100 - delayReduce) / 100;
+
if(int32(m_timer) < delaytime)
{
delaytime = m_timer;
@@ -5272,7 +6012,9 @@ void Spell::DelayedChannel()
}
else
m_timer -= delaytime;
+
sLog.outDebug("Spell %u partially interrupted for %i ms, new duration: %u ms", m_spellInfo->Id, delaytime, m_timer);
+
for(std::list<TargetInfo>::const_iterator ihit = m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
if ((*ihit).missCondition == SPELL_MISS_NONE)
@@ -5284,11 +6026,14 @@ void Spell::DelayedChannel()
}
}
}
+
// partially interrupt persistent area auras
if(DynamicObject* dynObj = m_caster->GetDynObject(m_spellInfo->Id))
dynObj->Delay(delaytime);
+
SendChannelUpdate(m_timer);
}
+
void Spell::UpdatePointers()
{
if(m_originalCasterGUID==m_caster->GetGUID())
@@ -5298,13 +6043,17 @@ void Spell::UpdatePointers()
m_originalCaster = ObjectAccessor::GetUnit(*m_caster,m_originalCasterGUID);
if(m_originalCaster && !m_originalCaster->IsInWorld()) m_originalCaster = NULL;
}
+
if(m_castItemGUID && m_caster->GetTypeId() == TYPEID_PLAYER)
m_CastItem = ((Player*)m_caster)->GetItemByGuid(m_castItemGUID);
+
m_targets.Update(m_caster);
}
+
bool Spell::CheckTargetCreatureType(Unit* target) const
{
uint32 spellCreatureTargetMask = m_spellInfo->TargetCreatureType;
+
// Curse of Doom & Exorcism: not find another way to fix spell target check :/
if (m_spellInfo->SpellFamilyName==SPELLFAMILY_WARLOCK && m_spellInfo->Category == 1179 ||
// TODO: will be removed in 3.2.x
@@ -5313,21 +6062,27 @@ bool Spell::CheckTargetCreatureType(Unit* target) const
// 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;
+
// Polymorph and Grounding Totem
if (target->GetEntry() == 5925 && m_spellInfo->SpellFamilyName == SPELLFAMILY_MAGE && (m_spellInfo->SpellFamilyFlags[0] & 0x1000000) && m_spellInfo->EffectApplyAuraName[0] == SPELL_AURA_MOD_CONFUSE)
return true;
+
if (spellCreatureTargetMask)
{
uint32 TargetCreatureType = target->GetCreatureTypeMask();
+
return !TargetCreatureType || (spellCreatureTargetMask & TargetCreatureType);
}
return true;
}
+
CurrentSpellTypes Spell::GetCurrentContainer()
{
if (IsNextMeleeSwingSpell())
@@ -5339,6 +6094,7 @@ CurrentSpellTypes Spell::GetCurrentContainer()
else
return(CURRENT_GENERIC_SPELL);
}
+
bool Spell::CheckTarget(Unit* target, uint32 eff)
{
// Check targets for creature type mask and remove not appropriate (skip explicit self target case, maybe need other explicit targets)
@@ -5347,11 +6103,13 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
if (!CheckTargetCreatureType(target))
return false;
}
+
// Check Aura spell req (need for AoE spells)
if(m_spellInfo->targetAuraSpell && !target->HasAura(m_spellInfo->targetAuraSpell))
return false;
if (m_spellInfo->excludeTargetAuraSpell && target->HasAura(m_spellInfo->excludeTargetAuraSpell))
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())
@@ -5359,6 +6117,7 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
// any unattackable target skipped
if (target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE))
return false;
+
// unselectable targets skipped in all cases except TARGET_UNIT_NEARBY_ENTRY targeting
// in case TARGET_UNIT_NEARBY_ENTRY target selected by server always and can't be cheated
/*if( target->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE) &&
@@ -5366,14 +6125,17 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
m_spellInfo->EffectImplicitTargetB[eff] != TARGET_UNIT_NEARBY_ENTRY )
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;
}
+
switch(m_spellInfo->EffectApplyAuraName[eff])
{
case SPELL_AURA_NONE:
@@ -5392,9 +6154,11 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
return false;
break;
}
+
//Do not do further checks for triggered spells
if(m_IsTriggeredSpell)
return true;
+
//Check targets for LOS visibility (except spells without range limitations )
switch(m_spellInfo->Effect[eff])
{
@@ -5410,14 +6174,18 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
{
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
@@ -5433,34 +6201,43 @@ bool Spell::CheckTarget(Unit* target, uint32 eff)
return false;
break;
}
+
return true;
}
+
bool Spell::IsNeedSendToClient() const
{
return m_spellInfo->SpellVisual[0] || m_spellInfo->SpellVisual[1] || IsChanneledSpell(m_spellInfo) ||
m_spellInfo->speed > 0.0f || !m_triggeredByAuraSpell && !m_IsTriggeredSpell;
}
+
bool Spell::HaveTargetsForEffect( uint8 effect ) const
{
for(std::list<TargetInfo>::const_iterator itr = m_UniqueTargetInfo.begin(); itr != m_UniqueTargetInfo.end(); ++itr)
if(itr->effectMask & (1 << effect))
return true;
+
for(std::list<GOTargetInfo>::const_iterator itr = m_UniqueGOTargetInfo.begin(); itr != m_UniqueGOTargetInfo.end(); ++itr)
if(itr->effectMask & (1 << effect))
return true;
+
for(std::list<ItemTargetInfo>::const_iterator itr = m_UniqueItemInfo.begin(); itr != m_UniqueItemInfo.end(); ++itr)
if(itr->effectMask & (1 << effect))
return true;
+
return false;
}
+
SpellEvent::SpellEvent(Spell* spell) : BasicEvent()
{
m_Spell = spell;
}
+
SpellEvent::~SpellEvent()
{
if (m_Spell->getState() != SPELL_STATE_FINISHED)
m_Spell->cancel();
+
if (m_Spell->IsDeletable())
{
delete m_Spell;
@@ -5471,11 +6248,13 @@ SpellEvent::~SpellEvent()
(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())
{
@@ -5489,6 +6268,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
}
// event will be re-added automatically at the end of routine)
} break;
+
case SPELL_STATE_DELAYED:
{
// first, check, if we have just started
@@ -5512,6 +6292,7 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
((Player*)m_Spell->GetCaster())->SetSpellModTakingSpell(m_Spell, true);
// do the action (pass spell to channeling state)
m_Spell->handle_immediate();
+
// And remove after effect handling
((Player*)m_Spell->GetCaster())->SetSpellModTakingSpell(m_Spell, false);
}
@@ -5542,26 +6323,31 @@ bool SpellEvent::Execute(uint64 e_time, uint32 p_time)
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();
}
+
bool SpellEvent::IsDeletable() const
{
return m_Spell->IsDeletable();
}
+
bool Spell::IsValidSingleTargetEffect(Unit const* target, Targets type) const
{
switch(type)
@@ -5578,6 +6364,7 @@ bool Spell::IsValidSingleTargetEffect(Unit const* target, Targets type) const
}
return true;
}
+
bool Spell::IsValidSingleTargetSpell(Unit const* target) const
{
for(int i = 0; i < 3; ++i)
@@ -5590,6 +6377,7 @@ bool Spell::IsValidSingleTargetSpell(Unit const* target) const
}
return true;
}
+
void Spell::CalculateDamageDoneForAllTargets()
{
float multiplier[3];
@@ -5605,6 +6393,7 @@ void Spell::CalculateDamageDoneForAllTargets()
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_EFFECT_PAST_FIRST, multiplier[i], this);
}
}
+
bool usesAmmo=true;
Unit::AuraEffectList const& Auras = m_caster->GetAurasByType(SPELL_AURA_ABILITY_CONSUME_NO_AMMO);
for(Unit::AuraEffectList::const_iterator j = Auras.begin();j != Auras.end(); ++j)
@@ -5612,15 +6401,19 @@ void Spell::CalculateDamageDoneForAllTargets()
if((*j)->isAffectedOnSpell(m_spellInfo))
usesAmmo=false;
}
+
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
{
TargetInfo &target = *ihit;
+
uint32 mask = target.effectMask;
if(!mask)
continue;
+
Unit* unit = m_caster->GetGUID()==target.targetGUID ? m_caster : ObjectAccessor::GetUnit(*m_caster, target.targetGUID);
if (!unit) // || !unit->isAlive()) do we need to check alive here?
continue;
+
if (usesAmmo)
{
bool ammoTaken=false;
@@ -5642,6 +6435,7 @@ void Spell::CalculateDamageDoneForAllTargets()
break;
}
}
+
if (target.missCondition==SPELL_MISS_NONE) // In case spell hit target, do all effect on that target
{
target.damage += CalculateDamageDone(unit, mask, multiplier);
@@ -5657,6 +6451,7 @@ void Spell::CalculateDamageDoneForAllTargets()
}
}
}
+
int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *multiplier)
{
int32 damageDone = 0;
@@ -5667,6 +6462,7 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul
{
m_damage = 0;
damage = CalculateDamage(i, NULL);
+
switch(m_spellInfo->Effect[i])
{
case SPELL_EFFECT_SCHOOL_DAMAGE:
@@ -5682,6 +6478,7 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul
SpellDamageHeal(i);
break;
}
+
if(m_damage > 0)
{
if(IsAreaEffectTarget[m_spellInfo->EffectImplicitTargetA[i]] || IsAreaEffectTarget[m_spellInfo->EffectImplicitTargetB[i]])
@@ -5695,20 +6492,27 @@ int32 Spell::CalculateDamageDone(Unit *unit, const uint32 effectMask, float *mul
m_damage *= m_damageMultipliers[i];
m_damageMultipliers[i] *= multiplier[i];
}
+
damageDone += m_damage;
}
}
+
return damageDone;
}
+
SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& skillId, int32& reqSkillValue, int32& skillValue)
{
if(!lockId) // possible case for GO and maybe for items.
return SPELL_CAST_OK;
+
// Get LockInfo
LockEntry const *lockInfo = sLockStore.LookupEntry(lockId);
+
if (!lockInfo)
return SPELL_FAILED_BAD_TARGETS;
+
bool reqKey = false; // some locks not have reqs
+
for(int j = 0; j < 8; ++j)
{
switch(lockInfo->Type[j])
@@ -5723,31 +6527,41 @@ SpellCastResult Spell::CanOpenLock(uint32 effIndex, uint32 lockId, SkillType& sk
case LOCK_KEY_SKILL:
{
reqKey = true;
+
// wrong locktype, skip
if(uint32(m_spellInfo->EffectMiscValue[effIndex]) != lockInfo->Index[j])
continue;
+
skillId = SkillByLockType(LockType(lockInfo->Index[j]));
+
if ( skillId != SKILL_NONE )
{
// skill bonus provided by casting spell (mostly item spells)
// add the damage modifier from the spell casted (cheat lock / skeleton key etc.) (use m_currentBasePoints, CalculateDamage returns wrong value)
uint32 spellSkillBonus = uint32(m_currentBasePoints[effIndex]+1);
reqSkillValue = lockInfo->Skill[j];
+
// castitem check: rogue using skeleton keys. the skill values should not be added in this case.
skillValue = m_CastItem || m_caster->GetTypeId()!= TYPEID_PLAYER ?
0 : ((Player*)m_caster)->GetSkillValue(skillId);
+
skillValue += spellSkillBonus;
+
if (skillValue < reqSkillValue)
return SPELL_FAILED_LOW_CASTLEVEL;
}
+
return SPELL_CAST_OK;
}
}
}
+
if(reqKey)
return SPELL_FAILED_BAD_TARGETS;
+
return SPELL_CAST_OK;
}
+
void Spell::SetSpellValue(SpellValueMod mod, int32 value)
{
switch(mod)
@@ -5772,6 +6586,7 @@ void Spell::SetSpellValue(SpellValueMod mod, int32 value)
break;
}
}
+
float tangent(float x)
{
x = tan(x);
@@ -5783,35 +6598,47 @@ float tangent(float x)
if(x <= 100000.0f) return -100000.0f;
return 0.0f;
}
+
#define DEBUG_TRAJ(a) //a
+
void Spell::SelectTrajTargets()
{
if(!m_targets.HasTraj())
return;
+
float dist2d = m_targets.GetDist2d();
if(!dist2d)
return;
+
float dz = m_targets.m_dstPos.m_positionZ - m_targets.m_srcPos.m_positionZ;
+
UnitList unitList;
SearchAreaTarget(unitList, dist2d, PUSH_IN_THIN_LINE, SPELL_TARGETS_ANY);
if(unitList.empty())
return;
+
unitList.sort(TargetDistanceOrder(m_caster));
+
float b = tangent(m_targets.m_elevation);
float a = (dz - dist2d * b) / (dist2d * dist2d);
if(a > -0.0001f) a = 0;
DEBUG_TRAJ(sLog.outError("Spell::SelectTrajTargets: a %f b %f", a, b);)
+
float bestDist = GetSpellMaxRange(m_spellInfo, false);
+
UnitList::const_iterator itr = unitList.begin();
for(; itr != unitList.end(); ++itr)
{
if(m_caster == *itr || m_caster->IsOnVehicle(*itr) || (*itr)->GetVehicle())//(*itr)->IsOnVehicle(m_caster))
continue;
+
const float size = std::max((*itr)->GetObjectSize() * 0.7f, 1.0f); // 1/sqrt(3)
// TODO: all calculation should be based on src instead of m_caster
const float objDist2d = m_targets.m_srcPos.GetExactDist2d(*itr) * cos(m_targets.m_srcPos.GetRelativeAngle(*itr));
const float dz = (*itr)->GetPositionZ() - m_targets.m_srcPos.m_positionZ;
+
DEBUG_TRAJ(sLog.outError("Spell::SelectTrajTargets: check %u, dist between %f %f, height between %f %f.", (*itr)->GetEntry(), objDist2d - size, objDist2d + size, dz - size, dz + size);)
+
float dist = objDist2d - size;
float height = dist * (a * dist + b);
DEBUG_TRAJ(sLog.outError("Spell::SelectTrajTargets: dist %f, height %f.", dist, height);)
@@ -5820,21 +6647,26 @@ void Spell::SelectTrajTargets()
bestDist = dist > 0 ? dist : 0;
break;
}
+
#define CHECK_DIST {\
DEBUG_TRAJ(sLog.outError("Spell::SelectTrajTargets: dist %f, height %f.", dist, height);)\
if(dist > bestDist) continue;\
if(dist < objDist2d + size && dist > objDist2d - size) { bestDist = dist; break; }\
}
+
if(!a)
{
height = dz - size;
dist = height / b;
CHECK_DIST;
+
height = dz + size;
dist = height / b;
CHECK_DIST;
+
continue;
}
+
height = dz - size;
float sqrt1 = b * b + 4 * a * height;
if(sqrt1 > 0)
@@ -5843,6 +6675,7 @@ void Spell::SelectTrajTargets()
dist = (sqrt1 - b) / (2 * a);
CHECK_DIST;
}
+
height = dz + size;
float sqrt2 = b * b + 4 * a * height;
if(sqrt2 > 0)
@@ -5850,20 +6683,24 @@ void Spell::SelectTrajTargets()
sqrt2 = sqrt(sqrt2);
dist = (sqrt2 - b) / (2 * a);
CHECK_DIST;
+
dist = (-sqrt2 - b) / (2 * a);
CHECK_DIST;
}
+
if(sqrt1 > 0)
{
dist = (-sqrt1 - b) / (2 * a);
CHECK_DIST;
}
}
+
if(m_targets.m_srcPos.GetExactDist2d(&m_targets.m_dstPos) > bestDist)
{
float x = m_targets.m_srcPos.m_positionX + cos(m_caster->GetOrientation()) * bestDist;
float y = m_targets.m_srcPos.m_positionY + sin(m_caster->GetOrientation()) * bestDist;
float z = m_targets.m_srcPos.m_positionZ + bestDist * (a * bestDist + b);
+
if(itr != unitList.end())
{
float distSq = (*itr)->GetExactDistSq(x, y, z);
@@ -5876,23 +6713,29 @@ void Spell::SelectTrajTargets()
x += factor * ((*itr)->GetPositionX() - x);
y += factor * ((*itr)->GetPositionY() - y);
z += factor * ((*itr)->GetPositionZ() - z);
+
distSq = (*itr)->GetExactDistSq(x, y, z);
DEBUG_TRAJ(sLog.outError("Initial %f %f %f %f %f", x, y, z, distSq, sizeSq);)
}
}
+
m_targets.setDst(x, y, z);
}
}
+
void Spell::FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* target, float radius, bool raid, bool withPets, bool withcaster )
{
Player *pTarget = target->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 && (raid || subgroup==Target->GetSubGroup())
&& !m_caster->IsHostileTo(Target))
@@ -5900,6 +6743,7 @@ void Spell::FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* target, float ra
if (Target==m_caster && withcaster ||
Target!=m_caster && m_caster->IsWithinDistInMap(Target, radius))
TagUnitMap.push_back(Target);
+
if (withPets)
if (Pet* pet = Target->GetPet())
if (pet==m_caster && withcaster ||
@@ -5914,6 +6758,7 @@ void Spell::FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* target, float ra
if (ownerOrSelf==m_caster && withcaster ||
ownerOrSelf!=m_caster && m_caster->IsWithinDistInMap(ownerOrSelf, radius))
TagUnitMap.push_back(ownerOrSelf);
+
if (withPets)
if (Guardian* pet = ownerOrSelf->GetGuardianPet())
if (pet==m_caster && withcaster ||
@@ -5921,13 +6766,16 @@ void Spell::FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* target, float ra
TagUnitMap.push_back(pet);
}
}
+
void Spell::FillRaidOrPartyManaPriorityTargets( UnitList &TagUnitMap, Unit* target, float radius, uint32 count, bool raid, bool withPets, bool withCaster )
{
FillRaidOrPartyTargets(TagUnitMap,target,radius,raid,withPets,withCaster);
+
PrioritizeManaUnitQueue manaUsers;
for(UnitList::const_iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end() && manaUsers.size() < count; ++itr)
if ((*itr)->getPowerType() == POWER_MANA && !(*itr)->isDead())
manaUsers.push(PrioritizeManaUnitWraper(*itr));
+
TagUnitMap.clear();
while(!manaUsers.empty())
{
@@ -5935,17 +6783,20 @@ void Spell::FillRaidOrPartyManaPriorityTargets( UnitList &TagUnitMap, Unit* targ
manaUsers.pop();
}
}
+
void Spell::FillRaidOrPartyHealthPriorityTargets( UnitList &TagUnitMap, Unit* target, float radius, uint32 count, bool raid, bool withPets, bool withCaster )
{
FillRaidOrPartyTargets(TagUnitMap,target,radius,raid,withPets,withCaster);
+
PrioritizeHealthUnitQueue healthQueue;
for(UnitList::const_iterator itr = TagUnitMap.begin(); itr != TagUnitMap.end() && healthQueue.size() < count; ++itr)
if (!(*itr)->isDead())
healthQueue.push(PrioritizeHealthUnitWraper(*itr));
+
TagUnitMap.clear();
while(!healthQueue.empty())
{
TagUnitMap.push_back(healthQueue.top().getUnit());
healthQueue.pop();
}
-}
+} \ No newline at end of file
diff --git a/src/game/Spell.h b/src/game/Spell.h
index 5f77e72bdd9..f045a1415e0 100644
--- a/src/game/Spell.h
+++ b/src/game/Spell.h
@@ -17,15 +17,19 @@
* 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"
#include "SharedDefines.h"
+
class Unit;
class Player;
class GameObject;
class DynamicObject;
class Aura;
+
enum SpellCastTargetFlags
{
TARGET_FLAG_SELF = 0x00000000,
@@ -48,6 +52,7 @@ enum SpellCastTargetFlags
TARGET_FLAG_UNK2 = 0x00010000, // pguid, not used in any spells as of 3.0.3 (can be set dynamically)
TARGET_FLAG_GLYPH = 0x00020000 // used in glyph spells
};
+
enum SpellCastFlags
{
CAST_FLAG_NONE = 0x00000000,
@@ -74,12 +79,14 @@ enum SpellCastFlags
CAST_FLAG_UNKNOWN20 = 0x00100000,
CAST_FLAG_UNKNOWN7 = 0x00200000 // wotlk, rune cooldown list
};
+
enum SpellRangeFlag
{
SPELL_RANGE_DEFAULT = 0,
SPELL_RANGE_MELEE = 1, //melee
SPELL_RANGE_RANGED = 2, //hunter range and ranged weapon
};
+
enum SpellNotifyPushType
{
PUSH_NONE = 0,
@@ -92,37 +99,51 @@ enum SpellNotifyPushType
PUSH_CASTER_CENTER, //this is never used in grid search
PUSH_CHAIN,
};
+
bool IsQuestTameSpell(uint32 spellId);
+
namespace Trinity
{
struct SpellNotifierCreatureAndPlayer;
}
+
typedef std::list<Unit*> UnitList;
+
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_srcPos = target.m_srcPos;
m_dstPos.Relocate(target.m_dstPos);
+
m_elevation = target.m_elevation;
m_speed = target.m_speed;
+
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);
@@ -130,9 +151,11 @@ class SpellCastTargets
void setSrc(Position *pos);
void setDst(float x, float y, float z, uint32 mapId = MAPID_INVALID);
void setDst(Position *pos);
+
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; }
@@ -147,24 +170,30 @@ class SpellCastTargets
m_itemTargetEntry = m_itemTarget->GetEntry();
}
}
+
bool IsEmpty() const { return m_GOTargetGUID==0 && m_unitTargetGUID==0 && m_itemTarget==0 && m_CorpseTargetGUID==0; }
bool HasSrc() const { return m_targetMask & TARGET_FLAG_SOURCE_LOCATION; }
bool HasDst() const { return m_targetMask & TARGET_FLAG_DEST_LOCATION; }
bool HasTraj() const { return m_speed != 0; }
+
float GetDist2d() const { return m_srcPos.GetExactDist2d(&m_dstPos); }
float GetSpeedXY() const { return m_speed * cos(m_elevation); }
float GetSpeedZ() const { return m_speed * sin(m_elevation); }
+
void Update(Unit* caster);
+
Position m_srcPos;
WorldLocation m_dstPos;
float m_elevation, m_speed;
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;
@@ -172,6 +201,7 @@ class SpellCastTargets
uint64 m_itemTargetGUID;
uint32 m_itemTargetEntry;
};
+
struct SpellValue
{
explicit SpellValue(SpellEntry const *proto)
@@ -185,6 +215,7 @@ struct SpellValue
uint32 MaxAffectedTargets;
float RadiusMod;
};
+
enum SpellState
{
SPELL_STATE_NULL = 0,
@@ -194,6 +225,7 @@ enum SpellState
SPELL_STATE_IDLE = 4,
SPELL_STATE_DELAYED = 5
};
+
enum ReplenishType
{
REPLENISH_UNDEFINED = 0,
@@ -201,6 +233,7 @@ enum ReplenishType
REPLENISH_MANA = 21,
REPLENISH_RAGE = 22
};
+
enum SpellTargets
{
SPELL_TARGETS_NONE = 0,
@@ -210,11 +243,13 @@ enum SpellTargets
SPELL_TARGETS_CHAINHEAL,
SPELL_TARGETS_ANY,
};
+
class Spell
{
friend struct Trinity::SpellNotifierCreatureAndPlayer;
friend void Unit::SetCurrentCastedSpell( Spell * pSpell );
public:
+
void EffectNULL(uint32 );
void EffectUnused(uint32 );
void EffectDistract(uint32 i);
@@ -327,9 +362,12 @@ class Spell
void EffectPlayMusic(uint32 i);
void EffectSpecCount(uint32 i);
void EffectActivateSpec(uint32 i);
+
typedef std::set<Aura *> UsedSpellMods;
+
Spell( Unit* Caster, SpellEntry const *info, bool triggered, uint64 originalCasterGUID = 0, Spell** triggeringContainer = NULL, bool skipCheck = false );
~Spell();
+
void prepare(SpellCastTargets const* targets, AuraEffect* triggeredByAura = NULL);
void cancel();
void update(uint32 difftime);
@@ -337,43 +375,54 @@ class Spell
void finish(bool ok = true);
void TakePower();
void TakeAmmo();
+
void TakeRunePower();
void TakeReagents();
void TakeCastItem();
void TriggerSpell();
+
SpellCastResult CheckCast(bool strict);
SpellCastResult CheckPetCast(Unit* target);
+
// handlers
void handle_immediate();
uint64 handle_delayed(uint64 t_offset);
// handler helpers
void _handle_immediate_phase();
void _handle_finish_phase();
+
SpellCastResult CheckItems();
SpellCastResult CheckRange(bool strict);
SpellCastResult CheckPower();
SpellCastResult CheckRuneCost(uint32 runeCostID);
SpellCastResult CheckCasterAuras() const;
+
int32 CalculateDamage(uint8 i, Unit* target) { return m_caster->CalculateSpellDamage(m_spellInfo,i,m_currentBasePoints[i],target); }
+
bool HaveTargetsForEffect(uint8 effect) const;
void Delayed();
void DelayedChannel();
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 SelectSpellTargets();
void SelectEffectTargets(uint32 i, uint32 cur);
void SelectTrajTargets();
void FillRaidOrPartyTargets( UnitList &TagUnitMap, Unit* target, float radius, bool raid, bool withPets, bool withcaster );
void FillRaidOrPartyManaPriorityTargets( UnitList &TagUnitMap, Unit* target, float radius, uint32 count, bool raid, bool withPets, bool withcaster );
void FillRaidOrPartyHealthPriorityTargets( UnitList &TagUnitMap, Unit* target, float radius, uint32 count, bool raid, bool withPets, bool withcaster );
+
template<typename T> WorldObject* FindCorpseUsing();
+
bool CheckTarget( Unit* target, uint32 eff );
bool CanAutoCast(Unit* target);
void CheckSrc() { if(!m_targets.HasSrc()) m_targets.setSrc(m_caster); }
void CheckDst() { if(!m_targets.HasDst()) m_targets.setDst(m_caster); }
+
static void MANGOS_DLL_SPEC SendCastResult(Player* caster, SpellEntry const* spellInfo, uint8 cast_count, SpellCastResult result);
void SendCastResult(SpellCastResult result);
void SendSpellStart();
@@ -385,9 +434,11 @@ class Spell
void SendChannelStart(uint32 duration);
void SendResurrectRequest(Player* target);
void SendPlaySpellVisual(uint32 SpellID);
+
void HandleEffects(Unit *pUnitTarget,Item *pItemTarget,GameObject *pGOTarget,uint32 i);
void HandleThreatSpells(uint32 spellId);
//void HandleAddAura(Unit* Target);
+
const SpellEntry * const m_spellInfo;
int32 m_currentBasePoints[3]; // cache SpellEntry::EffectBasePoints and use for set custom base points
Item* m_CastItem;
@@ -397,7 +448,9 @@ class Spell
uint32 m_preCastSpell;
SpellCastTargets m_targets;
int8 m_comboPointGain;
+
UsedSpellMods m_appliedMods;
+
int32 GetCastTime() const { return m_casttime; }
bool IsAutoRepeat() const { return m_autoRepeat; }
void SetAutoRepeat(bool rep) { m_autoRepeat = rep; }
@@ -410,6 +463,7 @@ class Spell
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->AttributesEx2 & SPELL_ATTR_EX2_NOT_RESET_AUTOSHOT); }
+
bool IsDeletable() const { return !m_referencedFromCurrentSpell && !m_executedCurrently; }
void SetReferencedFromCurrent(bool yes) { m_referencedFromCurrentSpell = yes; }
bool IsInterruptable() const { return !m_executedCurrently; }
@@ -417,26 +471,40 @@ class Spell
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 CheckTargetCreatureType(Unit* target) const;
+
void AddTriggeredSpell(SpellEntry const* spell) { m_TriggerSpells.push_back(spell); }
+
void CleanupTargetList();
+
void SetSpellValue(SpellValueMod mod, int32 value);
//void SetSpellValue(SpellValueMod mod, float value);
protected:
+
void SendLoot(uint64 guid, LootType loottype);
+
Unit* const m_caster;
+
SpellValue * const m_spellValue;
+
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 damage enemies 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
@@ -445,18 +513,22 @@ class Spell
bool m_canReflect; // can reflect this spell?
bool m_autoRepeat;
uint8 m_runesState;
+
uint8 m_delayAtDamageCount;
bool isDelayableNoMore()
{
if(m_delayAtDamageCount >= 2)
return true;
+
m_delayAtDamageCount++;
return false;
}
+
// 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_referencedFromCurrentSpell; // mark as references to prevent deleted and access by dead pointers
bool m_executedCurrently; // mark as executed to prevent deleted and access by dead pointers
@@ -464,6 +536,7 @@ class Spell
bool m_needComboPoints;
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;
@@ -471,15 +544,19 @@ class Spell
int32 damage;
Aura * m_spellAura; // only used in DoAllEffectOnTarget
DynamicObject *m_spellDynObj; // only used in DoAllEffectOnTarget
+
// this is set in Spell Hit, but used in Apply Aura handler
DiminishingLevels m_diminishLevel;
DiminishingGroup m_diminishGroup;
+
// -------------------------------------------
GameObject* focusObject;
+
// Damage and healing in effects need just calculate
int32 m_damage; // Damge in effects count here
int32 m_healing; // Healing in effects count here
int32 m_healthLeech; // Health leech in effects for all targets count here
+
//******************************************
// Spell trigger system
//******************************************
@@ -488,6 +565,7 @@ class Spell
uint32 m_procEx;
bool m_canTrigger;
void prepareDataForTriggerSystem(AuraEffect * triggeredByAura);
+
//*****************************************
// Spell target subsystem
//*****************************************
@@ -507,6 +585,7 @@ class Spell
};
std::list<TargetInfo> m_UniqueTargetInfo;
uint8 m_needAliveTargetMask; // Mask req. alive targets
+
struct GOTargetInfo
{
uint64 targetGUID;
@@ -515,12 +594,14 @@ class Spell
bool processed:1;
};
std::list<GOTargetInfo> m_UniqueGOTargetInfo;
+
struct ItemTargetInfo
{
Item *item;
uint8 effectMask;
};
std::list<ItemTargetInfo> m_UniqueItemInfo;
+
void AddUnitTarget(Unit* target, uint32 effIndex);
void AddUnitTarget(uint64 unitGUID, uint32 effIndex);
void AddGOTarget(GameObject* target, uint32 effIndex);
@@ -542,26 +623,34 @@ class Spell
void SpellDamageSchoolDmg(uint32 i);
void SpellDamageWeaponDmg(uint32 i);
void SpellDamageHeal(uint32 i);
+
void GetSummonPosition(uint32 i, Position &pos, float radius = 0.0f, uint32 count = 0);
void SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const *properties);
+
SpellCastResult CanOpenLock(uint32 effIndex, uint32 lockid, SkillType& skillid, int32& reqSkillValue, int32& skillValue);
// -------------------------------------------
+
//List For Triggered Spells
typedef std::vector<SpellEntry const*> TriggerSpells;
TriggerSpells m_TriggerSpells;
typedef std::vector< std::pair<SpellEntry const*, int32> > ChanceTriggerSpells;
ChanceTriggerSpells m_ChanceTriggerSpells;
+
uint32 m_spellState;
uint32 m_timer;
+
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;
+
uint32 m_customAttr;
bool m_skipCheck;
uint32 m_effectMask;
uint8 m_auraScaleMask;
+
#ifdef MAP_BASED_RAND_GEN
int32 irand(int32 min, int32 max) { return int32 (m_caster->GetMap()->mtRand.randInt(max - min)) + min; }
uint32 urand(uint32 min, uint32 max) { return m_caster->GetMap()->mtRand.randInt(max - min) + min; }
@@ -570,6 +659,7 @@ class Spell
double rand_chance() { return m_caster->GetMap()->mtRand.randExc(100.0); }
#endif
};
+
namespace Trinity
{
struct TRINITY_DLL_DECL SpellNotifierCreatureAndPlayer
@@ -581,6 +671,7 @@ namespace Trinity
const Unit * const i_source;
uint32 i_entry;
const Position * const i_pos;
+
SpellNotifierCreatureAndPlayer(Unit *source, std::list<Unit*> &data, float radius, SpellNotifyPushType type,
SpellTargets TargetType = SPELL_TARGETS_ENEMY, const Position *pos = NULL, uint32 entry = 0)
: i_source(source), i_data(&data), i_radius(radius), i_push_type(type)
@@ -588,13 +679,16 @@ namespace Trinity
{
assert(i_source);
}
+
template<class T> inline void Visit(GridRefManager<T> &m)
{
for(typename GridRefManager<T>::iterator itr = m.begin(); itr != m.end(); ++itr)
{
Unit *target = (Unit*)itr->getSource();
+
if(!target->InSamePhase(i_source))
continue;
+
switch (i_TargetType)
{
case SPELL_TARGETS_ENEMY:
@@ -627,6 +721,7 @@ namespace Trinity
default:
break;
}
+
switch(i_push_type)
{
case PUSH_SRC_CENTER:
@@ -655,24 +750,29 @@ namespace Trinity
}
}
}
+
#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);
virtual bool IsDeletable() const;
diff --git a/src/game/SpellAuraDefines.h b/src/game/SpellAuraDefines.h
index f5af5e22f84..ffcbdacda34 100644
--- a/src/game/SpellAuraDefines.h
+++ b/src/game/SpellAuraDefines.h
@@ -19,8 +19,10 @@
*/
#ifndef TRINITY_SPELLAURADEFINES_H
#define TRINITY_SPELLAURADEFINES_H
+
#define MAX_AURAS 64 // client support up to 255, but it will cause problems with group auras updating
#define FRIENDLY_AA_REMOVE_TIME 2*IN_MILISECONDS
+
enum AURA_FLAGS
{
AFLAG_NONE = 0x00,
@@ -33,12 +35,14 @@ enum AURA_FLAGS
AFLAG_UNK2 = 0x40,
AFLAG_NEGATIVE = 0x80
};
+
//m_schoolAbsorb
enum DAMAGE_ABSORB_TYPE
{
ALL_DAMAGE_ABSORB = -2,
ONLY_MAGIC_ABSORB = -1,
};
+
enum AuraType
{
SPELL_AURA_NONE = 0,
@@ -338,6 +342,7 @@ enum AuraType
SPELL_AURA_PREVENT_REGENERATE_POWER,
TOTAL_AURAS = 295
};
+
enum AreaAuraType
{
AREA_AURA_PARTY,
diff --git a/src/game/SpellAuras.cpp b/src/game/SpellAuras.cpp
index 711605c2da7..77b21f17562 100644
--- a/src/game/SpellAuras.cpp
+++ b/src/game/SpellAuras.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -48,6 +49,7 @@
#include "GridNotifiersImpl.h"
#include "Vehicle.h"
#include "CellImpl.h"
+
#define Aura AuraEffect
pAuraHandler AuraHandler[TOTAL_AURAS]=
{
@@ -348,6 +350,7 @@ pAuraHandler AuraHandler[TOTAL_AURAS]=
&Aura::HandleNoImmediateEffect //294 SPELL_AURA_PREVENT_REGENERATE_POWER implemented in Player::Regenerate(Powers power)
};
#undef Aura
+
Aura::Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObject *source, Unit *caster, int32 *currentBasePoints, Item* castItem) :
m_spellProto(spellproto),
m_target(target), m_sourceGuid(source->GetGUID()), m_casterGuid(caster->GetGUID()), m_castItemGuid(castItem ? castItem->GetGUID() : 0),
@@ -357,18 +360,26 @@ Aura::Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObje
{
assert(target);
assert(spellproto && spellproto == sSpellStore.LookupEntry( spellproto->Id ) && "`info` must be pointer to sSpellStore element");
+
m_auraFlags = effMask;
+
if(m_spellProto->manaPerSecond || m_spellProto->manaPerSecondPerLevel)
m_timeCla = 1000;
+
m_isPassive = IsPassiveSpell(GetId());
+
m_isSingleTargetAura = IsSingleTargetSpell(m_spellProto);
+
//damage = caster->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,target);
m_maxduration = caster->CalcSpellDuration(m_spellProto);
+
if(m_maxduration == -1 || m_isPassive && m_spellProto->DurationIndex == 0)
m_permanent = true;
else
m_permanent = false;
+
Player* modOwner = caster->GetSpellModOwner();
+
if(!m_permanent && modOwner)
{
modOwner->ApplySpellMod(GetId(), SPELLMOD_DURATION, m_maxduration);
@@ -376,15 +387,20 @@ Aura::Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObje
if (m_maxduration<=0)
m_maxduration = 1;
}
+
m_duration = m_maxduration;
+
m_isDeathPersist = IsDeathPersistentSpell(m_spellProto);
+
m_procCharges = m_spellProto->procCharges;
if(modOwner)
modOwner->ApplySpellMod(GetId(), SPELLMOD_CHARGES, m_procCharges);
+
m_isRemovedOnShapeLost = (caster == target &&
m_spellProto->Stances &&
!(m_spellProto->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) &&
!(m_spellProto->Attributes & SPELL_ATTR_NOT_SHAPESHIFT));
+
for (uint8 i=0 ;i<MAX_SPELL_EFFECTS;++i)
{
if (m_auraFlags & (uint8(1) << i))
@@ -397,6 +413,7 @@ Aura::Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObje
m_partAuras[i] = NULL;
}
}
+
// Aura is positive when it is casted by friend and at least one aura is positive
// or when it is casted by enemy and at least one aura is negative
bool swap = false;
@@ -404,6 +421,7 @@ Aura::Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObje
m_positive = false;
else
m_positive = !caster->IsHostileTo(m_target);
+
for(uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if((1<<i & GetEffectMask()) && m_positive == IsPositiveEffect(GetId(), i))
@@ -415,6 +433,7 @@ Aura::Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObje
if (!swap)
m_positive = !m_positive;
}
+
Aura::~Aura()
{
// free part auras memory
@@ -422,23 +441,28 @@ Aura::~Aura()
if (m_partAuras[i])
delete m_partAuras[i];
}
+
AuraEffect::AuraEffect(Aura *parentAura, uint8 effIndex, int32 *currentBasePoints) :
m_parentAura(parentAura), m_spellmod(NULL), m_periodicTimer(0), m_isPeriodic(false), m_isAreaAura(false), m_isPersistent(false),
m_target(parentAura->GetTarget()), m_tickNumber(0)
, m_spellProto(parentAura->GetSpellProto()), m_effIndex(effIndex), m_auraName(AuraType(m_spellProto->EffectApplyAuraName[m_effIndex]))
{
assert(m_auraName < TOTAL_AURAS);
+
if(currentBasePoints)
m_currentBasePoints = *currentBasePoints;
else
m_currentBasePoints = m_spellProto->EffectBasePoints[m_effIndex];
+
Unit *caster = GetParentAura()->GetCaster();
if(caster)
m_amount = caster->CalculateSpellDamage(m_spellProto, m_effIndex, m_currentBasePoints, m_target);
else
m_amount = m_currentBasePoints + m_spellProto->EffectBaseDice[m_effIndex];
+
if (int32 amount = CalculateCrowdControlAuraAmount(caster))
m_amount = amount;
+
if(!m_amount && caster)
if(uint64 itemGUID = GetParentAura()->GetCastItemGUID())
if(Player *playerCaster = dynamic_cast<Player*>(caster))
@@ -460,37 +484,47 @@ m_target(parentAura->GetTarget()), m_tickNumber(0)
break;
}
}
+
if(m_amount)
break;
}
}
}
+
Player* modOwner = caster ? caster->GetSpellModOwner() : NULL;
m_amplitude = m_spellProto->EffectAmplitude[m_effIndex];
+
//apply casting time mods for channeled spells
if (modOwner && m_amplitude && IsChanneledSpell(m_spellProto))
modOwner->ModSpellCastTime(m_spellProto, m_amplitude);
+
// Apply periodic time mod
if(modOwner && m_amplitude)
modOwner->ApplySpellMod(GetId(), SPELLMOD_ACTIVATION_TIME, m_amplitude);
+
// Start periodic on next tick or at aura apply
if (!(m_spellProto->AttributesEx5 & SPELL_ATTR_EX5_START_PERIODIC_AT_APPLY))
m_periodicTimer += m_amplitude;
+
m_isApplied = false;
}
+
AreaAuraEffect::AreaAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints)
: AuraEffect(parentAura, effIndex, currentBasePoints)
{
m_removeTime = FRIENDLY_AA_REMOVE_TIME;
m_isAreaAura = true;
+
if (m_spellProto->Effect[effIndex] == SPELL_EFFECT_APPLY_AREA_AURA_ENEMY)
m_radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex]));
else
m_radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(GetSpellProto()->EffectRadiusIndex[m_effIndex]));
+
Unit *source = GetSource();
assert(source);
if(Player* modOwner = source->GetSpellModOwner()) // source or caster? should be the same
modOwner->ApplySpellMod(GetId(), SPELLMOD_RADIUS, m_radius);
+
switch(m_spellProto->Effect[effIndex])
{
case SPELL_EFFECT_APPLY_AREA_AURA_PARTY:
@@ -525,11 +559,13 @@ AreaAuraEffect::AreaAuraEffect(Aura * parentAura, uint32 effIndex, int32 *curren
break;
}
}
+
PersistentAreaAuraEffect::PersistentAreaAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints)
: AuraEffect(parentAura, effIndex, currentBasePoints)
{
m_isPersistent = true;
}
+
DynamicObject *PersistentAreaAuraEffect::GetSource() const
{
uint64 guid = GetParentAura()->GetSourceGUID();
@@ -537,6 +573,7 @@ DynamicObject *PersistentAreaAuraEffect::GetSource() const
return ObjectAccessor::GetObjectInWorld(guid, (DynamicObject*)NULL);
return NULL;
}
+
AuraEffect* CreateAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints)
{
// TODO: source should belong to aura, but not areaeffect. multiple areaaura/persistent aura should use one source
@@ -566,28 +603,35 @@ AuraEffect* CreateAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentB
}
return NULL;
}
+
Unit* Aura::GetCaster() const
{
if(m_casterGuid == m_target->GetGUID())
return m_target;
+
//return ObjectAccessor::GetUnit(*m_target,m_casterGuid);
//must return caster even if it's in another grid/map
return ObjectAccessor::GetObjectInWorld(m_casterGuid, (Unit*)NULL);
}
+
Unit* Aura::GetUnitSource() const
{
if(m_sourceGuid == m_target->GetGUID())
return m_target;
+
return ObjectAccessor::GetObjectInWorld(m_sourceGuid, (Unit*)NULL);
}
+
void Aura::Update(uint32 diff)
{
// TODO: store pointer to caster in aura class for update/mod handling code
+
if (m_duration > 0)
{
m_duration -= diff;
if (m_duration < 0)
m_duration = 0;
+
// all spells with manaPerSecond/manaPerSecondPerLevel have aura in effect 0
if(m_timeCla)
{
@@ -598,6 +642,7 @@ void Aura::Update(uint32 diff)
if(int32 manaPerSecond = m_spellProto->manaPerSecond + m_spellProto->manaPerSecondPerLevel * caster->getLevel())
{
m_timeCla += 1000 - diff;
+
Powers powertype = Powers(m_spellProto->powerType);
if(powertype == POWER_HEALTH)
{
@@ -623,6 +668,7 @@ void Aura::Update(uint32 diff)
}
}
}
+
// Apply charged spellmods for channeled auras
// used for example when triggered spell of spell:10 is modded
Spell *modSpell = NULL;
@@ -630,12 +676,15 @@ void Aura::Update(uint32 diff)
if(IS_PLAYER_GUID(GetCasterGUID()) && (modOwner = (Player*)GetCaster())
&& (modSpell = modOwner->FindCurrentSpellBySpellId(GetId())))
modOwner->SetSpellModTakingSpell(modSpell, true);
+
for (uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if (m_partAuras[i])
m_partAuras[i]->Update(diff);
+
if (modOwner)
modOwner->SetSpellModTakingSpell(modSpell, false);
}
+
void AuraEffect::Update(uint32 diff)
{
if (m_isPeriodic && (GetParentAura()->GetAuraDuration() >=0 || GetParentAura()->IsPassive() || GetParentAura()->IsPermanent()))
@@ -645,13 +694,16 @@ void AuraEffect::Update(uint32 diff)
else // tick also at m_periodicTimer==0 to prevent lost last tick in case max m_duration == (max m_periodicTimer)*N
{
++m_tickNumber;
+
// update before applying (aura can be removed in TriggerSpell or PeriodicTick calls)
m_periodicTimer += m_amplitude - diff;
+
if(!m_target->hasUnitState(UNIT_STAT_ISOLATED))
PeriodicTick();
}
}
}
+
void AreaAuraEffect::Update(uint32 diff)
{
// update for the source of the aura
@@ -664,9 +716,11 @@ void AreaAuraEffect::Update(uint32 diff)
m_target->RemoveAura(GetParentAura());
return;
}
+
if( !source->hasUnitState(UNIT_STAT_ISOLATED) )
{
std::list<Unit *> targets;
+
switch(m_areaAuraType)
{
case AREA_AURA_PARTY:
@@ -698,6 +752,7 @@ void AreaAuraEffect::Update(uint32 diff)
break;
}
}
+
for(std::list<Unit*>::iterator tIter = targets.begin(); tIter != targets.end(); tIter++)
{
if(Aura *aur = (*tIter)->GetAura(GetId(), GetCasterGUID()))
@@ -719,6 +774,7 @@ void AreaAuraEffect::Update(uint32 diff)
if(skip)
continue;
}
+
// Select lower rank of aura if needed
if(SpellEntry const *actualSpellInfo = spellmgr.SelectAuraRankForPlayerLevel(GetSpellProto(), (*tIter)->getLevel()))
{
@@ -727,6 +783,7 @@ void AreaAuraEffect::Update(uint32 diff)
if (newBp == m_spellProto->EffectBasePoints[m_effIndex])
newBp = actualSpellInfo->EffectBasePoints[m_effIndex];
(*tIter)->AddAuraEffect(actualSpellInfo, GetEffIndex(), source, caster, &newBp);
+
if(m_areaAuraType == AREA_AURA_ENEMY)
caster->CombatStart(*tIter);
}
@@ -739,12 +796,15 @@ void AreaAuraEffect::Update(uint32 diff)
// WARNING: the aura may get deleted during the update
// DO NOT access its members after update!
AuraEffect::Update(diff);
+
// Speedup - no need to do more checks
if (GetParentAura()->IsRemoved())
return;
+
// Caster may be deleted due to update
Unit *caster = GetCaster();
Unit *source = GetSource();
+
// 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
@@ -789,6 +849,7 @@ void AreaAuraEffect::Update(uint32 diff)
}
}
}
+
void PersistentAreaAuraEffect::Update(uint32 diff)
{
/*
@@ -812,20 +873,26 @@ void PersistentAreaAuraEffect::Update(uint32 diff)
return;
}
}
+
// 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
m_target->RemoveAura(GetParentAura());
}
+
void AuraEffect::ApplyModifier(bool apply, bool Real, bool changeAmount)
{
if (GetParentAura()->IsRemoved())
return;
+
if (apply)
HandleAuraEffectSpecificMods(true, Real, changeAmount);
+
(*this.*AuraHandler [m_auraName])(apply,Real, changeAmount);
+
if (!apply)
HandleAuraEffectSpecificMods(false, Real, changeAmount);
}
+
void AuraEffect::RecalculateAmount(bool applied)
{
Unit *caster = GetParentAura()->GetCaster();
@@ -841,34 +908,42 @@ void AuraEffect::RecalculateAmount(bool applied)
ApplyModifier(true,false,true);
}
}
+
void AuraEffect::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, GetCasterGUID());
}
+
void Aura::ApplyAllModifiers(bool apply, bool Real)
{
for (uint8 i = 0; i<MAX_SPELL_EFFECTS;++i)
if (m_partAuras[i])
m_partAuras[i]->ApplyModifier(apply, Real);
}
+
void Aura::HandleAuraSpecificMods(bool apply)
{
//**************************************************************************************
// Function called after applying all mods from aura or after removing all mods from it
//**************************************************************************************
+
//********************
// MODS AT AURA APPLY
//********************
@@ -882,6 +957,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
{
uint32 zone, area;
m_target->GetZoneAndAreaId(zone,area);
+
for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
{
// some auras remove at aura remove
@@ -896,6 +972,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
}
}
}
+
if (m_spellProto->SpellFamilyName == SPELLFAMILY_MAGE)
{
if (m_spellProto->SpellFamilyFlags[1] & 0x00000002 && m_spellProto->SpellFamilyFlags[2] & 0x00000008)
@@ -908,6 +985,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
if (roll_chance_i(aureff->GetAmount()))
{
uint32 spell_id = 0;
+
switch (aureff->GetId())
{
case 31571: spell_id = 57529; break;
@@ -940,6 +1018,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
Unit * caster = GetCaster();
if (!caster)
return;
+
// Polymorph Sound - Sheep && Penguin
if (m_spellProto->SpellIconID == 82 && m_spellProto->SpellVisual[0] == 12978)
{
@@ -959,6 +1038,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
Unit * caster = GetCaster();
if (!caster)
return;
+
// Improved Devouring Plague
if (AuraEffect const * aurEff = caster->GetDummyAura(SPELLFAMILY_PRIEST, 3790, 1))
{
@@ -966,12 +1046,13 @@ void Aura::HandleAuraSpecificMods(bool apply)
caster->CastCustomSpell(m_target, 63675, &basepoints0, NULL, NULL, true, NULL, GetPartAura(0));
}
}
- // Renew
+ // Renew
else if (GetSpellProto()->SpellFamilyFlags[0] & 0x00000040 && GetPartAura(0))
{
Unit * caster = GetCaster();
if (!caster)
return;
+
// Empowered Renew
if (AuraEffect const * aurEff = caster->GetDummyAura(SPELLFAMILY_PRIEST, 3021, 1))
{
@@ -999,6 +1080,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
Unit * caster = GetCaster();
if (!caster)
return;
+
AuraEffect * aurEff = NULL;
// Ebon Plaguebringer / Crypt Fever
Unit::AuraEffectList const& TalentAuras = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
@@ -1056,9 +1138,11 @@ void Aura::HandleAuraSpecificMods(bool apply)
}
}
}
+
//*******************************
// MODS AT AURA APPLY AND REMOVE
//*******************************
+
// Aura Mastery Triggered Spell Handler
// If apply Concentration Aura -> trigger -> apply Aura Mastery Immunity
// If remove Concentration Aura -> trigger -> remove Aura Mastery Immunity
@@ -1081,6 +1165,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
return;
}
}
+
// Bestial Wrath
if (GetSpellProto()->Id == 19574)
{
@@ -1097,11 +1182,13 @@ void Aura::HandleAuraSpecificMods(bool apply)
}
}
}
+
if (GetSpellSpecific(m_spellProto->Id) == SPELL_PRESENCE)
{
AuraEffect *bloodPresenceAura=0; // healing by damage done
AuraEffect *frostPresenceAura=0; // increased health
AuraEffect *unholyPresenceAura=0; // increased movement speed, faster rune recovery
+
// Improved Presences
Unit::AuraEffectList const& vDummyAuras = m_target->GetAurasByType(SPELL_AURA_DUMMY);
for(Unit::AuraEffectList::const_iterator itr = vDummyAuras.begin(); itr != vDummyAuras.end(); ++itr)
@@ -1131,6 +1218,7 @@ void Aura::HandleAuraSpecificMods(bool apply)
}
}
}
+
uint32 presence=GetId();
if (apply)
{
@@ -1186,9 +1274,11 @@ void Aura::HandleAuraSpecificMods(bool apply)
}
}
}
+
//*********************
// MODS AT AURA REMOVE
//*********************
+
if(!apply)
{
// Spell Reflection
@@ -1219,14 +1309,17 @@ void Aura::HandleAuraSpecificMods(bool apply)
Unit *caster = GetCaster();
if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *player = ((Player*)caster);
// Glyph of Guardian Spirit
if(AuraEffect * aurEff = player->GetAuraEffect(63231, 0))
{
if (!player->HasSpellCooldown(47788))
return;
+
player->RemoveSpellCooldown(m_spellProto->Id, true);
player->AddSpellCooldown(m_spellProto->Id, 0, uint32(time(NULL) + aurEff->GetAmount()));
+
WorldPacket data(SMSG_SPELL_COOLDOWN, 8+1+4+4);
data << uint64(player->GetGUID());
data << uint8(0x0); // flags (0x1, 0x2)
@@ -1283,13 +1376,16 @@ void Aura::HandleAuraSpecificMods(bool apply)
}
}
}
+
void AuraEffect::HandleAuraEffectSpecificMods(bool apply, bool Real, bool changeAmount)
{
//***********************************************************************
// Function called before aura effect handler apply or after it's remove
//***********************************************************************
+
if(!Real && !changeAmount)
return;
+
if(apply)
{
// prevent double apply bonuses
@@ -1392,10 +1488,13 @@ void AuraEffect::HandleAuraEffectSpecificMods(bool apply, bool Real, bool change
// 0.01*$AP*cp
if (caster->GetTypeId() != TYPEID_PLAYER)
return;
+
uint8 cp = ((Player*)caster)->GetComboPoints();
+
// Idol of Feral Shadows. Cant be handled as SpellMod in SpellAura:Dummy due its dependency from CPs
if (AuraEffect const * aurEff = caster->GetAuraEffect(34241,0))
m_amount += cp * aurEff->GetAmount();
+
m_amount += int32(caster->GetTotalAttackPowerValue(BASE_ATTACK) * cp / 100);
}
// TODO: i do not know what is this for so i simply disable it
@@ -1455,8 +1554,10 @@ void AuraEffect::HandleAuraEffectSpecificMods(bool apply, bool Real, bool change
{
int32 value = int32((m_amount*-1)-10);
uint32 defva = uint32(((Player*)caster)->GetSkillValue(SKILL_DEFENSE) + ((Player*)caster)->GetRatingBonusValue(CR_DEFENSE_SKILL));
+
if(defva > 400)
value += int32((defva-400)*0.15);
+
// Glyph of Icebound Fortitude
if (AuraEffect *auradummy = caster->GetAuraEffect(58625,0))
{
@@ -1472,6 +1573,7 @@ void AuraEffect::HandleAuraEffectSpecificMods(bool apply, bool Real, bool change
default:
break;
}
+
if (DoneActualBenefit != 0.0f)
{
DoneActualBenefit *= caster->CalculateLevelPenalty(GetSpellProto());
@@ -1480,13 +1582,16 @@ void AuraEffect::HandleAuraEffectSpecificMods(bool apply, bool Real, bool change
}
}
}
+
void Aura::SendAuraUpdate()
{
if (m_auraSlot>=MAX_AURAS)
return;
WorldPacket data(SMSG_AURA_UPDATE);
+
data.append(m_target->GetPackGUID());
data << uint8(m_auraSlot);
+
if(!m_target->GetVisibleAura(m_auraSlot))
{
data << uint32(0);
@@ -1494,10 +1599,12 @@ void Aura::SendAuraUpdate()
m_target->SendMessageToSet(&data, true);
return;
}
+
data << uint32(GetId());
data << uint8(m_auraFlags);
data << uint8(m_auraLevel);
data << uint8(m_stackAmount > 1 ? m_stackAmount : (m_procCharges) ? m_procCharges : 1);
+
if(!(m_auraFlags & AFLAG_CASTER))
{
if (Unit * caster = GetCaster())
@@ -1505,20 +1612,25 @@ void Aura::SendAuraUpdate()
else
data << uint8(0);
}
+
if(m_auraFlags & AFLAG_DURATION)
{
data << uint32(m_maxduration);
data << uint32(m_duration);
}
+
m_target->SendMessageToSet(&data, true);
}
+
bool Aura::IsVisible() const
{
// passive auras (except totem auras) do not get placed in the slots
// area auras with SPELL_AURA_NONE are not shown on target
//(m_spellProto->Attributes & 0x80 && GetTalentSpellPos(GetId()))
+
if(!m_isPassive)
return true;
+
bool noneAreaAura = true;
for(uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
@@ -1529,6 +1641,7 @@ bool Aura::IsVisible() const
if(Unit *source = ((AreaAuraEffect*)m_partAuras[i])->GetSource())
if(source->isTotem())
return true;
+
if(m_partAuras[i]->GetAuraName() != SPELL_AURA_NONE)
noneAreaAura = false;
}
@@ -1536,17 +1649,22 @@ bool Aura::IsVisible() const
noneAreaAura = false;
}
}
+
if(noneAreaAura)
return false;
+
return IsAuraType(SPELL_AURA_ABILITY_IGNORE_AURASTATE);
}
+
void Aura::_AddAura()
{
if (!GetId())
return;
if(!m_target)
return;
+
Unit* caster = GetCaster();
+
// set infinity cooldown state for spells
if(caster && caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -1556,6 +1674,7 @@ void Aura::_AddAura()
((Player*)caster)->AddSpellAndCategoryCooldowns(m_spellProto,castItem ? castItem->GetEntry() : 0, NULL,true);
}
}
+
if(IsVisible())
{
// Try find slot for aura
@@ -1580,6 +1699,7 @@ void Aura::_AddAura()
}
}
}
+
// Register Visible Aura
if(slot < MAX_AURAS)
{
@@ -1596,12 +1716,15 @@ void Aura::_AddAura()
else
sLog.outDebug("Aura: %u Effect: %d could not find empty unit visible slot",GetId(), GetEffectMask());
}
+
// Sitdown on apply aura req seated
if (m_spellProto->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED && !m_target->IsSitState())
m_target->SetStandState(UNIT_STAND_STATE_SIT);
+
// register aura diminishing on apply
if (getDiminishGroup() != DIMINISHING_NONE )
m_target->ApplyDiminishingAura(getDiminishGroup(),true);
+
// Apply linked auras (On first aura apply)
uint32 id = GetId();
if(spellmgr.GetSpellCustomAttr(id) & SPELL_ATTR_CU_LINK_AURA)
@@ -1615,8 +1738,10 @@ void Aura::_AddAura()
caster->AddAura(*itr, m_target);
}
}
+
HandleAuraSpecificMods(true);
}
+
bool Aura::SetPartAura(AuraEffect* aurEff, uint8 effIndex)
{
if (IsRemoved())
@@ -1629,10 +1754,13 @@ bool Aura::SetPartAura(AuraEffect* aurEff, uint8 effIndex)
SendAuraUpdate();
return true;
}
+
void Aura::_RemoveAura()
{
Unit* caster = GetCaster();
+
uint8 slot = GetAuraSlot();
+
if (Aura * foundAura = m_target->GetAura(GetId(), GetCasterGUID()))
{
// allow use single slot only by auras from same caster
@@ -1644,6 +1772,7 @@ void Aura::_RemoveAura()
slot = MAX_AURAS;
}
}
+
// update for out of range group members
if (slot < MAX_AURAS)
{
@@ -1651,13 +1780,16 @@ void Aura::_RemoveAura()
m_target->UpdateAuraForGroup(slot);
SendAuraUpdate();
}
+
// unregister aura diminishing (and store last time)
if (getDiminishGroup() != DIMINISHING_NONE )
m_target->ApplyDiminishingAura(getDiminishGroup(),false);
+
// since now aura cannot apply/remove it's modifiers
m_isRemoved = true;
// disable client server communication for removed aura
SetAuraSlot(MAX_AURAS);
+
// reset cooldown state for spells
if(caster && caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -1695,6 +1827,7 @@ void Aura::_RemoveAura()
}
}
}
+
// Proc on aura remove (only spell flags for now)
if (caster)
{
@@ -1721,6 +1854,7 @@ void Aura::_RemoveAura()
}
HandleAuraSpecificMods(false);
}
+
void Aura::SetStackAmount(uint8 stackAmount, bool applied)
{
bool refresh = stackAmount >= m_stackAmount;
@@ -1735,17 +1869,20 @@ void Aura::SetStackAmount(uint8 stackAmount, bool applied)
}
}
}
+
if (refresh)
RefreshAura();
else
SendAuraUpdate();
}
+
// TODO: lifebloom should bloom when each stack is dispelled
bool Aura::modStackAmount(int32 num)
{
// Can`t mod
if (!m_spellProto->StackAmount)
return true;
+
// Modify stack but limit it
int32 stackAmount = m_stackAmount + num;
if (stackAmount > m_spellProto->StackAmount)
@@ -1755,10 +1892,12 @@ bool Aura::modStackAmount(int32 num)
m_stackAmount = 0;
return true; // need remove aura
}
+
// Update stack amount
SetStackAmount(stackAmount);
return false;
}
+
void Aura::SetAuraDuration(int32 duration, bool withMods)
{
if (withMods)
@@ -1773,6 +1912,7 @@ void Aura::SetAuraDuration(int32 duration, bool withMods)
//m_permanent=false;
SendAuraUpdate();
}
+
void Aura::SetAuraCharges(uint8 charges)
{
if (m_procCharges == charges)
@@ -1780,6 +1920,7 @@ void Aura::SetAuraCharges(uint8 charges)
m_procCharges = charges;
SendAuraUpdate();
}
+
bool Aura::DropAuraCharge()
{
if(m_procCharges) //auras without charges always have charge = 0
@@ -1794,20 +1935,26 @@ bool Aura::DropAuraCharge()
}
return false;
}
+
bool Aura::CanBeSaved() const
{
if (IsPassive())
return false;
+
if(IsPersistent())
return false;
+
if (GetCasterGUID() != m_target->GetGUID())
if (IsSingleTargetSpell(GetSpellProto()) || IsAreaAura())
return false;
+
// Can't be saved - aura handler relies on calculated amount and changes it
if (IsAuraType(SPELL_AURA_CONVERT_RUNE))
return false;
+
return true;
}
+
bool Aura::IsPersistent() const
{
return IS_DYNAMICOBJECT_GUID(m_sourceGuid);
@@ -1818,6 +1965,7 @@ bool Aura::IsPersistent() const
return false;
*/
}
+
bool Aura::IsAreaAura() const
{
for(uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
@@ -1825,6 +1973,7 @@ bool Aura::IsAreaAura() const
return true;
return false;
}
+
bool Aura::IsAuraType(AuraType type) const
{
for(uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
@@ -1834,6 +1983,7 @@ bool Aura::IsAuraType(AuraType type) const
}
return false;
}
+
void Aura::SetLoadedState(uint64 caster_guid,int32 maxduration,int32 duration,int32 charges, uint8 stackamount, int32 * amount)
{
*const_cast<uint64*>(&m_casterGuid) = caster_guid;
@@ -1845,12 +1995,14 @@ void Aura::SetLoadedState(uint64 caster_guid,int32 maxduration,int32 duration,in
if(m_partAuras[i])
m_partAuras[i]->SetAmount(amount[i]);
}
+
void AuraEffect::HandleShapeshiftBoosts(bool apply)
{
uint32 spellId = 0;
uint32 spellId2 = 0;
uint32 spellId3 = 0;
uint32 HotWSpellId = 0;
+
switch(GetMiscValue())
{
case FORM_CAT:
@@ -1915,7 +2067,9 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply)
case FORM_CREATUREBEAR:
break;
}
+
uint32 form = GetMiscValue()-1;
+
if(apply)
{
// Remove cooldown of spells triggered on stance change - they may share cooldown with stance spell
@@ -1925,12 +2079,14 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply)
((Player *)m_target)->RemoveSpellCooldown(spellId);
m_target->CastSpell(m_target, spellId, true, NULL, this );
}
+
if (spellId2)
{
if(m_target->GetTypeId() == TYPEID_PLAYER)
((Player *)m_target)->RemoveSpellCooldown(spellId2);
m_target->CastSpell(m_target, spellId2, true, NULL, this);
}
+
if(m_target->GetTypeId() == TYPEID_PLAYER)
{
const PlayerSpellMap& sp_list = ((Player *)m_target)->GetSpellMap();
@@ -1961,6 +2117,7 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply)
int32 HotWMod = (*i)->GetAmount();
if(GetMiscValue() == FORM_CAT)
HotWMod /= 2;
+
m_target->CastCustomSpell(m_target, HotWSpellId, &HotWMod, NULL, NULL, true, NULL, this);
break;
}
@@ -2031,6 +2188,7 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply)
m_target->RemoveAurasDueToSpell(spellId);
if (spellId2)
m_target->RemoveAurasDueToSpell(spellId2);
+
Unit::AuraMap& tAuras = m_target->GetAuras();
for (Unit::AuraMap::iterator itr = tAuras.begin(); itr != tAuras.end();)
{
@@ -2045,6 +2203,7 @@ void AuraEffect::HandleShapeshiftBoosts(bool apply)
}
}
}
+
bool AuraEffect::isAffectedOnSpell(SpellEntry const *spell) const
{
if (!spell)
@@ -2052,11 +2211,13 @@ bool AuraEffect::isAffectedOnSpell(SpellEntry const *spell) const
// Check family name
if (spell->SpellFamilyName != m_spellProto->SpellFamilyName)
return false;
+
// Check EffectClassMask
if (m_spellProto->EffectSpellClassMask[m_effIndex] & spell->SpellFamilyFlags)
return true;
return false;
}
+
void Aura::UnregisterSingleCastAura()
{
if (IsSingleTarget())
@@ -2075,6 +2236,7 @@ void Aura::UnregisterSingleCastAura()
m_isSingleTargetAura = false;
}
}
+
/*********************************************************/
/*** BASIC AURA FUNCTION ***/
/*********************************************************/
@@ -2082,9 +2244,12 @@ void AuraEffect::HandleAddModifier(bool apply, bool Real, bool changeAmount)
{
if(m_target->GetTypeId() != TYPEID_PLAYER || (!Real && !changeAmount))
return;
+
uint32 modOp = GetMiscValue();
+
if(modOp >= MAX_SPELLMOD)
return;
+
if (apply)
{
SpellModifier *mod = new SpellModifier(GetParentAura());
@@ -2092,11 +2257,15 @@ void AuraEffect::HandleAddModifier(bool apply, bool Real, bool changeAmount)
mod->value = m_amount;
mod->type = SpellModType(m_auraName); // SpellModType value == spell aura types
mod->spellId = GetId();
+
mod->mask = m_spellProto->EffectSpellClassMask[m_effIndex];
mod->charges = GetParentAura()->GetAuraCharges();
+
m_spellmod = mod;
}
+
((Player*)m_target)->AddSpellMod(m_spellmod, apply);
+
// Auras with charges do not mod amount of passive auras
if (GetParentAura()->GetAuraCharges())
return;
@@ -2148,17 +2317,22 @@ void AuraEffect::HandleAddModifier(bool apply, bool Real, bool changeAmount)
break;
}
}
+
void AuraEffect::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];
+
SpellEntry const *triggeredSpellInfo = 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 (triggeredSpellInfo == NULL)
{
@@ -2210,6 +2384,7 @@ void AuraEffect::TriggerSpell()
{
int32 heal = caster->GetMaxHealth() / 10;
caster->DealHeal(m_target, heal, auraSpellInfo);
+
int32 mana = caster->GetMaxPower(POWER_MANA);
if (mana)
{
@@ -2263,7 +2438,9 @@ void AuraEffect::TriggerSpell()
// missing lootid has been reported on startup - just return
if (!creature->GetCreatureInfo()->SkinLootId)
return;
+
player->AutoStoreLoot(creature->GetCreatureInfo()->SkinLootId,LootTemplates_Skinning,true);
+
creature->ForcedDespawn();
}
return;
@@ -2307,8 +2484,11 @@ void AuraEffect::TriggerSpell()
{
if(m_target->GetTypeId() != TYPEID_UNIT)
return;
+
caster->CastSpell(caster, 38495, true, NULL, this);
+
Creature* creatureTarget = (Creature*)m_target;
+
creatureTarget->ForcedDespawn();
return;
}
@@ -2365,21 +2545,26 @@ void AuraEffect::TriggerSpell()
case 53304:
if (m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
if (((Player*)m_target)->isMoving())
{
m_amount = m_target->CalculateSpellDamage(m_spellProto,m_effIndex,m_currentBasePoints,m_target);
return;
}
+
// We are standing at the moment
if (m_amount > 0)
{
--m_amount;
return;
}
+
trigger_spell_id = 64418 + auraId - 53302;
+
// If aura is active - no need to continue
if (target->HasAura(trigger_spell_id))
return;
+
break;
default:
break;
@@ -2410,6 +2595,7 @@ void AuraEffect::TriggerSpell()
break;
}
}
+
if(all)
caster->CastSpell(caster,38437,true, NULL, this);
else
@@ -2424,6 +2610,7 @@ void AuraEffect::TriggerSpell()
default:
break;
}
+
// Reget trigger spell proto
triggeredSpellInfo = sSpellStore.LookupEntry(trigger_spell_id);
}
@@ -2461,6 +2648,7 @@ void AuraEffect::TriggerSpell()
return;
}
}
+
if(triggeredSpellInfo)
{
if(!caster->GetSpellMaxRangeForTarget(m_target,sSpellRangeStore.LookupEntry(triggeredSpellInfo->rangeIndex)))
@@ -2470,28 +2658,36 @@ void AuraEffect::TriggerSpell()
else if(target->GetTypeId()!=TYPEID_UNIT || !Script->EffectDummyCreature(caster, GetId(), GetEffIndex(), (Creature*)target))
sLog.outError("AuraEffect::TriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",GetId(),GetEffIndex());
}
+
Unit* AuraEffect::GetTriggerTarget() const
{
Unit* target = ObjectAccessor::GetUnit(*m_target, m_target->GetUInt64Value(UNIT_FIELD_TARGET));
return target ? target : m_target;
}
+
void AuraEffect::TriggerSpellWithValue()
{
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];
int32 basepoints0 = this->GetAmount();
+
caster->CastCustomSpell(target, trigger_spell_id, &basepoints0, 0, 0, true, 0, this);
}
+
/*********************************************************/
/*** AURA EFFECTS ***/
/*********************************************************/
+
void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
Unit* caster = GetCaster();
+
// spells required only Real aura add/remove
if (Real)
{
@@ -2499,7 +2695,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
if(apply)
{
// Overpower
- if (caster && m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR &&
+ if (caster && m_spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR &&
m_spellProto->SpellFamilyFlags[0] & 0x4)
{
// Must be casting target
@@ -2544,7 +2740,9 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
Unit * pet = m_target->GetGuardianPet();
if (!pet)
return;
+
m_target->CastSpell(m_target,34027,true,NULL,this);
+
// set 3 stacks and 3 charges (to make all auras not disappear at once)
Aura* owner_aura = m_target->GetAura(34027,GetCasterGUID());
Aura* pet_aura = pet->GetAura(58914, GetCasterGUID());
@@ -2626,11 +2824,13 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
m_target->CastSpell(target, 51699, true);
return;
}
+
//Druid, Survival Instincts
if(GetSpellProto()->SpellFamilyName==SPELLFAMILY_DRUID && GetSpellProto()->SpellFamilyFlags[2]& 0x40 )
{
if(!m_target)
return;
+
int32 bp0 = int32(m_target->GetMaxHealth() * m_amount / 100);
m_target->CastCustomSpell(m_target, 50322, &bp0, NULL, NULL, true);
}
@@ -2662,10 +2862,12 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
case 30102: finalSpelId = 30103; break;
case 30105: finalSpelId = 30104; break;
}
+
if(finalSpelId)
caster->CastSpell(m_target,finalSpelId,true,NULL,this);
return;
}
+
switch(m_spellProto->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
@@ -2687,6 +2889,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
if (m_target->GetMap()->IsDungeon())
{
uint32 spellId = m_target->GetMap()->IsHeroic() ? 46163 : 44190;
+
m_target->CastSpell(m_target, spellId, true, NULL, this);
}
return;
@@ -2755,7 +2958,9 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
}
}
}
+
// AT APPLY & REMOVE
+
switch(m_spellProto->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
@@ -2777,6 +2982,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
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;
@@ -2805,6 +3011,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
if (!caster)
return;
+
Unit *owner = caster->GetOwner();
if (owner && owner->GetTypeId() == TYPEID_PLAYER)
{
@@ -2820,6 +3027,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
if (!caster)
return;
+
Unit *owner = caster->GetOwner();
if (owner && owner->GetTypeId() == TYPEID_PLAYER)
{
@@ -2837,7 +3045,9 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 FactionID = 0;
+
if(apply)
{
switch(m_spellProto->Id)
@@ -2876,6 +3086,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
}
return;
}
+
break;
}
case SPELLFAMILY_MAGE:
@@ -2918,6 +3129,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
if (m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
if(apply)
{
SpellModifier *mod = new SpellModifier;
@@ -2926,8 +3138,10 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
mod->type = SPELLMOD_FLAT;
mod->spellId = GetId();
mod->mask[1] = 0x0010;
+
m_spellmod = mod;
}
+
((Player*)m_target)->AddSpellMod(m_spellmod, apply);
return;
}
@@ -2939,6 +3153,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
if (!m_target->IsInFeralForm())
return;
+
int32 bp0 = int32(m_target->GetMaxHealth() * m_amount / 100);
m_target->CastCustomSpell(m_target, 50322, &bp0, NULL, NULL, true);
}
@@ -2963,12 +3178,14 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
// Final heal only on dispelled or duration end
if (GetParentAura()->GetRemoveMode() != AURA_REMOVE_BY_EXPIRE && GetParentAura()->GetRemoveMode() != AURA_REMOVE_BY_ENEMY_SPELL)
return;
+
// final heal
//if(m_target->IsInWorld())
// This may be a hack, but we need a way to count healing bonus three times
//for(uint8 i = 0; i < GetParentAura()->GetStackAmount(); ++i)
// Update: apparently not anymore, it should only count once, so.. commented out.
m_target->CastCustomSpell(m_target,33778,&m_amount,NULL,NULL,true,NULL,this,GetCasterGUID());
+
// restore mana
if (caster)
{
@@ -3004,6 +3221,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
break;
}
}
+
if (Real)
{
// pet auras
@@ -3015,6 +3233,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
m_target->RemovePetAura(petSpell);
return;
}
+
if(GetEffIndex()==0 && m_target->GetTypeId()==TYPEID_PLAYER)
{
SpellAreaForAreaMapBounds saBounds = spellmgr.GetSpellAreaForAuraMapBounds(GetId());
@@ -3022,6 +3241,7 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
{
uint32 zone, area;
m_target->GetZoneAndAreaId(zone,area);
+
for(SpellAreaForAreaMap::const_iterator itr = saBounds.first; itr != saBounds.second; ++itr)
{
// some auras remove at aura remove
@@ -3038,11 +3258,13 @@ void AuraEffect::HandleAuraDummy(bool apply, bool Real, bool changeAmount)
}
}
}
+
void AuraEffect::HandleAuraMounted(bool apply, bool Real, bool /*changeAmount*/)
{
// only at real add/remove aura
if(!Real)
return;
+
if(apply)
{
CreatureInfo const* ci = objmgr.GetCreatureTemplate(GetMiscValue());
@@ -3051,13 +3273,16 @@ void AuraEffect::HandleAuraMounted(bool apply, bool Real, bool /*changeAmount*/)
sLog.outErrorDb("AuraMounted: `creature_template`='%u' not found in database (only need it modelid)",GetMiscValue());
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;
+
//some spell has one aura of mount and one of vehicle
for(uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
if(GetSpellProto()->Effect[i] == SPELL_EFFECT_SUMMON
@@ -3070,11 +3295,13 @@ void AuraEffect::HandleAuraMounted(bool apply, bool Real, bool /*changeAmount*/)
m_target->Unmount();
}
}
+
void AuraEffect::HandleAuraWaterWalk(bool apply, bool Real, bool /*changeAmount*/)
{
// only at real add/remove aura
if(!Real)
return;
+
WorldPacket data;
if(apply)
data.Initialize(SMSG_MOVE_WATER_WALK, 8+4);
@@ -3084,11 +3311,13 @@ void AuraEffect::HandleAuraWaterWalk(bool apply, bool Real, bool /*changeAmount*
data << uint32(0);
m_target->SendMessageToSet(&data,true);
}
+
void AuraEffect::HandleAuraFeatherFall(bool apply, bool Real, bool /*changeAmount*/)
{
// only at real add/remove aura
if(!Real)
return;
+
WorldPacket data;
if(apply)
data.Initialize(SMSG_MOVE_FEATHER_FALL, 8+4);
@@ -3097,15 +3326,18 @@ void AuraEffect::HandleAuraFeatherFall(bool apply, bool Real, bool /*changeAmoun
data.append(m_target->GetPackGUID());
data << uint32(0);
m_target->SendMessageToSet(&data, true);
+
// start fall from current height
if(!apply && m_target->GetTypeId() == TYPEID_PLAYER)
((Player*)m_target)->SetFallInformation(0, m_target->GetPositionZ());
}
+
void AuraEffect::HandleAuraHover(bool apply, bool Real, bool /*changeAmount*/)
{
// only at real add/remove aura
if(!Real)
return;
+
WorldPacket data;
if(apply)
data.Initialize(SMSG_MOVE_SET_HOVER, 8+4);
@@ -3115,16 +3347,19 @@ void AuraEffect::HandleAuraHover(bool apply, bool Real, bool /*changeAmount*/)
data << uint32(0);
m_target->SendMessageToSet(&data,true);
}
+
void AuraEffect::HandleWaterBreathing(bool apply, bool Real, bool /*changeAmount*/)
{
// update timers in client
if(m_target->GetTypeId()==TYPEID_PLAYER)
((Player*)m_target)->UpdateMirrorTimers();
}
+
void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmount)
{
if(!Real && !changeAmount)
return;
+
uint32 modelid = 0;
Powers PowerType = POWER_MANA;
ShapeshiftForm form = ShapeshiftForm(GetMiscValue());
@@ -3211,6 +3446,7 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
default:
sLog.outError("Auras: Unknown Shapeshift Type: %u", GetMiscValue());
}
+
// remove polymorph before changing display id to keep new display id
switch ( form )
{
@@ -3226,6 +3462,7 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
{
// remove movement affects
m_target->RemoveMovementImpairingAuras();
+
// and polymorphic affects
if(m_target->IsPolymorphed())
m_target->RemoveAurasDueToSpell(m_target->getTransForm());
@@ -3234,20 +3471,25 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
default:
break;
}
+
if(apply)
{
// remove other shapeshift before applying a new one
if(m_target->m_ShapeShiftFormSpellId)
m_target->RemoveAurasDueToSpell(m_target->m_ShapeShiftFormSpellId);
+
m_target->SetByteValue(UNIT_FIELD_BYTES_2, 3, form);
+
if(modelid > 0)
m_target->SetDisplayId(modelid);
+
if(PowerType != POWER_MANA)
{
uint32 oldVal = m_target->GetPower(PowerType);
// reset power to default values only at power change
if(m_target->getPowerType()!=PowerType)
m_target->setPowerType(PowerType);
+
switch(form)
{
case FORM_CAT:
@@ -3258,6 +3500,7 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
int32 FurorChance = 0;
if(AuraEffect const * dummy = m_target->GetDummyAura(SPELLFAMILY_DRUID, 238, 0))
FurorChance = dummy->GetAmount();
+
if (GetMiscValue() == FORM_CAT)
{
int32 basePoints = (FurorChance < oldVal ? FurorChance : oldVal);
@@ -3275,6 +3518,7 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
break;
}
}
+
m_target->m_ShapeShiftFormSpellId = GetId();
m_target->m_form = form;
}
@@ -3287,6 +3531,7 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
m_target->setPowerType(POWER_MANA);
m_target->m_ShapeShiftFormSpellId = 0;
m_target->m_form = FORM_NONE;
+
switch(form)
{
// Nordrassil Harness - bonus
@@ -3332,11 +3577,14 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
break;
}
}
+
// adding/removing linked auras
// add/remove the shapeshift aura's boosts
HandleShapeshiftBoosts(apply);
+
if(m_target->GetTypeId()==TYPEID_PLAYER)
((Player*)m_target)->InitDataForForm();
+
if(m_target->getClass() == CLASS_DRUID)
{
if(form == FORM_CAT && apply)
@@ -3373,6 +3621,7 @@ void AuraEffect::HandleAuraModShapeshift(bool apply, bool Real, bool changeAmoun
}
}
}
+
void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*/)
{
if (apply)
@@ -3383,6 +3632,7 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*
// player applied only
if (m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
switch (GetId())
{
// Orb of Deception
@@ -3451,22 +3701,28 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*
else
{
uint32 model_id;
+
if (uint32 modelid = ci->GetRandomValidModelId())
model_id = modelid; // Will use the default model here
+
// Polymorph (sheep)
if (GetSpellProto()->SpellFamilyName == SPELLFAMILY_MAGE && GetSpellProto()->SpellIconID == 82 && GetSpellProto()->SpellVisual[0] == 12978)
if (Unit * caster = GetCaster())
if (caster->HasAura(52648)) // Glyph of the Penguin
model_id = 26452;
+
m_target->SetDisplayId(model_id);
+
// Dragonmaw Illusion (set mount model also)
if(GetId()==42016 && m_target->GetMountID() && !m_target->GetAurasByType(SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED).empty())
m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314);
}
}
+
// update active transform spell only not set or not overwriting negative by positive case
if (!m_target->getTransForm() || !IsPositiveSpell(GetId()) || IsPositiveSpell(m_target->getTransForm()))
m_target->setTransForm(GetId());
+
// polymorph case
if (Real && m_target->GetTypeId() == TYPEID_PLAYER && m_target->IsPolymorphed())
{
@@ -3474,6 +3730,7 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*
// only if caster is Player (after patch 2.4.2)
if (IS_PLAYER_GUID(GetCasterGUID()) )
((Player*)m_target)->setRegenTimerCount(1*IN_MILISECONDS);
+
//dismount polymorphed target (after patch 2.4.2)
if (m_target->IsMounted())
m_target->RemoveAurasByType(SPELL_AURA_MOUNTED);
@@ -3484,6 +3741,7 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*
// ApplyModifier(true) will reapply it if need
m_target->setTransForm(0);
m_target->SetDisplayId(m_target->GetNativeDisplayId());
+
// re-aplly some from still active with preference negative cases
Unit::AuraEffectList const& otherTransforms = m_target->GetAurasByType(SPELL_AURA_TRANSFORM);
if (!otherTransforms.empty())
@@ -3501,6 +3759,7 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*
}
handledAura->ApplyModifier(true);
}
+
// Dragonmaw Illusion (restore mount model)
if (GetId()==42016 && m_target->GetMountID()==16314)
{
@@ -3512,38 +3771,49 @@ void AuraEffect::HandleAuraTransform(bool apply, bool Real, bool /*changeAmount*
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 AuraEffect::HandleForceReaction(bool apply, bool Real, bool changeAmount)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
if(!Real && !changeAmount)
return;
+
Player* player = (Player*)m_target;
+
uint32 faction_id = GetMiscValue();
uint32 faction_rank = m_amount;
+
player->GetReputationMgr().ApplyForceReaction(faction_id,ReputationRank(faction_rank),apply);
player->GetReputationMgr().SendForceReactions();
}
+
void AuraEffect::HandleAuraModSkill(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 prot=GetSpellProto()->EffectMiscValue[m_effIndex];
int32 points = m_amount;
+
((Player*)m_target)->ModifySkillBonus(prot,(apply ? points: -points),m_auraName==SPELL_AURA_MOD_SKILL_TALENT);
if(prot == SKILL_DEFENSE)
((Player*)m_target)->UpdateDefenseBonusesMod();
}
+
void AuraEffect::HandleChannelDeathItem(bool apply, bool Real, bool /*changeAmount*/)
{
if(Real && !apply)
@@ -3552,6 +3822,7 @@ void AuraEffect::HandleChannelDeathItem(bool apply, bool Real, bool /*changeAmou
Unit* victim = m_target;
if(!caster || caster->GetTypeId() != TYPEID_PLAYER || !victim)// || m_removeMode!=AURA_REMOVE_BY_DEATH)
return;
+
//we cannot check removemode = death
//talent will remove the caster's aura->interrupt channel->remove victim aura
if(victim->GetHealth() > 0)
@@ -3559,9 +3830,11 @@ void AuraEffect::HandleChannelDeathItem(bool apply, bool Real, bool /*changeAmou
// Item amount
if (m_amount <= 0)
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() <= Trinity::XP::GetGrayLevel(caster->getLevel()) ||
@@ -3570,6 +3843,7 @@ void AuraEffect::HandleChannelDeathItem(bool apply, bool Real, bool /*changeAmou
//Adding items
uint32 noSpaceForCount = 0;
uint32 count = m_amount;
+
ItemPosCountVec dest;
uint8 msg = ((Player*)caster)->CanStoreNewItem( NULL_BAG, NULL_SLOT, dest, spellInfo->EffectItemType[m_effIndex], count, &noSpaceForCount);
if( msg != EQUIP_ERR_OK )
@@ -3579,10 +3853,12 @@ void AuraEffect::HandleChannelDeathItem(bool apply, bool Real, bool /*changeAmou
if (count==0)
return;
}
+
Item* newitem = ((Player*)caster)->StoreNewItem(dest, spellInfo->EffectItemType[m_effIndex], true);
((Player*)caster)->SendNewItem(newitem, count, true, false);
}
}
+
void AuraEffect::HandleBindSight(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
@@ -3590,64 +3866,83 @@ void AuraEffect::HandleBindSight(bool apply, bool Real, bool /*changeAmount*/)
Unit* caster = GetCaster();
if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)caster)->SetViewpoint(m_target, apply);
}
+
void AuraEffect::HandleFarSight(bool apply, bool Real, bool /*changeAmount*/)
{
//Handled by client
}
+
void AuraEffect::HandleAuraTrackCreatures(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
m_target->SetUInt32Value(PLAYER_TRACK_CREATURES, apply ? ((uint32)1)<<(GetMiscValue()-1) : 0 );
}
+
void AuraEffect::HandleAuraTrackResources(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
m_target->SetUInt32Value(PLAYER_TRACK_RESOURCES, apply ? ((uint32)1)<<(GetMiscValue()-1): 0 );
}
+
void AuraEffect::HandleAuraTrackStealthed(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
m_target->ApplyModFlag(PLAYER_FIELD_BYTES,PLAYER_FIELD_BYTE_TRACK_STEALTHED,apply);
}
+
void AuraEffect::HandleAuraModScale(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplyPercentModFloatValue(OBJECT_FIELD_SCALE_X,m_amount,apply);
}
+
void AuraEffect::HandleAuraModPetTalentsPoints(bool Apply, bool Real, bool changeAmount)
{
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
// Recalculate pet tlaent points
if (Pet *pet = ((Player*)m_target)->GetPet())
- pet->InitTalentForLevel();
+ pet->InitTalentForLevel();
}
+
void AuraEffect::HandleModConfuse(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
//m_target->SetConfused(apply, GetCasterGUID(), GetId());
m_target->SetControlled(apply, UNIT_STAT_CONFUSED);
}
+
void AuraEffect::HandleModFear(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
return;
+
//m_target->SetFeared(apply, GetCasterGUID(), GetId());
m_target->SetControlled(apply, UNIT_STAT_FLEEING);
}
+
void AuraEffect::HandleFeignDeath(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
if( apply )
{
/*
@@ -3656,6 +3951,7 @@ void AuraEffect::HandleFeignDeath(bool apply, bool Real, bool /*changeAmount*/)
data<<uint8(0);
m_target->SendMessageToSet(&data,true);
*/
+
std::list<Unit*> targets;
Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(m_target, m_target, m_target->GetMap()->GetVisibilityDistance());
Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(m_target, targets, u_check);
@@ -3664,6 +3960,7 @@ void AuraEffect::HandleFeignDeath(bool apply, bool Real, bool /*changeAmount*/)
{
if(!(*iter)->hasUnitState(UNIT_STAT_CASTING))
continue;
+
for(uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
{
if((*iter)->GetCurrentSpell(i)
@@ -3679,9 +3976,11 @@ void AuraEffect::HandleFeignDeath(bool apply, bool Real, bool /*changeAmount*/)
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();
m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
+
// prevent interrupt message
if(GetCasterGUID()==m_target->GetGUID() && m_target->GetCurrentSpell(CURRENT_GENERIC_SPELL))
m_target->FinishSpell(CURRENT_GENERIC_SPELL, false);
@@ -3702,17 +4001,21 @@ void AuraEffect::HandleFeignDeath(bool apply, bool Real, bool /*changeAmount*/)
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 AuraEffect::HandleAuraModDisarm(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
return;
AuraType type = AuraType(GetAuraName());
+
//Prevent handling aura twice
if(apply ? m_target->GetAurasByType(type).size() > 1 : m_target->HasAuraType(type))
return;
+
uint32 field, flag, slot;
WeaponAttackType attType;
switch (type)
@@ -3738,8 +4041,10 @@ void AuraEffect::HandleAuraModDisarm(bool apply, bool Real, bool /*changeAmount*
default:
return;
}
+
if(!apply)
m_target->RemoveFlag(field, flag);
+
if (m_target->GetTypeId() == TYPEID_PLAYER)
{
// This is between the two because there is a check in _ApplyItemMods
@@ -3748,22 +4053,28 @@ void AuraEffect::HandleAuraModDisarm(bool apply, bool Real, bool /*changeAmount*
if(Item *pItem = ((Player*)m_target)->GetItemByPos( INVENTORY_SLOT_BAG_0, slot ))
((Player*)m_target)->_ApplyItemMods(pItem, slot, !apply);
}
+
if(apply)
m_target->SetFlag(field, flag);
+
if (m_target->GetTypeId() == TYPEID_UNIT && ((Creature*)m_target)->GetCurrentEquipmentId())
m_target->UpdateDamagePhysical(attType);
}
+
void AuraEffect::HandleModStealth(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
if(apply)
{
// drop flag at stealth in bg
m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
+
m_target->SetStandFlags(UNIT_STAND_FLAGS_CREEP);
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_STEALTH);
@@ -3773,21 +4084,26 @@ void AuraEffect::HandleModStealth(bool apply, bool Real, bool /*changeAmount*/)
m_target->RemoveStandFlags(UNIT_STAND_FLAGS_CREEP);
if(m_target->GetTypeId()==TYPEID_PLAYER)
m_target->RemoveFlag(PLAYER_FIELD_BYTES2, 0x2000);
+
if(m_target->GetVisibility() != VISIBILITY_OFF)
m_target->SetVisibility(VISIBILITY_ON);
}
}
+
void AuraEffect::HandleInvisibility(bool apply, bool Real, bool /*changeAmount*/)
{
if(apply)
{
m_target->m_invisibilityMask |= (1 << GetMiscValue());
+
if(Real)
{
m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
+
// apply glow vision
if(m_target->GetTypeId()==TYPEID_PLAYER)
m_target->SetFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+
m_target->SetToNotify();
}
}
@@ -3798,16 +4114,19 @@ void AuraEffect::HandleInvisibility(bool apply, bool Real, bool /*changeAmount*/
Unit::AuraEffectList const& auras = m_target->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
for(Unit::AuraEffectList::const_iterator itr = auras.begin(); itr != auras.end(); ++itr)
m_target->m_invisibilityMask |= (1 << GetMiscValue());
+
// only at real aura remove and if not have different invisibility auras.
if(Real)
{
// remove glow vision
if(!m_target->m_invisibilityMask && m_target->GetTypeId() == TYPEID_PLAYER)
m_target->RemoveFlag(PLAYER_FIELD_BYTES2,PLAYER_FIELD_BYTE2_INVISIBILITY_GLOW);
+
m_target->SetToNotify();
}
}
}
+
void AuraEffect::HandleInvisibilityDetect(bool apply, bool Real, bool /*changeAmount*/)
{
if(apply)
@@ -3825,11 +4144,13 @@ void AuraEffect::HandleInvisibilityDetect(bool apply, bool Real, bool /*changeAm
if(Real && m_target->GetTypeId()==TYPEID_PLAYER)
m_target->SetToNotify();
}
+
void AuraEffect::HandleAuraModSilence(bool apply, bool Real, bool /*changeAmount*/)
{
// only at real add/remove aura
if(!Real)
return;
+
if(apply)
{
m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_SILENCED);
@@ -3845,19 +4166,25 @@ void AuraEffect::HandleAuraModSilence(bool apply, bool Real, bool /*changeAmount
// 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 AuraEffect::HandleModThreat(bool apply, bool Real, bool changeAmount)
{
// only at real add/remove aura
if(!Real && !changeAmount)
return;
+
if (!m_target->isAlive())
return;
+
Unit* caster = GetCaster();
+
if (!caster || !caster->isAlive())
return;
+
int level_diff = 0;
int multiplier = 0;
if (apply && !m_target->isBeingLoaded())
@@ -3875,37 +4202,50 @@ void AuraEffect::HandleModThreat(bool apply, bool Real, bool changeAmount)
multiplier = 1;
break;
}
+
if (level_diff > 0)
m_amount += multiplier * level_diff;
}
+
if (m_target->GetTypeId() == TYPEID_PLAYER)
for(int8 x=0;x < MAX_SPELL_SCHOOL;x++)
if (GetMiscValue() & int32(1<<x))
ApplyPercentModFloatVar(m_target->m_threatModifier[x], m_amount, apply);
}
+
void AuraEffect::HandleAuraModTotalThreat(bool apply, bool Real, bool changeAmount)
{
// only at real add/remove aura
if(!Real && !changeAmount)
return;
+
if (!m_target->isAlive() || m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
Unit* caster = GetCaster();
+
if (!caster || !caster->isAlive())
return;
+
float threatMod = apply ? float(m_amount) : float(-m_amount);
+
m_target->getHostilRefManager().threatAssist(caster, threatMod);
}
+
void AuraEffect::HandleModTaunt(bool apply, bool Real, bool /*changeAmount*/)
{
// only at real add/remove aura
if (!Real)
return;
+
if (!m_target->isAlive() || !m_target->CanHaveThreatList())
return;
+
Unit* caster = GetCaster();
+
if (!caster || !caster->isAlive())
return;
+
if (apply)
m_target->TauntApply(caster);
else
@@ -3914,6 +4254,7 @@ void AuraEffect::HandleModTaunt(bool apply, bool Real, bool /*changeAmount*/)
m_target->TauntFadeOut(caster);
}
}
+
/*********************************************************/
/*** MODIFY SPEED ***/
/*********************************************************/
@@ -3922,26 +4263,32 @@ void AuraEffect::HandleAuraModIncreaseSpeed(bool apply, bool Real, bool changeAm
// all applied/removed only at real aura add/remove
if(!Real && !changeAmount)
return;
+
if(apply) // Dash wont work if you are not in cat form
if(m_spellProto->SpellFamilyName==SPELLFAMILY_DRUID && m_spellProto->SpellFamilyFlags[2] & 0x8 && m_target->m_form != FORM_CAT )
{
m_target->HandleAuraEffect(this, false);
return;
}
+
m_target->UpdateSpeed(MOVE_RUN, true);
}
+
void AuraEffect::HandleAuraModIncreaseMountedSpeed(bool /*apply*/, bool Real, bool changeAmount)
{
// all applied/removed only at real aura add/remove
if(!Real && !changeAmount)
return;
+
m_target->UpdateSpeed(MOVE_RUN, true);
}
+
void AuraEffect::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real, bool changeAmount)
{
// all applied/removed only at real aura add/remove
if(!Real && !changeAmount)
return;
+
// Enable Fly mode for flying mounts
if (m_auraName == SPELL_AURA_MOD_INCREASE_MOUNTED_FLIGHT_SPEED)
{
@@ -3953,27 +4300,34 @@ void AuraEffect::HandleAuraModIncreaseFlightSpeed(bool apply, bool Real, bool ch
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->HasAuraEffect(42016,0) && m_target->GetMountID())
m_target->SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID,16314);
}
+
m_target->UpdateSpeed(MOVE_FLIGHT, true);
}
+
void AuraEffect::HandleAuraModIncreaseSwimSpeed(bool /*apply*/, bool Real, bool changeAmount)
{
// all applied/removed only at real aura add/remove
if(!Real && !changeAmount)
return;
+
m_target->UpdateSpeed(MOVE_SWIM, true);
}
+
void AuraEffect::HandleAuraModDecreaseSpeed(bool apply, bool Real, bool changeAmount)
{
// all applied/removed only at real aura add/remove
if(!Real && !changeAmount)
return;
+
m_target->UpdateSpeed(MOVE_RUN, true);
m_target->UpdateSpeed(MOVE_SWIM, true);
m_target->UpdateSpeed(MOVE_FLIGHT, true);
@@ -3981,18 +4335,22 @@ void AuraEffect::HandleAuraModDecreaseSpeed(bool apply, bool Real, bool changeAm
m_target->UpdateSpeed(MOVE_SWIM_BACK, true);
m_target->UpdateSpeed(MOVE_FLIGHT_BACK, true);
}
+
void AuraEffect::HandleAuraModUseNormalSpeed(bool /*apply*/, bool Real, bool changeAmount)
{
// all applied/removed only at real aura add/remove
if(!Real && !changeAmount)
return;
+
m_target->UpdateSpeed(MOVE_RUN, true);
m_target->UpdateSpeed(MOVE_SWIM, true);
m_target->UpdateSpeed(MOVE_FLIGHT, true);
}
+
/*********************************************************/
/*** IMMUNITY ***/
/*********************************************************/
+
void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
@@ -4004,6 +4362,7 @@ void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real, bool /*change
immunity_list.push_back(SPELL_AURA_MOD_DISARM);
if (GetMiscValue() & (1<<1))
immunity_list.push_back(SPELL_AURA_TRANSFORM);
+
// These flag can be recognized wrong:
if (GetMiscValue() & (1<<6))
immunity_list.push_back(SPELL_AURA_MOD_DECREASE_SPEED);
@@ -4013,6 +4372,7 @@ void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real, bool /*change
immunity_list.push_back(SPELL_AURA_MOD_CONFUSE);
if (GetMiscValue() & (1<<9))
immunity_list.push_back(SPELL_AURA_MOD_FEAR);
+
// Patch 3.0.3 Bladestorm now breaks all snares and roots on the warrior when activated.
// however not all mechanic specified in immunity
if (apply && GetId()==46924)
@@ -4020,6 +4380,7 @@ void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real, bool /*change
m_target->RemoveAurasByType(SPELL_AURA_MOD_ROOT);
m_target->RemoveAurasByType(SPELL_AURA_MOD_DECREASE_SPEED);
}
+
if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
{
for (std::list <AuraType>::iterator iter = immunity_list.begin(); iter != immunity_list.end();++iter)
@@ -4032,12 +4393,14 @@ void AuraEffect::HandleModStateImmunityMask(bool apply, bool Real, bool /*change
m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,*iter,apply);
}
}
+
void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
return;
uint32 mechanic;
mechanic = 1 << GetMiscValue();
+
//immune movement impairment and loss of control
if(GetId()==42292 || GetId()==59752)
mechanic=IMMUNE_TO_MOVEMENT_IMPAIRMENT_AND_LOSS_CONTROL_MASK;
@@ -4045,8 +4408,10 @@ void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeA
// in DBC wrong mechanic immune since 3.0.x
else if (GetId() == 25771)
mechanic = 1 << MECHANIC_IMMUNE_SHIELD;
+
if (!mechanic)
return;
+
if(apply && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
{
Unit::AuraMap& Auras = m_target->GetAuras();
@@ -4067,16 +4432,20 @@ void AuraEffect::HandleModMechanicImmunity(bool apply, bool Real, bool /*changeA
++iter;
}
}
+
m_target->ApplySpellImmune(GetId(),IMMUNITY_MECHANIC,GetMiscValue(),apply);
+
// Demonic Empowerment -- voidwalker -- missing movement impairing effects immunity
if (GetId() == 54508)
{
if (apply)
m_target->RemoveMovementImpairingAuras();
+
m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,SPELL_AURA_MOD_ROOT,apply);
m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,SPELL_AURA_MOD_DECREASE_SPEED,apply);
}
}
+
void AuraEffect::HandleAuraModEffectImmunity(bool apply, bool Real, bool /*changeAmount*/)
{
// when removing flag aura, handle flag drop
@@ -4094,25 +4463,32 @@ void AuraEffect::HandleAuraModEffectImmunity(bool apply, bool Real, bool /*chang
sOutdoorPvPMgr.HandleDropFlag((Player*)m_target,GetSpellProto()->Id);
}
}
+
m_target->ApplySpellImmune(GetId(),IMMUNITY_EFFECT,GetMiscValue(),apply);
}
+
void AuraEffect::HandleAuraModStateImmunity(bool apply, bool Real, bool /*changeAmount*/)
{
if(apply && Real && GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY)
{
m_target->RemoveAurasByType(AuraType(GetMiscValue()), NULL , GetParentAura());
}
+
m_target->ApplySpellImmune(GetId(),IMMUNITY_STATE,GetMiscValue(),apply);
}
+
void AuraEffect::HandleAuraModSchoolImmunity(bool apply, bool Real, bool /*changeAmount*/)
{
if(apply && GetMiscValue() == SPELL_SCHOOL_MASK_NORMAL)
m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
+
m_target->ApplySpellImmune(GetId(),IMMUNITY_SCHOOL,GetMiscValue(),apply);
+
// remove all flag auras (they are positive, but they must be removed when you are immune)
if( this->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY
&& this->GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_DAMAGE_REDUCED_SHIELD )
m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
+
// TODO: optimalize this cycle - use RemoveAurasWithInterruptFlags call or something else
if( Real && apply
&& GetSpellProto()->AttributesEx & SPELL_ATTR_EX_DISPEL_AURAS_ON_IMMUNITY
@@ -4142,17 +4518,21 @@ void AuraEffect::HandleAuraModSchoolImmunity(bool apply, bool Real, bool /*chang
m_target->clearUnitState(UNIT_STAT_ISOLATED);
}
}
+
void AuraEffect::HandleAuraModDmgImmunity(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplySpellImmune(GetId(),IMMUNITY_DAMAGE,GetMiscValue(),apply);
}
+
void AuraEffect::HandleAuraModDispelImmunity(bool apply, bool Real, bool /*changeAmount*/)
{
// all applied/removed only at real aura add/remove
if(!Real)
return;
+
m_target->ApplySpellDispelImmunity(m_spellProto, DispelType(GetMiscValue()), apply);
}
+
void AuraEffect::HandleAuraProcTriggerSpell(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
@@ -4173,6 +4553,7 @@ void AuraEffect::HandleAuraProcTriggerSpell(bool apply, bool Real, bool /*change
((Player*)m_target)->AddSpellMod(m_spellmod, apply);
}
}
+
void AuraEffect::HandleAuraModStalked(bool apply, bool Real, bool /*changeAmount*/)
{
// used by spells: Hunter's Mark, Mind Vision, Syndicate Tracker (MURP) DND
@@ -4181,31 +4562,39 @@ void AuraEffect::HandleAuraModStalked(bool apply, bool Real, bool /*changeAmount
else
m_target->RemoveFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_TRACK_UNIT);
}
+
/*********************************************************/
/*** PERIODIC ***/
/*********************************************************/
+
void AuraEffect::HandlePeriodicTriggerSpell(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicTriggerSpellWithValue(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicEnergize(bool apply, bool Real, bool changeAmount)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandleAuraPowerBurn(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandleAuraPeriodicDummy(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
Unit* caster = GetCaster();
+
SpellEntry const*spell = GetSpellProto();
switch( spell->SpellFamilyName)
{
@@ -4251,38 +4640,48 @@ void AuraEffect::HandleAuraPeriodicDummy(bool apply, bool Real, bool changeAmoun
break;
}
}
+
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicHeal(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicDamage(bool apply, bool Real, bool changeAmount)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicDamagePCT(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicLeech(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicManaLeech(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandlePeriodicHealthFunnel(bool apply, bool Real, bool /*changeAmount*/)
{
m_isPeriodic = apply;
}
+
/*********************************************************/
/*** MODIFY STATS ***/
/*********************************************************/
+
/********************************/
/*** RESISTANCE ***/
/********************************/
+
void AuraEffect::HandleAuraModResistanceExclusive(bool apply, bool Rea, bool /*changeAmount*/)
{
for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
@@ -4295,6 +4694,7 @@ void AuraEffect::HandleAuraModResistanceExclusive(bool apply, bool Rea, bool /*c
}
}
}
+
void AuraEffect::HandleAuraModResistance(bool apply, bool Real, bool /*changeAmount*/)
{
for(int8 x = SPELL_SCHOOL_NORMAL; x < MAX_SPELL_SCHOOL;x++)
@@ -4307,6 +4707,7 @@ void AuraEffect::HandleAuraModResistance(bool apply, bool Real, bool /*changeAmo
}
}
}
+
void AuraEffect::HandleAuraModBaseResistancePCT(bool apply, bool Real, bool /*changeAmount*/)
{
// only players have base stats
@@ -4325,6 +4726,7 @@ void AuraEffect::HandleAuraModBaseResistancePCT(bool apply, bool Real, bool /*ch
}
}
}
+
void AuraEffect::HandleModResistancePercent(bool apply, bool Real, bool /*changeAmount*/)
{
for(int8 i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; i++)
@@ -4340,6 +4742,7 @@ void AuraEffect::HandleModResistancePercent(bool apply, bool Real, bool /*change
}
}
}
+
void AuraEffect::HandleModBaseResistance(bool apply, bool Real, bool /*changeAmount*/)
{
// only players have base stats
@@ -4356,9 +4759,11 @@ void AuraEffect::HandleModBaseResistance(bool apply, bool Real, bool /*changeAmo
m_target->HandleStatModifier(UnitMods(UNIT_MOD_RESISTANCE_START + i), TOTAL_VALUE, float(m_amount), apply);
}
}
+
/********************************/
/*** STAT ***/
/********************************/
+
void AuraEffect::HandleAuraModStat(bool apply, bool Real, bool /*changeAmount*/)
{
if (GetMiscValue() < -2 || GetMiscValue() > 4)
@@ -4366,6 +4771,7 @@ void AuraEffect::HandleAuraModStat(bool apply, bool Real, bool /*changeAmount*/)
sLog.outError("WARNING: Spell %u effect %u have unsupported misc value (%i) for SPELL_AURA_MOD_STAT ",GetId(),GetEffIndex(),GetMiscValue());
return;
}
+
for(int32 i = STAT_STRENGTH; i < MAX_STATS; i++)
{
// -1 or -2 is all stats ( misc < -2 checked in function beginning )
@@ -4378,6 +4784,7 @@ void AuraEffect::HandleAuraModStat(bool apply, bool Real, bool /*changeAmount*/)
}
}
}
+
void AuraEffect::HandleModPercentStat(bool apply, bool Real, bool /*changeAmount*/)
{
if (GetMiscValue() < -1 || GetMiscValue() > 4)
@@ -4385,47 +4792,58 @@ void AuraEffect::HandleModPercentStat(bool apply, bool Real, bool /*changeAmount
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(GetMiscValue() == i || GetMiscValue() == -1)
m_target->HandleStatModifier(UnitMods(UNIT_MOD_STAT_START + i), BASE_PCT, float(m_amount), apply);
}
}
+
void AuraEffect::HandleModSpellDamagePercentFromStat(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
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 AuraEffect::HandleModSpellHealingPercentFromStat(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
// Recalculate bonus
((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
}
+
void AuraEffect::HandleModSpellDamagePercentFromAttackPower(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
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 AuraEffect::HandleModSpellHealingPercentFromAttackPower(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
// Recalculate bonus
((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
}
+
void AuraEffect::HandleModHealingDone(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
@@ -4434,6 +4852,7 @@ void AuraEffect::HandleModHealingDone(bool /*apply*/, bool Real, bool /*changeAm
// this information is for client side only
((Player*)m_target)->UpdateSpellDamageAndHealingBonus();
}
+
void AuraEffect::HandleModTotalPercentStat(bool apply, bool Real, bool /*changeAmount*/)
{
if (GetMiscValue() < -1 || GetMiscValue() > 4)
@@ -4441,9 +4860,11 @@ void AuraEffect::HandleModTotalPercentStat(bool apply, bool Real, bool /*changeA
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(GetMiscValue() == i || GetMiscValue() == -1)
@@ -4453,6 +4874,7 @@ void AuraEffect::HandleModTotalPercentStat(bool apply, bool Real, bool /*changeA
m_target->ApplyStatPercentBuffMod(Stats(i), m_amount, apply );
}
}
+
//recalculate current HP/MP after applying aura modifications (only for spells with 0x10 flag)
if ((GetMiscValue() == STAT_STAMINA) && (maxHPValue > 0) && (m_spellProto->Attributes & 0x10))
{
@@ -4461,10 +4883,12 @@ void AuraEffect::HandleModTotalPercentStat(bool apply, bool Real, bool /*changeA
m_target->SetHealth(newHPValue);
}
}
+
void AuraEffect::HandleAuraModResistenceOfStatPercent(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
if(GetMiscValue() != SPELL_SCHOOL_MASK_NORMAL)
{
// support required adding replace UpdateArmor by loop by UpdateResistence at intellect update
@@ -4472,9 +4896,11 @@ void AuraEffect::HandleAuraModResistenceOfStatPercent(bool /*apply*/, bool Real,
sLog.outError("Aura SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT(182) need adding support for non-armor resistances!");
return;
}
+
// Recalculate Armor
m_target->UpdateArmor();
}
+
/********************************/
/*** HEAL & ENERGIZE ***/
/********************************/
@@ -4482,24 +4908,30 @@ void AuraEffect::HandleAuraModTotalHealthPercentRegen(bool apply, bool Real, boo
{
m_isPeriodic = apply;
}
+
void AuraEffect::HandleAuraModTotalEnergyPercentRegen(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_amplitude == 0)
m_amplitude = 1000;
+
m_periodicTimer = m_amplitude;
m_isPeriodic = apply;
}
+
void AuraEffect::HandleModRegen(bool apply, bool Real, bool /*changeAmount*/) // eating
{
if(m_amplitude == 0)
m_amplitude = 5000;
+
m_periodicTimer = 5000;
m_isPeriodic = apply;
}
+
void AuraEffect::HandleModPowerRegen(bool apply, bool Real, bool changeAmount) // drinking
{
if(!Real && !changeAmount)
return;
+
Powers pt = m_target->getPowerType();
if(m_amplitude == 0)
{
@@ -4509,32 +4941,42 @@ void AuraEffect::HandleModPowerRegen(bool apply, bool Real, bool changeAmount)
else
m_amplitude = 2000;
}
+
m_periodicTimer = 5000;
+
if (m_target->GetTypeId() == TYPEID_PLAYER && GetMiscValue() == POWER_MANA)
((Player*)m_target)->UpdateManaRegen();
+
m_isPeriodic = apply;
}
+
void AuraEffect::HandleModPowerRegenPCT(bool /*apply*/, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if (m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
// Update manaregen value
if (GetMiscValue() == POWER_MANA)
((Player*)m_target)->UpdateManaRegen();
}
+
void AuraEffect::HandleModManaRegen(bool /*apply*/, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if (m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
//Note: an increase in regen does NOT cause threat.
((Player*)m_target)->UpdateManaRegen();
}
+
void AuraEffect::HandleComprehendLanguage(bool apply, bool Real, bool /*changeAmount*/)
{
if(apply)
@@ -4542,6 +4984,7 @@ void AuraEffect::HandleComprehendLanguage(bool apply, bool Real, bool /*changeAm
else
m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_COMPREHEND_LANG);
}
+
void AuraEffect::HandleAuraModIncreaseHealth(bool apply, bool Real, bool changeAmount)
{
if(Real || changeAmount)
@@ -4561,26 +5004,33 @@ void AuraEffect::HandleAuraModIncreaseHealth(bool apply, bool Real, bool changeA
}
}
}
+
void AuraEffect::HandleAuraModIncreaseMaxHealth(bool apply, bool Real, bool /*changeAmount*/)
{
uint32 oldhealth = m_target->GetHealth();
double healthPercentage = (double)oldhealth / (double)m_target->GetMaxHealth();
+
m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_VALUE, float(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 AuraEffect::HandleAuraModIncreaseEnergy(bool apply, bool Real, bool /*changeAmount*/)
{
Powers powerType = m_target->getPowerType();
if(int32(powerType) != GetMiscValue())
return;
+
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + powerType);
+
// Special case with temporary increase max/current power (percent)
if (GetId()==64904) // Hymn of Hope
{
@@ -4592,61 +5042,79 @@ void AuraEffect::HandleAuraModIncreaseEnergy(bool apply, bool Real, bool /*chang
}
return;
}
+
// generic flat case
m_target->HandleStatModifier(unitMod, TOTAL_VALUE, float(m_amount), apply);
}
+
void AuraEffect::HandleAuraModIncreaseEnergyPercent(bool apply, bool /*Real*/, bool /*changeAmount*/)
{
Powers powerType = m_target->getPowerType();
if(int32(powerType) != GetMiscValue())
return;
+
UnitMods unitMod = UnitMods(UNIT_MOD_POWER_START + powerType);
+
m_target->HandleStatModifier(unitMod, TOTAL_PCT, float(m_amount), apply);
}
+
void AuraEffect::HandleAuraModIncreaseHealthPercent(bool apply, bool /*Real*/, bool /*changeAmount*/)
{
m_target->HandleStatModifier(UNIT_MOD_HEALTH, TOTAL_PCT, float(m_amount), apply);
}
+
void AuraEffect::HandleAuraIncreaseBaseHealthPercent(bool apply, bool /*Real*/, bool /*changeAmount*/)
{
m_target->HandleStatModifier(UNIT_MOD_HEALTH, BASE_PCT, float(m_amount), apply);
}
+
/********************************/
/*** FIGHT ***/
/********************************/
+
void AuraEffect::HandleAuraModParryPercent(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
((Player*)m_target)->UpdateParryPercentage();
}
+
void AuraEffect::HandleAuraModDodgePercent(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
((Player*)m_target)->UpdateDodgePercentage();
//sLog.outError("BONUS DODGE CHANCE: + %f", float(m_amount));
}
+
void AuraEffect::HandleAuraModBlockPercent(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
((Player*)m_target)->UpdateBlockPercentage();
//sLog.outError("BONUS BLOCK CHANCE: + %f", float(m_amount));
}
+
void AuraEffect::HandleAuraModRegenInterrupt(bool /*apply*/, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
((Player*)m_target)->UpdateManaRegen();
}
+
void AuraEffect::HandleAuraModWeaponCritPercent(bool apply, bool Real, bool changeAmount)
{
if(m_target->GetTypeId()!=TYPEID_PLAYER)
return;
+
// apply item specific bonuses for already equipped weapon
if(Real || changeAmount)
{
@@ -4654,9 +5122,11 @@ void AuraEffect::HandleAuraModWeaponCritPercent(bool apply, bool Real, bool chan
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
// GetMiscValue() comparison with item generated damage types
+
if (GetSpellProto()->EquippedItemClass == -1)
{
((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
@@ -4668,6 +5138,7 @@ void AuraEffect::HandleAuraModWeaponCritPercent(bool apply, bool Real, bool chan
// done in Player::_ApplyWeaponDependentAuraMods
}
}
+
void AuraEffect::HandleModHitChance(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() == TYPEID_PLAYER)
@@ -4681,6 +5152,7 @@ void AuraEffect::HandleModHitChance(bool apply, bool Real, bool /*changeAmount*/
m_target->m_modRangedHitChance += apply ? m_amount : (-m_amount);
}
}
+
void AuraEffect::HandleModSpellHitChance(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() == TYPEID_PLAYER)
@@ -4688,40 +5160,49 @@ void AuraEffect::HandleModSpellHitChance(bool apply, bool Real, bool /*changeAmo
else
m_target->m_modSpellHitChance += apply ? m_amount: (-m_amount);
}
+
void AuraEffect::HandleModSpellCritChance(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId() == TYPEID_PLAYER)
((Player*)m_target)->UpdateAllSpellCritChances();
else
m_target->m_baseSpellCritChance += apply ? m_amount:-m_amount;
}
+
void AuraEffect::HandleModSpellCritChanceShool(bool /*apply*/, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
for(int school = SPELL_SCHOOL_NORMAL; school < MAX_SPELL_SCHOOL; ++school)
if (GetMiscValue() & (1<<school))
((Player*)m_target)->UpdateSpellCritChance(school);
}
+
/********************************/
/*** ATTACK SPEED ***/
/********************************/
+
void AuraEffect::HandleModCastingSpeed(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplyCastTimePercentMod(m_amount,apply);
}
+
void AuraEffect::HandleModMeleeRangedSpeedPct(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_amount,apply);
m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_amount,apply);
m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_amount, apply);
}
+
void AuraEffect::HandleModCombatSpeedPct(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplyCastTimePercentMod(m_amount,apply);
@@ -4729,76 +5210,94 @@ void AuraEffect::HandleModCombatSpeedPct(bool apply, bool Real, bool /*changeAmo
m_target->ApplyAttackTimePercentMod(OFF_ATTACK,m_amount,apply);
m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_amount, apply);
}
+
void AuraEffect::HandleModAttackSpeed(bool apply, bool Real, bool /*changeAmount*/)
{
if(!m_target->isAlive() )
return;
+
m_target->ApplyAttackTimePercentMod(BASE_ATTACK,m_amount,apply);
}
+
void AuraEffect::HandleHaste(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplyAttackTimePercentMod(BASE_ATTACK, m_amount,apply);
m_target->ApplyAttackTimePercentMod(OFF_ATTACK, m_amount,apply);
m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_amount,apply);
}
+
void AuraEffect::HandleAuraModRangedHaste(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->ApplyAttackTimePercentMod(RANGED_ATTACK, m_amount, apply);
}
+
void AuraEffect::HandleRangedAmmoHaste(bool apply, bool Real, bool /*changeAmount*/)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
m_target->ApplyAttackTimePercentMod(RANGED_ATTACK,m_amount, apply);
}
+
/********************************/
/*** ATTACK POWER ***/
/********************************/
+
void AuraEffect::HandleAuraModAttackPower(bool apply, bool Real, bool /*changeAmount*/)
{
m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_VALUE, float(m_amount), apply);
}
+
void AuraEffect::HandleAuraModRangedAttackPower(bool apply, bool Real, bool /*changeAmount*/)
{
if((m_target->getClassMask() & CLASSMASK_WAND_USERS)!=0)
return;
+
m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER_RANGED, TOTAL_VALUE, float(m_amount), apply);
}
+
void AuraEffect::HandleAuraModAttackPowerPercent(bool apply, bool Real, bool /*changeAmount*/)
{
//UNIT_FIELD_ATTACK_POWER_MULTIPLIER = multiplier - 1
m_target->HandleStatModifier(UNIT_MOD_ATTACK_POWER, TOTAL_PCT, float(m_amount), apply);
}
+
void AuraEffect::HandleAuraModRangedAttackPowerPercent(bool apply, bool Real, bool /*changeAmount*/)
{
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_amount), apply);
}
+
void AuraEffect::HandleAuraModRangedAttackPowerOfStatPercent(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
// Recalculate bonus
if(m_target->GetTypeId() == TYPEID_PLAYER && !(m_target->getClassMask() & CLASSMASK_WAND_USERS))
((Player*)m_target)->UpdateAttackPowerAndDamage(true);
}
+
void AuraEffect::HandleAuraModAttackPowerOfStatPercent(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
// Recalculate bonus
if(m_target->GetTypeId() == TYPEID_PLAYER)
((Player*)m_target)->UpdateAttackPowerAndDamage(false);
}
+
void AuraEffect::HandleAuraModAttackPowerOfArmor(bool /*apply*/, bool Real, bool /*changeAmount*/)
{
// spells required only Real aura add/remove
if(!Real)
return;
+
// Recalculate bonus
if(m_target->GetTypeId() == TYPEID_PLAYER)
((Player*)m_target)->UpdateAttackPowerAndDamage(false);
@@ -4815,6 +5314,7 @@ void AuraEffect::HandleModDamageDone(bool apply, bool Real, bool changeAmount)
if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply);
}
+
// GetMiscValue() 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
@@ -4823,6 +5323,7 @@ void AuraEffect::HandleModDamageDone(bool apply, bool Real, bool changeAmount)
// mods must be applied base at equipped weapon class and subclass comparison
// with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
// GetMiscValue() comparison with item generated damage types
+
if((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0)
{
// apply generic physical damage bonuses including wand case
@@ -4836,6 +5337,7 @@ void AuraEffect::HandleModDamageDone(bool apply, bool Real, bool changeAmount)
{
// done in Player::_ApplyWeaponDependentAuraMods
}
+
if(m_target->GetTypeId() == TYPEID_PLAYER)
{
if(m_amount > 0)
@@ -4844,16 +5346,20 @@ void AuraEffect::HandleModDamageDone(bool apply, bool Real, bool changeAmount)
m_target->ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_NEG,m_amount,apply);
}
}
+
// Skip non magic case for speedup
if((GetMiscValue() & 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)
@@ -4878,9 +5384,11 @@ void AuraEffect::HandleModDamageDone(bool apply, bool Real, bool changeAmount)
pet->UpdateAttackPowerAndDamage();
}
}
+
void AuraEffect::HandleModDamagePercentDone(bool apply, bool Real, bool changeAmount)
{
sLog.outDebug("AURA MOD DAMAGE type:%u negative:%u", GetMiscValue(), m_amount > 0);
+
// apply item specific bonuses for already equipped weapon
if((Real || changeAmount) && m_target->GetTypeId()==TYPEID_PLAYER)
{
@@ -4888,6 +5396,7 @@ void AuraEffect::HandleModDamagePercentDone(bool apply, bool Real, bool changeAm
if(Item* pItem = ((Player*)m_target)->GetWeaponForAttack(WeaponAttackType(i)))
((Player*)m_target)->_ApplyWeaponDependentAuraDamageMod(pItem,WeaponAttackType(i),this,apply);
}
+
// GetMiscValue() 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
@@ -4896,6 +5405,7 @@ void AuraEffect::HandleModDamagePercentDone(bool apply, bool Real, bool changeAm
// mods must be applied base at equipped weapon class and subclass comparison
// with spell->EquippedItemClass and EquippedItemSubClassMask and EquippedItemInventoryTypeMask
// GetMiscValue() comparison with item generated damage types
+
if((GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL) != 0)
{
// apply generic physical damage bonuses including wand case
@@ -4913,52 +5423,65 @@ void AuraEffect::HandleModDamagePercentDone(bool apply, bool Real, bool changeAm
if(m_target->GetTypeId() == TYPEID_PLAYER)
m_target->ApplyModSignedFloatValue(PLAYER_FIELD_MOD_DAMAGE_DONE_PCT,m_amount/100.0f,apply);
}
+
// Skip non magic case for speedup
if((GetMiscValue() & 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_amount/100.0f,apply);
}
+
void AuraEffect::HandleModOffhandDamagePercent(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
sLog.outDebug("AURA MOD OFFHAND DAMAGE");
+
m_target->HandleStatModifier(UNIT_MOD_DAMAGE_OFFHAND, TOTAL_PCT, float(m_amount), apply);
}
+
/********************************/
/*** POWER COST ***/
/********************************/
+
void AuraEffect::HandleModPowerCostPCT(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
float amount = m_amount /100.0f;
for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
if(GetMiscValue() & (1<<i))
m_target->ApplyModSignedFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+i,amount,apply);
}
+
void AuraEffect::HandleModPowerCost(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
for(int i = 0; i < MAX_SPELL_SCHOOL; ++i)
if(GetMiscValue() & (1<<i))
m_target->ApplyModInt32Value(UNIT_FIELD_POWER_COST_MODIFIER+i,m_amount,apply);
}
+
void AuraEffect::HandleNoReagentUseAura(bool Apply, bool Real, bool /*changeAmount*/)
{
// spells required only Real aura add/remove
@@ -4970,19 +5493,24 @@ void AuraEffect::HandleNoReagentUseAura(bool Apply, bool Real, bool /*changeAmou
Unit::AuraEffectList const& noReagent = m_target->GetAurasByType(SPELL_AURA_NO_REAGENT_USE);
for(Unit::AuraEffectList::const_iterator i = noReagent.begin(); i != noReagent.end(); ++i)
mask |= (*i)->m_spellProto->EffectSpellClassMask[(*i)->m_effIndex];
+
m_target->SetUInt32Value(PLAYER_NO_REAGENT_COST_1 , mask[0]);
m_target->SetUInt32Value(PLAYER_NO_REAGENT_COST_1+1, mask[1]);
m_target->SetUInt32Value(PLAYER_NO_REAGENT_COST_1+2, mask[2]);
}
+
/*********************************************************/
/*** OTHERS ***/
/*********************************************************/
+
void AuraEffect::HandleAuraAllowOnlyAbility(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
if(!apply && m_target->HasAuraType(SPELL_AURA_ALLOW_ONLY_ABILITY))
return;
+
if(m_target->GetTypeId()==TYPEID_PLAYER)
{
if (apply)
@@ -4991,14 +5519,17 @@ void AuraEffect::HandleAuraAllowOnlyAbility(bool apply, bool Real, bool /*change
m_target->RemoveFlag(PLAYER_FLAGS, PLAYER_ALLOW_ONLY_ABILITY);
}
}
+
void AuraEffect::HandleAuraEmpathy(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real || 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 AuraEffect::HandleAuraUntrackable(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
@@ -5008,15 +5539,18 @@ void AuraEffect::HandleAuraUntrackable(bool apply, bool Real, bool /*changeAmoun
else
m_target->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, UNIT_BYTE1_FLAG_UNTRACKABLE);
}
+
void AuraEffect::HandleAuraModPacify(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real || 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 AuraEffect::HandleAuraModPacifyAndSilence(bool apply, bool Real, bool changeAmount)
{
// Vengeance of the Blue Flight
@@ -5030,22 +5564,27 @@ void AuraEffect::HandleAuraModPacifyAndSilence(bool apply, bool Real, bool chang
HandleAuraModPacify(apply,Real, changeAmount);
HandleAuraModSilence(apply,Real, changeAmount);
}
+
void AuraEffect::HandleAuraGhost(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real || 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 AuraEffect::HandleAuraAllowFlight(bool apply, bool Real, bool /*changeAmount*/)
{
// all applied/removed only at real aura add/remove
if(!Real)
return;
+
if(m_target->GetTypeId() == TYPEID_UNIT)
m_target->SetFlying(apply);
+
if(Player *plr = m_target->m_movedPlayer)
{
// allow fly
@@ -5058,24 +5597,30 @@ void AuraEffect::HandleAuraAllowFlight(bool apply, bool Real, bool /*changeAmoun
data << uint32(0); // unk
plr->SendDirectMessage(&data);
}
+
//m_target->SendMessageToSet(&data, true);
}
+
void AuraEffect::HandleModRating(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
for (uint32 rating = 0; rating < MAX_COMBAT_RATING; ++rating)
if (GetMiscValue() & (1 << rating))
((Player*)m_target)->ApplyRatingMod(CombatRating(rating), m_amount, apply);
}
+
void AuraEffect::HandleModRatingFromStat(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
// Just recalculate ratings
@@ -5083,6 +5628,7 @@ void AuraEffect::HandleModRatingFromStat(bool apply, bool Real, bool changeAmoun
if (GetMiscValue() & (1 << rating))
((Player*)m_target)->ApplyRatingMod(CombatRating(rating), 0, apply);
}
+
void AuraEffect::HandleForceMoveForward(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real || m_target->GetTypeId() != TYPEID_PLAYER)
@@ -5092,64 +5638,80 @@ void AuraEffect::HandleForceMoveForward(bool apply, bool Real, bool /*changeAmou
else
m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_FORCE_MOVE);
}
+
void AuraEffect::HandleAuraModExpertise(bool /*apply*/, bool Real, bool changeAmount)
{
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)m_target)->UpdateExpertise(BASE_ATTACK);
((Player*)m_target)->UpdateExpertise(OFF_ATTACK);
}
+
void AuraEffect::HandleModTargetResistance(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
return;
// applied to damage as HandleNoImmediateEffect in Unit::CalcAbsorbResist and Unit::CalcArmorReducedDamage
+
// show armor penetration
if (m_target->GetTypeId() == TYPEID_PLAYER && (GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL))
m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_PHYSICAL_RESISTANCE,m_amount, apply);
+
// show as spell penetration only full spell penetration bonuses (all resistances except armor and holy
if (m_target->GetTypeId() == TYPEID_PLAYER && (GetMiscValue() & SPELL_SCHOOL_MASK_SPELL)==SPELL_SCHOOL_MASK_SPELL)
m_target->ApplyModInt32Value(PLAYER_FIELD_MOD_TARGET_RESISTANCE,m_amount, apply);
}
+
void AuraEffect::HandleShieldBlockValue(bool apply, bool Real, bool /*changeAmount*/)
{
BaseModType modType = FLAT_MOD;
if(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_amount), apply);
}
+
void AuraEffect::HandleAuraRetainComboPoints(bool apply, bool Real, bool changeAmount)
{
// spells required only Real aura add/remove
if(!Real && !changeAmount)
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 && GetParentAura()->GetAuraDuration()==0 && target->GetComboTarget())
if(Unit* unit = ObjectAccessor::GetUnit(*m_target,target->GetComboTarget()))
target->AddComboPoints(unit, -m_amount);
}
+
void AuraEffect::HandleModUnattackable( bool Apply, bool Real , bool /*changeAmount*/)
{
if(!Real)
return;
+
if(Apply)
{
m_target->CombatStop();
m_target->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_IMMUNE_OR_LOST_SELECTION);
}
+
m_target->ApplyModFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE, Apply);
}
+
void AuraEffect::HandleSpiritOfRedemption( bool apply, bool Real , bool /*changeAmount*/)
{
// spells required only Real aura add/remove
if(!Real)
return;
+
// prepare spirit state
if(apply)
{
@@ -5157,20 +5719,24 @@ void AuraEffect::HandleSpiritOfRedemption( bool apply, bool Real , bool /*change
{
// disable breath/etc timers
((Player*)m_target)->StopMirrorTimers();
+
// set stand state (expected in this form)
if(!m_target->IsStandState())
m_target->SetStandState(UNIT_STAND_STATE_STAND);
}
+
m_target->SetHealth(1);
}
// die at aura end
else
m_target->setDeathState(JUST_DIED);
}
+
void AuraEffect::PeriodicTick()
{
if(!m_target->isAlive())
return;
+
switch(GetAuraName())
{
case SPELL_AURA_PERIODIC_DAMAGE:
@@ -5179,13 +5745,16 @@ void AuraEffect::PeriodicTick()
Unit *pCaster = GetCaster();
if(!pCaster)
return;
+
// Consecrate ticks can miss and will not show up in the combat log
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(GetSpellProto()))
return;
+
// some auras remove at specific health level or more
if(m_auraName==SPELL_AURA_PERIODIC_DAMAGE)
{
@@ -5227,15 +5796,19 @@ void AuraEffect::PeriodicTick()
break;
}
}
+
uint32 absorb=0;
uint32 resist=0;
CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
// ignore non positive values (can be result apply spellmods to aura damage
//uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0;
uint32 pdamage = GetAmount() > 0 ? GetAmount() : 0;
+
if(GetAuraName() == SPELL_AURA_PERIODIC_DAMAGE)
{
pdamage = pCaster->SpellDamageBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount());
+
// Calculate armor mitigation if it is a physical spell
// But not for bleed mechanic spells
if ( GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL &&
@@ -5245,6 +5818,7 @@ void AuraEffect::PeriodicTick()
cleanDamage.mitigated_damage += pdamage - pdamageReductedArmor;
pdamage = pdamageReductedArmor;
}
+
// Curse of Agony damage-per-tick calculation
if (GetSpellProto()->SpellFamilyName==SPELLFAMILY_WARLOCK && (GetSpellProto()->SpellFamilyFlags[0] & 0x400) && GetSpellProto()->SpellIconID==544)
{
@@ -5266,21 +5840,29 @@ void AuraEffect::PeriodicTick()
}
else
pdamage = uint32(m_target->GetMaxHealth()*pdamage/100);
+
bool crit = IsPeriodicTickCrit(pCaster);
if (crit)
pdamage = pCaster->SpellCriticalDamageBonus(m_spellProto, pdamage, m_target);
+
//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, m_spellProto);
+
sLog.outDetail("PeriodicTick: %u (TypeId: %u) attacked %u (TypeId: %u) for %u dmg inflicted by %u abs is %u",
GUID_LOPART(GetCasterGUID()), GuidHigh2TypeId(GUID_HIPART(GetCasterGUID())), m_target->GetGUIDLow(), m_target->GetTypeId(), pdamage, GetId(),absorb);
+
pCaster->DealDamageMods(m_target,pdamage,&absorb);
+
SpellPeriodicAuraLogInfo pInfo(this, pdamage, 0, absorb, resist, 0.0f, crit);
m_target->SendPeriodicAuraLog(&pInfo);
+
Unit* target = m_target; // aura can be deleted in DealDamage
SpellEntry const* spellProto = GetSpellProto();
+
// Set trigger flag
uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;
uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;
@@ -5289,6 +5871,7 @@ void AuraEffect::PeriodicTick()
if (pdamage)
procVictim|=PROC_FLAG_TAKEN_ANY_DAMAGE;
pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, pdamage, BASE_ATTACK, spellProto);
+
pCaster->DealDamage(target, pdamage, &cleanDamage, DOT, GetSpellSchoolMask(spellProto), spellProto, true);
break;
}
@@ -5297,23 +5880,30 @@ void AuraEffect::PeriodicTick()
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
if(m_target->IsImmunedToDamage(GetSpellProto()))
return;
+
uint32 absorb=0;
uint32 resist=0;
CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL );
+
//uint32 pdamage = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0;
uint32 pdamage = GetAmount() > 0 ? GetAmount() : 0;
pdamage = pCaster->SpellDamageBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount());
+
bool crit = IsPeriodicTickCrit(pCaster);
if (crit)
pdamage = pCaster->SpellCriticalDamageBonus(m_spellProto, pdamage, m_target);
+
//Calculate armor mitigation if it is a physical spell
if (GetSpellSchoolMask(GetSpellProto()) & SPELL_SCHOOL_MASK_NORMAL)
{
@@ -5321,20 +5911,27 @@ void AuraEffect::PeriodicTick()
cleanDamage.mitigated_damage += pdamage - pdamageReductedArmor;
pdamage = pdamageReductedArmor;
}
+
//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, m_spellProto);
+
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",
GUID_LOPART(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, crit);
+
Unit* target = m_target; // aura can be deleted in DealDamage
SpellEntry const* spellProto = GetSpellProto();
float multiplier = spellProto->EffectMultipleValue[GetEffIndex()] > 0 ? spellProto->EffectMultipleValue[GetEffIndex()] : 1;
int32 stackAmount = GetParentAura()->GetStackAmount();
+
// Set trigger flag
uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;
uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;
@@ -5344,14 +5941,18 @@ void AuraEffect::PeriodicTick()
procVictim|=PROC_FLAG_TAKEN_ANY_DAMAGE;
pCaster->ProcDamageAndSpell(target, procAttacker, procVictim, procEx, pdamage, BASE_ATTACK, spellProto);
int32 new_damage = pCaster->DealDamage(target, pdamage, &cleanDamage, DOT, GetSpellSchoolMask(spellProto), spellProto, false);
+
if (!target->isAlive() && pCaster->IsNonMeleeSpellCasted(false))
for (uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; ++i)
if (Spell* spell = pCaster->GetCurrentSpell(CurrentSpellTypes(i)))
if (spell->m_spellInfo->Id == GetId())
spell->cancel();
+
if(Player *modOwner = pCaster->GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_MULTIPLE_VALUE, multiplier);
+
uint32 heal = uint32(pCaster->SpellHealingBonus(pCaster, spellProto, uint32(new_damage * multiplier), DOT, stackAmount));
+
int32 gain = pCaster->DealHeal(pCaster, heal, spellProto);
pCaster->getHostilRefManager().threatAssist(pCaster, gain * 0.5f, spellProto);
break;
@@ -5361,18 +5962,22 @@ void AuraEffect::PeriodicTick()
Unit *donator = GetCaster();
if(!donator || !donator->GetHealth())
return;
+
uint32 pdamage = GetAmount() * GetParentAura()->GetStackAmount();
if(donator->GetHealth() < pdamage)
pdamage = donator->GetHealth() - 1;
if(!pdamage)
return;
+
Unit* target = m_target; // aura can be deleted in DealDamage
SpellEntry const* spellProto = GetSpellProto();
//donator->SendSpellNonMeleeDamageLog(donator, GetId(), pdamage, GetSpellSchoolMask(spellProto), 0, 0, false, 0);
donator->ModifyHealth(-(int32)pdamage);
sLog.outDetail("PeriodicTick: donator %u target %u damage %u.", donator->GetEntry(), target->GetEntry(), pdamage);
+
if(spellProto->EffectMultipleValue[GetEffIndex()] > 0)
pdamage *= spellProto->EffectMultipleValue[GetEffIndex()];
+
donator->DealHeal(target, pdamage, spellProto);
break;
}
@@ -5382,14 +5987,18 @@ void AuraEffect::PeriodicTick()
Unit *pCaster = GetCaster();
if(!pCaster)
return;
+
// heal for caster damage (must be alive)
if(m_target != pCaster && GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_HEALTH_FUNNEL && !pCaster->isAlive())
return;
+
if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetHealth()==m_target->GetMaxHealth())
return;
+
// ignore non positive values (can be result apply spellmods to aura damage
//uint32 amount = GetModifierValuePerStack() > 0 ? GetModifierValuePerStack() : 0;
int32 pdamage = GetAmount() > 0 ? GetAmount() : 0;
+
if(m_auraName==SPELL_AURA_OBS_MOD_HEALTH)
pdamage = uint32(m_target->GetMaxHealth() * pdamage * GetParentAura()->GetStackAmount() / 100);
else
@@ -5401,24 +6010,33 @@ void AuraEffect::PeriodicTick()
int32 remainingTicks = int32(float(GetParentAura()->GetAuraDuration()) / m_amplitude + 0.5);
pdamage = int32(pdamage) + int32(pdamage)*ticks*(-6+2*remainingTicks)/100;
}
+
pdamage = pCaster->SpellHealingBonus(m_target, GetSpellProto(), pdamage, DOT, GetParentAura()->GetStackAmount());
}
+
bool crit = IsPeriodicTickCrit(pCaster);
if (crit)
pdamage = pCaster->SpellCriticalHealingBonus(m_spellProto, pdamage, 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());
+
int32 gain = m_target->ModifyHealth(pdamage);
+
SpellPeriodicAuraLogInfo pInfo(this, pdamage, pdamage - gain, 0, 0, 0.0f, crit);
m_target->SendPeriodicAuraLog(&pInfo);
+
// 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);
+
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 = GetParentAura()->GetCastItemGUID()!=0;
+
// Health Funnel
// heal for caster damage
if(m_target!=pCaster && GetSpellProto()->AttributesEx2 & SPELL_ATTR_EX2_HEALTH_FUNNEL)
@@ -5427,6 +6045,7 @@ void AuraEffect::PeriodicTick()
if(pCaster->GetHealth() <= dmg && pCaster->GetTypeId()==TYPEID_PLAYER)
{
pCaster->RemoveAurasDueToSpell(GetId());
+
// finish current generic/channeling spells, don't affect autorepeat
pCaster->FinishSpell(CURRENT_GENERIC_SPELL);
pCaster->FinishSpell(CURRENT_CHANNELED_SPELL);
@@ -5437,10 +6056,12 @@ void AuraEffect::PeriodicTick()
uint32 absorb = 0;
pCaster->DealDamageMods(pCaster,damage,&absorb);
pCaster->SendSpellNonMeleeDamageLog(pCaster, GetId(), damage, GetSpellSchoolMask(GetSpellProto()), absorb, 0, false, 0, false);
+
CleanDamage cleanDamage = CleanDamage(0, 0, BASE_ATTACK, MELEE_HIT_NORMAL );
pCaster->DealDamage(pCaster, damage, &cleanDamage, NODAMAGE, GetSpellSchoolMask(GetSpellProto()), GetSpellProto(), true);
}
}
+
uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;
uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;
uint32 procEx = PROC_EX_NORMAL_HIT | PROC_EX_INTERNAL_HOT;
@@ -5453,23 +6074,31 @@ void AuraEffect::PeriodicTick()
{
if(GetMiscValue() < 0 || GetMiscValue() >= MAX_POWERS)
return;
+
Powers power = Powers(GetMiscValue());
+
// power type might have changed between aura applying and tick (druid's shapeshift)
if(m_target->getPowerType() != power)
return;
+
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(GetSpellProto()))
return;
+
// ignore non positive values (can be result apply spellmods to aura damage
uint32 pdamage = m_amount > 0 ? m_amount : 0;
+
// Special case: draining x% of mana (up to a maximum of 2*x% of the caster's maximum mana)
// It's mana percent cost spells, m_amount is percent drain from target
if (m_spellProto->ManaCostPercentage)
@@ -5480,28 +6109,39 @@ void AuraEffect::PeriodicTick()
if(pdamage > maxmana)
pdamage = maxmana;
}
+
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());
+
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);
}
+
SpellPeriodicAuraLogInfo pInfo(this, drain_amount, 0, 0, 0, gain_multiplier, false);
m_target->SendPeriodicAuraLog(&pInfo);
+
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());
}
+
// Mark of Kaz'rogal
if(GetId() == 31447 && m_target->GetPower(power) == 0)
{
@@ -5509,11 +6149,13 @@ void AuraEffect::PeriodicTick()
// Remove aura
GetParentAura()->SetAuraDuration(0);
}
+
// Mark of Kazzak
if(GetId() == 32960)
{
int32 modifier = (m_target->GetPower(power) * 0.05f);
m_target->ModifyPower(power, -modifier);
+
if(m_target->GetPower(power) == 0)
{
m_target->CastSpell(m_target, 32961, true, 0, this);
@@ -5528,6 +6170,7 @@ void AuraEffect::PeriodicTick()
int32 manaFeedVal = 0;
if (AuraEffect const * aurEff = GetParentAura()->GetPartAura(1))
manaFeedVal = aurEff->GetAmount();
+
if(manaFeedVal > 0)
{
manaFeedVal = manaFeedVal * gain_amount / 100;
@@ -5540,21 +6183,28 @@ void AuraEffect::PeriodicTick()
{
if(GetMiscValue() < 0)
return;
+
Powers power;
if (GetMiscValue() == POWER_ALL)
power = m_target->getPowerType();
else
power = Powers(GetMiscValue());
+
if(m_target->GetMaxPower(power) == 0)
return;
+
if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetPower(power)==m_target->GetMaxPower(power))
return;
+
uint32 amount = m_amount * m_target->GetMaxPower(power) /100;
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(), amount, GetId());
+
SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f, false);
m_target->SendPeriodicAuraLog(&pInfo);
+
int32 gain = m_target->ModifyPower(power,amount);
+
if(Unit* pCaster = GetCaster())
m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
break;
@@ -5564,17 +6214,25 @@ void AuraEffect::PeriodicTick()
// ignore non positive values (can be result apply spellmods to aura damage
if(m_amount < 0 || GetMiscValue() >= MAX_POWERS)
return;
+
Powers power = Powers(GetMiscValue());
+
if(m_target->GetMaxPower(power) == 0)
return;
+
if(GetParentAura()->GetAuraDuration() ==-1 && m_target->GetPower(power)==m_target->GetMaxPower(power))
return;
+
uint32 amount = m_amount;
+
SpellPeriodicAuraLogInfo pInfo(this, amount, 0, 0, 0, 0.0f, false);
m_target->SendPeriodicAuraLog(&pInfo);
+
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(), amount, GetId());
+
int32 gain = m_target->ModifyPower(power,amount);
+
if(Unit* pCaster = GetCaster())
m_target->getHostilRefManager().threatAssist(pCaster, float(gain) * 0.5f, GetSpellProto());
break;
@@ -5584,32 +6242,45 @@ void AuraEffect::PeriodicTick()
Unit *pCaster = GetCaster();
if(!pCaster)
return;
+
// Check for immune (not use charges)
if(m_target->IsImmunedToDamage(GetSpellProto()))
return;
+
int32 pdamage = m_amount > 0 ? m_amount : 0;
+
Powers powerType = Powers(GetMiscValue());
+
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()]);
+
SpellEntry const* spellProto = GetSpellProto();
//maybe has to be sent different to client, but not by SMSG_PERIODICAURALOG
SpellNonMeleeDamage damageInfo(pCaster, m_target, spellProto->Id, spellProto->SchoolMask);
//no SpellDamageBonus for burn mana
pCaster->CalculateSpellDamageTaken(&damageInfo, gain, spellProto);
+
pCaster->DealDamageMods(damageInfo.target,damageInfo.damage,&damageInfo.absorb);
+
pCaster->SendSpellNonMeleeDamageLog(&damageInfo);
+
// Set trigger flag
uint32 procAttacker = PROC_FLAG_ON_DO_PERIODIC;
uint32 procVictim = PROC_FLAG_ON_TAKE_PERIODIC;
uint32 procEx = createProcExtendMask(&damageInfo, SPELL_MISS_NONE) | PROC_EX_INTERNAL_DOT;
if (damageInfo.damage)
procVictim|=PROC_FLAG_TAKEN_ANY_DAMAGE;
+
pCaster->ProcDamageAndSpell(damageInfo.target, procAttacker, procVictim, procEx, damageInfo.damage, BASE_ATTACK, spellProto);
+
pCaster->DealSpellDamage(&damageInfo, true);
break;
}
@@ -5625,6 +6296,7 @@ void AuraEffect::PeriodicTick()
Powers pt = m_target->getPowerType();
if(int32(pt) != GetMiscValue())
return;
+
if ( GetSpellProto()->AuraInterruptFlags & AURA_INTERRUPT_FLAG_NOT_SEATED )
{
// eating anim
@@ -5637,6 +6309,7 @@ void AuraEffect::PeriodicTick()
if (m_target->isInCombat())
m_target->ModifyPower(pt,m_amount);
}
+
// Anger Management
// amount = 1+ 16 = 17 = 3,4*5 = 10,2*5/3
// so 17 is rounded amount for 5 sec tick grow ~ 1 range grow in 3 sec
@@ -5667,11 +6340,13 @@ void AuraEffect::PeriodicTick()
Unit *pCaster = GetCaster();
if (!pCaster)
return;
+
if (pCaster->GetTypeId() == TYPEID_UNIT && ((Creature*)pCaster)->isTotem() && ((Totem*)pCaster)->GetTotemType() != TOTEM_STATUE)
{
uint32 procAttacker = PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT;
uint32 procVictim = PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT;
SpellEntry const *spellProto = GetSpellProto();
+
if (spellProto->SpellFamilyName == SPELLFAMILY_GENERIC) // SPELLFAMILY_GENERIC proc by triggered spell
{
uint32 trigger_spell_id = spellProto->EffectTriggerSpell[m_effIndex];
@@ -5681,6 +6356,7 @@ void AuraEffect::PeriodicTick()
else
((Totem*)pCaster)->GetOwner()->ProcDamageAndSpell(pCaster, procAttacker, procVictim, PROC_EX_NORMAL_HIT, 0, BASE_ATTACK, spellProto);
}
+
TriggerSpell();
break;
}
@@ -5693,6 +6369,7 @@ void AuraEffect::PeriodicTick()
break;
}
}
+
void AuraEffect::PeriodicDummyTick()
{
Unit *caster = GetCaster();
@@ -5748,6 +6425,7 @@ void AuraEffect::PeriodicDummyTick()
// on 0 tick - 0 (handled in 2 second)
// on 1 tick - 166% (handled in 4 second)
// on 2 tick - 133% (handled in 6 second)
+
// Apply bonus for 1 - 4 tick
switch (m_tickNumber)
{
@@ -5883,22 +6561,30 @@ void AuraEffect::PeriodicDummyTick()
{
// eff_radius ==0
float radius = GetSpellMaxRange(spell, false);
+
CellPair p(MaNGOS::ComputeCellPair(caster->GetPositionX(),caster->GetPositionY()));
Cell cell(p);
cell.data.Part.reserved = ALL_DISTRICT;
+
MaNGOS::AnyUnfriendlyVisibleUnitInObjectRangeCheck u_check(caster, caster, radius);
MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyVisibleUnitInObjectRangeCheck> checker(caster,targets, u_check);
+
TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyVisibleUnitInObjectRangeCheck>, GridTypeMapContainer > grid_object_checker(checker);
TypeContainerVisitor<MaNGOS::UnitListSearcher<MaNGOS::AnyUnfriendlyVisibleUnitInObjectRangeCheck>, WorldTypeMapContainer > world_object_checker(checker);
+
CellLock<GridReadGuard> cell_lock(cell, p);
+
cell_lock->Visit(cell_lock, grid_object_checker, *caster->GetMap(), *caster, radius);
cell_lock->Visit(cell_lock, world_object_checker, *caster->GetMap(), *caster, radius);
}
+
if(targets.empty())
return;
+
std::list<Unit*>::const_iterator itr = targets.begin();
std::advance(itr, rand()%targets.size());
Unit* target = *itr;
+
caster->CastSpell(target, 57840, true);
caster->CastSpell(target, 57841, true);
return;
@@ -5990,6 +6676,7 @@ void AuraEffect::PeriodicDummyTick()
return;
if(((Player*)m_target)->getClass() != CLASS_DEATH_KNIGHT)
return;
+
// Remove death rune added on proc
for (uint8 i=0;i<MAX_RUNES && m_amount;++i)
{
@@ -6005,8 +6692,10 @@ void AuraEffect::PeriodicDummyTick()
((Player*)m_target)->GetBaseRune(i) != RUNE_BLOOD )
continue;
}
+
if (!(m_amount & (1<<i)))
continue;
+
((Player*)m_target)->ConvertRune(i,((Player*)m_target)->GetBaseRune(i));
}
m_amount = 0;
@@ -6018,10 +6707,12 @@ void AuraEffect::PeriodicDummyTick()
break;
}
}
+
void AuraEffect::HandlePreventFleeing(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
Unit::AuraEffectList const& fearAuras = m_target->GetAurasByType(SPELL_AURA_MOD_FEAR);
if( !fearAuras.empty() )
{
@@ -6032,15 +6723,18 @@ void AuraEffect::HandlePreventFleeing(bool apply, bool Real, bool /*changeAmount
m_target->SetFeared(true);*/
}
}
+
void AuraEffect::HandleArenaPreparation(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
if(apply)
m_target->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION);
else
m_target->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PREPARATION);
}
+
/**
* Such auras are applied from a caster(=player) to a vehicle.
* This has been verified using spell #49256
@@ -6049,11 +6743,14 @@ void AuraEffect::HandleAuraControlVehicle(bool apply, bool Real, bool /*changeAm
{
if(!Real)
return;
+
if(!m_target->IsVehicle())
return;
+
Unit *caster = GetParentAura()->GetUnitSource();
if(!caster || caster == m_target)
return;
+
if (apply)
{
//if(caster->GetTypeId() == TYPEID_PLAYER)
@@ -6069,20 +6766,26 @@ void AuraEffect::HandleAuraControlVehicle(bool apply, bool Real, bool /*changeAm
if(caster->GetTypeId() == TYPEID_UNIT)
((Creature*)caster)->RemoveCorpse();
}
+
// some SPELL_AURA_CONTROL_VEHICLE auras have a dummy effect on the player - remove them
caster->RemoveAurasDueToSpell(GetId());
caster->ExitVehicle();
}
}
+
void AuraEffect::HandleAuraConvertRune(bool apply, bool Real, bool changeAmount)
{
if(!Real && !changeAmount)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *plr = (Player*)m_target;
+
if(plr->getClass() != CLASS_DEATH_KNIGHT)
return;
+
uint32 runes = 0;
// convert number of runes specified in aura amount of rune type in miscvalue to runetype in miscvalueb
for(uint32 i = 0; i < MAX_RUNES && m_amount; ++i)
@@ -6110,55 +6813,70 @@ void AuraEffect::HandleAuraConvertRune(bool apply, bool Real, bool changeAmount)
if (apply)
m_amount = runes;
}
+
// Control Auras
+
void AuraEffect::HandleAuraModStun(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
m_target->SetControlled(apply, UNIT_STAT_STUNNED);
}
+
void AuraEffect::HandleAuraModRoot(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
m_target->SetControlled(apply, UNIT_STAT_ROOT);
}
+
// Charm Auras
+
void AuraEffect::HandleModPossess(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
Unit* caster = GetCaster();
if(caster && caster->GetTypeId() == TYPEID_UNIT)
{
HandleModCharm(apply, Real, false);
return;
}
+
if(apply)
m_target->SetCharmedBy(caster, CHARM_TYPE_POSSESS);
else
m_target->RemoveCharmedBy(caster);
}
+
// only one spell has this aura
void AuraEffect::HandleModPossessPet(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
Unit* caster = GetCaster();
if(!caster || caster->GetTypeId() != TYPEID_PLAYER)
return;
+
//seems it may happen that when removing it is no longer owner's pet
//if(((Player*)caster)->GetPet() != m_target)
// return;
+
if(apply)
{
if(((Player*)caster)->GetPet() != m_target)
return;
+
m_target->SetCharmedBy(caster, CHARM_TYPE_POSSESS);
}
else
{
m_target->RemoveCharmedBy(caster);
+
// Reinitialize the pet bar and make the pet come back to the owner
((Player*)caster)->PetSpellInitialize();
if(!m_target->getVictim())
@@ -6169,31 +6887,40 @@ void AuraEffect::HandleModPossessPet(bool apply, bool Real, bool /*changeAmount*
}
}
}
+
void AuraEffect::HandleModCharm(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
Unit* caster = GetCaster();
+
if(apply)
m_target->SetCharmedBy(caster, CHARM_TYPE_CHARM);
else
m_target->RemoveCharmedBy(caster);
}
+
void AuraEffect::HandleCharmConvert(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
Unit* caster = GetCaster();
+
if(apply)
m_target->SetCharmedBy(caster, CHARM_TYPE_CONVERT);
else
m_target->RemoveCharmedBy(caster);
}
+
void AuraEffect::HandlePhase(bool apply, bool Real, bool /*changeAmount*/)
{
if(!Real)
return;
+
// no-phase is also phase state so same code for apply and remove
+
// phase auras normally not expected at BG but anyway better check
if(m_target->GetTypeId()==TYPEID_PLAYER)
{
@@ -6201,17 +6928,21 @@ void AuraEffect::HandlePhase(bool apply, bool Real, bool /*changeAmount*/)
if(((Player*)m_target)->InBattleGround())
if(BattleGround *bg = ((Player*)m_target)->GetBattleGround())
bg->EventPlayerDroppedFlag((Player*)m_target);
+
// GM-mode have mask 0xFFFFFFFF
if(!((Player*)m_target)->isGameMaster())
m_target->SetPhaseMask(apply ? GetMiscValue() : PHASEMASK_NORMAL,false);
+
((Player*)m_target)->GetSession()->SendSetPhaseShift(apply ? GetMiscValue() : PHASEMASK_NORMAL);
}
else
m_target->SetPhaseMask(apply ? GetMiscValue() : PHASEMASK_NORMAL,false);
+
// need triggering visibility update base at phase update of not GM invisible (other GMs anyway see in any phases)
if(m_target->GetVisibility()!=VISIBILITY_OFF)
m_target->SetVisibility(m_target->GetVisibility());
}
+
void AuraEffect::HandleAuraInitializeImages( bool Apply, bool Real , bool /*changeAmount*/)
{
if (!Real)
@@ -6250,10 +6981,12 @@ void AuraEffect::HandleAuraInitializeImages( bool Apply, bool Real , bool /*chan
}
}
}
+
void AuraEffect::HandleAuraCloneCaster( bool Apply, bool Real , bool /*changeAmount*/)
{
if (!Real)
return;
+
if (Apply)
{
Unit * caster = GetCaster();
@@ -6269,6 +7002,7 @@ void AuraEffect::HandleAuraCloneCaster( bool Apply, bool Real , bool /*changeAmo
m_target->RemoveFlag(UNIT_FIELD_FLAGS_2, UNIT_FLAG2_MIRROR_IMAGE);
}
}
+
void AuraEffect::HandleAuraModCritPct(bool apply, bool Real, bool changeAmount)
{
/*
@@ -6277,8 +7011,10 @@ void AuraEffect::HandleAuraModCritPct(bool apply, bool Real, bool changeAmount)
m_target->m_baseSpellCritChance += apply ? m_amount:-m_amount;
return;
}
+
if(Real || changeAmount)
((Player*)m_target)->UpdateAllSpellCritChances();
+
((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
@@ -6286,18 +7022,23 @@ void AuraEffect::HandleAuraModCritPct(bool apply, bool Real, bool changeAmount)
// spells required only Real aura add/remove
if(!Real)
return;
+
if(m_target->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)m_target)->HandleBaseModValue(CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
((Player*)m_target)->HandleBaseModValue(OFFHAND_CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
((Player*)m_target)->HandleBaseModValue(RANGED_CRIT_PERCENTAGE, FLAT_MOD, float (m_amount), apply);
+
// included in Player::UpdateSpellCritChance calculation
((Player*)m_target)->UpdateAllSpellCritChances();
}
+
void AuraEffect::HandleAuraLinked(bool apply, bool Real, bool /*changeAmount*/)
{
if (!Real)
return;
+
if (apply)
{
Unit * caster = GetCaster();
@@ -6312,20 +7053,25 @@ void AuraEffect::HandleAuraLinked(bool apply, bool Real, bool /*changeAmount*/)
else
m_target->RemoveAura(m_spellProto->EffectTriggerSpell[m_effIndex], GetCasterGUID(), AuraRemoveMode(GetParentAura()->GetRemoveMode()));
}
+
int32 AuraEffect::CalculateCrowdControlAuraAmount(Unit * caster)
{
// Damage cap for CC effects
if (!m_spellProto->procFlags)
return 0;
+
if (m_auraName !=SPELL_AURA_MOD_CONFUSE &&
m_auraName !=SPELL_AURA_MOD_FEAR &&
m_auraName !=SPELL_AURA_MOD_STUN &&
m_auraName !=SPELL_AURA_MOD_ROOT &&
m_auraName !=SPELL_AURA_TRANSFORM)
return 0;
+
int32 damageCap = (int32)(m_target->GetMaxHealth()*0.10f);
+
if (!caster)
return damageCap;
+
// Glyphs increasing damage cap
Unit::AuraEffectList const& overrideClassScripts = caster->GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
for(Unit::AuraEffectList::const_iterator itr = overrideClassScripts.begin();itr != overrideClassScripts.end(); ++itr)
@@ -6342,6 +7088,7 @@ int32 AuraEffect::CalculateCrowdControlAuraAmount(Unit * caster)
}
return damageCap;
}
+
bool AuraEffect::IsPeriodicTickCrit(Unit const * pCaster) const
{
Unit::AuraEffectList const& mPeriodicCritAuras= pCaster->GetAurasByType(SPELL_AURA_ABILITY_PERIODIC_CRIT);
diff --git a/src/game/SpellAuras.h b/src/game/SpellAuras.h
index ae1a017bee7..821e208d6b7 100644
--- a/src/game/SpellAuras.h
+++ b/src/game/SpellAuras.h
@@ -19,14 +19,18 @@
*/
#ifndef TRINITY_SPELLAURAS_H
#define TRINITY_SPELLAURAS_H
+
#include "SpellAuraDefines.h"
+
class Unit;
struct SpellEntry;
struct SpellModifier;
struct ProcTriggerSpell;
+
// forward decl
class Aura;
class AuraEffect;
+
typedef void(AuraEffect::*pAuraHandler)(bool Apply, bool Real, bool changeAmount);
// Real == true at aura add/remove
// Real == false at aura mod unapply/reapply; when adding/removing dependent aura/item/stat mods
@@ -41,30 +45,35 @@ typedef void(AuraEffect::*pAuraHandler)(bool Apply, bool Real, bool changeAmount
// each setting object update field code line moved under if(Real) check is significant Trinity speedup, and less server->client data sends
// each packet sending code moved under if(Real) check is _large_ Trinity speedup, and lot less server->client data sends
//
-// changeAmount == true at changing existing aura amount - called wit real == false
+// changeAmount == true at changing existing aura amount - called wit real == false
// if aura has amount dependant effect handler has to allow proceeding it
// example: change speed aura, modifier aura
+
class TRINITY_DLL_SPEC Aura
{
friend void Player::SendAurasForTarget(Unit *target);
public:
virtual ~Aura();
explicit Aura(SpellEntry const* spellproto, uint32 effMask, Unit *target, WorldObject *source, Unit *caster, int32 *currentBasePoints = NULL, Item *castItem = NULL);
+
SpellEntry const* GetSpellProto() const { return m_spellProto; }
uint32 GetId() const{ return m_spellProto->Id; }
uint64 GetCastItemGUID() const { return m_castItemGuid; }
+
uint64 const& GetCasterGUID() const { return m_casterGuid; }
Unit* GetCaster() const;
uint64 const& GetSourceGUID() const { return m_sourceGuid; }
Unit *GetUnitSource() const;
Unit* GetTarget() const { return m_target; }
time_t GetAuraApplyTime() const { return m_applyTime; }
+
int32 GetAuraMaxDuration() const { return m_maxduration; }
void SetAuraMaxDuration(int32 duration) { m_maxduration = duration; }
int32 GetAuraDuration() const { return m_duration; }
void SetAuraDuration(int32 duration, bool withMods = false);
void RefreshAura() { SetAuraDuration(m_maxduration);}
bool IsExpired() const { return !GetAuraDuration() && !(IsPermanent() || IsPassive()); }
+
void SendAuraUpdate();
uint8 GetAuraSlot() const { return m_auraSlot; }
void SetAuraSlot(uint8 slot) { m_auraSlot = slot; }
@@ -73,24 +82,30 @@ class TRINITY_DLL_SPEC Aura
bool DropAuraCharge();
void SetProcDamage(uint32 val) { m_procDamage = val; }
uint32 GetProcDamage() const { return m_procDamage; }
+
uint8 GetStackAmount() const { return m_stackAmount; }
void SetStackAmount(uint8 num, bool applied = true);
bool modStackAmount(int32 num); // return true if last charge dropped
+
void SetRemoveMode(AuraRemoveMode mode) { m_removeMode = mode; }
uint8 GetRemoveMode() const {return m_removeMode;}
+
inline uint8 GetEffectMask() const {return m_auraFlags & 7;}
AuraEffect * GetPartAura (uint8 effIndex) const {assert (effIndex < MAX_SPELL_EFFECTS); return m_partAuras[effIndex];}
bool SetPartAura(AuraEffect* aurEff, uint8 effIndex);
+
bool IsPositive() const { return m_positive; }
void SetNegative() { m_positive = false; }
void SetPositive() { m_positive = true; }
bool IsPermanent() const { return m_permanent; }
void SetPermanent(bool val) { m_permanent = val; }
+
bool IsPassive() const { return m_isPassive; }
bool IsDeathPersistent() const { return m_isDeathPersist; }
bool IsRemovedOnShapeLost() const { return m_isRemovedOnShapeLost; }
bool CanBeSaved() const;
bool IsRemoved() const { return m_isRemoved; }
+
bool IsPersistent() const;
bool IsAreaAura() const;
bool IsAuraType(AuraType type) const;
@@ -104,16 +119,21 @@ class TRINITY_DLL_SPEC Aura
}
void ApplyAllModifiers(bool apply, bool Real=false);
void HandleAuraSpecificMods(bool apply);
+
void Update(uint32 diff);
+
void _AddAura();
void _RemoveAura();
+
// Allow Apply Aura Handler to modify and access m_AuraDRGroup
void setDiminishGroup(DiminishingGroup group) { m_AuraDRGroup = group; }
DiminishingGroup getDiminishGroup() const { return m_AuraDRGroup; }
+
// Single cast aura helpers
void UnregisterSingleCastAura();
bool IsSingleTarget() const {return m_isSingleTargetAura;}
void SetIsSingleTarget(bool val) { m_isSingleTargetAura = val;}
+
private:
const SpellEntry * const m_spellProto;
Unit * const m_target;
@@ -122,18 +142,23 @@ class TRINITY_DLL_SPEC Aura
const uint64 m_casterGuid;
const uint64 m_castItemGuid; // it is NOT safe to keep a pointer to the item because it may get deleted
const time_t m_applyTime;
+
int32 m_maxduration; // Max aura duration
int32 m_duration; // Current time
int32 m_timeCla; // Timer for power per sec calcultion
+
AuraRemoveMode m_removeMode:8; // Store info for know remove aura reason
DiminishingGroup m_AuraDRGroup:8; // Diminishing
+
uint8 m_auraSlot; // Aura slot on unit (for show in client)
uint8 m_auraFlags; // Aura info flag (for send data to client)
uint8 m_auraLevel; // Aura level (store caster level for correct show level dep amount)
uint8 m_procCharges; // Aura charges (0 for infinite)
uint8 m_stackAmount; // Aura stack amount
AuraEffect * m_partAuras[3];
+
uint32 m_procDamage; // used in aura proc code
+
bool m_isDeathPersist:1;
bool m_isRemovedOnShapeLost:1;
bool m_isPassive:1;
@@ -141,8 +166,10 @@ class TRINITY_DLL_SPEC Aura
bool m_permanent:1;
bool m_isRemoved:1;
bool m_isSingleTargetAura:1; // true if it's a single target spell and registered at caster - can change at spell steal for example
+
bool IsVisible() const;
};
+
class TRINITY_DLL_SPEC AuraEffect
{
public:
@@ -306,20 +333,26 @@ class TRINITY_DLL_SPEC AuraEffect
void HandleAuraCloneCaster(bool Apply, bool Real, bool changeAmount);
void HandleAuraModCritPct(bool Apply, bool Real, bool changeAmount);
void HandleAuraLinked(bool Apply, bool Real, bool changeAmount);
+
void HandleAuraEffectSpecificMods(bool apply, bool Real, bool changeAmount);
int32 CalculateCrowdControlAuraAmount(Unit * caster);
+
// add/remove SPELL_AURA_MOD_SHAPESHIFT (36) linked auras
void HandleShapeshiftBoosts(bool apply);
+
Unit * GetCaster() const { return m_parentAura->GetCaster(); }
uint64 GetCasterGUID() const{ return m_parentAura->GetCasterGUID(); }
Aura * GetParentAura() const { return m_parentAura; }
+
SpellEntry const* GetSpellProto() const { return m_spellProto; }
uint32 GetId() const { return m_spellProto->Id; }
uint32 GetEffIndex() const { return m_effIndex; }
int32 GetBasePoints() const { return m_currentBasePoints; }
int32 GetAuraAmplitude(){return m_amplitude;}
void ResetPeriodicTimer(){m_periodicTimer = m_amplitude;}
+
virtual void Update(uint32 diff);
+
uint32 GetTickNumber() const { return m_tickNumber; }
int32 GetTotalTicks () const { return m_amplitude ? (GetParentAura()->GetAuraMaxDuration() / m_amplitude) : 1;}
bool IsAreaAura() const { return m_isAreaAura; }
@@ -328,34 +361,43 @@ class TRINITY_DLL_SPEC AuraEffect
bool IsApplied() const { return m_isApplied; }
void SetApplied (bool val) {m_isApplied = val; }
bool isAffectedOnSpell(SpellEntry const *spell) const;
+
void ApplyModifier(bool apply, bool Real = false, bool changeAmount=false);
void RecalculateAmount(bool applied = true);
void HandleAuraEffect(bool apply);
void ApplyAllModifiers(bool apply, bool Real);
+
Unit* GetTriggerTarget() const;
void TriggerSpell();
void TriggerSpellWithValue();
void PeriodicTick();
void PeriodicDummyTick();
+
int32 GetMiscBValue() const {return m_spellProto->EffectMiscValueB[m_effIndex];}
int32 GetMiscValue() const {return m_spellProto->EffectMiscValue[m_effIndex];}
uint32 GetAuraName() const {return m_auraName;}
int32 GetAmount() const {return m_amount;}
void SetAmount(int32 amount) { m_amount = amount; }
void CleanupTriggeredSpells();
+
protected:
explicit AuraEffect(Aura * parentAura, uint8 effIndex, int32 *currentBasePoints = NULL);
Aura * const m_parentAura;
Unit * const m_target;
+
uint32 m_tickNumber;
+
const SpellEntry * const m_spellProto;
const uint8 m_effIndex;
const AuraType m_auraName;
int32 m_currentBasePoints;
int32 m_amount;
+
SpellModifier *m_spellmod;
+
int32 m_periodicTimer; // Timer for periodic auras
int32 m_amplitude;
+
bool m_isPeriodic:1;
bool m_isAreaAura:1;
bool m_isPersistent:1;
@@ -363,6 +405,7 @@ class TRINITY_DLL_SPEC AuraEffect
private:
bool IsPeriodicTickCrit(Unit const * pCaster) const;
};
+
class TRINITY_DLL_SPEC AreaAuraEffect : public AuraEffect
{
public:
@@ -375,6 +418,7 @@ class TRINITY_DLL_SPEC AreaAuraEffect : public AuraEffect
int32 m_removeTime;
AreaAuraType m_areaAuraType;
};
+
class TRINITY_DLL_SPEC PersistentAreaAuraEffect : public AuraEffect
{
public:
@@ -384,5 +428,7 @@ class TRINITY_DLL_SPEC PersistentAreaAuraEffect : public AuraEffect
protected:
explicit PersistentAreaAuraEffect(Aura * parentAura, uint32 eff, int32 *currentBasePoints = NULL);
};
+
AuraEffect* CreateAuraEffect(Aura * parentAura, uint32 effIndex, int32 *currentBasePoints = NULL);
+
#endif
diff --git a/src/game/SpellEffects.cpp b/src/game/SpellEffects.cpp
index df396c7f24e..d9aedd5fc98 100644
--- a/src/game/SpellEffects.cpp
+++ b/src/game/SpellEffects.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -60,6 +61,7 @@
#include "SkillDiscovery.h"
#include "Formulas.h"
#include "Vehicle.h"
+
pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
{
&Spell::EffectNULL, // 0
@@ -226,34 +228,44 @@ pEffect SpellEffects[TOTAL_SPELL_EFFECTS]=
&Spell::EffectSpecCount, //161 SPELL_EFFECT_TALENT_SPEC_COUNT second talent spec (learn/revert)
&Spell::EffectActivateSpec, //162 SPELL_EFFECT_TALENT_SPEC_SELECT activate primary/secondary spec
};
+
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)
{
@@ -270,31 +282,41 @@ void Spell::EffectInstaKill(uint32 /*i*/)
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();
+
m_caster->DealDamage(unitTarget, unitTarget->GetHealth(), 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->CalculateSimpleValue(i);
+
m_caster->CalcAbsorbResist(m_caster, GetSpellSchoolMask(m_spellInfo), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist, m_spellInfo);
+
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(DAMAGE_FIRE, damage);
}
+
void Spell::EffectSchoolDMG(uint32 effect_idx)
{
}
+
void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
{
bool apply_direct_bonus = true;
+
if (unitTarget && unitTarget->isAlive())
{
switch (m_spellInfo->SpellFamilyName)
@@ -308,8 +330,10 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
for (std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
if (ihit->effectMask & (1<<effect_idx))
++count;
+
damage /= count; // divide to all targets
}
+
switch(m_spellInfo->Id) // better way to check unknown
{
// Positive/Negative Charge
@@ -347,6 +371,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
aur->SetStackAmount(count);
}
}
+
if (unitTarget->HasAura(m_triggeredByAuraSpell->Id))
damage = 0;
break;
@@ -376,6 +401,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
// don't damage self and only players
if(unitTarget->GetGUID() == m_caster->GetGUID() || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
float radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[0]));
if (!radius) return;
float distance = m_caster->GetDistance2d(unitTarget);
@@ -397,6 +423,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
}
break;
}
+
case SPELLFAMILY_WARRIOR:
{
// Bloodthirst
@@ -438,6 +465,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
else if (m_spellInfo->TargetAuraState == AURA_STATE_CONFLAGRATE)
{
AuraEffect const* aura = NULL; // found req. aura for damage calculation
+
Unit::AuraEffectList const &mPeriodic = unitTarget->GetAurasByType(SPELL_AURA_PERIODIC_DAMAGE);
for(Unit::AuraEffectList::const_iterator i = mPeriodic.begin(); i != mPeriodic.end(); ++i)
{
@@ -445,16 +473,19 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
if ((*i)->GetSpellProto()->SpellFamilyName != SPELLFAMILY_WARLOCK ||
(*i)->GetCasterGUID() != m_caster->GetGUID())
continue;
+
// Immolate
if ((*i)->GetSpellProto()->SpellFamilyFlags[0] & 0x4)
{
aura = *i; // it selected always if exist
break;
}
+
// Shadowflame
if ((*i)->GetSpellProto()->SpellFamilyFlags[2] & 0x00000002)
aura = *i; // remember but wait possible Immolate as primary priority
}
+
// found Immolate or Shadowflame
if (aura)
{
@@ -465,6 +496,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
// Glyph of Conflagrate
if (!m_caster->HasAura(56235))
unitTarget->RemoveAurasDueToSpell(aura->GetId(), m_caster->GetGUID());
+
break;
}
}
@@ -546,6 +578,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
{
float ap = m_caster->GetTotalAttackPowerValue(BASE_ATTACK);
damage += irand(int32(ap * combo * 0.03f), int32(ap * combo * 0.07f));
+
// Eviscerate and Envenom Bonus Damage (item set effect)
if (m_caster->HasAura(37169))
damage += combo*40;
@@ -575,9 +608,11 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
break;
}
}
+
// TODO: should this be put on taken but not done?
if(found)
damage += m_spellInfo->EffectBasePoints[1];
+
if (m_caster->GetTypeId() == TYPEID_PLAYER)
{
// Add Ammo and Weapon damage plus RAP * 0.1
@@ -593,6 +628,7 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
damage += ((Player*)m_caster)->GetAmmoDPS()*item->GetProto()->Delay/1000;
}
}
+
}
break;
}
@@ -624,19 +660,24 @@ void Spell::SpellDamageSchoolDmg(uint32 effect_idx)
break;
}
}
+
if(m_originalCaster && damage > 0 && apply_direct_bonus)
damage = m_originalCaster->SpellDamageBonus(unitTarget, m_spellInfo, (uint32)damage, SPELL_DIRECT_DAMAGE);
+
m_damage += damage;
}
}
+
void Spell::EffectDummy(uint32 i)
{
if (!unitTarget && !gameObjTarget && !itemTarget)
return;
+
uint32 spell_id = 0;
int32 bp = 0;
bool triggered = true;
SpellCastTargets targets;
+
// selection by spell family
switch (m_spellInfo->SpellFamilyName)
{
@@ -651,9 +692,12 @@ void Spell::EffectDummy(uint32 i)
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
if(ihit->effectMask & (1<<i))
++count;
+
damage = 12000; // maybe wrong value
damage /= count;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(42784);
+
// now deal the damage
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin();ihit != m_UniqueTargetInfo.end();++ihit)
if(ihit->effectMask & (1<<i))
@@ -666,6 +710,7 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 spell_id = 0;
switch(urand(1,5))
{
@@ -682,6 +727,7 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 spell_id = 0;
switch(urand(1,2))
{
@@ -707,6 +753,7 @@ void Spell::EffectDummy(uint32 i)
{
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
@@ -714,6 +761,7 @@ void Spell::EffectDummy(uint32 i)
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 12162: damage *= 0.16f; break; // Rank 1
@@ -723,6 +771,7 @@ void Spell::EffectDummy(uint32 i)
sLog.outError("Spell::EffectDummy: Spell %u not handled in DW",m_spellInfo->Id);
return;
};
+
// get remaining damage of old Deep Wound aura
AuraEffect* deepWound = unitTarget->GetAuraEffect(12721, 0);
if (deepWound)
@@ -730,6 +779,7 @@ void Spell::EffectDummy(uint32 i)
int32 remainingTicks = deepWound->GetParentAura()->GetAuraDuration() / deepWound->GetAuraAmplitude();
damage += remainingTicks * deepWound->GetAmount();
}
+
// 1 tick/sec * 6 sec = 6 ticks
int32 deepWoundsDotBasePoints0 = int32(damage / 6);
m_caster->CastCustomSpell(unitTarget, 12721, &deepWoundsDotBasePoints0, NULL, NULL, true, NULL);
@@ -739,14 +789,18 @@ void Spell::EffectDummy(uint32 i)
{
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;
}
@@ -755,6 +809,7 @@ void Spell::EffectDummy(uint32 i)
// can be used for different aura triggering, so select by aura
if (!m_triggeredByAuraSpell || !unitTarget)
return;
+
switch (m_triggeredByAuraSpell->Id)
{
case 26467: // Persistent Shield
@@ -771,6 +826,7 @@ void Spell::EffectDummy(uint32 i)
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
return;
+
((Creature*)unitTarget)->ForcedDespawn();
return;
}
@@ -778,6 +834,7 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 spell_id = 0;
switch (urand(1, 3))
{
@@ -785,6 +842,7 @@ void Spell::EffectDummy(uint32 i)
case 2: spell_id = 16593; break;
default:spell_id = 16591; break;
}
+
m_caster->CastSpell(m_caster, spell_id, true, NULL);
return;
}
@@ -792,6 +850,7 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget || !m_originalCaster)
return;
+
if(m_originalCaster->GetTypeId() == TYPEID_PLAYER)
{
WorldPacket data(SMSG_SPIRIT_HEALER_CONFIRM, 8);
@@ -804,9 +863,11 @@ void Spell::EffectDummy(uint32 i)
{
if(!itemTarget && m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
uint32 spell_id = roll_chance_i(50)
? 17269 // Create Resonating Skull
: 17270; // Create Bone Dust
+
m_caster->CastSpell(m_caster, spell_id, true, NULL);
return;
}
@@ -818,13 +879,18 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget || !unitTarget->isAlive() || unitTarget->GetTypeId() != TYPEID_UNIT || ((Creature*)unitTarget)->isPet())
return;
+
Creature* creatureTarget = (Creature*)unitTarget;
+
GameObject* Crystal_Prison = m_caster->SummonGameObject(179644, creatureTarget->GetPositionX(), creatureTarget->GetPositionY(), creatureTarget->GetPositionZ(), creatureTarget->GetOrientation(), 0, 0, 0, 0, creatureTarget->GetRespawnTime()-time(NULL));
sLog.outDebug("SummonGameObject at SpellEfects.cpp EffectDummy for Spell 23019");
+
creatureTarget->ForcedDespawn();
+
WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
data << uint64(Crystal_Prison->GetGUID());
m_caster->SendMessageToSet(&data, true);
+
return;
}
case 23074: // Arcanite Dragonling
@@ -871,9 +937,12 @@ void Spell::EffectDummy(uint32 i)
{
if (!m_caster->HasAuraType(SPELL_AURA_MOUNTED))
return;
+
float flyspeed = m_caster->GetSpeedRate(MOVE_FLIGHT);
float speed = m_caster->GetSpeedRate(MOVE_RUN);
+
m_caster->RemoveAurasByType(SPELL_AURA_MOUNTED);
+
//5 different spells used depending on mounted speed and if mount can fly or not
if (flyspeed >= 4.1f)
// Flying Reindeer
@@ -890,6 +959,7 @@ void Spell::EffectDummy(uint32 i)
else
// Reindeer
m_caster->CastSpell(m_caster, 25858, true); //60% ground Reindeer
+
return;
}
case 26074: // Holiday Cheer
@@ -916,9 +986,11 @@ void Spell::EffectDummy(uint32 i)
{
if( m_caster->GetTypeId() != TYPEID_PLAYER )
return;
+
uint32 spell_id = roll_chance_i(50)
? 29277 // Summon Purified Helboar Meat
: 29278; // Summon Toxic Helboar Meat
+
m_caster->CastSpell(m_caster,spell_id,true,NULL);
return;
}
@@ -945,7 +1017,9 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
uint32 spell_id = 0;
+
switch(urand(1,5))
{
case 1: spell_id = 33053; break; // Mr Pinchy's Blessing
@@ -954,6 +1028,7 @@ void Spell::EffectDummy(uint32 i)
case 4: spell_id = 33062; break; // Tiny Magical Crawdad
case 5: spell_id = 33064; break; // Mr. Pinchy's Gift
}
+
m_caster->CastSpell(m_caster, spell_id, true, NULL);
return;
}
@@ -966,6 +1041,7 @@ void Spell::EffectDummy(uint32 i)
case 3742: spell_id = 35744; break; // Socrethar Portal
default: return;
}
+
m_caster->CastSpell(m_caster, spell_id, true);
return;
}
@@ -973,6 +1049,7 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget)
return;
+
int32 basepoints0 = 100;
m_caster->CastCustomSpell(unitTarget, 37675, &basepoints0, NULL, NULL, true);
return;
@@ -1006,6 +1083,7 @@ void Spell::EffectDummy(uint32 i)
default:
return;
}
+
DoCreateItem(i, newitemid);
return;
}
@@ -1014,7 +1092,9 @@ void Spell::EffectDummy(uint32 i)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *player = (Player*)m_caster;
+
if (player && player->GetQuestStatus(11379) == QUEST_STATUS_INCOMPLETE)
{
Creature *creature = player->FindNearestCreature(19973, 10, false);
@@ -1023,6 +1103,7 @@ void Spell::EffectDummy(uint32 i)
SendCastResult(SPELL_FAILED_NOT_HERE);
return;
}
+
player->CastSpell(player, 43753, false);
}
return;
@@ -1031,7 +1112,9 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
return;
+
((Creature*)unitTarget)->ForcedDespawn();
+
//cast spell Raptor Capture Credit
m_caster->CastSpell(m_caster, 42337, true, NULL);
return;
@@ -1041,10 +1124,13 @@ void Spell::EffectDummy(uint32 i)
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT
|| unitTarget->GetEntry() != 16880 || ((Creature*)unitTarget)->isPet())
return;
+
((Creature*)unitTarget)->UpdateEntry(16992);
((Player*)m_caster)->RewardPlayerAndGroupAtEvent(16992, unitTarget);
+
if (unitTarget->IsAIEnabled)
((Creature*)unitTarget)->AI()->AttackStart(m_caster);
+
return;
}
case 44997: // Converting Sentry
@@ -1070,6 +1156,7 @@ void Spell::EffectDummy(uint32 i)
{
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;
@@ -1083,14 +1170,17 @@ void Spell::EffectDummy(uint32 i)
// 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;
}
@@ -1098,8 +1188,10 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
return;
+
((Creature*)unitTarget)->ForcedDespawn();
return;
+
}
case 52308: // Take Sputum Sample
{
@@ -1109,6 +1201,7 @@ void Spell::EffectDummy(uint32 i)
{
uint32 spellID = m_spellInfo->CalculateSimpleValue(0);
uint32 reqAuraID = m_spellInfo->CalculateSimpleValue(1);
+
if (m_caster->HasAuraEffect(reqAuraID,0))
m_caster->CastSpell(m_caster,spellID,true,NULL);
return;
@@ -1137,6 +1230,7 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 spell_id = 0;
switch(urand(1,3))
{
@@ -1186,6 +1280,7 @@ void Spell::EffectDummy(uint32 i)
}
return;
}
+
//All IconID Check in there
switch(m_spellInfo->SpellIconID)
{
@@ -1198,9 +1293,11 @@ void Spell::EffectDummy(uint32 i)
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);
@@ -1216,11 +1313,13 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
// immediately finishes the cooldown on Frost spells
const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap();
for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+
if( spellInfo->SpellFamilyName == SPELLFAMILY_MAGE &&
(GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST) &&
spellInfo->Id != 11958 && GetSpellRecoveryTime(spellInfo) > 0 )
@@ -1271,10 +1370,12 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget)
return;
+
uint32 rage=0;
// Glyph of Execution bonus
if (AuraEffect *aura = m_caster->GetAuraEffect(58367, 0))
rage+=aura->GetAmount();
+
spell_id = 20647;
// Sudden death cost modifier
if (Aura * aur = m_caster->GetAura(52437))
@@ -1290,6 +1391,7 @@ void Spell::EffectDummy(uint32 i)
rage += m_caster->GetPower(POWER_RAGE);
m_caster->SetPower(POWER_RAGE,0);
}
+
bp = damage+int32(rage * m_spellInfo->DmgMultiplier[i] +
m_caster->GetTotalAttackPowerValue(BASE_ATTACK)*0.2f);
break;
@@ -1351,15 +1453,19 @@ void Spell::EffectDummy(uint32 i)
{
// Shouldn't Appear in Combat Log
unitTarget->ModifyHealth(-damage);
+
int32 mana = damage;
// Improved Life Tap mod
if (AuraEffect const * aurEff = m_caster->GetDummyAura(SPELLFAMILY_WARLOCK, 208, 0))
mana = (aurEff->GetAmount() + 100)* mana / 100;
+
m_caster->CastCustomSpell(unitTarget, 31818, &mana, NULL, NULL, true);
+
// Mana Feed
int32 manaFeedVal = 0;
if (AuraEffect const * aurEff = m_caster->GetAuraEffect(SPELL_AURA_ADD_FLAT_MODIFIER, SPELLFAMILY_WARLOCK, 1982, 0))
manaFeedVal = aurEff->GetAmount();
+
if(manaFeedVal > 0)
{
manaFeedVal = manaFeedVal * mana / 100;
@@ -1377,6 +1483,7 @@ void Spell::EffectDummy(uint32 i)
{
if (!unitTarget || !unitTarget->isAlive())
return;
+
int hurt = 0;
int heal = 0;
switch(m_spellInfo->Id)
@@ -1407,9 +1514,11 @@ void Spell::EffectDummy(uint32 i)
m_caster->RemoveAurasDueToSpell(m_triggeredByAuraSpell->Id);
return;
}
+
//Any effect which causes you to lose control of your character will supress the starfall effect.
if(m_caster->hasUnitState(UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_ROOT | UNIT_STAT_CONFUSED))
return;
+
m_caster->CastSpell(unitTarget, damage, true);
return;
}
@@ -1427,26 +1536,34 @@ void Spell::EffectDummy(uint32 i)
{
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;
}
@@ -1454,11 +1571,13 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
//immediately finishes the cooldown on certain Rogue abilities
const SpellCooldowns& cm = ((Player *)m_caster)->GetSpellCooldownMap();
for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+
if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && (spellInfo->SpellFamilyFlags[1] & 0x00000240 || spellInfo->SpellFamilyFlags[0] & 0x00000860))
((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true);
else
@@ -1480,11 +1599,13 @@ void Spell::EffectDummy(uint32 i)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
// immediately finishes the cooldown on your other Hunter abilities except Bestial Wrath
const SpellCooldowns& cm = ((Player*)m_caster)->GetSpellCooldownMap();
for (SpellCooldowns::const_iterator itr = cm.begin(); itr != cm.end();)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(itr->first);
+
if (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && spellInfo->Id != 23989 && GetSpellRecoveryTime(spellInfo) > 0 )
((Player*)m_caster)->RemoveSpellCooldown((itr++)->first,true);
else
@@ -1496,6 +1617,7 @@ void Spell::EffectDummy(uint32 i)
{
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
// break Auto Shot and autohit
m_caster->InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
m_caster->AttackStop();
@@ -1534,8 +1656,10 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget)
return;
+
int hurt = 0;
int heal = 0;
+
switch(m_spellInfo->Id)
{
case 20473: hurt = 25912; heal = 25914; break;
@@ -1549,13 +1673,16 @@ void Spell::EffectDummy(uint32 i)
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;
}
}
+
switch(m_spellInfo->Id)
{
case 54171: //Divine Storm
@@ -1566,15 +1693,18 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget)
return;
+
SpellEntry const* spell_proto = sSpellStore.LookupEntry(damage);
if(!spell_proto)
return;
+
m_caster->CastSpell(unitTarget, spell_proto, true, NULL);
return;
}
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())
{
@@ -1583,12 +1713,15 @@ void Spell::EffectDummy(uint32 i)
SendCastResult(SPELL_FAILED_TARGET_AFFECTING_COMBAT);
return;
}
+
// Righteous Defense (step 2) (in old version 31980 dummy effect)
// Clear targets for eff 1
for(std::list<TargetInfo>::iterator ihit= m_UniqueTargetInfo.begin(); ihit != m_UniqueTargetInfo.end(); ++ihit)
ihit->effectMask &= ~(1<<1);
+
// not empty (checked)
Unit::AttackerSet const& attackers = unitTarget->getAttackers();
+
// chance to be selected from list
float chance = 100.0f / attackers.size();
uint32 count = 0;
@@ -1599,6 +1732,7 @@ void Spell::EffectDummy(uint32 i)
++count;
AddUnitTarget((*aItr), 1);
}
+
// now let next effect cast spell at each target.
return;
}
@@ -1606,6 +1740,7 @@ void Spell::EffectDummy(uint32 i)
{
if(!unitTarget)
return;
+
uint32 spell_id = 0;
switch(unitTarget->getClass())
{
@@ -1615,6 +1750,7 @@ void Spell::EffectDummy(uint32 i)
case CLASS_SHAMAN: spell_id = 37881; break;
default: return; // ignore for not healing classes
}
+
m_caster->CastSpell(m_caster, spell_id, true);
return;
}
@@ -1637,14 +1773,18 @@ void Spell::EffectDummy(uint32 i)
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", spell_id);
return;
}
+
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
for(uint8 j = BASE_ATTACK; j <= OFF_ATTACK; ++j)
{
if(Item* item = ((Player*)m_caster)->GetWeaponForAttack(WeaponAttackType(j)))
@@ -1652,10 +1792,12 @@ void Spell::EffectDummy(uint32 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);
@@ -1704,6 +1846,7 @@ void Spell::EffectDummy(uint32 i)
{
if (m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
if (Item *item = ((Player*)m_caster)->GetItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND))
{
// Damage is increased by 25% if your off-hand weapon is enchanted with Flametongue.
@@ -1777,6 +1920,7 @@ void Spell::EffectDummy(uint32 i)
{
if ( m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
// Do we have talent Master of Ghouls?
if(m_caster->HasAura(52143))
// summon as pet
@@ -1784,6 +1928,7 @@ void Spell::EffectDummy(uint32 i)
else
// or guardian
bp = 46585;
+
if (m_targets.HasDst())
{
targets.setDst(&m_targets.m_dstPos);
@@ -1803,6 +1948,7 @@ void Spell::EffectDummy(uint32 i)
{
if (m_targets.HasDst())
targets.setDst(&m_targets.m_dstPos);
+
spell_id = m_currentBasePoints[0];
}
// Corpse Explosion
@@ -1836,30 +1982,36 @@ void Spell::EffectDummy(uint32 i)
m_caster->RemoveAurasDueToSpell(50514, m_caster->GetGUID());
else
m_caster->ModifyPower(POWER_RUNIC_POWER,-30);
+
return;
}
break;
}
+
//spells triggered by dummy effect should not miss
if(spell_id)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry( spell_id );
+
if(!spellInfo)
{
sLog.outError("EffectDummy of spell %u: triggering unknown spell id %i\n", m_spellInfo->Id, spell_id);
return;
}
+
targets.setUnitTarget(unitTarget);
Spell* spell = new Spell(m_caster, spellInfo, triggered, m_originalCasterGUID, NULL, true);
if(bp) spell->m_currentBasePoints[0] = bp;
spell->prepare(&targets);
}
+
// pet auras
if(PetAura const* petSpell = spellmgr.GetPetAura(m_spellInfo->Id,i))
{
m_caster->AddPetAura(petSpell);
return;
}
+
// Script based implementation. Must be used only for not good for implementation in core spell effects
// So called only for not proccessed cases
if(gameObjTarget)
@@ -1869,43 +2021,56 @@ void Spell::EffectDummy(uint32 i)
else if(itemTarget)
Script->EffectDummyItem(m_caster, m_spellInfo->Id, i, itemTarget);
}
+
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", 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();
+
m_caster->CastSpell(unitTarget,spellInfo,false);
}
+
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;
}
+
if (damage)
{
if(m_spellInfo->EffectBasePoints[i] > 0)
@@ -1922,8 +2087,10 @@ void Spell::EffectForceCast(uint32 i)
return;
}
}
+
unitTarget->CastSpell(unitTarget, spellInfo, true, NULL, NULL, m_originalCasterGUID);
}
+
void Spell::EffectTriggerSpell(uint32 effIndex)
{
// only unit case known
@@ -1933,7 +2100,9 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
sLog.outError("Spell::EffectTriggerSpell (Spell: %u): Unsupported non-unit case!",m_spellInfo->Id);
return;
}
+
uint32 triggered_spell_id = m_spellInfo->EffectTriggerSpell[effIndex];
+
// special cases
switch(triggered_spell_id)
{
@@ -1943,6 +2112,7 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
// Glyph of Mirror Image
if (m_caster->HasAura(63093))
m_caster->CastSpell(m_caster, 65047, true); // Mirror Image
+
break;
}
// Vanish (not exist)
@@ -1950,9 +2120,11 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
{
unitTarget->RemoveMovementImpairingAuras();
unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED);
+
// if this spell is given to NPC it must handle rest by it's own AI
if (unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
// get highest rank of the Stealth spell
uint32 spellId = 0;
SpellEntry const *spellInfo;
@@ -1962,21 +2134,26 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
// 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;
+
spellInfo = sSpellStore.LookupEntry(itr->first);
if (!spellInfo)
continue;
+
if (spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & SPELLFAMILYFLAG_ROGUE_STEALTH)
{
spellId = spellInfo->Id;
break;
}
}
+
// no Stealth spell found
if (!spellId)
return;
+
// reset cooldown on it if needed
if (((Player*)unitTarget)->HasSpellCooldown(spellId))
((Player*)unitTarget)->RemoveSpellCooldown(spellId);
+
// Push stealth to list because it must be handled after combat remove
m_TriggerSpells.push_back(spellInfo);
return;
@@ -1987,6 +2164,7 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
unitTarget->RemoveMovementImpairingAuras();
unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STALKED);
unitTarget->RemoveAurasByType(SPELL_AURA_MOD_STUN);
+
// Cast Lesser Invisibility
triggered_spell_id = 7870;
break;
@@ -2002,6 +2180,7 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
SpellEntry const* spell = sSpellStore.LookupEntry(24575);
if (!spell)
return;
+
for (int j=0; j < spell->StackAmount; ++j)
m_caster->CastSpell(unitTarget, spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
return;
@@ -2013,6 +2192,7 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
SpellEntry const* spell = sSpellStore.LookupEntry(26464);
if (!spell)
return;
+
for (int j=0; j < spell->StackAmount; ++j)
m_caster->CastSpell(unitTarget, spell->Id, true, m_CastItem, NULL, m_originalCasterGUID);
return;
@@ -2052,6 +2232,7 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
return;
}
}
+
// normal case
SpellEntry const *spellInfo = sSpellStore.LookupEntry( triggered_spell_id );
if (!spellInfo)
@@ -2059,47 +2240,59 @@ void Spell::EffectTriggerSpell(uint32 effIndex)
sLog.outError("EffectTriggerSpell of spell %u: triggering unknown spell id %i", m_spellInfo->Id,triggered_spell_id);
return;
}
+
// Remove spell cooldown (not category) if spell triggering spell with cooldown and same category
// Needed by freezing arrow and few other spells
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime
&& m_spellInfo->Category == spellInfo->Category)
((Player*)m_caster)->RemoveSpellCooldown(spellInfo->Id);
+
// Note: not exist spells with weapon req. and IsSpellHaveCasterSourceTargets == true
// so this just for speedup places in else
Unit *caster = IsSpellWithCasterSourceTargetsOnly(spellInfo) ? unitTarget : m_caster;
+
caster->CastSpell(unitTarget,spellInfo,true,NULL,NULL,m_originalCasterGUID);
}
+
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 (eff: %u): triggering unknown spell id %u",
m_spellInfo->Id,effect_idx,triggered_spell_id);
return;
}
+
if (m_CastItem)
DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
// Remove spell cooldown (not category) if spell triggering spell with cooldown and same category
// Needed by freezing arrow and few other spells
if (m_caster->GetTypeId() == TYPEID_PLAYER && m_spellInfo->CategoryRecoveryTime && spellInfo->CategoryRecoveryTime
&& m_spellInfo->Category == spellInfo->Category)
((Player*)m_caster)->RemoveSpellCooldown(spellInfo->Id);
+
float x, y, z;
m_targets.m_dstPos.GetPosition(x, y, z);
m_caster->CastSpell(x, y, z, spellInfo->Id, true, m_CastItem, 0, m_originalCasterGUID);
}
+
void Spell::EffectJump(uint32 i)
{
if(m_caster->isInFlight())
return;
+
// Init dest coordinates
float x,y,z,o;
if(m_targets.HasDst())
{
m_targets.m_dstPos.GetPosition(x, y, z);
+
if(m_spellInfo->EffectImplicitTargetA[i] == TARGET_DEST_TARGET_BACK)
{
// explicit cast data from client or server-side cast
@@ -2111,6 +2304,7 @@ void Spell::EffectJump(uint32 i)
pTarget = m_caster->getVictim();
else if(m_caster->GetTypeId() == TYPEID_PLAYER)
pTarget = ObjectAccessor::GetUnit(*m_caster, ((Player*)m_caster)->GetSelection());
+
o = pTarget ? pTarget->GetOrientation() : m_caster->GetOrientation();
}
else
@@ -2131,6 +2325,7 @@ void Spell::EffectJump(uint32 i)
sLog.outError( "Spell::EffectJump - unsupported target mode for spell ID %u", m_spellInfo->Id );
return;
}
+
//m_caster->NearTeleportTo(x,y,z,o,true);
float speedZ;
if(m_spellInfo->EffectMiscValue[i])
@@ -2142,16 +2337,19 @@ void Spell::EffectJump(uint32 i)
float speedXY = m_caster->GetExactDist2d(x, y) * 10.0f / speedZ;
m_caster->GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ);
}
+
void Spell::EffectTeleportUnits(uint32 i)
{
if(!unitTarget || unitTarget->isInFlight())
return;
+
// If not exist data for dest location - return
if(!m_targets.HasDst())
{
sLog.outError( "Spell::EffectTeleportUnits - does not have destination for spell ID %u\n", m_spellInfo->Id );
return;
}
+
// Init dest coordinates
uint32 mapid = m_targets.m_dstPos.GetMapId();
if(mapid == MAPID_INVALID) mapid = unitTarget->GetMapId();
@@ -2159,10 +2357,12 @@ void Spell::EffectTeleportUnits(uint32 i)
m_targets.m_dstPos.GetPosition(x, y, z);
float orientation = m_targets.getUnitTarget() ? m_targets.getUnitTarget()->GetOrientation() : unitTarget->GetOrientation();
sLog.outDebug("Spell::EffectTeleportUnits - teleport unit to %u %f %f %f\n", mapid, x, y, z);
+
if(mapid == unitTarget->GetMapId())
unitTarget->NearTeleportTo(x, y, z, orientation, unitTarget == m_caster);
else if(unitTarget->GetTypeId() == TYPEID_PLAYER)
((Player*)unitTarget)->TeleportTo(mapid, x, y, z, orientation, unitTarget==m_caster ? TELE_TO_SPELL : 0);
+
// post effects for TARGET_DST_DB
switch ( m_spellInfo->Id )
{
@@ -2259,32 +2459,41 @@ void Spell::EffectTeleportUnits(uint32 i)
}
}
}
+
void Spell::EffectApplyAura(uint32 i)
{
if (m_spellAura)
if (AuraEffect * AurEff = m_spellAura->GetPartAura(i))
unitTarget->HandleAuraEffect(AurEff, true);
}
+
void Spell::EffectApplyAreaAura(uint32 i)
{
if (m_spellAura)
if (AuraEffect * AurEff = m_spellAura->GetPartAura(i))
unitTarget->HandleAuraEffect(AurEff, true);
}
+
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 has 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())
@@ -2293,31 +2502,41 @@ void Spell::EffectPowerDrain(uint32 i)
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);
+
// Don`t restore from self drain
if(drain_power == POWER_MANA && m_caster != unitTarget)
{
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->EnergizeBySpell(m_caster, m_spellInfo->Id, gain, POWER_MANA);
}
}
+
void Spell::EffectSendEvent(uint32 EffectIndex)
{
/*
@@ -2326,11 +2545,14 @@ void Spell::EffectSendEvent(uint32 EffectIndex)
sLog.outDebug("Spell ScriptStart %u for spellid %u in EffectSendEvent ", m_spellInfo->EffectMiscValue[EffectIndex], m_spellInfo->Id);
m_caster->GetMap()->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())
@@ -2339,7 +2561,9 @@ void Spell::EffectPowerBurn(uint32 i)
return;
if(damage < 0)
return;
+
Unit* caster = m_originalCaster ? m_originalCaster : m_caster;
+
// burn x% of target's mana, up to maximum of 2x% of caster's mana (Mana Burn)
if(m_spellInfo->ManaCostPercentage)
{
@@ -2347,16 +2571,22 @@ void Spell::EffectPowerBurn(uint32 i)
damage = unitTarget->GetMaxPower(powertype) * damage / 100;
if(damage > maxdamage) damage = maxdamage;
}
+
int32 curPower = int32(unitTarget->GetPower(powertype));
+
uint32 power = damage;
// resilience reduce mana draining effect at spell crit damage reduction (added in 2.4)
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_damage+=new_damage; should not apply spell bonus
//TODO: no log
@@ -2364,19 +2594,24 @@ void Spell::EffectPowerBurn(uint32 i)
if(m_originalCaster)
m_originalCaster->DealDamage(unitTarget, new_damage);
}
+
void Spell::EffectHeal( uint32 /*i*/ )
{
}
+
void Spell::SpellDamageHeal(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)
{
@@ -2387,6 +2622,7 @@ void Spell::SpellDamageHeal(uint32 /*i*/)
damageAmount+= aurEff->GetAmount();
m_caster->RemoveAurasDueToSpell(45062);
}
+
addhealth += damageAmount;
}
// Swiftmend - consumes Regrowth or Rejuvenation
@@ -2404,16 +2640,19 @@ void Spell::SpellDamageHeal(uint32 /*i*/)
targetAura = *i;
}
}
+
if(!targetAura)
{
sLog.outError("Target(GUID:" UI64FMTD ") has aurastate AURA_STATE_SWIFTMEND but no matching aura.", unitTarget->GetGUID());
return;
}
+
int32 tickheal = targetAura->GetAmount();
if(Unit* auraCaster = targetAura->GetCaster())
tickheal = auraCaster->SpellHealingBonus(unitTarget, targetAura->GetSpellProto(), tickheal, DOT);
//int32 tickheal = targetAura->GetSpellProto()->EffectBasePoints[idx] + 1;
//It is said that talent bonus should not be included
+
int32 tickcount = 0;
// Rejuvenation
if (targetAura->GetSpellProto()->SpellFamilyFlags[0] & 0x10)
@@ -2421,10 +2660,13 @@ void Spell::SpellDamageHeal(uint32 /*i*/)
// Regrowth
else // if (targetAura->GetSpellProto()->SpellFamilyFlags[0] & 0x40)
tickcount = 6;
+
addhealth += tickheal * tickcount;
+
// Glyph of Swiftmend
if(!caster->HasAura(54824))
unitTarget->RemoveAura(targetAura->GetId(), targetAura->GetCasterGUID());
+
//addhealth += tickheal * tickcount;
//addhealth = caster->SpellHealingBonus(m_spellInfo, addhealth,HEAL, unitTarget);
}
@@ -2446,28 +2688,35 @@ void Spell::SpellDamageHeal(uint32 /*i*/)
}
else
addhealth = caster->SpellHealingBonus(unitTarget, m_spellInfo, addhealth, HEAL);
+
m_damage -= addhealth;
}
}
+
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;
+
// Rune Tap - Party
if (m_spellInfo->Id == 59754 && unitTarget == m_caster)
return;
+
uint32 addhealth = unitTarget->GetMaxHealth() * damage / 100;
if(Player* modOwner = m_caster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DAMAGE, addhealth, this);
+
int32 gain = caster->DealHeal(unitTarget, addhealth, m_spellInfo);
unitTarget->getHostilRefManager().threatAssist(m_caster, float(gain) * 0.5f, m_spellInfo);
}
}
+
void Spell::EffectHealMechanical( uint32 /*i*/ )
{
// Mechanic creature type should be correctly checked by targetCreatureType field
@@ -2475,25 +2724,33 @@ void Spell::EffectHealMechanical( uint32 /*i*/ )
{
// 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(unitTarget, m_spellInfo, uint32(damage), HEAL);
caster->DealHeal(unitTarget, addhealth, m_spellInfo);
}
}
+
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);
+
// Do not apply multiplier to damage if it's Death Coil
int32 new_damage;
if (m_spellInfo->SpellFamilyFlags[0] & 0x80000)
@@ -2504,6 +2761,7 @@ void Spell::EffectHealthLeech(uint32 i)
new_damage = m_caster->SpellNonMeleeDamageLog(unitTarget, m_spellInfo->Id, new_damage );
if(curHealth < new_damage)
new_damage = curHealth;
+
if(m_caster->isAlive())
{
// Insead add it just to healing if it's Death Coil
@@ -2515,11 +2773,14 @@ void Spell::EffectHealthLeech(uint32 i)
// m_healthLeech+=tmpvalue;
// m_damage+=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)
@@ -2527,6 +2788,7 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype)
player->SendEquipError( EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
+
// bg reward have some special in code work
uint32 bgType = 0;
switch(m_spellInfo->Id)
@@ -2546,7 +2808,9 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype)
default:
break;
}
+
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)
{
@@ -2569,10 +2833,12 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype)
}
else
num_to_add = 2;
+
if (num_to_add < 1)
num_to_add = 1;
if (num_to_add > pProto->GetMaxStackSize())
num_to_add = pProto->GetMaxStackSize();
+
// init items_count to 1, since 1 item will be created regardless of specialization
int items_count=1;
// the chance to create additional items
@@ -2586,8 +2852,10 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype)
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;
@@ -2604,26 +2872,32 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype)
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, bgType == 0);
+
// we succeeded in creating at least one item, so a levelup is possible
if(bgType == 0)
player->UpdateCraftSkill(m_spellInfo->Id);
}
+
/*
// for battleground marks send by mail if not add all expected
if(no_space > 0 && bgType)
@@ -2631,39 +2905,49 @@ void Spell::DoCreateItem(uint32 i, uint32 itemtype)
if(BattleGround* bg = sBattleGroundMgr.GetBattleGroundTemplate(BattleGroundTypeId(bgType)))
bg->SendRewardMarkByMail(player, newitemid, no_space);
}
-*/
+*/
}
+
void Spell::EffectCreateItem(uint32 i)
{
DoCreateItem(i,m_spellInfo->EffectItemType[i]);
}
+
void Spell::EffectCreateItem2(uint32 i)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
Player* player = (Player*)m_caster;
+
uint32 item_id = m_spellInfo->EffectItemType[i];
+
DoCreateItem(i, item_id);
+
// special case: fake item replaced by generate using spell_loot_template
if(IsLootCraftingSpell(m_spellInfo))
{
if(!player->HasItemCount(item_id, 1))
return;
+
// remove reagent
uint32 count = 1;
player->DestroyItemCount(item_id, count, true);
+
// create some random items
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell);
}
}
+
void Spell::EffectCreateRandomItem(uint32 i)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
Player* player = (Player*)m_caster;
+
// create some random items
player->AutoStoreLoot(m_spellInfo->Id, LootTemplates_Spell);
}
+
void Spell::EffectPersistentAA(uint32 i)
{
if(m_spellDynObj)
@@ -2672,9 +2956,11 @@ void Spell::EffectPersistentAA(uint32 i)
m_spellDynObj->AddEffect(i);
return;
}
+
float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if(Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, radius);
+
Unit *caster = m_caster->GetEntry() == WORLD_TRIGGER ? m_originalCaster : m_caster;
int32 duration = GetSpellDuration(m_spellInfo);
DynamicObject* dynObj = new DynamicObject;
@@ -2688,15 +2974,19 @@ void Spell::EffectPersistentAA(uint32 i)
dynObj->GetMap()->Add(dynObj);
m_spellDynObj = 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;
+
Powers power = Powers(m_spellInfo->EffectMiscValue[i]);
+
// Some level depends spells
int level_multiplier = 0;
int level_diff = 0;
@@ -2724,13 +3014,18 @@ void Spell::EffectEnergize(uint32 i)
default:
break;
}
+
if (level_diff > 0)
damage -= level_multiplier * level_diff;
+
if(damage < 0)
return;
+
if(unitTarget->GetMaxPower(power) == 0)
return;
+
m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, damage, power);
+
// Mad Alchemist's Potion
if (m_spellInfo->Id == 45051)
{
@@ -2743,8 +3038,10 @@ void Spell::EffectEnergize(uint32 i)
if(uint32 mask = spellmgr.GetSpellElixirMask(spell_id))
elixir_mask |= mask;
}
+
// get available elixir mask any not active type from battle/guardian (and flask if no any)
elixir_mask = (elixir_mask & ELIXIR_FLASK_MASK) ^ ELIXIR_FLASK_MASK;
+
// get all available elixirs by mask and spell level
std::vector<uint32> elixirs;
SpellElixirMap const& m_spellElixirs = spellmgr.GetSpellElixirMap();
@@ -2754,12 +3051,15 @@ void Spell::EffectEnergize(uint32 i)
{
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
@@ -2768,30 +3068,38 @@ void Spell::EffectEnergize(uint32 i)
}
}
}
+
void Spell::EffectEnergizePct(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;
m_caster->EnergizeBySpell(unitTarget, m_spellInfo->Id, gain, power);
}
+
void Spell::SendLoot(uint64 guid, LootType loottype)
{
Player* player = (Player*)m_caster;
if (!player)
return;
+
if (gameObjTarget)
{
if (Script->GOHello(player, gameObjTarget))
return;
+
switch (gameObjTarget->GetGoType())
{
case GAMEOBJECT_TYPE_DOOR:
@@ -2799,16 +3107,19 @@ void Spell::SendLoot(uint64 guid, LootType loottype)
gameObjTarget->UseDoorOrButton();
player->GetMap()->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)
@@ -2817,24 +3128,31 @@ void Spell::SendLoot(uint64 guid, LootType loottype)
player->GetMap()->ScriptsStart(sEventScripts, gameObjTarget->GetGOInfo()->goober.eventId, player, gameObjTarget);
gameObjTarget->EventInform(gameObjTarget->GetGOInfo()->goober.eventId);
}
+
// 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;
+
Script->GOHello(player, gameObjTarget);
gameObjTarget->GetMap()->ScriptsStart(sGameObjectScripts, gameObjTarget->GetDBTableGUIDLow(), player, gameObjTarget);
+
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)
@@ -2842,17 +3160,21 @@ void Spell::SendLoot(uint64 guid, LootType loottype)
sLog.outDebug("Chest ScriptStart id %u for GO %u", gameObjTarget->GetGOInfo()->chest.eventId,gameObjTarget->GetDBTableGUIDLow());
player->GetMap()->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
default:
break;
}
}
+
// Send loot
player->SendLoot(guid, loottype);
}
+
void Spell::EffectOpenLock(uint32 effIndex)
{
if(!m_caster || m_caster->GetTypeId() != TYPEID_PLAYER)
@@ -2860,9 +3182,12 @@ void Spell::EffectOpenLock(uint32 effIndex)
sLog.outDebug( "WORLD: Open Lock - No Player Caster!");
return;
}
+
Player* player = (Player*)m_caster;
+
uint32 lockId = 0;
uint64 guid = 0;
+
// Get lockId
if(gameObjTarget)
{
@@ -2909,16 +3234,20 @@ void Spell::EffectOpenLock(uint32 effIndex)
sLog.outDebug( "WORLD: Open Lock - No GameObject/Item Target!");
return;
}
+
SkillType skillId = SKILL_NONE;
int32 reqSkillValue = 0;
int32 skillValue;
+
SpellCastResult res = CanOpenLock(effIndex, lockId, skillId, reqSkillValue, skillValue);
if(res != SPELL_CAST_OK)
{
SendCastResult(res);
return;
}
+
SendLoot(guid, LOOT_SKINNING);
+
// not allow use skill grow at item base open
if(!m_CastItem && skillId != SKILL_NONE)
{
@@ -2940,34 +3269,44 @@ void Spell::EffectOpenLock(uint32 effIndex)
}
}
}
+
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 j= PERM_ENCHANTMENT_SLOT; j<=TEMP_ENCHANTMENT_SLOT; ++j)
{
if(m_CastItem->GetEnchantmentId(EnchantmentSlot(j)))
pNewItem->SetEnchantment(EnchantmentSlot(j), m_CastItem->GetEnchantmentId(EnchantmentSlot(j)), m_CastItem->GetEnchantmentDuration(EnchantmentSlot(j)), m_CastItem->GetEnchantmentCharges(EnchantmentSlot(j)));
}
+
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;
@@ -2975,10 +3314,13 @@ void Spell::EffectSummonChangeItem(uint32 i)
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;
}
@@ -2990,10 +3332,13 @@ void Spell::EffectSummonChangeItem(uint32 i)
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;
}
@@ -3005,23 +3350,29 @@ void Spell::EffectSummonChangeItem(uint32 i)
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::EffectProficiency(uint32 /*i*/)
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
Player *p_target = (Player*)unitTarget;
+
uint32 subClassMask = m_spellInfo->EquippedItemSubClassMask;
if(m_spellInfo->EquippedItemClass == ITEM_CLASS_WEAPON && !(p_target->GetWeaponProficiency() & subClassMask))
{
@@ -3034,28 +3385,36 @@ void Spell::EffectProficiency(uint32 /*i*/)
p_target->SendProficiency(ITEM_CLASS_ARMOR, p_target->GetArmorProficiency());
}
}
+
void Spell::EffectSummonType(uint32 i)
{
uint32 entry = m_spellInfo->EffectMiscValue[i];
if(!entry)
return;
+
SummonPropertiesEntry const *properties = sSummonPropertiesStore.LookupEntry(m_spellInfo->EffectMiscValueB[i]);
if(!properties)
{
sLog.outError("EffectSummonType: Unhandled summon type %u", m_spellInfo->EffectMiscValueB[i]);
return;
}
+
if(!m_originalCaster)
return;
+
int32 duration = GetSpellDuration(m_spellInfo);
if(Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
+
Position pos;
GetSummonPosition(i, pos);
+
/*//totem must be at same Z in case swimming caster and etc.
if( fabs( z - m_caster->GetPositionZ() ) > 5 )
z = m_caster->GetPositionZ();
+
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)
{
@@ -3069,7 +3428,9 @@ void Spell::EffectSummonType(uint32 i)
}
}
}*/
+
TempSummon *summon = NULL;
+
switch(properties->Category)
{
default:
@@ -3095,12 +3456,15 @@ void Spell::EffectSummonType(uint32 i)
summon = m_caster->GetMap()->SummonCreature(entry, pos, properties, duration, m_originalCaster);
if(!summon || !summon->isTotem())
return;
+
if(damage) // if not spell info, DB values used
{
summon->SetMaxHealth(damage);
summon->SetHealth(damage);
}
+
//summon->SetUInt32Value(UNIT_CREATED_BY_SPELL,m_spellInfo->Id);
+
if(m_originalCaster->GetTypeId() == TYPEID_PLAYER
&& properties->Slot >= SUMMON_SLOT_TOTEM
&& properties->Slot < MAX_TOTEM_SLOT)
@@ -3120,11 +3484,15 @@ void Spell::EffectSummonType(uint32 i)
summon = m_caster->GetMap()->SummonCreature(entry, pos, properties, duration, m_originalCaster);
if(!summon || !summon->HasUnitTypeMask(UNIT_MASK_MINION))
return;
+
//summon->InitPetCreateSpells(); // e.g. disgusting oozeling has a create spell as summon...
summon->SelectLevel(summon->GetCreatureInfo()); // some summoned creaters have different from 1 DB data for level/hp
summon->SetUInt32Value(UNIT_NPC_FLAGS, summon->GetCreatureInfo()->npcflag);
+
summon->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
+
summon->AI()->EnterEvadeMode();
+
std::string name = m_originalCaster->GetName();
name.append(petTypeSuffix[3]);
summon->SetName( name );
@@ -3133,13 +3501,17 @@ void Spell::EffectSummonType(uint32 i)
default:
{
float radius = GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
int32 amount = damage > 0 ? damage : 1;
if (m_spellInfo->Id == 18662) // Curse of Doom
amount = 1;
+
for(uint32 count = 0; count < amount; ++count)
{
GetSummonPosition(i, pos, radius, count);
+
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
+
TempSummon * summon = m_originalCaster->SummonCreature(entry, pos, summonType, duration);
if (!summon)
continue;
@@ -3167,38 +3539,49 @@ void Spell::EffectSummonType(uint32 i)
summon = m_caster->GetMap()->SummonCreature(entry, pos, properties, duration, m_caster);
if(!summon || !summon->IsVehicle())
return;
+
if(damage)
m_caster->CastSpell(summon, damage, true);
m_caster->EnterVehicle(summon->GetVehicleKit());
break;
}
}
+
if(summon)
{
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
}
}
+
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) || (m_spellInfo->Id==SPELL_ID_GENERIC_LEARN_PET)) ? damage : m_spellInfo->EffectTriggerSpell[i];
player->learnSpell(spellToLearn,false);
+
sLog.outDebug( "Spell: Player %u has learned spell %u from NpcGUID=%u", player->GetGUIDLow(), spellToLearn, m_caster->GetGUIDLow() );
}
+
typedef std::list< std::pair<uint32, uint64> > DispelList;
void Spell::EffectDispel(uint32 i)
{
if(!unitTarget)
return;
+
Unit::AuraList dispel_list;
+
// Create dispel mask by dispel type
uint32 dispel_type = m_spellInfo->EffectMiscValue[i];
uint32 dispelMask = GetDispellMask( DispelType(dispel_type) );
@@ -3211,17 +3594,21 @@ void Spell::EffectDispel(uint32 i)
if(aur->GetSpellProto()->Dispel == DISPEL_MAGIC)
{
bool positive = aur->IsPositive() ? (!(aur->GetSpellProto()->AttributesEx & SPELL_ATTR_EX_NEGATIVE)) : false;
+
// do not remove positive auras if friendly target
// negative auras if non-friendly target
if(positive == unitTarget->IsFriendlyTo(m_caster))
continue;
}
+
for (uint8 i = aur->GetStackAmount(); i; --i)
dispel_list.push_back(aur);
}
}
+
if (dispel_list.empty())
return;
+
// Ok if exist some buffs for dispel try dispel it
uint32 failCount = 0;
DispelList success_list;
@@ -3232,6 +3619,7 @@ void Spell::EffectDispel(uint32 i)
// Random select buff for dispel
Unit::AuraList::iterator itr = dispel_list.begin();
std::advance(itr, urand(0, dispel_list.size() - 1));
+
if (GetDispelChance((*itr)->GetCaster(), (*itr)->GetId()))
{
success_list.push_back(std::make_pair((*itr)->GetId(), (*itr)->GetCasterGUID()));
@@ -3250,10 +3638,13 @@ void Spell::EffectDispel(uint32 i)
dataFail << uint32((*itr)->GetId()); // Spell Id
}
}
+
if (failCount)
m_caster->SendMessageToSet(&dataFail, true);
+
if (success_list.empty())
return;
+
WorldPacket dataSuccess(SMSG_SPELLDISPELLOG, 8+8+4+1+4+damage*5);
// Send packet header
dataSuccess.append(unitTarget->GetPackGUID()); // Victim GUID
@@ -3269,6 +3660,7 @@ void Spell::EffectDispel(uint32 i)
unitTarget->RemoveAurasDueToSpellByDispel(itr->first, itr->second, m_caster);
}
m_caster->SendMessageToSet(&dataSuccess, true);
+
// On success dispel
// Devour Magic
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK && m_spellInfo->Category == SPELLCATEGORY_DEVOUR_MAGIC)
@@ -3277,26 +3669,32 @@ void Spell::EffectDispel(uint32 i)
m_caster->CastCustomSpell(m_caster, 19658, &heal_amount, NULL, NULL, true);
}
}
+
void Spell::EffectDualWield(uint32 /*i*/)
{
unitTarget->SetCanDualWield(true);
if(unitTarget->GetTypeId() == TYPEID_UNIT)
((Creature*)unitTarget)->UpdateDamagePhysical(OFF_ATTACK);
}
+
void Spell::EffectPull(uint32 /*i*/)
{
// TODO: create a proper pull towards distract spell center for distract
sLog.outDebug("WORLD: Spell Effect DUMMY");
}
+
void Spell::EffectDistract(uint32 /*i*/)
{
// Check for possible target
if (!unitTarget || unitTarget->isInCombat())
return;
+
// target must be OK to do this
if( unitTarget->hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING ) )
return;
+
float angle = unitTarget->GetAngle(&m_targets.m_dstPos);
+
if ( unitTarget->GetTypeId() == TYPEID_PLAYER )
{
// For players just turn them
@@ -3311,17 +3709,21 @@ void Spell::EffectDistract(uint32 /*i*/)
unitTarget->GetMotionMaster()->MoveDistract(damage * IN_MILISECONDS);
}
}
+
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
@@ -3336,10 +3738,12 @@ void Spell::EffectPickPocket(uint32 /*i*/)
}
}
}
+
void Spell::EffectAddFarsight(uint32 i)
{
if (m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
int32 duration = GetSpellDuration(m_spellInfo);
DynamicObject* dynObj = new DynamicObject;
@@ -3350,37 +3754,49 @@ void Spell::EffectAddFarsight(uint32 i)
}
dynObj->SetUInt32Value(DYNAMICOBJECT_BYTES, 0x80000002);
m_caster->AddDynObject(dynObj);
+
dynObj->setActive(true); //must before add to map to be put in world container
dynObj->GetMap()->Add(dynObj); //grid will also be loaded
+
// Need to update visibility of object for client to accept farsight guid
((Player*)m_caster)->SetViewpoint(dynObj, true);
//((Player*)m_caster)->UpdateVisibilityOf(dynObj);
}
+
void Spell::EffectTeleUnitsFaceCaster(uint32 i)
{
if(!unitTarget)
return;
+
if(unitTarget->isInFlight())
return;
+
float dis = m_caster->GetSpellRadiusForTarget(unitTarget, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
+
float fx,fy,fz;
m_caster->GetClosePoint(fx,fy,fz,unitTarget->GetObjectSize(),dis);
+
unitTarget->NearTeleportTo(fx,fy,fz,-m_caster->GetOrientation(),unitTarget==m_caster);
}
+
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;
+
// not scale value for item based reward (/10 value expected)
if(m_CastItem)
{
@@ -3388,6 +3804,7 @@ void Spell::EffectAddHonor(uint32 /*i*/)
sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %d honor points (item %u) for player: %u", m_spellInfo->Id, damage/10, m_CastItem->GetEntry(),((Player*)unitTarget)->GetGUIDLow());
return;
}
+
// do not allow to add too many honor for player (50 * 21) = 1040 at level 70, or (50 * 31) = 1550 at level 80
if( damage <= 50)
{
@@ -3402,6 +3819,7 @@ void Spell::EffectAddHonor(uint32 /*i*/)
sLog.outError("SpellEffect::AddHonor (spell_id %u) rewards %u honor points (non scale) for player: %u", m_spellInfo->Id, damage, ((Player*)unitTarget)->GetGUIDLow());
}
}
+
void Spell::EffectTradeSkill(uint32 /*i*/)
{
if(unitTarget->GetTypeId() != TYPEID_PLAYER)
@@ -3410,13 +3828,16 @@ void Spell::EffectTradeSkill(uint32 /*i*/)
// uint16 skillmax = ((Player*)unitTarget)->(skillid);
// ((Player*)unitTarget)->SetSkill(skillid,skillval?skillval:1,skillmax+75);
}
+
void Spell::EffectEnchantItemPerm(uint32 effect_idx)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
if (!itemTarget)
return;
+
Player* p_caster = (Player*)m_caster;
+
// Handle vellums
if (itemTarget->IsWeaponVellum() || itemTarget->IsArmorVellum())
{
@@ -3434,16 +3855,20 @@ void Spell::EffectEnchantItemPerm(uint32 effect_idx)
// do not increase skill if vellum used
if (!(m_CastItem && m_CastItem->GetProto()->Flags & ITEM_FLAGS_TRIGGERED_CAST))
p_caster->UpdateCraftSkill(m_spellInfo->Id);
+
uint32 enchant_id = m_spellInfo->EffectMiscValue[effect_idx];
if (!enchant_id)
return;
+
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(p_caster->GetSession()->GetAccountId(),"GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)",
@@ -3451,26 +3876,34 @@ void Spell::EffectEnchantItemPerm(uint32 effect_idx)
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::EffectEnchantItemPrismatic(uint32 effect_idx)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
if (!itemTarget)
return;
+
Player* p_caster = (Player*)m_caster;
+
uint32 enchant_id = m_spellInfo->EffectMiscValue[effect_idx];
if (!enchant_id)
return;
+
SpellItemEnchantmentEntry const *pEnchant = sSpellItemEnchantmentStore.LookupEntry(enchant_id);
if(!pEnchant)
return;
+
// support only enchantings with add socket in this slot
{
bool add_socket = false;
@@ -3489,10 +3922,12 @@ void Spell::EffectEnchantItemPrismatic(uint32 effect_idx)
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(p_caster->GetSession()->GetAccountId(),"GM %s (Account: %u) enchanting(perm): %s (Entry: %d) for player: %s (Account: %u)",
@@ -3500,24 +3935,33 @@ void Spell::EffectEnchantItemPrismatic(uint32 effect_idx)
itemTarget->GetProto()->Name1,itemTarget->GetEntry(),
item_owner->GetName(),item_owner->GetSession()->GetAccountId());
}
+
// remove old enchanting before applying new if equipped
item_owner->ApplyEnchantment(itemTarget,PRISMATIC_ENCHANTMENT_SLOT,false);
+
itemTarget->SetEnchantment(PRISMATIC_ENCHANTMENT_SLOT, enchant_id, 0, 0);
+
// add new enchanting if equipped
item_owner->ApplyEnchantment(itemTarget,PRISMATIC_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 = CalculateDamage(1, NULL);//+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
@@ -3566,19 +4010,23 @@ void Spell::EffectEnchantItemTmp(uint32 i)
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
@@ -3604,10 +4052,12 @@ void Spell::EffectEnchantItemTmp(uint32 i)
// 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(p_caster->GetSession()->GetAccountId(),"GM %s (Account: %u) enchanting(temp): %s (Entry: %d) for player: %s (Account: %u)",
@@ -3615,49 +4065,69 @@ void Spell::EffectEnchantItemTmp(uint32 i)
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_UNIT)
return;
+
Creature* creatureTarget = (Creature*)unitTarget;
+
if(creatureTarget->isPet())
return;
+
if(m_caster->getClass() != CLASS_HUNTER)
return;
+
// cast finish successfully
//SendChannelUpdate(0);
finish();
+
Pet* pet = m_caster->CreateTamedPetFrom(creatureTarget,m_spellInfo->Id);
if(!pet) // in versy specific state like near world end/etc.
return;
+
// "kill" original creature
creatureTarget->ForcedDespawn();
+
uint32 level = (creatureTarget->getLevel() < (m_caster->getLevel() - 5)) ? (m_caster->getLevel() - 5) : creatureTarget->getLevel();
+
// prepare visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level - 1);
+
// add to world
pet->GetMap()->Add((Creature*)pet);
+
// visual effect for levelup
pet->SetUInt32Value(UNIT_FIELD_LEVEL, level);
+
// caster have pet now
m_caster->SetMinion(pet, true);
+
pet->InitTalentForLevel();
+
if(m_caster->GetTypeId() == TYPEID_PLAYER)
{
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
((Player*)m_caster)->PetSpellInitialize();
}
}
+
void Spell::EffectSummonPet(uint32 i)
{
Player *owner = NULL;
@@ -3668,7 +4138,9 @@ void Spell::EffectSummonPet(uint32 i)
else if(((Creature*)m_originalCaster)->isTotem())
owner = m_originalCaster->GetCharmerOrOwnerPlayerOrPlayerItself();
}
+
uint32 petentry = m_spellInfo->EffectMiscValue[i];
+
if(!owner)
{
SummonPropertiesEntry const *properties = sSummonPropertiesStore.LookupEntry(67);
@@ -3676,7 +4148,9 @@ void Spell::EffectSummonPet(uint32 i)
SummonGuardian(i, petentry, properties);
return;
}
+
Pet *OldSummon = owner->GetPet();
+
// if pet requested type already exist
if( OldSummon )
{
@@ -3685,30 +4159,38 @@ void Spell::EffectSummonPet(uint32 i)
// pet in corpse state can't be summoned
if( OldSummon->isDead() )
return;
+
assert(OldSummon->GetMap() == owner->GetMap());
+
//OldSummon->GetMap()->Remove((Creature*)OldSummon,false);
+
float px, py, pz;
owner->GetClosePoint(px, py, pz, OldSummon->GetObjectSize());
+
OldSummon->NearTeleportTo(px, py, pz, OldSummon->GetOrientation());
//OldSummon->Relocate(px, py, pz, OldSummon->GetOrientation());
//OldSummon->SetMap(owner->GetMap());
//owner->GetMap()->Add((Creature*)OldSummon);
+
if(owner->GetTypeId() == TYPEID_PLAYER && OldSummon->isControlled() )
{
((Player*)owner)->PetSpellInitialize();
}
return;
}
+
if(owner->GetTypeId() == TYPEID_PLAYER)
((Player*)owner)->RemovePet(OldSummon,(OldSummon->getPetType()==HUNTER_PET ? PET_SAVE_AS_DELETED : PET_SAVE_NOT_IN_SLOT),false);
else
return;
}
+
float x, y, z;
owner->GetClosePoint(x, y, z, owner->GetObjectSize());
Pet* pet = owner->SummonPet(petentry, x, y, z, owner->GetOrientation(), SUMMON_PET, 0);
if(!pet)
return;
+
if(m_caster->GetTypeId() == TYPEID_UNIT)
{
if ( ((Creature*)m_caster)->isTotem() )
@@ -3716,33 +4198,43 @@ void Spell::EffectSummonPet(uint32 i)
else
pet->SetReactState(REACT_DEFENSIVE);
}
+
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
+
// generate new name for summon pet
std::string new_name=objmgr.GeneratePetName(petentry);
if(!new_name.empty())
pet->SetName(new_name);
}
+
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->learnSpell(learn_spellproto->Id);
+
pet->SavePetToDB(PET_SAVE_AS_CURRENT);
_player->PetSpellInitialize();
}
+
void Spell::EffectTaunt(uint32 /*i*/)
{
if (!unitTarget)
return;
+
// 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->CanHaveThreatList()
@@ -3751,6 +4243,7 @@ void Spell::EffectTaunt(uint32 /*i*/)
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->getThreatManager().getCurrentVictim())
{
@@ -3759,22 +4252,27 @@ void Spell::EffectTaunt(uint32 /*i*/)
if(itsThreat > myThreat)
unitTarget->getThreatManager().addThreat(m_caster, itsThreat - myThreat);
}
+
//Set aggro victim to caster
if( !unitTarget->getThreatManager().getOnlineContainer().empty() )
if(HostilReference* forcedVictim = unitTarget->getThreatManager().getOnlineContainer().getReferenceByTarget(m_caster))
unitTarget->getThreatManager().setCurrentVictim(forcedVictim);
+
if(((Creature*)unitTarget)->IsAIEnabled && !((Creature*)unitTarget)->HasReactState(REACT_PASSIVE))
((Creature*)unitTarget)->AI()->AttackStart(m_caster);
}
+
void Spell::EffectWeaponDmg(uint32 i)
{
}
+
void Spell::SpellDamageWeaponDmg(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
@@ -3790,10 +4288,12 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
break;
}
}
+
// some spell specific modifiers
float totalDamagePercentMod = 1.0f; // applied to final bonus+weapon damage
int32 fixed_bonus = 0;
int32 spell_bonus = 0; // bonus specific for spell
+
switch(m_spellInfo->SpellFamilyName)
{
case SPELLFAMILY_WARRIOR:
@@ -3805,12 +4305,14 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
return;
SpellEntry const *spellInfo = NULL;
uint32 stack = 0;
+
if (Aura * aur = unitTarget->GetAura(58567, m_caster->GetGUID()))
{
aur->RefreshAura();
spellInfo = aur->GetSpellProto();
stack = aur->GetStackAmount();
}
+
for(uint8 j = 0; j < 3; j++)
{
if(m_spellInfo->Effect[j] == SPELL_EFFECT_NORMALIZED_WEAPON_DMG)
@@ -3819,6 +4321,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
break;
}
}
+
if(!spellInfo)
{
// get highest rank of the Sunder Armor spell
@@ -3828,9 +4331,11 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
// 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 *spellProto = sSpellStore.LookupEntry(itr->first);
if (!spellProto)
continue;
+
if (spellProto->SpellFamilyFlags[0] & SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR
&& spellProto->Id != m_spellInfo->Id
&& spellProto->SpellFamilyName == SPELLFAMILY_WARRIOR)
@@ -3890,6 +4395,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
}
}
}
+
if(found)
totalDamagePercentMod *= 1.2f; // 120% if poisoned
}
@@ -3903,6 +4409,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
spell_bonus += int32(0.23f*m_caster->SpellBaseDamageBonus(GetSpellSchoolMask(m_spellInfo)));
spell_bonus += int32(0.29f*m_caster->SpellBaseDamageBonusForVictim(GetSpellSchoolMask(m_spellInfo), unitTarget));
}
+
// Seal of Command Unleashed
else if(m_spellInfo->Id==20467)
{
@@ -3968,6 +4475,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
else if (m_spellInfo->SpellFamilyFlags[0] & 0x400000)
{
totalDamagePercentMod *= (float(unitTarget->GetDiseasesByCaster(m_caster->GetGUID())) * 12.5f + 100.0f) / 100.0f;
+
// Glyph of Blood Strike
if (AuraEffect * aurEff = m_caster->GetAuraEffect(59332,0))
{
@@ -3985,6 +4493,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
{
if (runic > 25)
runic = 25;
+
totalDamagePercentMod *= float((runic + 100.0f) / 100.0f);
}
}
@@ -4008,6 +4517,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
break;
}
}
+
bool normalized = false;
float weaponDamagePercentMod = 1.0;
for (int j = 0; j < 3; ++j)
@@ -4029,6 +4539,7 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
break; // not weapon damage effect, just skip
}
}
+
// apply to non-weapon bonus weapon total pct effect, weapon total flat effect included in weapon damage
if(fixed_bonus || spell_bonus)
{
@@ -4040,15 +4551,19 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
case OFF_ATTACK: unitMod = UNIT_MOD_DAMAGE_OFFHAND; break;
case RANGED_ATTACK: unitMod = UNIT_MOD_DAMAGE_RANGED; break;
}
+
float weapon_total_pct = 1.0f;
if ( m_spellInfo->SchoolMask & SPELL_SCHOOL_MASK_NORMAL )
weapon_total_pct = m_caster->GetModifierValue(unitMod, TOTAL_PCT);
+
if(fixed_bonus)
fixed_bonus = int32(fixed_bonus * weapon_total_pct);
if(spell_bonus)
spell_bonus = int32(spell_bonus * weapon_total_pct);
}
+
int32 weaponDamage = m_caster->CalculateDamage(m_attackType, normalized, true);
+
// Sequence is important
for (int j = 0; j < 3; ++j)
{
@@ -4067,32 +4582,41 @@ void Spell::SpellDamageWeaponDmg(uint32 i)
break; // not weapon damage effect, just skip
}
}
+
// only for Seal of Command
if(spell_bonus)
weaponDamage += spell_bonus;
+
// only for Mutilate
if(totalDamagePercentMod != 1.0f)
weaponDamage = int32(weaponDamage * totalDamagePercentMod);
+
// prevent negative damage
uint32 eff_damage = uint32(weaponDamage > 0 ? weaponDamage : 0);
+
// Add melee damage bonuses (also check for negative)
m_caster->MeleeDamageBonus(unitTarget, &eff_damage, m_attackType, m_spellInfo);
m_damage+= eff_damage;
}
+
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;
+
int32 addhealth;
if(m_spellInfo->SpellFamilyName == SPELLFAMILY_PALADIN) // Lay on Hands
addhealth = m_caster->GetMaxHealth();
@@ -4101,12 +4625,14 @@ void Spell::EffectHealMaxHealth(uint32 /*i*/)
if(m_originalCaster)
m_originalCaster->DealHeal(unitTarget, addhealth, m_spellInfo);
}
+
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)
@@ -4129,34 +4655,45 @@ void Spell::EffectInterruptCast(uint32 i)
}
}
}
+
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)
m_targets.m_dstPos.GetPosition(x, y, z);
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,
m_caster->GetPhaseMask(), x, y, z, target->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
{
delete pGameObj;
return;
}
+
int32 duration = GetSpellDuration(m_spellInfo);
+
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0);
pGameObj->SetSpellId(m_spellInfo->Id);
+
// Wild object not have owner and check clickable by players
map->Add(pGameObj);
+
if(pGameObj->GetGoType() == GAMEOBJECT_TYPE_FLAGDROP && m_caster->GetTypeId() == TYPEID_PLAYER)
{
Player *pl = (Player*)m_caster;
BattleGround* bg = ((Player *)m_caster)->GetBattleGround();
+
switch(pGameObj->GetMapId())
{
case 489: //WS
@@ -4164,8 +4701,10 @@ void Spell::EffectSummonObjectWild(uint32 i)
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);
}
break;
@@ -4180,6 +4719,7 @@ void Spell::EffectSummonObjectWild(uint32 i)
}
}
}
+
if(uint32 linkedEntry = pGameObj->GetGOInfo()->GetLinkedGameObjectEntry())
{
GameObject* linkedGO = new GameObject;
@@ -4188,6 +4728,7 @@ void Spell::EffectSummonObjectWild(uint32 i)
{
linkedGO->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0);
linkedGO->SetSpellId(m_spellInfo->Id);
+
// Wild object not have owner and check clickable by players
map->Add(linkedGO);
}
@@ -4199,9 +4740,11 @@ void Spell::EffectSummonObjectWild(uint32 i)
}
}
}
+
void Spell::EffectScriptEffect(uint32 effIndex)
{
// TODO: we must implement hunter pet summon at login there (spell 6962)
+
switch(m_spellInfo->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
@@ -4212,16 +4755,18 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(m_caster->GetTypeId()!=TYPEID_PLAYER)
return;
+
Player* plr = ((Player*)m_caster);
if(plr && plr->GetLastPetNumber())
{
- PetType NewPetType = (plr->getClass()==CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET;
+ PetType NewPetType = (plr->getClass()==CLASS_HUNTER) ? HUNTER_PET : SUMMON_PET;
if (Pet* NewPet = new Pet(plr,NewPetType))
{
if(NewPet->LoadPetFromDB(plr, 0, plr->GetLastPetNumber(), true))
{
NewPet->SetHealth(NewPet->GetMaxHealth());
NewPet->SetPower(NewPet->getPowerType(),NewPet->GetMaxPower(NewPet->getPowerType()));
+
switch (NewPet->GetEntry())
{
case 11859:
@@ -4247,6 +4792,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
uint32 countMax = 18000;
countMax += m_caster->HasAura(38414) ? 3000 : 0;
countMax += m_caster->HasAura(57865) ? 3000 : 0;
+
if (countMin < countMax)
{
aurEff->GetParentAura()->SetAuraDuration(uint32(aurEff->GetParentAura()->GetAuraDuration()+3000));
@@ -4264,11 +4810,13 @@ void Spell::EffectScriptEffect(uint32 effIndex)
uint32 countMax = 20000;
countMax += m_caster->HasAura(54818) ? 4000 : 0;
countMax += m_caster->HasAura(60141) ? 4000 : 0;
+
if (countMin < countMax)
{
aurEff->GetParentAura()->SetAuraDuration(uint32(aurEff->GetParentAura()->GetAuraDuration()+3000));
aurEff->GetParentAura()->SetAuraMaxDuration(countMin+2000);
}
+
}
return;
}
@@ -4280,11 +4828,13 @@ void Spell::EffectScriptEffect(uint32 effIndex)
uint32 countMin = aurEff->GetParentAura()->GetAuraMaxDuration();
uint32 countMax = 12000;
countMax += m_caster->HasAura(56801) ? 4000 : 0;
+
if (countMin < countMax)
{
aurEff->GetParentAura()->SetAuraDuration(uint32(aurEff->GetParentAura()->GetAuraDuration()+3000));
aurEff->GetParentAura()->SetAuraMaxDuration(countMin+2000);
}
+
}
return;
}
@@ -4320,12 +4870,15 @@ void Spell::EffectScriptEffect(uint32 effIndex)
case 26275:
{
uint32 spells[4] = { 26272, 26157, 26273, 26274 };
+
// check presence
for(uint8 j = 0; j < 4; ++j)
if(unitTarget->HasAuraEffect(spells[j],0))
return;
+
// select spell
uint32 iTmpSpellId = spells[urand(0,3)];
+
// cast
unitTarget->CastSpell(unitTarget, iTmpSpellId, true);
return;
@@ -4335,12 +4888,14 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
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;
}
@@ -4364,6 +4919,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
uint32 spellid;
switch(m_spellInfo->Id)
{
@@ -4378,6 +4934,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
default:
return;
}
+
unitTarget->CastSpell(unitTarget,spellid,false);
return;
}
@@ -4398,9 +4955,11 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || !unitTarget->isAlive())
return;
+
// Onyxia Scale Cloak
if(unitTarget->HasAura(22683))
return;
+
// Shadow Flame
m_caster->CastSpell(unitTarget, 22682, true);
return;
@@ -4468,6 +5027,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
unitTarget->CastSpell(unitTarget, 41131, true);
break;
}*/
@@ -4476,6 +5036,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
unitTarget->CastSpell(m_caster, 40903, true);
break;
}
@@ -4483,6 +5044,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
switch(((Player*)unitTarget)->GetBaseSkillValue(762))
{
case 75: unitTarget->CastSpell(unitTarget, 51621, true); break;;
@@ -4497,6 +5059,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
if(unitTarget)
{
switch(((Player*)unitTarget)->GetBaseSkillValue(762))
@@ -4513,9 +5076,11 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
int bag=19;
int slot=0;
Item* item = NULL;
+
while (bag < 256)
{
item = ((Player*)m_caster)->GetItemByPos(bag,slot);
@@ -4542,6 +5107,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
unitTarget->CastSpell(unitTarget, 44870, true);
break;
}
@@ -4550,7 +5116,9 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
unitTarget->RemoveAurasDueToSpell(46394);
+
break;
}
// Negative Energy
@@ -4558,6 +5126,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
m_caster->CastSpell(unitTarget, 46285, true);
break;
}
@@ -4566,6 +5135,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget)
return;
+
uint32 spellId = 0;
switch(rand() % 4)
{
@@ -4582,7 +5152,9 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)unitTarget)->ModifyMoney(5000 * GOLD);
+
break;
}
// Vigilance
@@ -4590,8 +5162,10 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
// Remove Taunt cooldown
((Player*)unitTarget)->RemoveSpellCooldown(355, true);
+
return;
}
// Death Knight Initiate Visual
@@ -4599,6 +5173,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT)
return;
+
uint32 iTmpSpellId = 0;
switch (unitTarget->GetDisplayId())
{
@@ -4624,6 +5199,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
case 25365: iTmpSpellId = 51546; break; // troll male
default: return;
}
+
unitTarget->CastSpell(unitTarget, iTmpSpellId, true);
Creature* npc = (Creature*)unitTarget;
npc->LoadEquipment(npc->GetEquipmentId());
@@ -4634,6 +5210,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!m_originalCaster)
return;
+
m_originalCaster->CastSpell(m_originalCaster, damage, false);
break;
}
@@ -4642,6 +5219,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!m_targets.HasDst())
return;
+
float x, y, z;
float radius = GetSpellRadius(m_spellInfo, effIndex, true);
for(uint32 i = 0; i < 15; ++i)
@@ -4683,8 +5261,10 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
// Prevent stacking of mounts
unitTarget->RemoveAurasByType(SPELL_AURA_MOUNTED);
+
// Triggered spell id dependent of riding skill
if(uint16 skillval = ((Player*)unitTarget)->GetSkillValue(SKILL_RIDING))
{
@@ -4700,10 +5280,13 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER || effIndex!=0)
return;
+
uint32 spellID = m_spellInfo->CalculateSimpleValue(0);
uint32 questID = m_spellInfo->CalculateSimpleValue(1);
+
if (((Player*)unitTarget)->GetQuestStatus(questID) == QUEST_STATUS_COMPLETE && !((Player*)unitTarget)->GetQuestRewardStatus (questID))
unitTarget->CastSpell(unitTarget, spellID, true);
+
return;
}
case 58941: // Rock Shards
@@ -4730,8 +5313,10 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
// Prevent stacking of mounts
unitTarget->RemoveAurasByType(SPELL_AURA_MOUNTED);
+
// Triggered spell id dependent of riding skill
if(uint16 skillval = ((Player*)unitTarget)->GetSkillValue(SKILL_RIDING))
{
@@ -4745,12 +5330,14 @@ void Spell::EffectScriptEffect(uint32 effIndex)
case 59317: // Teleporting
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
// return from top
if (((Player*)unitTarget)->GetAreaId() == 4637)
unitTarget->CastSpell(unitTarget, 59316, true);
// teleport atop
else
unitTarget->CastSpell(unitTarget, 59314, true);
+
return;
// random spell learn instead placeholder
case 60893: // Northrend Alchemy Research
@@ -4761,6 +5348,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
// learn random explicit discovery recipe (if any)
if(uint32 discoveredSpell = GetExplicitDiscoverySpell(m_spellInfo->Id, (Player*)m_caster))
((Player*)m_caster)->learnSpell(discoveredSpell, false);
@@ -4789,7 +5377,9 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if (m_caster->GetTypeId() != TYPEID_UNIT || !((Creature*)m_caster)->isSummon())
return;
+
uint32 spell_heal;
+
switch(m_caster->GetEntry())
{
case 31897: spell_heal = 7001; break;
@@ -4803,6 +5393,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
return;
}
Aura * chargesaura = m_caster->GetAura(59907);
+
if(chargesaura && chargesaura->GetAuraCharges() > 1)
{
chargesaura->SetAuraCharges(chargesaura->GetAuraCharges() - 1);
@@ -4830,6 +5421,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if(!unitTarget->m_SummonSlot[slot])
continue;
+
Creature* totem = unitTarget->GetMap()->GetCreature(unitTarget->m_SummonSlot[slot]);
if(totem && totem->isTotem())
{
@@ -4863,6 +5455,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
uint32 itemtype;
uint32 rank = 0;
+
// Improved Healthstone
if (AuraEffect const * aurEff = unitTarget->GetDummyAura(SPELLFAMILY_WARLOCK, 284, 0))
{
@@ -4873,6 +5466,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
else
sLog.outError("Unknown rank of Improved Healthstone id: %d", aurEff->GetId());
}
+
static uint32 const itypes[8][3] = {
{ 5512, 19004, 19005}, // Minor Healthstone
{ 5511, 19006, 19007}, // Lesser Healthstone
@@ -4883,6 +5477,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{36889, 36890, 36891}, // Demonic Healthstone
{36892, 36893, 36894} // Fel Healthstone
};
+
switch(m_spellInfo->Id)
{
case 6201:
@@ -5014,6 +5609,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
continue;
if (!aura->GetPartAura(0))
continue;
+
// Serpent Sting - Instantly deals 40% of the damage done by your Serpent Sting.
if (familyFlag[0] & 0x4000)
{
@@ -5041,6 +5637,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
//{
// spellId = 53366; // 53366 Chimera Shot - Wyvern
//}
+
// Refresh aura duration
aura->RefreshAura();
break;
@@ -5054,6 +5651,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
{
if (!unitTarget)
return;
+
// script effect have in value, but this outdated removed part
unitTarget->CastSpell(unitTarget, 62305, true);
return;
@@ -5072,6 +5670,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
return;
uint32 spellId1 = 0;
uint32 spellId2 = 0;
+
// Judgement self add switch
switch (m_spellInfo->Id)
{
@@ -5139,10 +5738,12 @@ void Spell::EffectScriptEffect(uint32 effIndex)
// 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))
@@ -5150,6 +5751,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
unitTarget->CastSpell(unitTarget, spellid+urand(0, 4), true);
break;
}
+
// Nightmare Vine
case 28720:
{
@@ -5187,7 +5789,7 @@ void Spell::EffectScriptEffect(uint32 effIndex)
case SPELLFAMILY_WARRIOR:
{
// Shattering Throw
- if ( m_spellInfo->SpellFamilyFlags[1] & 0x1 )
+ if ( m_spellInfo->SpellFamilyFlags[1] & 0x1 )
{
if(!unitTarget)
return;
@@ -5198,14 +5800,17 @@ void Spell::EffectScriptEffect(uint32 effIndex)
break;
}
}
+
// normal DB scripted effect
sLog.outDebug("Spell ScriptStart spellid %u in EffectScriptEffect ", m_spellInfo->Id);
m_caster->GetMap()->ScriptsStart(sSpellScripts, m_spellInfo->Id, m_caster, unitTarget);
}
+
void Spell::EffectSanctuary(uint32 /*i*/)
{
if(!unitTarget)
return;
+
std::list<Unit*> targets;
Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(unitTarget, unitTarget, m_caster->GetMap()->GetVisibilityDistance());
Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(unitTarget, targets, u_check);
@@ -5214,6 +5819,7 @@ void Spell::EffectSanctuary(uint32 /*i*/)
{
if(!(*iter)->hasUnitState(UNIT_STAT_CASTING))
continue;
+
for(uint32 i = CURRENT_FIRST_NON_MELEE_SPELL; i < CURRENT_MAX_SPELL; i++)
{
if((*iter)->GetCurrentSpell(i)
@@ -5223,6 +5829,7 @@ void Spell::EffectSanctuary(uint32 /*i*/)
}
}
}
+
unitTarget->CombatStop();
unitTarget->getHostilRefManager().deleteReferences(); // stop all fighting
// Vanish allows to remove all threat and cast regular stealth so other spells can be used
@@ -5231,25 +5838,33 @@ void Spell::EffectSanctuary(uint32 /*i*/)
((Player *)m_caster)->RemoveAurasByType(SPELL_AURA_MOD_ROOT);
}
}
+
void Spell::EffectAddComboPoints(uint32 /*i*/)
{
if(!unitTarget)
return;
+
if(!m_caster->m_movedPlayer)
return;
+
if(damage <= 0)
return;
+
m_caster->m_movedPlayer->AddComboPoints(unitTarget, damage, this);
}
+
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() || 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->GetMap()->Instanceable())
@@ -5258,21 +5873,26 @@ void Spell::EffectDuel(uint32 i)
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->GetPhaseMask(),
@@ -5284,20 +5904,24 @@ void Spell::EffectDuel(uint32 i)
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/IN_MILISECONDS : 0);
pGameObj->SetSpellId(m_spellInfo->Id);
+
m_caster->AddGameObject(pGameObj);
map->Add(pGameObj);
//END
+
// Send request
WorldPacket data(SMSG_DUEL_REQUESTED, 8 + 8);
data << uint64(pGameObj->GetGUID());
data << uint64(caster->GetGUID());
caster->GetSession()->SendPacket(&data);
target->GetSession()->SendPacket(&data);
+
// create duel-info
DuelInfo *duel = new DuelInfo;
duel->initiator = caster;
@@ -5305,29 +5929,38 @@ void Spell::EffectDuel(uint32 i)
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;
+
pTarget->TeleportTo(pTarget->GetStartPosition(), unitTarget == m_caster ? TELE_TO_SPELL : 0);
// 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)
@@ -5335,41 +5968,54 @@ void Spell::EffectStuck(uint32 /*i*/)
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->HasAura(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*IN_MILISECONDS); // 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];
+
gameObjTarget->GetMap()->ScriptCommandStart(activateCommand, delay_secs, m_caster, gameObjTarget);
}
+
void Spell::EffectApplyGlyph(uint32 i)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *player = (Player*)m_caster;
+
// apply new one
if(uint32 glyph = m_spellInfo->EffectMiscValue[i])
{
@@ -5383,6 +6029,7 @@ void Spell::EffectApplyGlyph(uint32 i)
return; // glyph slot mismatch
}
}
+
// remove old glyph
if(uint32 oldglyph = player->GetGlyph(m_glyphIndex))
{
@@ -5392,24 +6039,30 @@ void Spell::EffectApplyGlyph(uint32 i)
player->SetGlyph(m_glyphIndex, 0);
}
}
+
player->CastSpell(m_caster, gp->SpellId, true);
player->SetGlyph(m_glyphIndex, glyph);
player->SendTalentsInfoData(false);
}
}
}
+
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];
@@ -5418,34 +6071,45 @@ void Spell::EffectEnchantHeldItem(uint32 i)
duration = damage;//+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*IN_MILISECONDS, 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;
@@ -5455,40 +6119,54 @@ void Spell::EffectInebriate(uint32 /*i*/)
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;
+
Item* foodItem = m_targets.getItemTarget();
if(!foodItem)
return;
+
Pet *pet = _player->GetPet();
if(!pet)
return;
+
if(!pet->isAlive())
return;
+
int32 benefit = pet->GetCurrentFoodBenefitLevel(foodItem->GetProto()->ItemLevel);
if(benefit <= 0)
return;
+
uint32 count = 1;
_player->DestroyItemCount(foodItem,count,true);
// TODO: fix crash when a spell has two effects, both pointed at the same item target
+
m_caster->CastCustomSpell(pet, m_spellInfo->EffectTriggerSpell[i], &benefit, NULL, NULL, true);
}
+
void Spell::EffectDismissPet(uint32 /*i*/)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Pet* pet = ((Player*)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])
{
@@ -5498,12 +6176,14 @@ void Spell::EffectSummonObject(uint32 i)
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 = m_caster->GetMap()->GetGameObject(guid);
+
if(obj)
{
// Recast case - null spell id to make auras not be removed on object remove from world
@@ -5513,7 +6193,9 @@ void Spell::EffectSummonObject(uint32 i)
}
m_caster->m_ObjectSlot[slot] = 0;
}
+
GameObject* pGameObj = new GameObject;
+
float x, y, z;
// If dest location if present
if (m_targets.HasDst())
@@ -5521,6 +6203,7 @@ void Spell::EffectSummonObject(uint32 i)
// 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,
m_caster->GetPhaseMask(), x, y, z, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 0, GO_STATE_READY))
@@ -5528,27 +6211,33 @@ void Spell::EffectSummonObject(uint32 i)
delete pGameObj;
return;
}
+
//pGameObj->SetUInt32Value(GAMEOBJECT_LEVEL,m_caster->getLevel());
int32 duration = GetSpellDuration(m_spellInfo);
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 0);
pGameObj->SetSpellId(m_spellInfo->Id);
m_caster->AddGameObject(pGameObj);
+
map->Add(pGameObj);
WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
data << uint64(pGameObj->GetGUID());
m_caster->SendMessageToSet(&data, true);
+
m_caster->m_ObjectSlot[slot] = pGameObj->GetGUID();
}
+
void Spell::EffectResurrect(uint32 /*effIndex*/)
{
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
@@ -5570,62 +6259,81 @@ void Spell::EffectResurrect(uint32 /*effIndex*/)
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;
+
Unit *victim = unitTarget->getVictim();
+
// attack prevented
// fixme, some attacks may not target current victim, this is right now not handled
if (!victim || !unitTarget->IsWithinMeleeRange(victim) || !unitTarget->HasInArc( 2*M_PI/3, victim ))
return;
+
// Only for proc/log informations
unitTarget->m_extraAttacks = damage;
// Need to send log before attack is made
SendLogExecute();
m_needSpellLog = false;
+
unitTarget->AttackerStateUpdate(victim, BASE_ATTACK, true);
}
+
void Spell::EffectParry(uint32 /*i*/)
{
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
((Player*)unitTarget)->SetCanParry(true);
}
+
void Spell::EffectBlock(uint32 /*i*/)
{
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
((Player*)unitTarget)->SetCanBlock(true);
}
+
void Spell::EffectLeapForward(uint32 i)
{
if(unitTarget->isInFlight())
return;
+
if(!m_targets.HasDst())
return;
+
uint32 mapid = m_caster->GetMapId();
float dist = m_caster->GetSpellRadiusForTarget(unitTarget, sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
if(Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_RADIUS, dist);
+
float x,y,z;
float destx,desty,destz,ground,floor;
float orientation = unitTarget->GetOrientation(), step = dist/10.0f;
+
unitTarget->GetPosition(x,y,z);
destx = x + dist * cos(orientation);
desty = y + dist * sin(orientation);
ground = unitTarget->GetMap()->GetHeight(destx,desty,MAX_HEIGHT,true);
floor = unitTarget->GetMap()->GetHeight(destx,desty,z, true);
destz = fabs(ground - z) <= fabs(floor - z) ? ground:floor;
+
bool col = VMAP::VMapFactory::createOrGetVMapManager()->getObjectHitPos(mapid,x,y,z+0.5f,destx,desty,destz+0.5f,destx,desty,destz,-0.5f);
+
if(col) // We had a collision!
{
destx -= 0.6 * cos(orientation);
@@ -5633,6 +6341,7 @@ void Spell::EffectLeapForward(uint32 i)
dist = sqrt((x-destx)*(x-destx) + (y-desty)*(y-desty));
step = dist/10.0f;
}
+
int j = 0;
for(j; j<10 ;j++)
{
@@ -5651,27 +6360,39 @@ void Spell::EffectLeapForward(uint32 i)
return;
}
unitTarget->NearTeleportTo(destx, desty, destz + 0.07531, orientation, unitTarget==m_caster);
+
}
+
void Spell::EffectReputation(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *_player = (Player*)unitTarget;
+
int32 rep_change = damage;//+1; // field store reputation change -1
+
uint32 faction_id = m_spellInfo->EffectMiscValue[i];
+
FactionEntry const* factionEntry = sFactionStore.LookupEntry(faction_id);
+
if(!factionEntry)
return;
+
_player->GetReputationMgr().ModifyReputation(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())
@@ -5680,8 +6401,10 @@ void Spell::EffectSelfResurrect(uint32 i)
return;
if(!unitTarget->IsInWorld())
return;
+
uint32 health = 0;
uint32 mana = 0;
+
// flat case
if(damage < 0)
{
@@ -5695,45 +6418,61 @@ void Spell::EffectSelfResurrect(uint32 i)
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 = creature->GetCreatureInfo()->GetRequiredLootSkill();
+
((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(!m_caster)
return;
+
Unit *target = m_targets.getUnitTarget();
if(!target)
return;
+
float x, y, z;
target->GetContactPoint(m_caster, x, y, z);
m_caster->GetMotionMaster()->MoveCharge(x, y, z);
+
// not all charge effects used in negative spells
if ( !IsPositiveSpell(m_spellInfo->Id) && m_caster->GetTypeId() == TYPEID_PLAYER)
m_caster->Attack(target, true);
}
+
void Spell::EffectCharge2(uint32 /*i*/)
{
float x, y, z;
@@ -5748,12 +6487,15 @@ void Spell::EffectCharge2(uint32 /*i*/)
}
else
return;
+
m_caster->GetMotionMaster()->MoveCharge(x, y, z);
}
+
void Spell::EffectKnockBack(uint32 i)
{
if(!unitTarget)
return;
+
// Typhoon
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && m_spellInfo->SpellFamilyFlags[1] & 0x01000000)
{
@@ -5761,6 +6503,7 @@ void Spell::EffectKnockBack(uint32 i)
if (m_caster->HasAura(62135))
return;
}
+
// Thunderstorm
if (m_spellInfo->SpellFamilyName == SPELLFAMILY_SHAMAN && m_spellInfo->SpellFamilyFlags[1] & 0x00002000)
{
@@ -5768,6 +6511,7 @@ void Spell::EffectKnockBack(uint32 i)
if (m_caster->HasAura(62132))
return;
}
+
float ratio = m_caster->GetCombatReach() / std::max(unitTarget->GetCombatReach(), 1.0f);
if(ratio < 1.0f)
ratio = ratio * ratio * ratio * 0.1f; // volume = length^3
@@ -5777,13 +6521,16 @@ void Spell::EffectKnockBack(uint32 i)
float speedz = float(damage) * ratio;
if(speedxy < 0.1f && speedz < 0.1f)
return;
+
float x, y;
if(m_targets.HasDst() && !m_targets.HasTraj())
m_targets.m_dstPos.GetPosition(x, y);
else
m_caster->GetPosition(x, y);
+
unitTarget->KnockbackFrom(x, y, speedxy, speedz);
}
+
void Spell::EffectJump2(uint32 i)
{
float speedxy = float(m_spellInfo->EffectMiscValue[i])/10;
@@ -5799,24 +6546,32 @@ void Spell::EffectJump2(uint32 i)
m_caster->JumpTo(speedxy, speedz, m_spellInfo->SpellIconID != 1891);
}
}
+
void Spell::EffectSendTaxi(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)unitTarget)->ActivateTaxiPathTo(m_spellInfo->EffectMiscValue[i],m_spellInfo->Id);
}
+
void Spell::EffectPlayerPull(uint32 i)
{
if(!unitTarget)
return;
+
unitTarget->KnockbackFrom(m_caster->GetPositionX(), m_caster->GetPositionY(), float(damage ? damage : unitTarget->GetDistance2d(m_caster)), float(m_spellInfo->EffectMiscValue[i])/10);
}
+
void Spell::EffectDispelMechanic(uint32 i)
{
if(!unitTarget)
return;
+
uint32 mechanic = m_spellInfo->EffectMiscValue[i];
+
std::queue < std::pair < uint32, uint64 > > dispel_list;
+
Unit::AuraMap& Auras = unitTarget->GetAuras();
for(Unit::AuraMap::iterator iter = Auras.begin(); iter != Auras.end(); iter++)
{
@@ -5825,11 +6580,13 @@ void Spell::EffectDispelMechanic(uint32 i)
dispel_list.push(std::make_pair(iter->second->GetId(), iter->second->GetCasterGUID() ) );
}
}
+
for(;dispel_list.size();dispel_list.pop())
{
unitTarget->RemoveAura(dispel_list.front().first, dispel_list.front().second, AURA_REMOVE_BY_ENEMY_SPELL);
}
}
+
void Spell::EffectSummonDeadPet(uint32 /*i*/)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
@@ -5842,15 +6599,18 @@ void Spell::EffectSummonDeadPet(uint32 /*i*/)
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*/)
{
int32 mana = 0;
@@ -5858,6 +6618,7 @@ void Spell::EffectDestroyAllTotems(uint32 /*i*/)
{
if(!m_caster->m_SummonSlot[slot])
continue;
+
Creature* totem = m_caster->GetMap()->GetCreature(m_caster->m_SummonSlot[slot]);
if(totem && totem->isTotem())
{
@@ -5872,14 +6633,18 @@ void Spell::EffectDestroyAllTotems(uint32 /*i*/)
}
}
mana = mana * damage / 100;
+
if (mana)
m_caster->CastCustomSpell(m_caster, 39104, &mana, NULL, NULL, true);
}
+
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)
@@ -5887,17 +6652,22 @@ void Spell::EffectDurabilityDamage(uint32 i)
((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)
@@ -5905,30 +6675,40 @@ void Spell::EffectDurabilityDamagePCT(uint32 i)
((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.HasDst())
m_targets.m_dstPos.GetPosition(fx, fy, fz);
//FIXME: this can be better check for most objects but still hack
@@ -5943,8 +6723,10 @@ void Spell::EffectTransmitted(uint32 effIndex)
float min_dis = GetSpellMinRangeForFriend(sSpellRangeStore.LookupEntry(m_spellInfo->rangeIndex));
float max_dis = GetSpellMaxRangeForFriend(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)
{
@@ -5953,13 +6735,14 @@ void Spell::EffectTransmitted(uint32 effIndex)
{
fx = 36.69+irand(-8,8);//random place for the bobber
fy = -416.38+irand(-8,8);
- fz = -19.9645;//serpentshrine water level
+ fz = -19.9645;//serpentshrine water level
}else if ( !cMap->IsInWater(fx, fy, fz-0.5f, 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
if(cMap->GetId() != 548)//if map is not serpentshrine caverns
fz = cMap->GetWaterLevel(fx, fy);
@@ -5969,20 +6752,25 @@ void Spell::EffectTransmitted(uint32 effIndex)
{
m_caster->GetPosition(fx, fy, fz);
}
+
GameObject* pGameObj = new GameObject;
+
if(!pGameObj->Create(objmgr.GenerateLowGuid(HIGHGUID_GAMEOBJECT), name_id, cMap,
m_caster->GetPhaseMask(), fx, fy, fz, m_caster->GetOrientation(), 0.0f, 0.0f, 0.0f, 0.0f, 100, GO_STATE_READY))
{
delete pGameObj;
return;
}
+
int32 duration = GetSpellDuration(m_spellInfo);
+
switch(goinfo->type)
{
case GAMEOBJECT_TYPE_FISHINGNODE:
{
m_caster->SetUInt64Value(UNIT_FIELD_CHANNEL_OBJECT,pGameObj->GetGUID());
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 = 0;
@@ -5993,6 +6781,7 @@ void Spell::EffectTransmitted(uint32 effIndex)
case 2: lastSec = 13; break;
case 3: lastSec = 17; break;
}
+
duration = duration - lastSec*IN_MILISECONDS + FISHING_BOBBER_READY_TIME*IN_MILISECONDS;
break;
}
@@ -6013,17 +6802,24 @@ void Spell::EffectTransmitted(uint32 effIndex)
default:
break;
}
+
pGameObj->SetRespawnTime(duration > 0 ? duration/IN_MILISECONDS : 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");
//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->GetGOInfo()->GetLinkedGameObjectEntry())
{
GameObject* linkedGO = new GameObject;
@@ -6034,6 +6830,7 @@ void Spell::EffectTransmitted(uint32 effIndex)
//linkedGO->SetUInt32Value(GAMEOBJECT_LEVEL, m_caster->getLevel());
linkedGO->SetSpellId(m_spellInfo->Id);
linkedGO->SetOwnerGUID(m_caster->GetGUID());
+
linkedGO->GetMap()->Add(linkedGO);
}
else
@@ -6044,44 +6841,56 @@ void Spell::EffectTransmitted(uint32 effIndex)
}
}
}
+
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::EffectMilling(uint32 /*i*/)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player* p_caster = (Player*)m_caster;
if(!itemTarget || !(itemTarget->GetProto()->BagFamily & BAG_FAMILY_MASK_HERBS))
return;
+
if(itemTarget->GetCount() < 5)
return;
+
if( sWorld.getConfig(CONFIG_SKILL_MILLING))
{
uint32 SkillValue = p_caster->GetPureSkillValue(SKILL_INSCRIPTION);
uint32 reqSkillValue = itemTarget->GetProto()->RequiredSkillRank;
p_caster->UpdateGatherSkill(SKILL_INSCRIPTION, SkillValue, reqSkillValue);
}
+
((Player*)m_caster)->SendLoot(itemTarget->GetGUID(), LOOT_MILLING);
}
+
void Spell::EffectSkill(uint32 /*i*/)
{
sLog.outDebug("WORLD: SkillEFFECT");
}
+
/* 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.
@@ -6095,25 +6904,31 @@ void Spell::EffectSpiritHeal(uint32 /*i*/)
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::list <Aura *> steal_list;
// Create dispel mask by dispel type
uint32 dispelMask = GetDispellMask( DispelType(m_spellInfo->EffectMiscValue[i]) );
@@ -6132,6 +6947,7 @@ void Spell::EffectStealBeneficialBuff(uint32 i)
if (uint32 list_size = steal_list.size())
{
std::list < Aura * > success_list;
+
// dispel N = damage buffs (or while exist buffs for dispel)
for (int32 count=0; count < damage && list_size > 0; ++count, list_size = steal_list.size())
{
@@ -6160,38 +6976,49 @@ void Spell::EffectStealBeneficialBuff(uint32 i)
}
}
}
+
void Spell::EffectKillCreditPersonal(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)unitTarget)->KilledMonsterCredit(m_spellInfo->EffectMiscValue[i], 0);
}
+
void Spell::EffectKillCredit(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
int32 creatureEntry = m_spellInfo->EffectMiscValue[i];
if(!creatureEntry)
{
if(m_spellInfo->Id == 42793) // Burn Body
creatureEntry = 24008; // Fallen Combatant
}
+
if(creatureEntry)
((Player*)unitTarget)->RewardPlayerAndGroupAtEvent(creatureEntry, unitTarget);
}
+
void Spell::EffectQuestFail(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)unitTarget)->FailQuest(m_spellInfo->EffectMiscValue[i]);
}
+
void Spell::EffectActivateRune(uint32 eff_idx)
{
if(m_caster->GetTypeId() != TYPEID_PLAYER)
return;
+
Player *plr = (Player*)m_caster;
+
if(plr->getClass() != CLASS_DEATH_KNIGHT)
return;
+
for(uint32 j = 0; j < MAX_RUNES; ++j)
{
if(plr->GetRuneCooldown(j) && plr->GetCurrentRune(j) == RuneType(m_spellInfo->EffectMiscValue[eff_idx]))
@@ -6200,16 +7027,19 @@ void Spell::EffectActivateRune(uint32 eff_idx)
}
}
}
+
void Spell::EffectTitanGrip(uint32 /*eff_idx*/)
{
if (unitTarget && unitTarget->GetTypeId() == TYPEID_PLAYER)
((Player*)unitTarget)->SetCanTitanGrip(true);
}
+
void Spell::EffectRedirectThreat(uint32 /*i*/)
{
if(unitTarget)
m_caster->SetReducedThreatPercent((uint32)damage, unitTarget->GetGUID());
}
+
void Spell::EffectWMODamage(uint32 /*i*/)
{
if(gameObjTarget && gameObjTarget->GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING)
@@ -6217,6 +7047,7 @@ void Spell::EffectWMODamage(uint32 /*i*/)
Unit *caster = m_originalCaster;
if(!caster)
return;
+
FactionTemplateEntry const *casterft, *goft;
casterft = caster->getFactionTemplateEntry();
goft = sFactionTemplateStore.LookupEntry(gameObjTarget->GetUInt32Value(GAMEOBJECT_FACTION));
@@ -6225,20 +7056,25 @@ void Spell::EffectWMODamage(uint32 /*i*/)
gameObjTarget->TakenDamage((uint32)damage);
}
}
+
void Spell::EffectWMORepair(uint32 /*i*/)
{
if(gameObjTarget && gameObjTarget->GetGoType() == GAMEOBJECT_TYPE_DESTRUCTIBLE_BUILDING)
gameObjTarget->Rebuild();
}
+
void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const *properties)
{
Unit *caster = m_originalCaster;
if(!caster)
return;
+
if(caster->isTotem())
caster = ((Totem*)caster)->GetOwner();
+
// in another case summon new
uint32 level = caster->getLevel();
+
// level of pet summoned using engineering item based at engineering skill level
if(m_CastItem && caster->GetTypeId() == TYPEID_PLAYER)
{
@@ -6252,6 +7088,7 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const *
}
}
}
+
//float radius = GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(m_spellInfo->EffectRadiusIndex[i]));
float radius = 5.0f;
int32 amount = damage > 0 ? damage : 1;
@@ -6264,26 +7101,33 @@ void Spell::SummonGuardian(uint32 i, uint32 entry, SummonPropertiesEntry const *
int32 duration = GetSpellDuration(m_spellInfo);
if(Player* modOwner = m_originalCaster->GetSpellModOwner())
modOwner->ApplySpellMod(m_spellInfo->Id, SPELLMOD_DURATION, duration);
+
TempSummonType summonType = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
Map *map = caster->GetMap();
+
for(uint32 count = 0; count < amount; ++count)
{
Position pos;
GetSummonPosition(i, pos, radius, count);
+
TempSummon *summon = map->SummonCreature(entry, pos, properties, duration, caster);
if(!summon)
return;
if(summon->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
((Guardian*)summon)->InitStatsForLevel(level);
+
summon->SetUInt32Value(UNIT_CREATED_BY_SPELL, m_spellInfo->Id);
if(summon->HasUnitTypeMask(UNIT_MASK_MINION) && m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
((Minion*)summon)->SetFollowAngle(m_caster->GetAngle(summon));
+
summon->AI()->EnterEvadeMode();
}
}
+
void Spell::GetSummonPosition(uint32 i, Position &pos, float radius, uint32 count)
{
pos.SetOrientation(m_caster->GetOrientation());
+
if (m_targets.m_targetMask & TARGET_FLAG_DEST_LOCATION)
{
// Summon 1 unit in dest location
@@ -6317,37 +7161,47 @@ void Spell::GetSummonPosition(uint32 i, Position &pos, float radius, uint32 coun
pos.Relocate(x, y, z);
}
}
+
void Spell::EffectRenamePet(uint32 /*eff_idx*/)
{
if (!unitTarget || unitTarget->GetTypeId() != TYPEID_UNIT ||
!((Creature*)unitTarget)->isPet() || ((Pet*)unitTarget)->getPetType() != HUNTER_PET)
return;
+
unitTarget->SetByteValue(UNIT_FIELD_BYTES_2, 2, UNIT_RENAME_ALLOWED);
}
+
void Spell::EffectPlayMusic(uint32 i)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
uint32 soundid = m_spellInfo->EffectMiscValue[i];
+
if (!sSoundEntriesStore.LookupEntry(soundid))
{
sLog.outError("EffectPlayMusic: Sound (Id: %u) not exist in spell %u.",soundid,m_spellInfo->Id);
return;
}
+
WorldPacket data(SMSG_PLAY_MUSIC, 4);
data << uint32(soundid);
((Player*)unitTarget)->GetSession()->SendPacket(&data);
}
+
void Spell::EffectSpecCount(uint32 /*eff_idx*/)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)unitTarget)->UpdateSpecCount(damage);
}
+
void Spell::EffectActivateSpec(uint32 /*eff_idx*/)
{
if(!unitTarget || unitTarget->GetTypeId() != TYPEID_PLAYER)
return;
+
((Player*)unitTarget)->ActivateSpec(damage-1); // damage is 1 or 2, spec is 0 or 1
}
diff --git a/src/game/SpellHandler.cpp b/src/game/SpellHandler.cpp
index 31eb05ae802..b8aa4469472 100644
--- a/src/game/SpellHandler.cpp
+++ b/src/game/SpellHandler.cpp
@@ -17,6 +17,7 @@
* 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 "DBCStores.h"
#include "WorldPacket.h"
@@ -31,50 +32,61 @@
#include "TemporarySummon.h"
#include "SpellAuras.h"
#include "CreatureAI.h"
+
void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
{
// TODO: add targets.read() check
Player* pUser = _player;
+
// ignore for remote control state
if(pUser->m_mover != pUser)
return;
+
uint8 bagIndex, slot;
uint8 unk_flags; // flags (if 0x02 - some additional data are received)
uint8 cast_count; // next cast if exists (single or not)
uint64 item_guid;
uint32 glyphIndex; // something to do with glyphs?
uint32 spellid; // casted spell id
+
recvPacket >> bagIndex >> slot >> cast_count >> spellid >> item_guid >> glyphIndex >> unk_flags;
+
Item *pItem = pUser->GetUseableItemByPos(bagIndex, slot);
if(!pItem)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
+
if(pItem->GetGUID() != item_guid)
{
pUser->SendEquipError(EQUIP_ERR_ITEM_NOT_FOUND, NULL, NULL );
return;
}
+
sLog.outDetail("WORLD: CMSG_USE_ITEM packet, bagIndex: %u, slot: %u, cast_count: %u, spellid: %u, Item: %u, glyphIndex: %u, unk_flags: %u, data length = %i", bagIndex, slot, cast_count, spellid, pItem->GetEntry(), glyphIndex, unk_flags, (uint32)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) &&
@@ -83,6 +95,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
pUser->SendEquipError(EQUIP_ERR_NOT_DURING_ARENA_MATCH,pItem,NULL);
return;
}
+
if (pUser->isInCombat())
{
for(int i = 0; i < MAX_ITEM_PROTO_SPELLS; ++i)
@@ -97,6 +110,7 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
}
}
}
+
// 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 )
{
@@ -106,26 +120,31 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
pItem->SetBinding( true );
}
}
+
SpellCastTargets targets;
if (!targets.read(&recvPacket, pUser))
return;
+
targets.Update(pUser);
+
if (!pItem->IsTargetValidForItemUse(targets.getUnitTarget()))
{
// free gray item after use fail
pUser->SendEquipError(EQUIP_ERR_NONE, pItem, NULL);
+
// send spell error
if (SpellEntry const* spellInfo = sSpellStore.LookupEntry(spellid))
{
- // for implicit area/coord target spells
+ // for implicit area/coord target spells
if(!targets.getUnitTarget())
Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_NO_VALID_TARGETS);
- // for explicit target spells
+ // for explicit target spells
else
- Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_BAD_TARGETS);
+ Spell::SendCastResult(_player,spellInfo,cast_count,SPELL_FAILED_BAD_TARGETS);
}
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))
{
@@ -133,46 +152,59 @@ void WorldSession::HandleUseItemOpcode(WorldPacket& recvPacket)
pUser->CastItemUseSpell(pItem,targets,cast_count,glyphIndex);
}
}
+
#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)
{
sLog.outDetail("WORLD: CMSG_OPEN_ITEM packet, data length = %i",(uint32)recvPacket.size());
+
Player* pUser = _player;
+
// ignore for remote control state
if(pUser->m_mover != pUser)
return;
+
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;
}
+
if(!pUser->GetSession()->HandleOnItemOpen(pItem))
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->Skill[1] || lockInfo->Skill[0])
{
@@ -180,6 +212,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
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());
@@ -188,6 +221,7 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
Field *fields = result->Fetch();
uint32 entry = fields[0].GetUInt32();
uint32 flags = fields[1].GetUInt32();
+
pItem->SetUInt64Value(ITEM_FIELD_GIFTCREATOR, 0);
pItem->SetEntry(entry);
pItem->SetUInt32Value(ITEM_FIELD_FLAGS, flags);
@@ -205,36 +239,51 @@ void WorldSession::HandleOpenItemOpcode(WorldPacket& recvPacket)
else
pUser->SendLoot(pItem->GetGUID(),LOOT_CORPSE);
}
+
void WorldSession::HandleGameObjectUseOpcode( WorldPacket & recv_data )
{
uint64 guid;
+
recv_data >> guid;
+
sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_USE Message [guid=%u]", GUID_LOPART(guid));
+
// ignore for remote control state
if(_player->m_mover != _player)
return;
+
GameObject *obj = GetPlayer()->GetMap()->GetGameObject(guid);
+
if(!obj)
return;
+
if (Script->GOHello(_player, obj))
return;
+
obj->Use(_player);
}
+
void WorldSession::HandleGameobjectReportUse(WorldPacket& recvPacket)
{
uint64 guid;
recvPacket >> guid;
+
sLog.outDebug( "WORLD: Recvd CMSG_GAMEOBJ_REPORT_USE Message [in game guid: %u]", GUID_LOPART(guid));
+
// ignore for remote control state
if(_player->m_mover != _player)
return;
+
GameObject* go = GetPlayer()->GetMap()->GetGameObject(guid);
if(!go)
return;
+
if(!go->IsWithinDistInMap(_player,INTERACTION_DISTANCE))
return;
+
_player->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_USE_GAMEOBJECT, go->GetEntry());
}
+
void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
{
uint32 spellId;
@@ -242,6 +291,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
recvPacket >> cast_count;
recvPacket >> spellId;
recvPacket >> unk_flags; // flags (if 0x02 - some additional data are received)
+
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER)
@@ -249,15 +299,19 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet
return;
}
+
sLog.outDebug("WORLD: got cast spell packet, spellId - %u, cast_count: %u, unk_flags %u, data length = %i",
spellId, cast_count, unk_flags, (uint32)recvPacket.size());
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
+
if(!spellInfo)
{
sLog.outError("WORLD: unknown spell id %u", spellId);
recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet
return;
}
+
if(mover->GetTypeId()==TYPEID_PLAYER)
{
// not have spell in spellbook or spell passive and not casted by client
@@ -278,14 +332,17 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
return;
}
}
+
// Client is resending autoshot cast opcode when other spell is casted during shoot rotation
// Skip it to prevent "interrupt" message
if (IsAutoRepeatRangedSpell(spellInfo) && _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)
&& _player->GetCurrentSpell(CURRENT_AUTOREPEAT_SPELL)->m_spellInfo == spellInfo)
return;
+
// can't use our own spells when we're in possession of another unit,
if(_player->isPossessing())
return;
+
// client provided targets
SpellCastTargets targets;
if(!targets.read(&recvPacket,mover))
@@ -293,6 +350,7 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
recvPacket.rpos(recvPacket.wpos()); // prevent spam at ignore packet
return;
}
+
// some spell cast packet including more data (for projectiles?)
if (unk_flags & 0x02)
{
@@ -303,36 +361,46 @@ void WorldSession::HandleCastSpellOpcode(WorldPacket& recvPacket)
MovementInfo movementInfo;
ReadMovementInfo(recvPacket, &movementInfo);
}
+
// 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(mover, spellInfo, false);
spell->m_cast_count = cast_count; // set count of casts
spell->prepare(&targets);
}
+
void WorldSession::HandleCancelCastOpcode(WorldPacket& recvPacket)
{
uint32 spellId;
+
recvPacket.read_skip<uint8>(); // counter, increments with every CANCEL packet, don't use for now
recvPacket >> spellId;
+
if(_player->IsNonMeleeSpellCasted(false))
_player->InterruptNonMeleeSpells(false,spellId,false);
}
+
void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
{
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;
+
// channeled spell case (it currently casted then)
if (IsChanneledSpell(spellInfo))
{
@@ -341,96 +409,124 @@ void WorldSession::HandleCancelAuraOpcode( WorldPacket& recvPacket)
_player->InterruptSpell(CURRENT_CHANNELED_SPELL);
return;
}
+
// non channeled case
// maybe should only remove one buff when there are multiple?
_player->RemoveAurasDueToSpell(spellId, 0, AURA_REMOVE_BY_CANCEL);
}
+
void WorldSession::HandlePetCancelAuraOpcode( WorldPacket& recvPacket)
{
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::GetCreatureOrPetOrVehicle(*_player,guid);
+
if(!pet)
{
sLog.outError( "Pet %u not exist.", uint32(GUID_LOPART(guid)) );
return;
}
+
if(pet != GetPlayer()->GetGuardianPet() && 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);
}
+
void WorldSession::HandleCancelChanneling( WorldPacket & recv_data)
{
recv_data.read_skip<uint32>(); // spellid, not used
+
// ignore for remote control state (for player case)
Unit* mover = _player->m_mover;
if(mover != _player && mover->GetTypeId()==TYPEID_PLAYER)
return;
+
mover->InterruptSpell(CURRENT_CHANNELED_SPELL);
}
+
void WorldSession::HandleTotemDestroyed( WorldPacket& recvPacket)
{
// ignore for remote control state
if(_player->m_mover != _player)
return;
+
uint8 slotId;
+
recvPacket >> slotId;
+
++slotId;
if (slotId >= MAX_TOTEM_SLOT)
return;
+
if(!_player->m_SummonSlot[slotId])
return;
+
Creature* totem = GetPlayer()->GetMap()->GetCreature(_player->m_SummonSlot[slotId]);
// Don't unsummon sentry totem
if(totem && totem->isTotem() && totem->GetEntry() != SENTRY_TOTEM_ENTRY)
((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);
}
}
+
void WorldSession::HandleSpellClick( WorldPacket & recv_data )
{
uint64 guid;
recv_data >> guid;
+
// this will get something not in world. crash
Creature *unit = ObjectAccessor::GetCreatureOrPetOrVehicle(*_player, guid);
+
if(!unit)
return;
+
// TODO: Unit::SetCharmedBy: 28782 is not in world but 0 is trying to charm it! -> crash
if(!unit->IsInWorld())
{
@@ -438,6 +534,7 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data )
assert(false);
return;
}
+
SpellClickInfoMapBounds clickPair = objmgr.GetSpellClickInfoMapBounds(unit->GetEntry());
for(SpellClickInfoMap::const_iterator itr = clickPair.first; itr != clickPair.second; ++itr)
{
@@ -449,15 +546,19 @@ void WorldSession::HandleSpellClick( WorldPacket & recv_data )
caster->CastSpell(target, itr->second.spellId, true, NULL, NULL, origCasterGUID);
}
}
+
if(unit->IsVehicle())
_player->EnterVehicle(unit);
+
unit->AI()->DoAction(EVENT_SPELLCLICK);
}
+
void WorldSession::HandleMirrrorImageDataRequest( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_GET_MIRRORIMAGE_DATA");
uint64 guid;
recv_data >> guid;
+
// Get unit for which data is needed by client
Unit *unit = ObjectAccessor::GetObjectInWorld(guid, (Unit*)NULL);
if(!unit)
@@ -476,12 +577,14 @@ void WorldSession::HandleMirrrorImageDataRequest( WorldPacket & recv_data )
data << (uint8)pCreator->getGender();
data << (uint8)pCreator->getClass();
data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 0); // skin
+
data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 1); // face
data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 2); // hair
data << (uint8)pCreator->GetByteValue(PLAYER_BYTES, 3); // haircolor
data << (uint8)pCreator->GetByteValue(PLAYER_BYTES_2, 0); // facialhair
+
data << (uint32)pCreator->GetGuildId(); // unk
- static const EquipmentSlots ItemSlots[] =
+ static const EquipmentSlots ItemSlots[] =
{
EQUIPMENT_SLOT_HEAD,
EQUIPMENT_SLOT_SHOULDERS,
diff --git a/src/game/SpellMgr.cpp b/src/game/SpellMgr.cpp
index 3f11cfccf80..ef42f0d50ac 100644
--- a/src/game/SpellMgr.cpp
+++ b/src/game/SpellMgr.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -27,9 +28,11 @@
#include "Spell.h"
#include "BattleGroundMgr.h"
#include "CreatureAI.h"
+
bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS];
SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS];
+
SpellMgr::SpellMgr()
{
for(int i = 0; i < TOTAL_SPELL_EFFECTS; ++i)
@@ -94,6 +97,7 @@ SpellMgr::SpellMgr()
break;
}
}
+
for(int i = 0; i < TOTAL_SPELL_TARGETS; ++i)
{
switch(i)
@@ -206,6 +210,7 @@ SpellMgr::SpellMgr()
SpellTargetType[i] = TARGET_TYPE_DEFAULT;
}
}
+
for(int i = 0; i < TOTAL_SPELL_TARGETS; ++i)
{
switch(i)
@@ -234,14 +239,17 @@ SpellMgr::SpellMgr()
}
}
+
SpellMgr::~SpellMgr()
{
}
+
SpellMgr& SpellMgr::Instance()
{
static SpellMgr spellMgr;
return spellMgr;
}
+
bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const
{
for (uint8 i = 0; i< MAX_SPELL_EFFECTS; ++i)
@@ -251,6 +259,7 @@ bool SpellMgr::IsSrcTargetSpell(SpellEntry const *spellInfo) const
}
return false;
}
+
int32 GetSpellDuration(SpellEntry const *spellInfo)
{
if(!spellInfo)
@@ -260,6 +269,7 @@ int32 GetSpellDuration(SpellEntry const *spellInfo)
return 0;
return (du->Duration[0] == -1) ? -1 : abs(du->Duration[0]);
}
+
int32 GetSpellMaxDuration(SpellEntry const *spellInfo)
{
if(!spellInfo)
@@ -269,6 +279,7 @@ int32 GetSpellMaxDuration(SpellEntry const *spellInfo)
return 0;
return (du->Duration[2] == -1) ? -1 : abs(du->Duration[2]);
}
+
bool GetDispelChance(Unit* caster, uint32 spellId)
{
// we assume that aura dispel chance is 100% on start
@@ -283,19 +294,26 @@ bool GetDispelChance(Unit* caster, uint32 spellId)
// Try dispel
return !roll_chance_i(miss_chance);
}
+
uint32 GetSpellCastTime(SpellEntry const* spellInfo, Spell * 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 && spell->GetCaster())
spell->GetCaster()->ModSpellCastTime(spellInfo, castTime, spell);
+
if (spellInfo->Attributes & SPELL_ATTR_REQ_AMMO && (!spell || !(spell->IsAutoRepeat())))
castTime += 500;
+
return (castTime > 0) ? uint32(castTime) : 0;
}
+
bool IsPassiveSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
@@ -305,6 +323,7 @@ bool IsPassiveSpell(uint32 spellId)
return true;
return false;
}
+
bool IsAutocastableSpell(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
@@ -316,10 +335,12 @@ bool IsAutocastableSpell(uint32 spellId)
return false;
return true;
}
+
bool IsHigherHankOfSpell(uint32 spellId_1, uint32 spellId_2)
{
return spellmgr.GetSpellRank(spellId_1)<spellmgr.GetSpellRank(spellId_2);
}
+
uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, SpellSchoolMask schoolMask)
{
// Spell drain all exist power on cast (Only paladin lay of Hands)
@@ -334,6 +355,7 @@ uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, Spe
sLog.outError("CalculateManaCost: Unknown power type '%d' in spell %d", spellInfo->powerType, spellInfo->Id);
return 0;
}
+
// Base powerCost
int32 powerCost = spellInfo->manaCost;
// PCT cost from total amount
@@ -372,19 +394,23 @@ uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, Spe
// Apply cost mod by spell
if(Player* modOwner = caster->GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_COST, powerCost);
+
if(spellInfo->Attributes & SPELL_ATTR_LEVEL_DAMAGE_CALCULATION)
powerCost = int32(powerCost/ (1.117f* spellInfo->spellLevel / caster->getLevel() -0.1327f));
+
// PCT mod from user auras by school
powerCost = int32(powerCost * (1.0f+caster->GetFloatValue(UNIT_FIELD_POWER_COST_MULTIPLIER+school)));
if (powerCost < 0)
powerCost = 0;
return powerCost;
}
+
AuraState GetSpellAuraState(SpellEntry const * spellInfo)
{
// Seals
if (IsSealSpell(spellInfo))
return AURA_STATE_JUDGEMENT;
+
// Conflagrate aura state on Immolate and Shadowflame
if (spellInfo->SpellFamilyName == SPELLFAMILY_WARLOCK &&
// Immolate
@@ -392,27 +418,35 @@ AuraState GetSpellAuraState(SpellEntry const * spellInfo)
// Shadowflame
(spellInfo->SpellFamilyFlags[2] & 2)))
return AURA_STATE_CONFLAGRATE;
+
// Faerie Fire (druid versions)
if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x400)
return AURA_STATE_FAERIE_FIRE;
+
// Sting (hunter's pet ability)
if (spellInfo->Category == 1133)
return AURA_STATE_FAERIE_FIRE;
+
// Victorious
if (spellInfo->SpellFamilyName == SPELLFAMILY_WARRIOR && spellInfo->SpellFamilyFlags[1] & 0x00040000)
return AURA_STATE_WARRIOR_VICTORY_RUSH;
+
// Swiftmend state on Regrowth & Rejuvenation
if (spellInfo->SpellFamilyName == SPELLFAMILY_DRUID && spellInfo->SpellFamilyFlags[0] & 0x50 )
return AURA_STATE_SWIFTMEND;
+
// Deadly poison aura state
if(spellInfo->SpellFamilyName == SPELLFAMILY_ROGUE && spellInfo->SpellFamilyFlags[0] & 0x10000)
return AURA_STATE_DEADLY_POISON;
+
// Enrage aura state
if(spellInfo->Dispel == DISPEL_ENRAGE)
return AURA_STATE_ENRAGE;
+
// Bleeding aura state
if (GetAllSpellMechanicMask(spellInfo) & 1<<MECHANIC_BLEED)
return AURA_STATE_BLEEDING;
+
if(GetSpellSchoolMask(spellInfo) & SPELL_SCHOOL_MASK_FROST)
{
for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i)
@@ -424,11 +458,13 @@ AuraState GetSpellAuraState(SpellEntry const * spellInfo)
}
return AURA_STATE_NONE;
}
+
SpellSpecific GetSpellSpecific(uint32 spellId)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return SPELL_NORMAL;
+
switch(spellInfo->SpellFamilyName)
{
case SPELLFAMILY_GENERIC:
@@ -456,6 +492,7 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
break;
}
}
+
if(food && drink)
return SPELL_FOOD_AND_DRINK;
else if(food)
@@ -496,11 +533,14 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
// family flags 18(Molten), 25(Frost/Ice), 28(Mage)
if (spellInfo->SpellFamilyFlags[0] & 0x12040000)
return SPELL_MAGE_ARMOR;
+
// Arcane brillance and Arcane intelect (normal check fails because of flags difference)
if (spellInfo->SpellFamilyFlags[0] & 0x400)
return SPELL_MAGE_ARCANE_BRILLANCE;
+
if ((spellInfo->SpellFamilyFlags[0] & 0x1000000) && spellInfo->EffectApplyAuraName[0]==SPELL_AURA_MOD_CONFUSE)
return SPELL_MAGE_POLYMORPH;
+
break;
}
case SPELLFAMILY_WARRIOR:
@@ -509,6 +549,7 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
return SPELL_POSITIVE_SHOUT;
if (spellInfo->Id == 12292) // Death Wish
return SPELL_WARRIOR_ENRAGE;
+
break;
}
case SPELLFAMILY_WARLOCK:
@@ -516,9 +557,11 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
// only warlock curses have this
if (spellInfo->Dispel == DISPEL_CURSE)
return SPELL_CURSE;
+
// Warlock (Demon Armor | Demon Skin | Fel Armor)
if (spellInfo->SpellFamilyFlags[1] & 0x20000020 || spellInfo->SpellFamilyFlags[2] & 0x00000010)
return SPELL_WARLOCK_ARMOR;
+
//seed of corruption and corruption
if (spellInfo->SpellFamilyFlags[1] & 0x10 || spellInfo->SpellFamilyFlags[0] & 0x2)
return SPELL_WARLOCK_CORRUPTION;
@@ -531,9 +574,11 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
(spellInfo->InterruptFlags & SPELL_INTERRUPT_FLAG_AUTOATTACK) &&
(spellInfo->SpellIconID == 52 || spellInfo->SpellIconID == 79))
return SPELL_WELL_FED;
+
// Divine Spirit and Prayer of Spirit
if (spellInfo->SpellFamilyFlags[0] & 0x20)
return SPELL_PRIEST_DIVINE_SPIRIT;
+
break;
}
case SPELLFAMILY_HUNTER:
@@ -541,41 +586,52 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
// only hunter stings have this
if (spellInfo->Dispel == DISPEL_POISON)
return SPELL_STING;
+
// only hunter aspects have this (but not all aspects in hunter family)
if( spellInfo->SpellFamilyFlags.HasFlag(0x00380000, 0x00440000, 0x00001010))
return SPELL_ASPECT;
+
break;
}
case SPELLFAMILY_PALADIN:
{
if (IsSealSpell(spellInfo))
return SPELL_SEAL;
+
if (spellInfo->SpellFamilyFlags[0] & 0x11010002)
return SPELL_BLESSING;
+
if (spellInfo->SpellFamilyFlags[0] & 0x00002190)
return SPELL_HAND;
+
// Judgement of Wisdom, Judgement of Light, Judgement of Justice
if (spellInfo->Id == 20184 || spellInfo->Id == 20185 || spellInfo->Id == 20186)
return SPELL_JUDGEMENT;
+
// only paladin auras have this (for palaldin class family)
if( spellInfo->SpellFamilyFlags[2] & 0x00000020 )
return SPELL_AURA;
+
break;
}
case SPELLFAMILY_SHAMAN:
{
if (IsElementalShield(spellInfo))
return SPELL_ELEMENTAL_SHIELD;
+
break;
}
+
case SPELLFAMILY_POTION:
return spellmgr.GetSpellElixirSpecific(spellInfo->Id);
+
case SPELLFAMILY_DEATHKNIGHT:
if (spellInfo->Id == SPELL_ID_BLOOD_PRESENCE || spellInfo->Id == SPELL_ID_FROST_PRESENCE || spellInfo->Id == SPELL_ID_UNHOLY_PRESENCE)
//if (spellInfo->Category == 47)
return SPELL_PRESENCE;
break;
}
+
for(int i = 0; i < 3; ++i)
{
if(spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA)
@@ -599,8 +655,10 @@ SpellSpecific GetSpellSpecific(uint32 spellId)
// elixirs can have different families, but potion most ofc.
if(SpellSpecific sp = spellmgr.GetSpellElixirSpecific(spellInfo->Id))
return sp;
+
return SPELL_NORMAL;
}
+
// target not allow have more one spell specific from same caster
bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific spellSpec2)
{
@@ -621,6 +679,7 @@ bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1,SpellSpecific s
return false;
}
}
+
bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific spellSpec2)
{
switch(spellSpec1)
@@ -663,6 +722,7 @@ bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific
return false;
}
}
+
bool IsPositiveTarget(uint32 targetA, uint32 targetB)
{
// non-positive targets
@@ -683,10 +743,12 @@ bool IsPositiveTarget(uint32 targetA, uint32 targetB)
return IsPositiveTarget(targetB, 0);
return true;
}
+
bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const
{
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto) return false;
+
switch(spellId)
{
case 1852: // Silenced (GM)
@@ -705,6 +767,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con
case 12042: // Arcane Power
return true;
}
+
switch(spellproto->Mechanic)
{
case MECHANIC_IMMUNE_SHIELD:
@@ -712,12 +775,14 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con
default:
break;
}
+
// Special case: effects which determine positivity of whole spell
for (uint8 i = 0;i<MAX_SPELL_EFFECTS;++i)
{
if (spellproto->EffectApplyAuraName[i] == SPELL_AURA_MOD_STEALTH)
return true;
}
+
switch(spellproto->Effect[effIndex])
{
case SPELL_EFFECT_DUMMY:
@@ -736,6 +801,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con
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:
@@ -785,6 +851,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con
{
uint32 spellTriggeredId = spellproto->EffectTriggerSpell[effIndex];
SpellEntry const *spellTriggeredProto = sSpellStore.LookupEntry(spellTriggeredId);
+
if(spellTriggeredProto)
{
// non-positive targets of main spell return early
@@ -805,6 +872,7 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con
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;
@@ -909,26 +977,32 @@ bool SpellMgr::_isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) con
default:
break;
}
+
// non-positive targets
if(!IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex]))
return false;
+
// AttributesEx check
if(spellproto->AttributesEx & SPELL_ATTR_EX_NEGATIVE)
return false;
+
if (!deep && spellproto->EffectTriggerSpell[effIndex]
&& !spellproto->EffectApplyAuraName[effIndex]
&& IsPositiveTarget(spellproto->EffectImplicitTargetA[effIndex],spellproto->EffectImplicitTargetB[effIndex])
&& !_isPositiveSpell(spellproto->EffectTriggerSpell[effIndex], true))
return false;
+
// ok, positive
return true;
}
+
bool IsPositiveSpell(uint32 spellId)
{
if(!sSpellStore.LookupEntry(spellId)) // non-existing spells such as 61988 (Forbearance)
return false;
return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE);
}
+
bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
{
if(!sSpellStore.LookupEntry(spellId))
@@ -941,10 +1015,12 @@ bool IsPositiveEffect(uint32 spellId, uint32 effIndex)
case 2: return !(spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_NEGATIVE_EFF2);
}
}
+
bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const
{
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto) return false;
+
// spells with at least 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)
@@ -952,11 +1028,13 @@ bool SpellMgr::_isPositiveSpell(uint32 spellId, bool deep) const
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))
{
@@ -965,8 +1043,10 @@ bool IsSingleTargetSpell(SpellEntry const *spellInfo)
default:
break;
}
+
return false;
}
+
bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2)
{
// TODO - need better check
@@ -974,6 +1054,7 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI
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
@@ -987,8 +1068,10 @@ bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellI
default:
break;
}
+
return false;
}
+
SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32 form)
{
// talents that learn spells can have stance requirements that need ignore
@@ -996,11 +1079,15 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32
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 SPELL_CAST_OK;
+
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 SPELL_CAST_OK;
+
bool actAsShifted = false;
SpellShapeshiftEntry const *shapeInfo = NULL;
if (form > 0)
@@ -1013,6 +1100,7 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32
}
actAsShifted = !(shapeInfo->flags1 & 1); // shapeshift acts as normal form for spells
}
+
if(actAsShifted)
{
if (spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT) // not while shapeshifted
@@ -1026,6 +1114,7 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32
if(!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_NOT_NEED_SHAPESHIFT) && spellInfo->Stances != 0)
return SPELL_FAILED_ONLY_SHAPESHIFT;
}
+
// Check if stance disables cast of not-stance spells
// Example: cannot cast any other spells in zombie or ghoul form
// TODO: Find a way to disable use of these spells clientside
@@ -1034,40 +1123,55 @@ SpellCastResult GetErrorAtShapeshiftedCast (SpellEntry const *spellInfo, uint32
if(!(stanceMask & spellInfo->Stances))
return SPELL_FAILED_ONLY_SHAPESHIFT;
}
+
return SPELL_CAST_OK;
}
+
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();
+
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)
{
@@ -1082,26 +1186,32 @@ void SpellMgr::LoadSpellTargetPositions()
sLog.outErrorDb("Spell (Id: %u) listed in `spell_target_position` does not have target TARGET_DST_DB (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;
++count;
+
} while( result->NextRow() );
+
// Check all spells
for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
{
SpellEntry const * spellInfo = sSpellStore.LookupEntry(i);
if(!spellInfo)
continue;
+
bool found = false;
for(int j = 0; j < 3; ++j)
{
@@ -1128,28 +1238,37 @@ void SpellMgr::LoadSpellTargetPositions()
sLog.outDebug("Spell (ID: %u) does not have record in `spell_target_position`", i);
}
}
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u spell teleport coordinates", count );
}
+
bool SpellMgr::IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) const
{
// false for spellInfo == NULL
if (!spellInfo || !mod)
return false;
+
SpellEntry const *affect_spell = sSpellStore.LookupEntry(mod->spellId);
// False if affect_spell == NULL or spellFamily not equal
if (!affect_spell || affect_spell->SpellFamilyName != spellInfo->SpellFamilyName)
return false;
+
// true
if (mod->mask & 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 9 10
QueryResult *result = WorldDatabase.Query("SELECT entry, SchoolMask, SpellFamilyName, SpellFamilyMask0, SpellFamilyMask1, SpellFamilyMask2, procFlags, procEx, ppmRate, CustomChance, Cooldown FROM spell_proc_event");
if( !result )
@@ -1160,20 +1279,26 @@ void SpellMgr::LoadSpellProcEvents()
sLog.outString( ">> Loaded %u spell proc event conditions", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
uint32 customProc = 0;
do
{
Field *fields = result->Fetch();
+
bar.step();
+
uint32 entry = fields[0].GetUInt32();
+
const SpellEntry *spell = sSpellStore.LookupEntry(entry);
if (!spell)
{
sLog.outErrorDb("Spell %u listed in `spell_proc_event` does not exist", entry);
continue;
}
+
SpellProcEventEntry spe;
+
spe.schoolMask = fields[1].GetUInt32();
spe.spellFamilyName = fields[2].GetUInt32();
spe.spellFamilyMask[0] = fields[3].GetUInt32();
@@ -1184,7 +1309,9 @@ void SpellMgr::LoadSpellProcEvents()
spe.ppmRate = fields[8].GetFloat();
spe.customChance = fields[9].GetFloat();
spe.cooldown = fields[10].GetUInt32();
+
mSpellProcEventMap[entry] = spe;
+
if (spell->procFlags==0)
{
if (spe.procFlags == 0)
@@ -1196,13 +1323,16 @@ void SpellMgr::LoadSpellProcEvents()
}
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
if (customProc)
sLog.outString( ">> Loaded %u extra spell proc event conditions +%u custom", count, customProc );
else
sLog.outString( ">> Loaded %u extra spell proc event conditions", count );
}
+
void SpellMgr::LoadSpellBonusess()
{
mSpellBonusMap.clear(); // need for reload case
@@ -1217,45 +1347,62 @@ void SpellMgr::LoadSpellBonusess()
sLog.outString( ">> Loaded %u spell bonus data", count);
return;
}
+
barGoLink bar( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
bar.step();
uint32 entry = fields[0].GetUInt32();
+
const SpellEntry *spell = sSpellStore.LookupEntry(entry);
if (!spell)
{
sLog.outErrorDb("Spell %u listed in `spell_bonus_data` does not exist", entry);
continue;
}
+
SpellBonusEntry sbe;
+
sbe.direct_damage = fields[1].GetFloat();
sbe.dot_damage = fields[2].GetFloat();
sbe.ap_bonus = fields[3].GetFloat();
sbe.ap_dot_bonus = fields[4].GetFloat();
+
mSpellBonusMap[entry] = sbe;
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u extra spell bonus data", count);
}
+
bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active)
{
// No extra req need
uint32 procEvent_procEx = PROC_EX_NONE;
+
// check prockFlags for condition
if((procFlags & EventProcFlag) == 0)
return false;
+
bool hasFamilyMask = false;
+
/* Check Periodic Auras
+
*Dots can trigger if spell has no PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL
nor PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL
+
*Only Hots can trigger if spell has PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL
+
*Only dots can trigger if spell has both positivity flags or PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL
+
*Aura has to have PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL or spellfamily specified to trigger from Hot
+
*/
+
if (procFlags & PROC_FLAG_ON_DO_PERIODIC)
{
if (EventProcFlag & PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL)
@@ -1268,6 +1415,7 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
else if (EventProcFlag & PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL)
return false;
}
+
if (procFlags & PROC_FLAG_ON_TAKE_PERIODIC)
{
if (EventProcFlag & PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL)
@@ -1283,13 +1431,16 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
// Trap casts are active by default
if (procFlags & PROC_FLAG_ON_TRAP_ACTIVATION)
active = true;
+
// Always trigger for this
if (procFlags & (PROC_FLAG_KILLED | PROC_FLAG_KILL | PROC_FLAG_DEATH))
return true;
+
if (spellProcEvent) // Exist event data
{
// Store extra req
procEvent_procEx = spellProcEvent->procEx;
+
// For melee triggers
if (procSpell == NULL)
{
@@ -1302,9 +1453,11 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
// Check (if set) for school
if(spellProcEvent->schoolMask && (spellProcEvent->schoolMask & procSpell->SchoolMask) == 0)
return false;
+
// Check (if set) for spellFamilyName
if(spellProcEvent->spellFamilyName && (spellProcEvent->spellFamilyName != procSpell->SpellFamilyName))
return false;
+
// spellFamilyName is Ok need check for spellFamilyMask if present
if(spellProcEvent->spellFamilyMask)
{
@@ -1317,11 +1470,13 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
}
}
}
+
if (procExtra & (PROC_EX_INTERNAL_REQ_FAMILY))
{
if (!hasFamilyMask)
return false;
}
+
// Check for extra req (if none) and hit/crit
if (procEvent_procEx == PROC_EX_NONE)
{
@@ -1352,80 +1507,114 @@ bool SpellMgr::IsSpellProcEventCanTriggeredBy(SpellProcEventEntry const* spellPr
}
return false;
}
+
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();
+
uint32 entry = fields[0].GetUInt32();
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()
{
mSpellThreatMap.clear(); // need for reload case
+
uint32 count = 0;
+
// 0 1
QueryResult *result = WorldDatabase.Query("SELECT entry, Threat FROM spell_threat");
if( !result )
{
+
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u aggro generating spells", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
Field *fields = result->Fetch();
+
bar.step();
+
uint32 entry = fields[0].GetUInt32();
uint16 Threat = fields[1].GetUInt16();
+
if (!sSpellStore.LookupEntry(entry))
{
sLog.outErrorDb("Spell %u listed in `spell_threat` does not exist", entry);
continue;
}
+
mSpellThreatMap[entry] = Threat;
+
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u aggro generating spells", count );
}
+
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(IsPassiveSpell(spellInfo->Id)) // ranked passive spell
@@ -1434,8 +1623,10 @@ bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo)
return false;
if(IsProfessionOrRidingSpell(spellInfo->Id))
return false;
+
if(spellmgr.IsSkillBonusSpell(spellInfo->Id))
return false;
+
// All stance spells. if any better way, change it.
for (int i = 0; i < 3; ++i)
{
@@ -1462,58 +1653,77 @@ bool SpellMgr::canStackSpellRanks(SpellEntry const *spellInfo)
return true;
}
+
bool SpellMgr::IsProfessionOrRidingSpell(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 IsProfessionOrRidingSkill(skill);
}
+
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;
}
+
bool SpellMgr::IsSkillBonusSpell(uint32 spellId) const
{
SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
+
for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
{
SkillLineAbilityEntry const *pAbility = _spell_idx->second;
if (!pAbility || pAbility->learnOnGetSkill != ABILITY_LEARNED_ON_GET_PROFESSION_SKILL)
continue;
+
if(pAbility->req_skill_value > 0)
return true;
}
+
return false;
}
+
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)
{
@@ -1527,26 +1737,33 @@ SpellEntry const* SpellMgr::SelectAuraRankForPlayerLevel(SpellEntry const* spell
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::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;
barGoLink bar( sSpellStore.GetNumRows() );
@@ -1554,8 +1771,10 @@ void SpellMgr::LoadSpellLearnSkills()
{
bar.step();
SpellEntry const* entry = sSpellStore.LookupEntry(spell);
+
if(!entry)
continue;
+
for(int i = 0; i < 3; ++i)
{
if(entry->Effect[i]==SPELL_EFFECT_SKILL)
@@ -1567,67 +1786,86 @@ void SpellMgr::LoadSpellLearnSkills()
else
dbc_node.value = entry->CalculateSimpleValue(i)*75;
dbc_node.maxvalue = entry->CalculateSimpleValue(i)*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
+
// 0 1 2
QueryResult *result = WorldDatabase.Query("SELECT entry, SpellID, Active 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.active = fields[2].GetBool();
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` learning not existed spell %u",spell_id,node.spell);
continue;
}
+
if(GetTalentSpellCost(node.spell))
{
sLog.outErrorDb("Spell %u listed in `spell_learn_spell` attempt learning talent spell %u, skipped",spell_id,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)
@@ -1635,14 +1873,18 @@ void SpellMgr::LoadSpellLearnSpells()
SpellLearnSpellNode dbc_node;
dbc_node.spell = entry->EffectTriggerSpell[i];
dbc_node.active = true; // all dbc based learned spells is active (show in spell book or hide by client itself)
+
// ignore learning not existed spells (broken/outdated/or generic learnig spell 483
if(!sSpellStore.LookupEntry(dbc_node.spell))
continue;
+
// talent or passive spells or skill-step spells auto-casted and not need dependent learning,
// pet teaching spells don't must be dependent learning (casted)
// other required explicit dependent learning
dbc_node.autoLearned = entry->EffectImplicitTargetA[i] == TARGET_UNIT_PET || GetTalentSpellCost(spell) > 0 || IsPassiveSpell(spell) || IsSpellHaveEffect(entry,SPELL_EFFECT_SKILL_STEP);
+
SpellLearnSpellMapBounds db_node_bounds = GetSpellLearnSpellMapBounds(spell);
+
bool found = false;
for(SpellLearnSpellMap::const_iterator itr = db_node_bounds.first; itr != db_node_bounds.second; ++itr)
{
@@ -1654,6 +1896,7 @@ void SpellMgr::LoadSpellLearnSpells()
break;
}
}
+
if(!found) // add new spell-spell pair if not found
{
mSpellLearnSpells.insert(SpellLearnSpellMap::value_type(spell,dbc_node));
@@ -1662,36 +1905,49 @@ void SpellMgr::LoadSpellLearnSpells()
}
}
}
+
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)
{
@@ -1709,17 +1965,20 @@ void SpellMgr::LoadSpellScriptTarget()
sLog.outErrorDb("Table `spell_script_target`: spellId %u listed for TargetEntry %u does not have any implicit target TARGET_UNIT_NEARBY_ENTRY(38) or TARGET_DST_NEARBY_ENTRY (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<GameObjectInfo>(targetEntry))
{
sLog.outErrorDb("Table `spell_script_target`: gameobject template entry %u does not exist.",targetEntry);
@@ -1744,6 +2003,7 @@ void SpellMgr::LoadSpellScriptTarget()
continue;
}
const CreatureInfo* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(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);
@@ -1752,16 +2012,21 @@ void SpellMgr::LoadSpellScriptTarget()
break;
}
}
+
mSpellScriptTarget.insert(SpellScriptTarget::value_type(spellId,SpellTargetEntry(SpellScriptTargetType(type),targetEntry)));
+
++count;
} while (result->NextRow());
+
delete result;
+
// Check all spells
for(uint32 i = 1; i < sSpellStore.GetNumRows(); ++i)
{
SpellEntry const * spellInfo = sSpellStore.LookupEntry(i);
if(!spellInfo)
continue;
+
bool found = false;
for(int j = 0; j < 3; ++j)
{
@@ -1797,32 +2062,44 @@ void SpellMgr::LoadSpellScriptTarget()
sLog.outDebug("Spell (ID: %u) does not have record in `spell_script_target`", i);
}
}
+
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 3
QueryResult *result = WorldDatabase.Query("SELECT spell, effectId, 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();
+
uint32 spell = fields[0].GetUInt32();
uint8 eff = fields[1].GetUInt8();
uint32 pet = fields[2].GetUInt32();
uint32 aura = fields[3].GetUInt32();
+
SpellPetAuraMap::iterator itr = mSpellPetAuraMap.find((spell<<8) + eff);
if(itr != mSpellPetAuraMap.end())
{
@@ -1843,63 +2120,82 @@ void SpellMgr::LoadSpellPetAuras()
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[eff] == TARGET_UNIT_PET, spellInfo->CalculateSimpleValue(eff));
mSpellPetAuraMap[(spell<<8) + eff] = pa;
}
+
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u spell pet auras", count );
}
+
void SpellMgr::LoadPetLevelupSpellMap()
{
mPetLevelupSpellMap.clear(); // need for reload case
+
uint32 count = 0;
uint32 family_count = 0;
+
for (uint32 i = 0; i < sCreatureFamilyStore.GetNumRows(); ++i)
{
CreatureFamilyEntry const *creatureFamily = sCreatureFamilyStore.LookupEntry(i);
if(!creatureFamily) // not exist
continue;
+
for (uint8 j = 0; j < 2; ++j)
{
if (!creatureFamily->skillLine[j])
continue;
+
for (uint32 k = 0; k < sSkillLineAbilityStore.GetNumRows(); ++k)
{
SkillLineAbilityEntry const *skillLine = sSkillLineAbilityStore.LookupEntry(k);
if( !skillLine )
continue;
+
//if (skillLine->skillId!=creatureFamily->skillLine[0] &&
// (!creatureFamily->skillLine[1] || skillLine->skillId!=creatureFamily->skillLine[1]))
// continue;
+
if (skillLine->skillId!=creatureFamily->skillLine[j])
continue;
+
if(skillLine->learnOnGetSkill != ABILITY_LEARNED_ON_GET_RACE_OR_CLASS_SKILL)
continue;
+
SpellEntry const *spell = sSpellStore.LookupEntry(skillLine->spellId);
if(!spell) // not exist or triggered or talent
continue;
+
if(!spell->spellLevel)
continue;
+
PetLevelupSpellSet& spellSet = mPetLevelupSpellMap[creatureFamily->ID];
if(spellSet.empty())
++family_count;
- spellSet.insert(PetLevelupSpellSet::value_type(spell->spellLevel,spell->Id));
+
+ spellSet.insert(PetLevelupSpellSet::value_type(spell->spellLevel,spell->Id));
count++;
}
}
}
+
sLog.outString();
sLog.outString( ">> Loaded %u pet levelup and default spells for %u families", count, family_count );
}
+
bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntry& petDefSpells)
{
// skip empty list;
@@ -1914,6 +2210,7 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr
}
if(!have_spell)
return false;
+
// remove duplicates with levelupSpells if any
if(PetLevelupSpellSet const *levelupSpells = cInfo->family ? spellmgr.GetPetLevelupSpellList(cInfo->family) : NULL)
{
@@ -1921,6 +2218,7 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr
{
if(!petDefSpells.spellid[j])
continue;
+
for(PetLevelupSpellSet::const_iterator itr = levelupSpells->begin(); itr != levelupSpells->end(); ++itr)
{
if (itr->second == petDefSpells.spellid[j])
@@ -1931,6 +2229,7 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr
}
}
}
+
// skip empty list;
have_spell = false;
for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
@@ -1941,40 +2240,50 @@ bool LoadPetDefaultSpells_helper(CreatureInfo const* cInfo, PetDefaultSpellsEntr
break;
}
}
+
return have_spell;
}
+
void SpellMgr::LoadPetDefaultSpells()
{
mPetDefaultSpellsMap.clear();
+
uint32 countCreature = 0;
uint32 countData = 0;
+
for(uint32 i = 0; i < sCreatureStorage.MaxEntry; ++i )
{
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(i);
if(!cInfo)
continue;
+
if(!cInfo->PetSpellDataId)
continue;
+
// for creature with PetSpellDataId get default pet spells from dbc
CreatureSpellDataEntry const* spellDataEntry = sCreatureSpellDataStore.LookupEntry(cInfo->PetSpellDataId);
if(!spellDataEntry)
continue;
+
int32 petSpellsId = -(int32)cInfo->PetSpellDataId;
PetDefaultSpellsEntry petDefSpells;
for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
petDefSpells.spellid[j] = spellDataEntry->spellId[j];
+
if(LoadPetDefaultSpells_helper(cInfo, petDefSpells))
{
mPetDefaultSpellsMap[petSpellsId] = petDefSpells;
++countData;
}
}
+
// different summon spells
for(uint32 i = 0; i < sSpellStore.GetNumRows(); ++i )
{
SpellEntry const* spellEntry = sSpellStore.LookupEntry(i);
if(!spellEntry)
continue;
+
for(int k = 0; k < 3; ++k)
{
if(spellEntry->Effect[k]==SPELL_EFFECT_SUMMON || spellEntry->Effect[k]==SPELL_EFFECT_SUMMON_PET)
@@ -1983,16 +2292,20 @@ void SpellMgr::LoadPetDefaultSpells()
CreatureInfo const* cInfo = sCreatureStorage.LookupEntry<CreatureInfo>(creature_id);
if(!cInfo)
continue;
+
// already loaded
if(cInfo->PetSpellDataId)
continue;
+
// for creature without PetSpellDataId get default pet spells from creature_template
int32 petSpellsId = cInfo->Entry;
if(mPetDefaultSpellsMap.find(cInfo->Entry) != mPetDefaultSpellsMap.end())
continue;
+
PetDefaultSpellsEntry petDefSpells;
for(int j = 0; j < MAX_CREATURE_SPELL_DATA_SLOT; ++j)
petDefSpells.spellid[j] = cInfo->spells[j];
+
if(LoadPetDefaultSpells_helper(cInfo, petDefSpells))
{
mPetDefaultSpellsMap[petSpellsId] = petDefSpells;
@@ -2001,16 +2314,20 @@ void SpellMgr::LoadPetDefaultSpells()
}
}
}
+
sLog.outString();
sLog.outString( ">> Loaded addition spells for %u pet spell data entries and %u summonable creature templates", countData, countCreature );
}
+
/// Some checks for spells, to prevent adding deprecated/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)
{
@@ -2018,6 +2335,7 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
{
case 0:
continue;
+
// craft spell for crafting non-existed item (break client recipes list show)
case SPELL_EFFECT_CREATE_ITEM:
case SPELL_EFFECT_CREATE_ITEM_2:
@@ -2033,6 +2351,7 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
}
return false;
}
+
need_check_reagents = true;
break;
}
@@ -2054,6 +2373,7 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
}
}
}
+
if(need_check_reagents)
{
for(int j = 0; j < 8; ++j)
@@ -2071,8 +2391,10 @@ bool SpellMgr::IsSpellValid(SpellEntry const* spellInfo, Player* pl, bool msg)
}
}
}
+
return true;
}
+
void SpellMgr::LoadSpellAreas()
{
mSpellAreaMap.clear(); // need for reload case
@@ -2080,22 +2402,31 @@ void SpellMgr::LoadSpellAreas()
mSpellAreaForActiveQuestMap.clear();
mSpellAreaForQuestEndMap.clear();
mSpellAreaForAuraMap.clear();
+
uint32 count = 0;
+
// 0 1 2 3 4 5 6 7 8
QueryResult *result = WorldDatabase.Query("SELECT spell, area, quest_start, quest_start_active, quest_end, aura_spell, racemask, gender, autocast FROM spell_area");
+
if( !result )
{
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u spell area requirements", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
Field *fields = result->Fetch();
+
bar.step();
+
uint32 spell = fields[0].GetUInt32();
SpellArea spellArea;
spellArea.spellId = spell;
@@ -2107,6 +2438,7 @@ void SpellMgr::LoadSpellAreas()
spellArea.raceMask = fields[6].GetUInt32();
spellArea.gender = Gender(fields[7].GetUInt8());
spellArea.autocast = fields[8].GetBool();
+
if(const SpellEntry* spellInfo = sSpellStore.LookupEntry(spell))
{
if(spellArea.autocast)
@@ -2117,6 +2449,7 @@ void SpellMgr::LoadSpellAreas()
sLog.outErrorDb("Spell %u listed in `spell_area` does not exist", spell);
continue;
}
+
{
bool ok = true;
SpellAreaMapBounds sa_bounds = GetSpellAreaMapBounds(spellArea.spellId);
@@ -2134,26 +2467,32 @@ void SpellMgr::LoadSpellAreas()
continue;
if (spellArea.gender != itr->second.gender)
continue;
+
// duplicate by requirements
ok =false;
break;
}
+
if(!ok)
{
sLog.outErrorDb("Spell %u listed in `spell_area` already listed with similar requirements.", spell);
continue;
}
+
}
+
if(spellArea.areaId && !GetAreaEntryByAreaID(spellArea.areaId))
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong area (%u) requirement", spell,spellArea.areaId);
continue;
}
+
if(spellArea.questStart && !objmgr.GetQuestTemplate(spellArea.questStart))
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong start quest (%u) requirement", spell,spellArea.questStart);
continue;
}
+
if(spellArea.questEnd)
{
if(!objmgr.GetQuestTemplate(spellArea.questEnd))
@@ -2161,12 +2500,14 @@ void SpellMgr::LoadSpellAreas()
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong end quest (%u) requirement", spell,spellArea.questEnd);
continue;
}
+
if(spellArea.questEnd==spellArea.questStart && !spellArea.questStartCanActive)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have quest (%u) requirement for start and end in same time", spell,spellArea.questEnd);
continue;
}
}
+
if(spellArea.auraSpell)
{
SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(spellArea.auraSpell));
@@ -2175,6 +2516,7 @@ void SpellMgr::LoadSpellAreas()
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong aura spell (%u) requirement", spell,abs(spellArea.auraSpell));
continue;
}
+
switch(spellInfo->EffectApplyAuraName[0])
{
case SPELL_AURA_DUMMY:
@@ -2185,11 +2527,13 @@ void SpellMgr::LoadSpellAreas()
sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell requirement (%u) without dummy/phase/ghost aura in effect 0", spell,abs(spellArea.auraSpell));
continue;
}
+
if(abs(spellArea.auraSpell)==spellArea.spellId)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement for itself", spell,abs(spellArea.auraSpell));
continue;
}
+
// not allow autocast chains by auraSpell field (but allow use as alternative if not present)
if(spellArea.autocast && spellArea.auraSpell > 0)
{
@@ -2203,11 +2547,13 @@ void SpellMgr::LoadSpellAreas()
break;
}
}
+
if(chain)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell);
continue;
}
+
SpellAreaMapBounds saBound2 = GetSpellAreaMapBounds(spellArea.auraSpell);
for(SpellAreaMap::const_iterator itr2 = saBound2.first; itr2 != saBound2.second; ++itr2)
{
@@ -2217,6 +2563,7 @@ void SpellMgr::LoadSpellAreas()
break;
}
}
+
if(chain)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have aura spell (%u) requirement that itself autocast from aura", spell,spellArea.auraSpell);
@@ -2224,20 +2571,25 @@ void SpellMgr::LoadSpellAreas()
}
}
}
+
if(spellArea.raceMask && (spellArea.raceMask & RACEMASK_ALL_PLAYABLE)==0)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong race mask (%u) requirement", spell,spellArea.raceMask);
continue;
}
+
if(spellArea.gender!=GENDER_NONE && spellArea.gender!=GENDER_FEMALE && spellArea.gender!=GENDER_MALE)
{
sLog.outErrorDb("Spell %u listed in `spell_area` have wrong gender (%u) requirement", spell,spellArea.gender);
continue;
}
+
SpellArea const* sa = &mSpellAreaMap.insert(SpellAreaMap::value_type(spell,spellArea))->second;
+
// for search by current zone/subzone at zone/subzone change
if(spellArea.areaId)
mSpellAreaForAreaMap.insert(SpellAreaForAreaMap::value_type(spellArea.areaId,sa));
+
// for search at quest start/reward
if(spellArea.questStart)
{
@@ -2246,18 +2598,24 @@ void SpellMgr::LoadSpellAreas()
else
mSpellAreaForQuestMap.insert(SpellAreaForQuestMap::value_type(spellArea.questStart,sa));
}
+
// for search at quest start/reward
if(spellArea.questEnd)
mSpellAreaForQuestEndMap.insert(SpellAreaForQuestMap::value_type(spellArea.questEnd,sa));
+
// for search at aura apply
if(spellArea.auraSpell)
mSpellAreaForAuraMap.insert(SpellAreaForAuraMap::value_type(abs(spellArea.auraSpell),sa));
+
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u spell area requirements", count );
}
+
SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player)
{
// normal case
@@ -2275,9 +2633,11 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
// Try search in next group
groupEntry = sAreaGroupStore.LookupEntry(groupEntry->nextGroup);
}
+
if (!found)
return SPELL_FAILED_INCORRECT_AREA;
}
+
// continent limitation (virtual continent)
if (spellInfo->AttributesEx4 & SPELL_ATTR_EX4_CAST_ONLY_IN_OUTLAND)
{
@@ -2286,6 +2646,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
if (!mapEntry || mapEntry->addon < 1 || !mapEntry->IsContinent())
return SPELL_FAILED_INCORRECT_AREA;
}
+
// raid instance limitation
if (spellInfo->AttributesEx6 & SPELL_ATTR_EX6_NOT_IN_RAID_INSTANCE)
{
@@ -2293,6 +2654,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
if (!mapEntry || mapEntry->IsRaid())
return SPELL_FAILED_NOT_IN_RAID_INSTANCE;
}
+
// DB base check (if non empty then must fit at least single for allow)
SpellAreaMapBounds saBounds = spellmgr.GetSpellAreaMapBounds(spellInfo->Id);
if (saBounds.first != saBounds.second)
@@ -2304,6 +2666,7 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
}
return SPELL_FAILED_INCORRECT_AREA;
}
+
// bg spell checks
switch(spellInfo->Id)
{
@@ -2323,17 +2686,21 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
if (!mapEntry)
return SPELL_FAILED_INCORRECT_AREA;
+
return zone_id == 4197 || mapEntry->IsBattleGround() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
}
case 44521: // Preparation
{
if (!player)
return SPELL_FAILED_REQUIRES_AREA;
+
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
if (!mapEntry)
return SPELL_FAILED_INCORRECT_AREA;
+
if (!mapEntry->IsBattleGround())
return SPELL_FAILED_REQUIRES_AREA;
+
BattleGround* bg = player->GetBattleGround();
return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
}
@@ -2345,21 +2712,26 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
if (!mapEntry)
return SPELL_FAILED_INCORRECT_AREA;
+
return mapEntry->IsBattleArena() && player && player->InBattleGround() ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
}
case 32727: // Arena Preparation
{
if (!player)
return SPELL_FAILED_REQUIRES_AREA;
+
MapEntry const* mapEntry = sMapStore.LookupEntry(map_id);
if(!mapEntry)
return SPELL_FAILED_INCORRECT_AREA;
+
if(!mapEntry->IsBattleArena())
return SPELL_FAILED_REQUIRES_AREA;
+
BattleGround* bg = player->GetBattleGround();
return bg && bg->GetStatus()==STATUS_WAIT_JOIN ? SPELL_CAST_OK : SPELL_FAILED_REQUIRES_AREA;
}
}
+
// aura limitations
for (uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
@@ -2373,25 +2745,32 @@ SpellCastResult SpellMgr::GetSpellAllowedInLocationError(SpellEntry const *spell
}
}
}
+
return SPELL_CAST_OK;
}
+
void SpellMgr::LoadSkillLineAbilityMap()
{
mSkillLineAbilityMap.clear();
+
barGoLink bar( sSkillLineAbilityStore.GetNumRows() );
uint32 count = 0;
+
for (uint32 i = 0; i < sSkillLineAbilityStore.GetNumRows(); ++i)
{
bar.step();
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 Data", count);
}
+
DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered)
{
// Explicit Diminishing Groups
@@ -2494,6 +2873,7 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto
default:
break;
}
+
// Get by mechanic
uint32 mechanic = GetAllSpellMechanicMask(spellproto);
if (mechanic == MECHANIC_NONE) return DIMINISHING_NONE;
@@ -2513,6 +2893,7 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto
(1<<MECHANIC_SAPPED))) return DIMINISHING_KNOCKOUT;
if (mechanic & (1<<MECHANIC_BANISH)) return DIMINISHING_BANISH;
if (mechanic & (1<<MECHANIC_HORROR)) return DIMINISHING_DEATHCOIL;
+
// Get by effect
for (uint8 i = 0 ; i < MAX_SPELL_EFFECTS; ++i)
{
@@ -2521,10 +2902,12 @@ DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto
}
return DIMINISHING_NONE;
}
+
int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto)
{
if(!IsDiminishingReturnsGroupDurationLimited(group))
return 0;
+
// Explicit diminishing duration
switch(spellproto->SpellFamilyName)
{
@@ -2559,8 +2942,10 @@ int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry cons
default:
break;
}
+
return 10 * IN_MILISECONDS;
}
+
bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
{
switch(group)
@@ -2584,6 +2969,7 @@ bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group)
}
return false;
}
+
DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
{
switch(group)
@@ -2609,8 +2995,10 @@ DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group)
default:
break;
}
+
return DRTYPE_NONE;
}
+
bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const
{
if(gender!=GENDER_NONE)
@@ -2619,30 +3007,35 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32
if(!player || gender != player->getGender())
return false;
}
+
if(raceMask)
{
// not in expected race
if(!player || !(raceMask & player->getRaceMask()))
return false;
}
+
if(areaId)
{
// not in expected zone
if(newZone!=areaId && newArea!=areaId)
return false;
}
+
if(questStart)
{
// not in expected required quest state
if(!player || (!questStartCanActive || !player->IsActiveQuest(questStart)) && !player->GetQuestRewardStatus(questStart))
return false;
}
+
if(questEnd)
{
// not in expected forbidden quest state
if(!player || player->GetQuestRewardStatus(questEnd))
return false;
}
+
if(auraSpell)
{
// not have expected aura
@@ -2655,9 +3048,12 @@ bool SpellArea::IsFitToRequirements(Player const* player, uint32 newZone, uint32
// not have expected aura
return !player->HasAura(-auraSpell);
}
+
return true;
}
+
//-----------TRINITY-------------
+
bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *spellInfo_2, bool sameCaster) const
{
SpellSpecific spellSpec_1 = GetSpellSpecific(spellInfo_1->Id);
@@ -2666,13 +3062,16 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp
if (IsSingleFromSpellSpecificPerTarget(spellSpec_1, spellSpec_2)
|| sameCaster && IsSingleFromSpellSpecificPerCaster(spellSpec_1, spellSpec_2))
return false;
+
if(spellInfo_1->SpellFamilyName != spellInfo_2->SpellFamilyName)
return true;
+
if(!sameCaster)
{
if(spellInfo_1->AttributesEx & SPELL_ATTR_EX_STACK_FOR_DIFF_CASTERS
|| spellInfo_1->AttributesEx3 & SPELL_ATTR_EX3_STACK_FOR_DIFF_CASTERS)
return true;
+
// check same periodic auras
for(uint32 i = 0; i < 3; ++i)
{
@@ -2680,10 +3079,12 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp
if(spellInfo_1->Effect[i] != SPELL_EFFECT_APPLY_AURA
&& spellInfo_1->Effect[i] != SPELL_EFFECT_PERSISTENT_AREA_AURA)
continue;
+
// not channeled AOE effects should not stack (blizzard should, but Consecration should not)
if((IsAreaEffectTarget[spellInfo_1->EffectImplicitTargetA[i]] || IsAreaEffectTarget[spellInfo_1->EffectImplicitTargetB[i]])
&& !IsChanneledSpell(spellInfo_1))
continue;
+
switch(spellInfo_1->EffectApplyAuraName[i])
{
// DOT or HOT from different casters will stack
@@ -2703,8 +3104,10 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp
}
}
}
+
uint32 spellId_1 = GetLastSpellInChain(spellInfo_1->Id);
uint32 spellId_2 = GetLastSpellInChain(spellInfo_2->Id);
+
// same spell
if (spellId_1 == spellId_2)
{
@@ -2714,6 +3117,7 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp
// same spell with same caster should not stack
return false;
}
+
// use icon to check generic spells
if(!spellInfo_1->SpellFamilyName)
{
@@ -2728,95 +3132,127 @@ bool SpellMgr::CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *sp
|| spellInfo_1->SpellFamilyFlags != spellInfo_2->SpellFamilyFlags)
return true;
}
+
//use data of highest rank spell(needed for spells which ranks have different effects)
spellInfo_1 = sSpellStore.LookupEntry(spellId_1);
spellInfo_2 = sSpellStore.LookupEntry(spellId_2);
+
//if spells do not have the same effect or aura or miscvalue, they will stack
for(uint32 i = 0; i < 3; ++i)
if(spellInfo_1->Effect[i] != spellInfo_2->Effect[i]
|| spellInfo_1->EffectApplyAuraName[i] != spellInfo_2->EffectApplyAuraName[i]
|| spellInfo_1->EffectMiscValue[i] != spellInfo_2->EffectMiscValue[i]) // paladin resist aura
return true; // need itemtype check? need an example to add that check
+
// different spells with same effect
return false;
}
+
bool IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def)
{
if (!dispelSpell) return false;
SpellEntry const *spellproto = sSpellStore.LookupEntry(spellId);
if (!spellproto) return false;
+
if (spellproto->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)
return false;
+
if(dispelSpell->Attributes & SPELL_ATTR_UNAFFECTED_BY_INVULNERABILITY)
return true;
+
return def;
}
+
void SpellMgr::LoadSpellEnchantProcData()
{
mSpellEnchantProcEventMap.clear(); // need for reload case
+
uint32 count = 0;
+
// 0 1 2 3
QueryResult *result = WorldDatabase.Query("SELECT entry, customChance, PPMChance, procEx FROM spell_enchant_proc_data");
if( !result )
{
+
barGoLink bar( 1 );
+
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded %u spell enchant proc event conditions", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
do
{
Field *fields = result->Fetch();
+
bar.step();
+
uint32 enchantId = fields[0].GetUInt32();
+
SpellItemEnchantmentEntry const *ench = sSpellItemEnchantmentStore.LookupEntry(enchantId);
if (!ench)
{
sLog.outErrorDb("Enchancment %u listed in `spell_enchant_proc_data` does not exist", enchantId);
continue;
}
+
SpellEnchantProcEntry spe;
+
spe.customChance = fields[1].GetUInt32();
spe.PPMChance = fields[2].GetFloat();
spe.procEx = fields[3].GetUInt32();
+
mSpellEnchantProcEventMap[enchantId] = spe;
+
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString( ">> Loaded %u enchant proc data definitions", count);
}
+
void SpellMgr::LoadSpellRequired()
{
mSpellsReqSpell.clear(); // need for reload case
mSpellReq.clear(); // need for reload case
+
QueryResult *result = WorldDatabase.Query("SELECT spell_id, req_spell from spell_required");
+
if(result == NULL)
{
barGoLink bar( 1 );
bar.step();
+
sLog.outString();
sLog.outString( ">> Loaded 0 spell required records" );
sLog.outErrorDb("`spell_required` table is empty!");
return;
}
uint32 rows = 0;
+
barGoLink bar( result->GetRowCount() );
do
{
bar.step();
Field *fields = result->Fetch();
+
uint32 spell_id = fields[0].GetUInt32();
uint32 spell_req = fields[1].GetUInt32();
+
mSpellsReqSpell.insert (std::pair<uint32, uint32>(spell_req, spell_id));
mSpellReq[spell_id] = spell_req;
++rows;
} while( result->NextRow() );
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u spell required records", rows );
}
+
struct SpellRankEntry
{
uint32 SkillId;
@@ -2832,15 +3268,18 @@ struct SpellRankEntry
flag96 Effect;
flag96 Aura;
uint16 TalentID;
+
bool operator < (const SpellRankEntry & _Right) const
{
return (SkillId != _Right.SkillId ? SkillId < _Right.SkillId
: SpellName!=_Right.SpellName ? SpellName < _Right.SpellName
: ProcFlags!=_Right.ProcFlags ? ProcFlags < _Right.ProcFlags
+
: Effect!=_Right.Effect ? Effect < _Right.Effect
: Aura!=_Right.Aura ? Aura < _Right.Aura
: TalentID!=_Right.TalentID ? TalentID < _Right.TalentID
: (CastingTimeIndex!=_Right.CastingTimeIndex) && (!CastingTimeIndex || !_Right.CastingTimeIndex || CastingTimeIndex==1 || !_Right.CastingTimeIndex==1) ? CastingTimeIndex < _Right.CastingTimeIndex
+
: SpellFamilyFlags!=_Right.SpellFamilyFlags ? SpellFamilyFlags < _Right.SpellFamilyFlags
: (SpellVisual!=_Right.SpellVisual) && (!SpellVisual || !_Right.SpellVisual) ? SpellVisual < _Right.SpellVisual
: (ManaCost!=_Right.ManaCost) && (!ManaCost || !_Right.ManaCost) ? ManaCost < _Right.ManaCost
@@ -2850,15 +3289,18 @@ struct SpellRankEntry
);
}
};
+
struct SpellRankValue
{
uint32 Id;
char const *Rank;
bool strict;
};
+
void SpellMgr::LoadSpellChains()
{
mSpellChains.clear(); // need for reload case
+
std::vector<uint32> ChainedSpells;
for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++)
{
@@ -2869,12 +3311,15 @@ void SpellMgr::LoadSpellChains()
continue;
ChainedSpells.push_back(AbilityInfo->forward_spellid);
}
+
std::multimap<SpellRankEntry, SpellRankValue> RankMap;
+
for (uint32 ability_id=0;ability_id<sSkillLineAbilityStore.GetNumRows();ability_id++)
{
SkillLineAbilityEntry const *AbilityInfo=sSkillLineAbilityStore.LookupEntry(ability_id);
if (!AbilityInfo)
continue;
+
//get only spell with lowest ability_id to prevent doubles
uint32 spell_id=AbilityInfo->spellId;
bool found=false;
@@ -2885,6 +3330,7 @@ void SpellMgr::LoadSpellChains()
}
if (found)
continue;
+
if(mSkillLineAbilityMap.lower_bound(spell_id)->second->id!=ability_id)
continue;
SpellEntry const *SpellInfo=sSpellStore.LookupEntry(spell_id);
@@ -2896,6 +3342,7 @@ void SpellMgr::LoadSpellChains()
//exception to polymorph spells-make pig and turtle other chain than sheep
if ((SpellInfo->SpellFamilyName==SPELLFAMILY_MAGE) && (SpellInfo->SpellFamilyFlags[0] & 0x1000000) && (SpellInfo->SpellIconID!=82))
continue;
+
SpellRankEntry entry;
SpellRankValue value;
entry.SkillId=AbilityInfo->skillId;
@@ -2922,8 +3369,11 @@ void SpellMgr::LoadSpellChains()
break;
}
}
+
barGoLink bar(RankMap.size());
+
uint32 count=0;
+
for (std::multimap<SpellRankEntry, SpellRankValue>::iterator itr = RankMap.begin();itr!=RankMap.end();)
{
SpellRankEntry entry=itr->first;
@@ -2934,6 +3384,7 @@ void SpellMgr::LoadSpellChains()
bar.step();
RankErrorMap.insert(std::pair<char const *, std::multimap<SpellRankEntry, SpellRankValue>::iterator>(itr2->second.Rank,itr2));
}
+
bool error=false;
//if strict == true strict check is not needed
if (!itr->second.strict)
@@ -2980,14 +3431,17 @@ void SpellMgr::LoadSpellChains()
currEntry.TargetAuraState=SpellInfo->TargetAuraState;
currEntry.SpellVisual=SpellInfo->SpellVisual[0];
currEntry.ManaCost=SpellInfo->manaCost;
+
//compare effects and casting time
currEntry.CastingTimeIndex=SpellInfo->CastingTimeIndex;
currEntry.Effect[0]=SpellInfo->Effect[0];
currEntry.Effect[1]=SpellInfo->Effect[1];
currEntry.Effect[2]=SpellInfo->Effect[2];
+
currEntry.Aura[0]=SpellInfo->EffectApplyAuraName[0];
currEntry.Aura[1]=SpellInfo->EffectApplyAuraName[1];
currEntry.Aura[2]=SpellInfo->EffectApplyAuraName[2];
+
SpellRankValue currValue;
currValue.Id=ConflictedSpells.front();
currValue.Rank=SpellInfo->Rank[sWorld.GetDefaultDbcLocale()];
@@ -3015,6 +3469,7 @@ void SpellMgr::LoadSpellChains()
else
itr2++;
}
+
//order spells by spellLevel
std::list<uint32> RankedSpells;
uint32 min_spell_lvl=0;
@@ -3033,6 +3488,7 @@ void SpellMgr::LoadSpellChains()
RankedSpells.push_back(min_itr->second.Id);
RankMap.erase(min_itr);
}
+
//use data from talent.dbc
uint16 talent_id=0;
for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();)
@@ -3055,11 +3511,14 @@ void SpellMgr::LoadSpellChains()
RankedSpells.push_front(TalentInfo->RankID[rank-1]);
}
}
+
//do not proceed for spells with less than 2 ranks
itr=RankMap.begin();
if (RankedSpells.size()<2)
continue;
+
count++;
+
uint32 spell_rank=1;
for(std::list<uint32>::iterator itr2 = RankedSpells.begin();itr2!=RankedSpells.end();spell_rank++)
{
@@ -3067,9 +3526,11 @@ void SpellMgr::LoadSpellChains()
mSpellChains[spell_id].rank=spell_rank;
mSpellChains[spell_id].first=RankedSpells.front();
mSpellChains[spell_id].last=RankedSpells.back();
+
itr2++;
if (spell_rank<2)
mSpellChains[spell_id].prev=0;
+
if (spell_id==RankedSpells.back())
mSpellChains[spell_id].next=0;
else
@@ -3079,16 +3540,20 @@ void SpellMgr::LoadSpellChains()
}
}
}
+
//uncomment these two lines to print yourself list of spell_chains on startup
//for (UNORDERED_MAP<uint32, SpellChainNode>::iterator itr=mSpellChains.begin();itr!=mSpellChains.end();itr++)
//sLog.outString( "Id: %u, Rank: %d , %s, %u, %u, %u, %u",itr->first,itr->second.rank, sSpellStore.LookupEntry(itr->first)->Rank[sWorld.GetDefaultDbcLocale()], itr->second.first, itr->second.last,itr->second.next ,itr->second.prev);
+
sLog.outString();
sLog.outString( ">> Loaded %u spell chains",count);
}
+
// set data in core for now
void SpellMgr::LoadSpellCustomAttr()
{
mSpellCustomAttr.resize(GetSpellStore()->GetNumRows());
+
SpellEntry *spellInfo;
for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
{
@@ -3096,6 +3561,7 @@ void SpellMgr::LoadSpellCustomAttr()
spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
if(!spellInfo)
continue;
+
for(uint32 j = 0; j < 3; ++j)
{
switch(spellInfo->EffectApplyAuraName[j])
@@ -3110,6 +3576,7 @@ void SpellMgr::LoadSpellCustomAttr()
default:
break;
}
+
switch(spellInfo->Effect[j])
{
case SPELL_EFFECT_SCHOOL_DAMAGE:
@@ -3134,6 +3601,7 @@ void SpellMgr::LoadSpellCustomAttr()
spellInfo->Effect[j] = SPELL_EFFECT_TRIGGER_MISSILE;
break;
}
+
switch(SpellTargetType[spellInfo->EffectImplicitTargetA[j]])
{
case TARGET_TYPE_UNIT_TARGET:
@@ -3146,6 +3614,7 @@ void SpellMgr::LoadSpellCustomAttr()
// break;
}
}
+
for(uint32 j = 0; j < 3; ++j)
{
switch(spellInfo->EffectApplyAuraName[j])
@@ -3161,16 +3630,20 @@ void SpellMgr::LoadSpellCustomAttr()
break;
}
}
+
if(!_isPositiveEffect(i, 0, false))
mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF0;
if(!_isPositiveEffect(i, 1, false))
mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF1;
if(!_isPositiveEffect(i, 2, false))
mSpellCustomAttr[i] |= SPELL_ATTR_CU_NEGATIVE_EFF2;
+
if(spellInfo->SpellVisual[0] == 3879)
mSpellCustomAttr[i] |= SPELL_ATTR_CU_CONE_BACK;
+
if(spellInfo->activeIconID == 2158) //flight
spellInfo->Attributes |= SPELL_ATTR_PASSIVE;
+
switch(i)
{
// Heart of the Crusader
@@ -3322,6 +3795,7 @@ void SpellMgr::LoadSpellCustomAttr()
default:
break;
}
+
switch(spellInfo->SpellFamilyName)
{
case SPELLFAMILY_WARRIOR:
@@ -3348,27 +3822,34 @@ void SpellMgr::LoadSpellCustomAttr()
break;
}
}
+
SummonPropertiesEntry *properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(121));
properties->Type = SUMMON_TYPE_TOTEM;
properties = const_cast<SummonPropertiesEntry*>(sSummonPropertiesStore.LookupEntry(647)); // 52893
properties->Type = SUMMON_TYPE_TOTEM;
+
CreatureAI::FillAISpellInfo();
}
+
// Fill custom data about enchancments
void SpellMgr::LoadEnchantCustomAttr()
{
uint32 size = sSpellItemEnchantmentStore.GetNumRows();
mEnchantCustomAttr.resize(size);
+
for (uint32 i = 0;i<size; ++i)
mEnchantCustomAttr[i] = 0;
+
for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i)
{
SpellEntry * spellInfo = (SpellEntry*)GetSpellStore()->LookupEntry(i);
if(!spellInfo)
continue;
+
// TODO: find a better check
if (!(spellInfo->AttributesEx2 & SPELL_ATTR_EX2_UNK13) || !(spellInfo->Attributes & SPELL_ATTR_NOT_SHAPESHIFT))
continue;
+
for(uint32 j = 0; j < 3; ++j)
{
if(spellInfo->Effect[j] == SPELL_EFFECT_ENCHANT_ITEM_TEMPORARY)
@@ -3383,18 +3864,23 @@ void SpellMgr::LoadEnchantCustomAttr()
}
}
}
+
bool SpellMgr::IsSkillTypeSpell(uint32 spellId, SkillType type) const
{
SkillLineAbilityMapBounds bounds = GetSkillLineAbilityMapBounds(spellId);
+
for(SkillLineAbilityMap::const_iterator _spell_idx = bounds.first; _spell_idx != bounds.second; ++_spell_idx)
if (_spell_idx->second->skillId == type)
return true;
+
return false;
}
+
void SpellMgr::LoadSpellLinked()
{
mSpellLinkedMap.clear(); // need for reload case
uint32 count = 0;
+
// 0 1 2
QueryResult *result = WorldDatabase.Query("SELECT spell_trigger, spell_effect, type FROM spell_linked_spell");
if( !result )
@@ -3405,14 +3891,19 @@ void SpellMgr::LoadSpellLinked()
sLog.outString( ">> Loaded %u linked spells", count );
return;
}
+
barGoLink bar( result->GetRowCount() );
+
do
{
Field *fields = result->Fetch();
+
bar.step();
+
int32 trigger = fields[0].GetInt32();
int32 effect = fields[1].GetInt32();
int32 type = fields[2].GetInt32();
+
SpellEntry const* spellInfo = sSpellStore.LookupEntry(abs(trigger));
if (!spellInfo)
{
@@ -3425,6 +3916,7 @@ void SpellMgr::LoadSpellLinked()
sLog.outErrorDb("Spell %u listed in `spell_linked_spell` does not exist", abs(effect));
continue;
}
+
if(trigger > 0)
{
switch(type)
@@ -3438,6 +3930,7 @@ void SpellMgr::LoadSpellLinked()
{
mSpellCustomAttr[-trigger] |= SPELL_ATTR_CU_LINK_REMOVE;
}
+
if(type) //we will find a better way when more types are needed
{
if(trigger > 0)
@@ -3446,16 +3939,21 @@ void SpellMgr::LoadSpellLinked()
trigger -= SPELL_LINKED_MAX_SPELLS * type;
}
mSpellLinkedMap[trigger].push_back(effect);
+
++count;
} while( result->NextRow() );
+
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u linked spells", count );
}
+
bool SpellMgr::CheckDB() const
{
SpellScriptTargetBounds bounds = spellmgr.GetSpellScriptTargetBounds(30531);
if(bounds.first == bounds.second || bounds.first->second.targetEntry != 17256)
return false;
+
return true;
}
diff --git a/src/game/SpellMgr.h b/src/game/SpellMgr.h
index 767069df840..9c7f4aca16a 100644
--- a/src/game/SpellMgr.h
+++ b/src/game/SpellMgr.h
@@ -17,21 +17,29 @@
* 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 "SpellAuraDefines.h"
#include "DBCStructure.h"
#include "DBCStores.h"
#include "Database/SQLStorage.h"
+
#include "Utilities/UnorderedMap.h"
+
#include "Player.h"
+
#include <map>
+
class Player;
class Spell;
struct SpellModifier;
+
// only used in code
enum SpellCategories
{
@@ -41,12 +49,14 @@ enum SpellCategories
SPELLCATEGORY_FOOD = 11,
SPELLCATEGORY_DRINK = 59,
};
+
enum SpellDisableTypes
{
SPELL_DISABLE_PLAYER = 1,
SPELL_DISABLE_CREATURE = 2,
SPELL_DISABLE_PET = 4
};
+
enum SpellEffectTargetTypes
{
SPELL_REQUIRE_NONE,
@@ -56,6 +66,7 @@ enum SpellEffectTargetTypes
SPELL_REQUIRE_CASTER,
SPELL_REQUIRE_GOBJECT,
};
+
enum SpellSelectTargetTypes
{
TARGET_TYPE_DEFAULT,
@@ -71,6 +82,7 @@ enum SpellSelectTargetTypes
TARGET_TYPE_DEST_SPECIAL,
TARGET_TYPE_CHANNEL,
};
+
//Some SpellFamilyFlags
#define SPELLFAMILYFLAG_ROGUE_VANISH 0x00000800
#define SPELLFAMILYFLAG_ROGUE_STEALTH 0x00400000
@@ -81,6 +93,7 @@ enum SpellSelectTargetTypes
//#define SPELLFAMILYFLAG_ROGUE__FINISHING_MOVE 0x9003E0000LL
#define SPELLFAMILYFLAG_WARRIOR_SUNDERARMOR 0x00004000
#define SPELLFAMILYFLAG_SHAMAN_FROST_SHOCK 0x80000000
+
// Spell clasification
enum SpellSpecific
{
@@ -115,7 +128,9 @@ enum SpellSpecific
SPELL_HAND = 28,
SPELL_PHASE = 29,
};
+
#define SPELL_LINKED_MAX_SPELLS 200000
+
enum SpellLinkedType
{
SPELL_LINK_CAST = 0, // +: cast; -: remove
@@ -123,8 +138,10 @@ enum SpellLinkedType
SPELL_LINK_AURA = 2 * 200000, // +: aura; -: immune
SPELL_LINK_REMOVE = 0,
};
+
SpellSpecific GetSpellSpecific(uint32 spellId);
AuraState GetSpellAuraState(SpellEntry const * spellInfo);
+
// Different spell properties
inline float GetSpellRadiusForHostile(SpellRadiusEntry const *radius) { return (radius ? radius->radiusHostile : 0); }
inline float GetSpellRadiusForFriend(SpellRadiusEntry const *radius) { return (radius ? radius->radiusFriend : 0); }
@@ -144,41 +161,48 @@ inline float GetSpellRadius(SpellEntry const *spellInfo, uint32 effectIdx, bool
? GetSpellRadiusForFriend(sSpellRadiusStore.LookupEntry(spellInfo->EffectRadiusIndex[effectIdx]))
: GetSpellRadiusForHostile(sSpellRadiusStore.LookupEntry(spellInfo->EffectRadiusIndex[effectIdx]));
}
+
inline float GetSpellMaxRange(SpellEntry const *spellInfo, bool positive)
{
return positive
? GetSpellMaxRangeForFriend(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex))
: GetSpellMaxRangeForHostile(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex));
}
+
inline float GetSpellMinRange(SpellEntry const *spellInfo, bool positive)
{
return positive
? GetSpellMinRangeForFriend(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex))
: GetSpellMinRangeForHostile(sSpellRangeStore.LookupEntry(spellInfo->rangeIndex));
}
+
inline float GetSpellMinRange(uint32 id, bool positive)
{
SpellEntry const *spellInfo = GetSpellStore()->LookupEntry(id);
if(!spellInfo) return 0;
return GetSpellMinRange(spellInfo, positive);
}
+
inline float GetSpellMaxRange(uint32 id, bool positive)
{
SpellEntry const *spellInfo = GetSpellStore()->LookupEntry(id);
if(!spellInfo) return 0;
return GetSpellMaxRange(spellInfo, positive);
}
+
/*struct DispelEntry
{
uint64 casterGuid;
uint32 spellId;
Unit * caster;
uint8 stackAmount;
+
bool operator < (const DispelEntry & _Right) const
{
return (spellId != _Right.spellId ? spellId < _Right.spellId : casterGuid < _Right.casterGuid);
}
};*/
+
inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect)
{
for(int i= 0; i < 3; ++i)
@@ -186,6 +210,7 @@ inline bool IsSpellHaveEffect(SpellEntry const *spellInfo, SpellEffects effect)
return true;
return false;
}
+
inline bool IsSpellHaveAura(SpellEntry const *spellInfo, AuraType aura)
{
for(int i= 0; i < 3; ++i)
@@ -193,7 +218,9 @@ inline bool IsSpellHaveAura(SpellEntry const *spellInfo, AuraType aura)
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.
@@ -201,6 +228,7 @@ inline bool IsSealSpell(SpellEntry const *spellInfo)
( spellInfo->SpellFamilyFlags[1] & 0x26000C00
|| spellInfo->SpellFamilyFlags[0] & 0x0A000000 );
}
+
inline bool IsElementalShield(SpellEntry const *spellInfo)
{
// family flags 10 (Lightning), 42 (Earth), 37 (Water), proc shield from T2 8 pieces bonus
@@ -208,48 +236,60 @@ inline bool IsElementalShield(SpellEntry const *spellInfo)
|| spellInfo->SpellFamilyFlags[0] & 0x00000400)
|| spellInfo->Id == 23552;
}
+
inline bool IsExplicitDiscoverySpell(SpellEntry const *spellInfo)
{
return ((spellInfo->Effect[0] == SPELL_EFFECT_CREATE_RANDOM_ITEM
&& spellInfo->Effect[1] == SPELL_EFFECT_SCRIPT_EFFECT)
|| spellInfo->Id == 64323); // Book of Glyph Mastery (Effect0==SPELL_EFFECT_SCRIPT_EFFECT without any other data)
}
+
inline bool IsLootCraftingSpell(SpellEntry const *spellInfo)
{
return (spellInfo->Effect[0]==SPELL_EFFECT_CREATE_RANDOM_ITEM ||
// different random cards from Inscription (121==Virtuoso Inking Set category)
(spellInfo->Effect[0]==SPELL_EFFECT_CREATE_ITEM_2 && spellInfo->TotemCategory[0] == 121));
}
+
bool IsHigherHankOfSpell(uint32 spellId_1,uint32 spellId_2);
bool IsSingleFromSpellSpecificPerCaster(SpellSpecific spellSpec1, SpellSpecific spellSpec2);
bool IsSingleFromSpellSpecificPerTarget(SpellSpecific spellSpec1, SpellSpecific spellSpec2);
bool IsPassiveSpell(uint32 spellId);
bool IsAutocastableSpell(uint32 spellId);
+
uint32 CalculatePowerCost(SpellEntry const * spellInfo, Unit const * caster, SpellSchoolMask schoolMask);
+
inline bool IsPassiveSpellStackableWithRanks(SpellEntry const* spellProto)
{
if(!IsPassiveSpell(spellProto->Id))
return false;
+
return !IsSpellHaveEffect(spellProto,SPELL_EFFECT_APPLY_AURA);
}
+
inline bool IsDeathPersistentSpell(SpellEntry const *spellInfo)
{
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 IsDispelableBySpell(SpellEntry const * dispelSpell, uint32 spellId, bool def = false);
+
bool IsSingleTargetSpell(SpellEntry const *spellInfo);
bool IsSingleTargetSpells(SpellEntry const *spellInfo1, SpellEntry const *spellInfo2);
+
extern bool IsAreaEffectTarget[TOTAL_SPELL_TARGETS];
extern SpellEffectTargetTypes EffectTargetType[TOTAL_SPELL_EFFECTS];
extern SpellSelectTargetTypes SpellTargetType[TOTAL_SPELL_TARGETS];
+
inline bool IsCasterSourceTarget(uint32 target)
{
switch (SpellTargetType[target])
@@ -264,6 +304,7 @@ inline bool IsCasterSourceTarget(uint32 target)
}
return false;
}
+
inline bool IsPositionTarget(uint32 target)
{
switch (SpellTargetType[target])
@@ -277,6 +318,7 @@ inline bool IsPositionTarget(uint32 target)
}
return false;
}
+
inline bool IsSpellWithCasterSourceTargetsOnly(SpellEntry const* spellInfo)
{
for(int i = 0; i < 3; ++i)
@@ -284,14 +326,17 @@ inline bool IsSpellWithCasterSourceTargetsOnly(SpellEntry const* spellInfo)
uint32 targetA = spellInfo->EffectImplicitTargetA[i];
if(targetA && !IsCasterSourceTarget(targetA))
return false;
+
uint32 targetB = spellInfo->EffectImplicitTargetB[i];
if(targetB && !IsCasterSourceTarget(targetB))
return false;
+
if(!targetA && !targetB)
return false;
}
return true;
}
+
inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo)
{
if(IsAreaEffectTarget[spellInfo->EffectImplicitTargetA[0]] || IsAreaEffectTarget[spellInfo->EffectImplicitTargetB[0]])
@@ -302,6 +347,7 @@ inline bool IsAreaOfEffectSpell(SpellEntry const *spellInfo)
return true;
return false;
}
+
inline bool IsAreaAuraEffect(uint32 effect)
{
if( effect == SPELL_EFFECT_APPLY_AREA_AURA_PARTY ||
@@ -322,6 +368,7 @@ inline bool IsDispel(SpellEntry const *spellInfo)
return true;
return false;
}
+
inline bool IsDispelSpell(SpellEntry const *spellInfo)
{
//spellsteal is also dispel
@@ -332,33 +379,41 @@ inline bool IsDispelSpell(SpellEntry const *spellInfo)
return true;
return false;
}
+
inline bool isSpellBreakStealth(SpellEntry const* spellInfo)
{
return !(spellInfo->AttributesEx & SPELL_ATTR_EX_NOT_BREAK_STEALTH);
}
+
inline bool IsAutoRepeatRangedSpell(SpellEntry const* spellInfo)
{
return spellInfo->AttributesEx2 & SPELL_ATTR_EX2_AUTOREPEAT_FLAG;
}
+
inline bool IsRangedWeaponSpell(SpellEntry const* spellInfo)
{
//spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED should be checked outside
return (spellInfo->SpellFamilyName == SPELLFAMILY_HUNTER && !(spellInfo->SpellFamilyFlags[1] & 0x10000000)) // for 53352, cannot find better way
|| (spellInfo->EquippedItemSubClassMask & ITEM_SUBCLASS_MASK_WEAPON_RANGED);
}
+
SpellCastResult 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;
@@ -368,6 +423,7 @@ inline uint32 GetSpellMechanicMask(SpellEntry const* spellInfo, int32 effect)
mask |= 1<<spellInfo->EffectMechanic[effect];
return mask;
}
+
inline uint32 GetAllSpellMechanicMask(SpellEntry const* spellInfo)
{
uint32 mask = 0;
@@ -378,6 +434,7 @@ inline uint32 GetAllSpellMechanicMask(SpellEntry const* spellInfo)
mask |= 1<<spellInfo->EffectMechanic[i];
return mask;
}
+
inline Mechanics GetEffectMechanic(SpellEntry const* spellInfo, int32 effect)
{
if (spellInfo->EffectMechanic[effect])
@@ -386,6 +443,7 @@ inline Mechanics GetEffectMechanic(SpellEntry const* spellInfo, int32 effect)
return Mechanics(spellInfo->Mechanic);
return MECHANIC_NONE;
}
+
inline uint32 GetDispellMask(DispelType dispel)
{
// If dispel all
@@ -394,41 +452,57 @@ inline uint32 GetDispellMask(DispelType dispel)
else
return (1 << dispel);
}
+
// Diminishing Returns interaction with spells
DiminishingGroup GetDiminishingReturnsGroupForSpell(SpellEntry const* spellproto, bool triggered);
bool IsDiminishingReturnsGroupDurationLimited(DiminishingGroup group);
DiminishingReturnsType GetDiminishingReturnsGroupType(DiminishingGroup group);
int32 GetDiminishingReturnsLimitDuration(DiminishingGroup group, SpellEntry const* spellproto);
+
// Spell proc event related declarations (accessed using SpellMgr functions)
enum ProcFlags
{
PROC_FLAG_NONE = 0x00000000,
+
PROC_FLAG_KILLED = 0x00000001, // 00 Killed by agressor
PROC_FLAG_KILL = 0x00000002, // 01 Kill target (in most cases need XP/Honor reward)
+
PROC_FLAG_SUCCESSFUL_MELEE_HIT = 0x00000004, // 02 Successful melee auto attack
PROC_FLAG_TAKEN_MELEE_HIT = 0x00000008, // 03 Taken damage from melee auto attack hit
+
PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT = 0x00000010, // 04 Successful attack by Spell that use melee weapon
PROC_FLAG_TAKEN_MELEE_SPELL_HIT = 0x00000020, // 05 Taken damage by Spell that use melee weapon
+
PROC_FLAG_SUCCESSFUL_RANGED_HIT = 0x00000040, // 06 Successful Ranged auto attack
PROC_FLAG_TAKEN_RANGED_HIT = 0x00000080, // 07 Taken damage from ranged auto attack
+
PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT = 0x00000100, // 08 Successful Ranged attack by Spell that use ranged weapon
PROC_FLAG_TAKEN_RANGED_SPELL_HIT = 0x00000200, // 09 Taken damage by Spell that use ranged weapon
+
PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT = 0x00000400, // 10 Successful Positive spell hit
PROC_FLAG_TAKEN_POSITIVE_SPELL = 0x00000800, // 11 Taken Positive spell hit
+
PROC_FLAG_SUCCESSFUL_NEGATIVE_SPELL_HIT = 0x00001000, // 12 Successful Negative spell hit
PROC_FLAG_TAKEN_NEGATIVE_SPELL_HIT = 0x00002000, // 13 Taken Negative spell hit
+
PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL = 0x00004000, // 14 Successful Positive Magic spell hit
PROC_FLAG_TAKEN_POSITIVE_MAGIC_SPELL = 0x00008000, // 15 Taken Positive Magic spell hit
+
PROC_FLAG_SUCCESSFUL_NEGATIVE_MAGIC_SPELL = 0x00010000, // 16 Successful Negative Magic spell hit
PROC_FLAG_TAKEN_NEGATIVE_MAGIC_SPELL = 0x00020000, // 17 Taken Negative Magic spell hit
+
PROC_FLAG_ON_DO_PERIODIC = 0x00040000, // 18 Successful do periodic (damage / healing, determined from 14,16 flags)
PROC_FLAG_ON_TAKE_PERIODIC = 0x00080000, // 19 Taken spell periodic (damage / healing, determined from 15,17 flags)
+
PROC_FLAG_TAKEN_ANY_DAMAGE = 0x00100000, // 20 Taken any damage
PROC_FLAG_ON_TRAP_ACTIVATION = 0x00200000, // 21 On trap activation (possibly needs name change to ON_GAMEOBJECT_CAST or USE)
+
PROC_FLAG_TAKEN_OFFHAND_HIT = 0x00400000, // 22 Taken off-hand melee attacks ( this is probably wrong )
PROC_FLAG_SUCCESSFUL_OFFHAND_HIT = 0x00800000, // 23 Successful off-hand melee attacks ( this is probably wrong )
+
PROC_FLAG_DEATH = 0x01000000 // 24 Died in any way
};
+
#define MELEE_BASED_TRIGGER_MASK (PROC_FLAG_SUCCESSFUL_MELEE_HIT | \
PROC_FLAG_TAKEN_MELEE_HIT | \
PROC_FLAG_SUCCESSFUL_MELEE_SPELL_HIT | \
@@ -437,6 +511,7 @@ enum ProcFlags
PROC_FLAG_TAKEN_RANGED_HIT | \
PROC_FLAG_SUCCESSFUL_RANGED_SPELL_HIT | \
PROC_FLAG_TAKEN_RANGED_SPELL_HIT)
+
enum ProcFlagsEx
{
PROC_EX_NONE = 0x0000000, // If none can tigger on Hit/Crit only (passive spells MUST defined by SpellFamily flag)
@@ -459,6 +534,7 @@ enum ProcFlagsEx
PROC_EX_EX_TRIGGER_ALWAYS = 0x0010000, // If set trigger always no matter of hit result
PROC_EX_EX_ONE_TIME_TRIGGER = 0x0020000, // If set trigger always but only one time (not implemented yet)
PROC_EX_ONLY_ACTIVE_SPELL = 0x0040000, // Spell has to do damage/heal to proc
+
// Flags for internal use - do not use these in db!
PROC_EX_INTERNAL_CANT_PROC = 0x0800000,
PROC_EX_INTERNAL_DOT = 0x1000000,
@@ -468,11 +544,13 @@ enum ProcFlagsEx
};
#define AURA_REMOVE_PROC_EX_MASK \
(PROC_EX_AURA_REMOVE_DESTROY | PROC_EX_AURA_REMOVE_EXPIRE)
+
#define AURA_SPELL_PROC_EX_MASK \
(PROC_EX_NORMAL_HIT | PROC_EX_CRITICAL_HIT | PROC_EX_MISS | \
PROC_EX_RESIST | PROC_EX_DODGE | PROC_EX_PARRY | PROC_EX_BLOCK | \
PROC_EX_EVADE | PROC_EX_IMMUNE | PROC_EX_DEFLECT | \
PROC_EX_ABSORB | PROC_EX_REFLECT | PROC_EX_INTERRUPT)
+
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
@@ -484,6 +562,7 @@ struct SpellProcEventEntry
float customChance; // Owerride chance (in most cases for debug only)
uint32 cooldown; // hidden cooldown used for some spell proc events, applied to _triggered_spell_
};
+
struct SpellBonusEntry
{
float direct_damage;
@@ -491,23 +570,29 @@ struct SpellBonusEntry
float ap_bonus;
float ap_dot_bonus;
};
+
typedef UNORDERED_MAP<uint32, SpellProcEventEntry> SpellProcEventMap;
+
struct SpellEnchantProcEntry
{
uint32 customChance;
float PPMChance;
uint32 procEx;
};
+
typedef UNORDERED_MAP<uint32, SpellEnchantProcEntry> SpellEnchantProcEventMap;
typedef UNORDERED_MAP<uint32, SpellBonusEntry> SpellBonusMap;
+
#define ELIXIR_BATTLE_MASK 0x01
#define ELIXIR_GUARDIAN_MASK 0x02
#define ELIXIR_FLASK_MASK (ELIXIR_BATTLE_MASK|ELIXIR_GUARDIAN_MASK)
#define ELIXIR_UNSTABLE_MASK 0x04
#define ELIXIR_SHATTRATH_MASK 0x08
#define ELIXIR_WELL_FED 0x10 // Some foods have SPELLFAMILY_POTION
+
typedef std::map<uint32, uint8> SpellElixirMap;
typedef std::map<uint32, uint16> SpellThreatMap;
+
// Spell script target related declarations (accessed using SpellMgr functions)
enum SpellScriptTargetType
{
@@ -516,15 +601,19 @@ enum SpellScriptTargetType
SPELL_TARGET_TYPE_DEAD = 2,
SPELL_TARGET_TYPE_CONTROLLED = 3,
};
+
#define MAX_SPELL_TARGET_TYPE 4
+
struct SpellTargetEntry
{
SpellTargetEntry(SpellScriptTargetType type_,uint32 targetEntry_) : type(type_), targetEntry(targetEntry_) {}
SpellScriptTargetType type;
uint32 targetEntry;
};
+
typedef std::multimap<uint32,SpellTargetEntry> SpellScriptTarget;
typedef std::pair<SpellScriptTarget::const_iterator,SpellScriptTarget::const_iterator> SpellScriptTargetBounds;
+
// coordinates for spells (accessed using SpellMgr functions)
struct SpellTargetPosition
{
@@ -534,7 +623,9 @@ struct SpellTargetPosition
float target_Z;
float target_Orientation;
};
+
typedef UNORDERED_MAP<uint32, SpellTargetPosition> SpellTargetPositionMap;
+
// Spell pet auras
class PetAura
{
@@ -543,11 +634,13 @@ class PetAura
{
auras.clear();
}
+
PetAura(uint32 petEntry, uint32 aura, bool _removeOnChangePet, int _damage) :
removeOnChangePet(_removeOnChangePet), damage(_damage)
{
auras[petEntry] = aura;
}
+
uint32 GetAura(uint32 petEntry) const
{
std::map<uint32, uint32>::const_iterator itr = auras.find(petEntry);
@@ -558,24 +651,29 @@ class PetAura
return itr2->second;
return 0;
}
+
void AddAura(uint32 petEntry, uint32 aura)
{
auras[petEntry] = aura;
}
+
bool IsRemovedOnChangePet() const
{
return removeOnChangePet;
}
+
int32 GetDamage() const
{
return damage;
}
+
private:
std::map<uint32, uint32> auras;
bool removeOnChangePet;
int32 damage;
};
typedef std::map<uint32, PetAura> SpellPetAuraMap;
+
struct SpellArea
{
uint32 spellId;
@@ -587,9 +685,11 @@ struct SpellArea
Gender gender; // can be applied only to gender
bool questStartCanActive; // if true then quest start can be active (not only rewarded)
bool autocast; // if true then auto applied at area enter, in other case just allowed to cast
+
// helpers
bool IsFitToRequirements(Player const* player, uint32 newZone, uint32 newArea) const;
};
+
typedef std::multimap<uint32,SpellArea> SpellAreaMap;
typedef std::multimap<uint32,SpellArea const*> SpellAreaForQuestMap;
typedef std::multimap<uint32,SpellArea const*> SpellAreaForAuraMap;
@@ -599,6 +699,7 @@ typedef std::pair<SpellAreaForQuestMap::const_iterator,SpellAreaForQuestMap::con
typedef std::pair<SpellAreaForAuraMap::const_iterator, SpellAreaForAuraMap::const_iterator> SpellAreaForAuraMapBounds;
typedef std::pair<SpellAreaForAreaMap::const_iterator, SpellAreaForAreaMap::const_iterator> SpellAreaForAreaMapBounds;
+
// Spell rank chain (accessed using SpellMgr functions)
struct SpellChainNode
{
@@ -608,10 +709,14 @@ struct SpellChainNode
uint32 last;
uint8 rank;
};
+
typedef UNORDERED_MAP<uint32, SpellChainNode> SpellChainMap;
+
// spell_id req_spell
typedef UNORDERED_MAP<uint32, uint32> SpellRequiredMap;
+
typedef std::multimap<uint32, uint32> SpellsRequiringSpellMap;
+
// Spell learning properties (accessed using SpellMgr functions)
struct SpellLearnSkillNode
{
@@ -619,39 +724,51 @@ struct SpellLearnSkillNode
uint32 value; // 0 - max skill value for player level
uint32 maxvalue; // 0 - max skill value for player level
};
+
typedef std::map<uint32, SpellLearnSkillNode> SpellLearnSkillMap;
+
struct SpellLearnSpellNode
{
uint32 spell;
bool active; // show in spellbook or not
bool autoLearned;
};
+
typedef std::multimap<uint32, SpellLearnSpellNode> SpellLearnSpellMap;
typedef std::pair<SpellLearnSpellMap::const_iterator,SpellLearnSpellMap::const_iterator> SpellLearnSpellMapBounds;
+
typedef std::multimap<uint32, SkillLineAbilityEntry const*> SkillLineAbilityMap;
typedef std::pair<SkillLineAbilityMap::const_iterator,SkillLineAbilityMap::const_iterator> SkillLineAbilityMapBounds;
+
typedef std::multimap<uint32, uint32> PetLevelupSpellSet;
typedef std::map<uint32, PetLevelupSpellSet> PetLevelupSpellMap;
+
struct PetDefaultSpellsEntry
{
uint32 spellid[MAX_CREATURE_SPELL_DATA_SLOT];
};
+
// < 0 for petspelldata id, > 0 for creature_id
typedef std::map<int32, PetDefaultSpellsEntry> PetDefaultSpellsMap;
+
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;
}
+
#define SPELL_ATTR_CU_CONE_BACK 0x00000002
#define SPELL_ATTR_CU_CONE_LINE 0x00000004
#define SPELL_ATTR_CU_SHARE_DAMAGE 0x00000008
@@ -668,30 +785,40 @@ inline bool IsProfessionSkill(uint32 skill)
#define SPELL_ATTR_CU_NEGATIVE_EFF1 0x00020000
#define SPELL_ATTR_CU_NEGATIVE_EFF2 0x00040000
#define SPELL_ATTR_CU_NEGATIVE 0x00070000
+
typedef std::vector<uint32> SpellCustomAttribute;
typedef std::vector<bool> EnchantCustomAttribute;
+
typedef std::map<int32, std::vector<int32> > SpellLinkedMap;
+
inline bool IsProfessionOrRidingSkill(uint32 skill)
{
return IsProfessionSkill(skill) || skill == SKILL_RIDING;
}
+
class SpellMgr
{
// Constructors
public:
SpellMgr();
~SpellMgr();
+
// Accessors (const or static functions)
public:
+
bool IsAffectedByMod(SpellEntry const *spellInfo, SpellModifier *mod) 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);
@@ -706,13 +833,16 @@ class SpellMgr
else
return SPELL_NORMAL;
}
+
uint16 GetSpellThreat(uint32 spellid) const
{
SpellThreatMap::const_iterator itr = mSpellThreatMap.find(spellid);
if(itr==mSpellThreatMap.end())
return 0;
+
return itr->second;
}
+
// Spell proc events
SpellProcEventEntry const* GetSpellProcEvent(uint32 spellId) const
{
@@ -721,7 +851,9 @@ class SpellMgr
return &itr->second;
return NULL;
}
+
bool IsSpellProcEventCanTriggeredBy( SpellProcEventEntry const * spellProcEvent, uint32 EventProcFlag, SpellEntry const * procSpell, uint32 procFlags, uint32 procExtra, bool active);
+
SpellEnchantProcEntry const* GetSpellEnchantProcEvent(uint32 enchId) const
{
SpellEnchantProcEventMap::const_iterator itr = mSpellEnchantProcEventMap.find(enchId);
@@ -729,6 +861,7 @@ class SpellMgr
return &itr->second;
return NULL;
}
+
// Spell bonus data
SpellBonusEntry const* GetSpellBonusData(uint32 spellId) const
{
@@ -745,6 +878,7 @@ class SpellMgr
}
return NULL;
}
+
// Spell target coordinates
SpellTargetPosition const* GetSpellTargetPosition(uint32 spell_id) const
{
@@ -753,27 +887,34 @@ class SpellMgr
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 GetSpellRequired(uint32 spell_id) const
{
SpellRequiredMap::const_iterator itr = mSpellReq.find(spell_id);
if(itr == mSpellReq.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 GetSpellWithRank(uint32 spell_id, uint32 rank) const
{
if(SpellChainNode const* node = GetSpellChainNode(spell_id))
@@ -783,52 +924,70 @@ class SpellMgr
}
return spell_id;
}
+
uint32 GetPrevSpellInChain(uint32 spell_id) const
{
if(SpellChainNode const* node = GetSpellChainNode(spell_id))
return node->prev;
+
return 0;
}
+
uint32 GetNextSpellInChain(uint32 spell_id) const
{
if(SpellChainNode const* node = GetSpellChainNode(spell_id))
return node->next;
+
return 0;
}
+
SpellsRequiringSpellMap const& GetSpellsRequiringSpell() const { return mSpellsReqSpell; }
+
uint8 GetSpellRank(uint32 spell_id) const
{
if(SpellChainNode const* node = GetSpellChainNode(spell_id))
return node->rank;
+
return 0;
}
+
uint32 GetLastSpellInChain(uint32 spell_id) const
{
if(SpellChainNode const* node = GetSpellChainNode(spell_id))
return node->last;
+
return spell_id;
}
+
uint32 IsArenaAllowedEnchancment(uint32 ench_id) const
{
return mEnchantCustomAttr[ench_id];
}
+
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 CanAurasStack(SpellEntry const *spellInfo_1, SpellEntry const *spellInfo_2, bool sameCaster) const;
+
SpellEntry const* SelectAuraRankForPlayerLevel(SpellEntry const* spellInfo, uint32 playerLevel) const;
+
// Spell learning
SpellLearnSkillNode const* GetSpellLearnSkill(uint32 spell_id) const
{
@@ -838,14 +997,17 @@ class SpellMgr
else
return NULL;
}
+
bool IsSpellLearnSpell(uint32 spell_id) const
{
return mSpellLearnSpells.find(spell_id) != mSpellLearnSpells.end();
}
+
SpellLearnSpellMapBounds GetSpellLearnSpellMapBounds(uint32 spell_id) const
{
return SpellLearnSpellMapBounds(mSpellLearnSpells.lower_bound(spell_id),mSpellLearnSpells.upper_bound(spell_id));
}
+
bool IsSpellLearnToSpell(uint32 spell_id1,uint32 spell_id2) const
{
SpellLearnSpellMapBounds bounds = GetSpellLearnSpellMapBounds(spell_id1);
@@ -854,24 +1016,30 @@ class SpellMgr
return true;
return false;
}
+
static bool IsProfessionOrRidingSpell(uint32 spellId);
static bool IsProfessionSpell(uint32 spellId);
static bool IsPrimaryProfessionSpell(uint32 spellId);
bool IsPrimaryProfessionFirstRankSpell(uint32 spellId) const;
+
bool IsSkillBonusSpell(uint32 spellId) const;
bool IsSkillTypeSpell(uint32 spellId, SkillType type) const;
+
// Spell script targets
SpellScriptTargetBounds GetSpellScriptTargetBounds(uint32 spell_id) const
{
return SpellScriptTargetBounds(mSpellScriptTarget.lower_bound(spell_id),mSpellScriptTarget.upper_bound(spell_id));
}
+
// Spell correctess for client using
static bool IsSpellValid(SpellEntry const * spellInfo, Player* pl = NULL, bool msg = true);
+
SkillLineAbilityMapBounds GetSkillLineAbilityMapBounds(uint32 spell_id) const
{
return SkillLineAbilityMapBounds(mSkillLineAbilityMap.lower_bound(spell_id),mSkillLineAbilityMap.upper_bound(spell_id));
}
+
PetAura const* GetPetAura(uint32 spell_id, uint8 eff)
{
SpellPetAuraMap::const_iterator itr = mSpellPetAuraMap.find((spell_id<<8) + eff);
@@ -880,6 +1048,7 @@ class SpellMgr
else
return NULL;
}
+
PetLevelupSpellSet const* GetPetLevelupSpellList(uint32 petFamily) const
{
PetLevelupSpellMap::const_iterator itr = mPetLevelupSpellMap.find(petFamily);
@@ -888,6 +1057,7 @@ class SpellMgr
else
return NULL;
}
+
uint32 GetSpellCustomAttr(uint32 spell_id) const
{
if(spell_id >= mSpellCustomAttr.size())
@@ -895,11 +1065,13 @@ class SpellMgr
else
return mSpellCustomAttr[spell_id];
}
+
const std::vector<int32> *GetSpellLinked(int32 spell_id) const
{
SpellLinkedMap::const_iterator itr = mSpellLinkedMap.find(spell_id);
return itr != mSpellLinkedMap.end() ? &(itr->second) : NULL;
}
+
// < 0 for petspelldata id, > 0 for creature_id
PetDefaultSpellsEntry const* GetPetDefaultSpellsEntry(int32 id) const
{
@@ -908,11 +1080,14 @@ class SpellMgr
return &itr->second;
return NULL;
}
+
SpellCastResult GetSpellAllowedInLocationError(SpellEntry const *spellInfo, uint32 map_id, uint32 zone_id, uint32 area_id, Player const* player = NULL);
+
SpellAreaMapBounds GetSpellAreaMapBounds(uint32 spell_id) const
{
return SpellAreaMapBounds(mSpellAreaMap.lower_bound(spell_id),mSpellAreaMap.upper_bound(spell_id));
}
+
SpellAreaForQuestMapBounds GetSpellAreaForQuestMapBounds(uint32 quest_id, bool active) const
{
if(active)
@@ -920,19 +1095,24 @@ class SpellMgr
else
return SpellAreaForQuestMapBounds(mSpellAreaForQuestMap.lower_bound(quest_id),mSpellAreaForQuestMap.upper_bound(quest_id));
}
+
SpellAreaForQuestMapBounds GetSpellAreaForQuestEndMapBounds(uint32 quest_id) const
{
return SpellAreaForQuestMapBounds(mSpellAreaForQuestEndMap.lower_bound(quest_id),mSpellAreaForQuestEndMap.upper_bound(quest_id));
}
+
SpellAreaForAuraMapBounds GetSpellAreaForAuraMapBounds(uint32 spell_id) const
{
return SpellAreaForAuraMapBounds(mSpellAreaForAuraMap.lower_bound(spell_id),mSpellAreaForAuraMap.upper_bound(spell_id));
}
+
SpellAreaForAreaMapBounds GetSpellAreaForAreaMapBounds(uint32 area_id) const
{
return SpellAreaForAreaMapBounds(mSpellAreaForAreaMap.lower_bound(area_id),mSpellAreaForAreaMap.upper_bound(area_id));
}
+
bool IsSrcTargetSpell(SpellEntry const *spellInfo) const;
+
inline bool IsCasterSourceTarget(uint32 target)
{
switch (SpellTargetType[target])
@@ -945,6 +1125,7 @@ class SpellMgr
}
return true;
}
+
inline bool IsSpellWithCasterSourceTargetsOnly(SpellEntry const* spellInfo)
{
for(int i = 0; i < 3; ++i)
@@ -953,9 +1134,11 @@ class SpellMgr
return false;
return true;
}
+
// Modifiers
public:
static SpellMgr& Instance();
+
// Loading data at server startup
void LoadSpellChains();
void LoadSpellRequired();
@@ -976,10 +1159,13 @@ class SpellMgr
void LoadPetLevelupSpellMap();
void LoadPetDefaultSpells();
void LoadSpellAreas();
+
bool CheckDB() const;
+
private:
bool _isPositiveSpell(uint32 spellId, bool deep) const;
bool _isPositiveEffect(uint32 spellId, uint32 effIndex, bool deep) const;
+
SpellScriptTarget mSpellScriptTarget;
SpellChainMap mSpellChains;
SpellsRequiringSpellMap mSpellsReqSpell;
@@ -1006,6 +1192,7 @@ class SpellMgr
SpellAreaForAuraMap mSpellAreaForAuraMap;
SpellAreaForAreaMap mSpellAreaForAreaMap;
};
+
#define spellmgr SpellMgr::Instance()
#endif
diff --git a/src/game/StatSystem.cpp b/src/game/StatSystem.cpp
index cdbda70bdf7..56bde0e5442 100644
--- a/src/game/StatSystem.cpp
+++ b/src/game/StatSystem.cpp
@@ -17,30 +17,37 @@
* 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:
@@ -57,11 +64,14 @@ bool Player::UpdateStats(Stats stat)
UpdateAllSpellCritChances();
UpdateArmor(); //SPELL_AURA_MOD_RESISTANCE_OF_INTELLECT_PERCENT, only armor currently
break;
+
case STAT_SPIRIT:
break;
+
default:
break;
}
+
if (stat == STAT_STRENGTH)
{
UpdateAttackPowerAndDamage(false);
@@ -81,8 +91,10 @@ bool Player::UpdateStats(Stats stat)
if (HasAuraTypeWithMiscvalue(SPELL_AURA_MOD_RANGED_ATTACK_POWER_OF_STAT_PERCENT, stat))
UpdateAttackPowerAndDamage(true);
}
+
UpdateSpellDamageAndHealingBonus();
UpdateManaRegen();
+
// Update ratings in exist SPELL_AURA_MOD_RATING_FROM_STAT and only depends from stat
uint32 mask = 0;
AuraEffectList const& modRatingFromStat = GetAurasByType(SPELL_AURA_MOD_RATING_FROM_STAT);
@@ -97,14 +109,17 @@ bool Player::UpdateStats(Stats stat)
}
return true;
}
+
void Player::ApplySpellPowerBonus(int32 amount, bool apply)
{
m_baseSpellPower+=apply?amount:-amount;
+
// For speed just update for client
ApplyModUInt32Value(PLAYER_FIELD_MOD_HEALING_DONE_POS, amount, apply);
for(int i = SPELL_SCHOOL_HOLY; i < MAX_SPELL_SCHOOL; ++i)
ApplyModUInt32Value(PLAYER_FIELD_MOD_DAMAGE_DONE_POS+i, amount, apply);;
}
+
void Player::UpdateSpellDamageAndHealingBonus()
{
// Magic damage modifiers implemented in Unit::SpellDamageBonus
@@ -115,6 +130,7 @@ void Player::UpdateSpellDamageAndHealingBonus()
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)
@@ -122,12 +138,15 @@ bool Player::UpdateAllStats()
float value = GetTotalStatValue(Stats(i));
SetStat(Stats(i), (int32)value);
}
+
UpdateArmor();
// calls UpdateAttackPowerAndDamage() in UpdateArmor for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
UpdateAttackPowerAndDamage(true);
UpdateMaxHealth();
+
for(int i = POWER_MANA; i < MAX_POWERS; ++i)
UpdateMaxPower(Powers(i));
+
UpdateAllCritPercentages();
UpdateAllSpellCritChances();
UpdateDefenseBonusesMod();
@@ -139,14 +158,17 @@ bool Player::UpdateAllStats()
RecalculateRating(CR_ARMOR_PENETRATION);
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);
@@ -154,14 +176,17 @@ void Player::UpdateResistances(uint32 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
AuraEffectList const& mResbyIntellect = GetAurasByType(SPELL_AURA_MOD_RESISTANCE_OF_STAT_PERCENT);
for(AuraEffectList::const_iterator i = mResbyIntellect.begin();i != mResbyIntellect.end(); ++i)
@@ -169,64 +194,87 @@ void Player::UpdateArmor()
if((*i)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)
value += int32(GetStat(Stats((*i)->GetMiscBValue())) * (*i)->GetAmount() / 100.0f);
}
+
value *= GetModifierValue(unitMod, TOTAL_PCT);
+
SetArmor(int32(value));
+
Pet *pet = GetPet();
if(pet)
pet->UpdateArmor();
+
UpdateAttackPowerAndDamage(); // armor dependent auras update for SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR
}
+
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::ApplyFeralAPBonus(int32 amount, bool apply)
{
m_baseFeralAP+= apply ? amount:-amount;
UpdateAttackPowerAndDamage();
}
+
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;
@@ -281,6 +329,7 @@ void Player::UpdateAttackPowerAndDamage(bool ranged )
}
default: break;
}
+
switch(m_form)
{
case FORM_CAT:
@@ -300,9 +349,12 @@ void Player::UpdateAttackPowerAndDamage(bool ranged )
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 )
{
@@ -318,15 +370,19 @@ void Player::UpdateAttackPowerAndDamage(bool ranged )
AuraEffectList const& mAPbyStat = GetAurasByType(SPELL_AURA_MOD_ATTACK_POWER_OF_STAT_PERCENT);
for(AuraEffectList::const_iterator i = mAPbyStat.begin();i != mAPbyStat.end(); ++i)
attPowerMod += int32(GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 100.0f);
+
AuraEffectList const& mAPbyArmor = GetAurasByType(SPELL_AURA_MOD_ATTACK_POWER_OF_ARMOR);
for(AuraEffectList::const_iterator iter = mAPbyArmor.begin(); iter != mAPbyArmor.end(); ++iter)
// always: ((*i)->GetModifier()->m_miscvalue == 1 == SPELL_SCHOOL_MASK_NORMAL)
attPowerMod += int32(GetArmor() / (*iter)->GetAmount());
}
+
float attPowerMultiplier = GetModifierValue(unitMod, TOTAL_PCT) - 1.0f;
+
SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
SetInt32Value(index_mod, (uint32)attPowerMod); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
SetFloatValue(index_mult, attPowerMultiplier); //UNIT_FIELD_(RANGED)_ATTACK_POWER_MULTIPLIER field
+
Pet *pet = GetPet(); //update pet's AP
//automatically update weapon damage after attack power modification
if(ranged)
@@ -342,18 +398,22 @@ void Player::UpdateAttackPowerAndDamage(bool ranged )
UpdateDamagePhysical(OFF_ATTACK);
if(getClass() == CLASS_SHAMAN) // mental quickness
UpdateSpellDamageAndHealingBonus();
+
if(pet && pet->IsPetGhoul()) // At ranged attack change for hunter pet
pet->UpdateAttackPowerAndDamage();
}
}
+
void Player::UpdateShieldBlockValue()
{
SetUInt32Value(PLAYER_SHIELD_BLOCK, GetShieldBlockValue());
}
+
void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bool addTotalPct, float& min_damage, float& max_damage)
{
UnitMods unitMod;
UnitMods attPower;
+
switch(attType)
{
case BASE_ATTACK:
@@ -370,17 +430,22 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo
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 = addTotalPct ? GetModifierValue(unitMod, TOTAL_PCT) : 1.0f;
+
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;
}
@@ -401,14 +466,18 @@ void Player::CalculateMinMaxDamage(WeaponAttackType attType, bool normalized, bo
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, true, mindamage, maxdamage);
+
switch(attType)
{
case BASE_ATTACK:
@@ -426,12 +495,14 @@ void Player::UpdateDamagePhysical(WeaponAttackType attType)
break;
}
}
+
void Player::UpdateDefenseBonusesMod()
{
UpdateBlockPercentage();
UpdateParryPercentage();
UpdateDodgePercentage();
}
+
void Player::UpdateBlockPercentage()
{
// No block
@@ -450,11 +521,13 @@ void Player::UpdateBlockPercentage()
}
SetStatFloatValue(PLAYER_BLOCK_PERCENTAGE, value);
}
+
void Player::UpdateCritPercentage(WeaponAttackType attType)
{
BaseModGroup modGroup;
uint16 index;
CombatRating cr;
+
switch(attType)
{
case OFF_ATTACK:
@@ -474,22 +547,27 @@ void Player::UpdateCritPercentage(WeaponAttackType attType)
cr = CR_CRIT_MELEE;
break;
}
+
float value = GetTotalPercentageModValue(modGroup) + GetRatingBonusValue(cr);
// Modify crit from weapon skill and maximized defense skill of same level victim difference
value += (int32(GetWeaponSkillValue(attType)) - int32(GetMaxSkillValueForLevel())) * 0.04f;
value = value < 0.0f ? 0.0f : value;
SetStatFloatValue(index, value);
}
+
void Player::UpdateAllCritPercentages()
{
float value = GetMeleeCritFromAgility();
+
SetBaseModValue(CRIT_PERCENTAGE, PCT_MOD, value);
SetBaseModValue(OFFHAND_CRIT_PERCENTAGE, PCT_MOD, value);
SetBaseModValue(RANGED_CRIT_PERCENTAGE, PCT_MOD, value);
+
UpdateCritPercentage(BASE_ATTACK);
UpdateCritPercentage(OFF_ATTACK);
UpdateCritPercentage(RANGED_ATTACK);
}
+
void Player::UpdateParryPercentage()
{
// No parry
@@ -508,6 +586,7 @@ void Player::UpdateParryPercentage()
}
SetStatFloatValue(PLAYER_PARRY_PERCENTAGE, value);
}
+
void Player::UpdateDodgePercentage()
{
// Dodge from agility
@@ -521,6 +600,7 @@ void Player::UpdateDodgePercentage()
value = value < 0.0f ? 0.0f : value;
SetStatFloatValue(PLAYER_DODGE_PERCENTAGE, value);
}
+
void Player::UpdateSpellCritChance(uint32 school)
{
// For normal school set zero crit chance
@@ -541,40 +621,50 @@ void Player::UpdateSpellCritChance(uint32 school)
crit += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_SPELL_CRIT_CHANCE_SCHOOL, 1<<school);
// Increase crit from spell crit ratings
crit += GetRatingBonusValue(CR_CRIT_SPELL);
+
// Store crit value
SetFloatValue(PLAYER_SPELL_CRIT_PERCENTAGE1 + school, crit);
}
+
void Player::UpdateArmorPenetration(int32 amount)
{
// Store Rating Value
SetUInt32Value(PLAYER_FIELD_COMBAT_RATING_1 + CR_ARMOR_PENETRATION, amount);
}
+
void Player::UpdateMeleeHitChances()
{
m_modMeleeHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
m_modMeleeHitChance+= GetRatingBonusValue(CR_HIT_MELEE);
}
+
void Player::UpdateRangedHitChances()
{
m_modRangedHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_HIT_CHANCE);
m_modRangedHitChance+= GetRatingBonusValue(CR_HIT_RANGED);
}
+
void Player::UpdateSpellHitChances()
{
m_modSpellHitChance = GetTotalAuraModifier(SPELL_AURA_MOD_SPELL_HIT_CHANCE);
m_modSpellHitChance+= GetRatingBonusValue(CR_HIT_SPELL);
}
+
void Player::UpdateAllSpellCritChances()
{
for (int i = SPELL_SCHOOL_NORMAL; i < MAX_SPELL_SCHOOL; ++i)
UpdateSpellCritChance(i);
}
+
void Player::UpdateExpertise(WeaponAttackType attack)
{
if(attack==RANGED_ATTACK)
return;
+
int32 expertise = int32(GetRatingBonusValue(CR_EXPERTISE));
+
Item *weapon = GetWeaponForAttack(attack);
+
AuraEffectList const& expAuras = GetAurasByType(SPELL_AURA_MOD_EXPERTISE);
for(AuraEffectList::const_iterator itr = expAuras.begin(); itr != expAuras.end(); ++itr)
{
@@ -585,8 +675,10 @@ void Player::UpdateExpertise(WeaponAttackType attack)
else if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
expertise += (*itr)->GetAmount();
}
+
if(expertise < 0)
expertise = 0;
+
switch(attack)
{
case BASE_ATTACK: SetUInt32Value(PLAYER_EXPERTISE, expertise); break;
@@ -594,11 +686,13 @@ void Player::UpdateExpertise(WeaponAttackType attack)
default: break;
}
}
+
void Player::ApplyManaRegenBonus(int32 amount, bool apply)
{
m_baseManaRegen+= apply ? amount : -amount;
UpdateManaRegen();
}
+
void Player::UpdateManaRegen()
{
float Intellect = GetStat(STAT_INTELLECT);
@@ -606,57 +700,76 @@ void Player::UpdateManaRegen()
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) + m_baseManaRegen) / 5.0f;
+
// Get bonus from SPELL_AURA_MOD_MANA_REGEN_FROM_STAT aura
AuraEffectList const& regenAura = GetAurasByType(SPELL_AURA_MOD_MANA_REGEN_FROM_STAT);
for(AuraEffectList::const_iterator i = regenAura.begin();i != regenAura.end(); ++i)
{
power_regen_mp5 += GetStat(Stats((*i)->GetMiscValue())) * (*i)->GetAmount() / 500.0f;
}
+
// 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(UNIT_FIELD_POWER_REGEN_INTERRUPTED_FLAT_MODIFIER, power_regen_mp5 + power_regen * modManaRegenInterrupt / 100.0f);
+
SetStatFloatValue(UNIT_FIELD_POWER_REGEN_FLAT_MODIFIER, 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();
UpdateAttackPowerAndDamage(true);
+
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)
@@ -667,40 +780,50 @@ void Creature::UpdateResistances(uint32 school)
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)
{
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;
}
+
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;
+
SetInt32Value(index, (uint32)base_attPower); //UNIT_FIELD_(RANGED)_ATTACK_POWER field
SetInt32Value(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);
@@ -710,6 +833,7 @@ void Creature::UpdateAttackPowerAndDamage(bool ranged)
UpdateDamagePhysical(OFF_ATTACK);
}
}
+
void Creature::UpdateDamagePhysical(WeaponAttackType attType)
{
UnitMods unitMod;
@@ -726,9 +850,12 @@ void Creature::UpdateDamagePhysical(WeaponAttackType attType)
unitMod = UNIT_MOD_DAMAGE_RANGED;
break;
}
+
//float att_speed = float(GetAttackTime(attType))/1000.0f;
+
float weapon_mindamage = GetWeaponDamageRange(attType, MINDAMAGE);
float weapon_maxdamage = GetWeaponDamageRange(attType, MAXDAMAGE);
+
/* difference in AP between current attack power and base value from DB */
float att_pwr_change = GetTotalAttackPowerValue(attType) - GetCreatureInfo()->attackpower;
float base_value = GetModifierValue(unitMod, BASE_VALUE) + (att_pwr_change * GetAPMultiplier(attType, false) / 14.0f);
@@ -736,13 +863,16 @@ void Creature::UpdateDamagePhysical(WeaponAttackType attType)
float total_value = GetModifierValue(unitMod, TOTAL_VALUE);
float total_pct = GetModifierValue(unitMod, TOTAL_PCT);
float dmg_multiplier = GetCreatureInfo()->dmg_multiplier;
+
if(!CanUseAttackType(attType))
{
weapon_mindamage = 0;
weapon_maxdamage = 0;
}
+
float mindamage = ((base_value + weapon_mindamage) * dmg_multiplier * base_pct + total_value) * total_pct;
float maxdamage = ((base_value + weapon_maxdamage) * dmg_multiplier * base_pct + total_value) * total_pct;
+
switch(attType)
{
case BASE_ATTACK:
@@ -760,11 +890,13 @@ void Creature::UpdateDamagePhysical(WeaponAttackType attType)
break;
}
}
+
/*#######################################
######## ########
######## PETS STAT SYSTEM ########
######## ########
#######################################*/
+
#define ENTRY_IMP 416
#define ENTRY_VOIDWALKER 1860
#define ENTRY_SUCCUBUS 1863
@@ -773,12 +905,15 @@ void Creature::UpdateDamagePhysical(WeaponAttackType attType)
#define ENTRY_WATER_ELEMENTAL 510
#define ENTRY_TREANT 1964
#define ENTRY_FIRE_ELEMENTAL 15438
+
bool Guardian::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 )
{
@@ -798,7 +933,9 @@ bool Guardian::UpdateStats(Stats stat)
if (IsPetGhoul())
value += float(owner->GetStat(stat)) * 0.3f;
}
+
SetStat(stat, int32(value));
+
switch(stat)
{
case STAT_STRENGTH: UpdateAttackPowerAndDamage(); break;
@@ -809,50 +946,64 @@ bool Guardian::UpdateStats(Stats stat)
default:
break;
}
+
return true;
}
+
bool Guardian::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 Guardian::UpdateResistances(uint32 school)
{
if(school > SPELL_SCHOOL_NORMAL)
{
float value = GetTotalAuraModValue(UnitMods(UNIT_MOD_RESISTANCE_START + school));
+
// hunter and warlock pets gain 40% of owner's resistance
if(isPet())
value += float(m_owner->GetResistance(SpellSchools(school))) * 0.4f;
+
SetResistance(SpellSchools(school), int32(value));
}
else
UpdateArmor();
}
+
void Guardian::UpdateArmor()
{
float value = 0.0f;
float bonus_armor = 0.0f;
UnitMods unitMod = UNIT_MOD_ARMOR;
+
// hunter and warlock pets gain 35% of owner's armor value
if(isPet())
bonus_armor = 0.35f * float(m_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 Guardian::UpdateMaxHealth()
{
UnitMods unitMod = UNIT_MOD_HEALTH;
float stamina = GetStat(STAT_STAMINA) - GetCreateStat(STAT_STAMINA);
+
float multiplicator;
switch(GetEntry())
{
@@ -863,17 +1014,22 @@ void Guardian::UpdateMaxHealth()
case ENTRY_FELGUARD: multiplicator = 11.0f; break;
default: multiplicator = 10.0f; break;
}
+
float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreateHealth();
value *= GetModifierValue(unitMod, BASE_PCT);
value += GetModifierValue(unitMod, TOTAL_VALUE) + stamina * multiplicator;
value *= GetModifierValue(unitMod, TOTAL_PCT);
+
SetMaxHealth((uint32)value);
}
+
void Guardian::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 multiplicator = 15.0f;
+
switch(GetEntry())
{
case ENTRY_IMP: multiplicator = 4.95f; break;
@@ -883,23 +1039,29 @@ void Guardian::UpdateMaxPower(Powers power)
case ENTRY_FELGUARD: multiplicator = 11.5f; break;
default: multiplicator = 15.0f; break;
}
+
float value = GetModifierValue(unitMod, BASE_VALUE) + GetCreatePowers(power);
value *= GetModifierValue(unitMod, BASE_PCT);
value += GetModifierValue(unitMod, TOTAL_VALUE) + addValue * multiplicator;
value *= GetModifierValue(unitMod, TOTAL_PCT);
+
SetMaxPower(power, uint32(value));
}
+
void Guardian::UpdateAttackPowerAndDamage(bool ranged)
{
if(ranged)
return;
+
float val = 0.0f;
float bonusAP = 0.0f;
UnitMods unitMod = UNIT_MOD_ATTACK_POWER;
+
if(GetEntry() == ENTRY_IMP) // 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)
{
@@ -933,24 +1095,30 @@ void Guardian::UpdateAttackPowerAndDamage(bool ranged)
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
SetInt32Value(UNIT_FIELD_ATTACK_POWER, (int32)base_attPower);
//UNIT_FIELD_(RANGED)_ATTACK_POWER_MODS field
SetInt32Value(UNIT_FIELD_ATTACK_POWER_MODS, (int32)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 Guardian::UpdateDamagePhysical(WeaponAttackType attType)
{
if(attType > BASE_ATTACK)
return;
+
float bonusDamage = 0.0f;
if(m_owner->GetTypeId() == TYPEID_PLAYER)
{
@@ -969,16 +1137,22 @@ void Guardian::UpdateDamagePhysical(WeaponAttackType attType)
bonusDamage = spellDmg * 0.4f;
}
}
+
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 + bonusDamage;
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 (isHunterPet() && attType == BASE_ATTACK)
{
@@ -999,6 +1173,7 @@ void Guardian::UpdateDamagePhysical(WeaponAttackType attType)
break;
}
}
+
SetStatFloatValue(UNIT_FIELD_MINDAMAGE, mindamage);
SetStatFloatValue(UNIT_FIELD_MAXDAMAGE, maxdamage);
}
diff --git a/src/game/TargetedMovementGenerator.cpp b/src/game/TargetedMovementGenerator.cpp
index 344df244f0b..3b001c1ed3c 100644
--- a/src/game/TargetedMovementGenerator.cpp
+++ b/src/game/TargetedMovementGenerator.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -24,7 +25,9 @@
#include "CreatureAI.h"
#include "DestinationHolderImp.h"
#include "World.h"
+
#define SMALL_ALPHA 0.05f
+
#include <cmath>
/*
struct StackCleaner
@@ -38,6 +41,7 @@ struct StackCleaner
}
};
*/
+
template<class T>
TargetedMovementGenerator<T>::TargetedMovementGenerator(Unit &target, float offset, float angle)
: TargetedMovementGeneratorBase(target)
@@ -45,14 +49,17 @@ TargetedMovementGenerator<T>::TargetedMovementGenerator(Unit &target, float offs
{
target.GetPosition(i_targetX, i_targetY, i_targetZ);
}
+
template<class T>
bool
TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return false;
+
if( owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED) )
return false;
+
float x, y, z;
Traveller<T> traveller(owner);
if(i_destinationHolder.HasDestination())
@@ -89,6 +96,7 @@ TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
if(i_target->IsWithinDist(&owner, i_offset * 0.8f))
stop = true;
}
+
if(stop)
{
owner.GetPosition(x, y, z);
@@ -98,9 +106,11 @@ TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
return false;
}
}
+
if(i_target->GetExactDistSq(i_targetX, i_targetY, i_targetZ) < 0.01f)
return false;
}
+
if(!i_offset)
{
// to nearest random contact position
@@ -116,6 +126,7 @@ TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
// 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.
@@ -126,6 +137,7 @@ TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
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 )
@@ -136,6 +148,7 @@ TargetedMovementGenerator<T>::_setTargetLocation(T &owner)
i_destinationHolder.StartTravel(traveller);
return true;
}
+
template<class T>
void
TargetedMovementGenerator<T>::Initialize(T &owner)
@@ -144,30 +157,37 @@ TargetedMovementGenerator<T>::Initialize(T &owner)
owner.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
else
owner.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
_setTargetLocation(owner);
}
+
template<class T>
void
TargetedMovementGenerator<T>::Finalize(T &owner)
{
owner.clearUnitState(UNIT_STAT_CHASE);
}
+
template<class T>
void
TargetedMovementGenerator<T>::Reset(T &owner)
{
Initialize(owner);
}
+
template<class T>
bool
TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
{
if (!i_target.isValid() || !i_target->IsInWorld())
return false;
+
if (!&owner || !owner.isAlive())
return true;
+
if (owner.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_FLEEING | UNIT_STAT_DISTRACTED))
return true;
+
// prevent movement while casting spells with cast time or channel time
if (owner.hasUnitState(UNIT_STAT_CASTING))
{
@@ -175,10 +195,13 @@ TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
owner.StopMoving();
return true;
}
+
// prevent crash after creature killed pet
if (!owner.hasUnitState(UNIT_STAT_FOLLOW) && owner.getVictim() != i_target.getTarget())
return true;
+
Traveller<T> traveller(owner);
+
if (!i_destinationHolder.HasDestination())
_setTargetLocation(owner);
else if (owner.IsStopped() && !i_destinationHolder.HasArrived())
@@ -187,11 +210,13 @@ TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
i_destinationHolder.StartTravel(traveller);
return true;
}
+
if (i_destinationHolder.UpdateTraveller(traveller, time_diff))
{
// put targeted movement generators on a higher priority
//if (owner.GetObjectSize())
//i_destinationHolder.ResetUpdate(50);
+
// target moved
if (i_targetX != i_target->GetPositionX() || i_targetY != i_target->GetPositionY()
|| i_targetZ != i_target->GetPositionZ())
@@ -200,36 +225,44 @@ TargetedMovementGenerator<T>::Update(T &owner, const uint32 & time_diff)
owner.SetInFront(i_target.getTarget());
i_target->GetPosition(i_targetX, i_targetY, i_targetZ);
}
+
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.IsWithinMeleeRange(i_target.getTarget()) && !owner.hasUnitState(UNIT_STAT_FOLLOW))
owner.Attack(i_target.getTarget(),true);
}
}
+
// Implemented for PetAI to handle resetting flags when pet owner reached
if (i_destinationHolder.HasArrived())
MovementInform(owner);
+
return true;
}
+
template<class T>
Unit*
TargetedMovementGenerator<T>::GetTarget() const
{
return i_target.getTarget();
}
+
template<class T>
void TargetedMovementGenerator<T>::MovementInform(T &unit)
{
}
+
template <> void TargetedMovementGenerator<Creature>::MovementInform(Creature &unit)
{
// Pass back the GUIDLow of the target. If it is pet's owner then PetAI will handle
unit.AI()->MovementInform(TARGETED_MOTION_TYPE, i_target.getTarget()->GetGUIDLow());
}
+
template void TargetedMovementGenerator<Player>::MovementInform(Player&); // Not implemented for players
template TargetedMovementGenerator<Player>::TargetedMovementGenerator(Unit &target, float offset, float angle);
template TargetedMovementGenerator<Creature>::TargetedMovementGenerator(Unit &target, float offset, float angle);
diff --git a/src/game/TargetedMovementGenerator.h b/src/game/TargetedMovementGenerator.h
index c830dce8078..c6e7ef05812 100644
--- a/src/game/TargetedMovementGenerator.h
+++ b/src/game/TargetedMovementGenerator.h
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TARGETEDMOVEMENTGENERATOR_H
#define TRINITY_TARGETEDMOVEMENTGENERATOR_H
+
#include "MovementGenerator.h"
#include "DestinationHolder.h"
#include "Traveller.h"
#include "FollowerReference.h"
+
class TRINITY_DLL_SPEC TargetedMovementGeneratorBase
{
public:
@@ -31,6 +34,7 @@ class TRINITY_DLL_SPEC TargetedMovementGeneratorBase
protected:
FollowerReference i_target;
};
+
template<class T>
class TRINITY_DLL_SPEC TargetedMovementGenerator
: public MovementGeneratorMedium< T, TargetedMovementGenerator<T> >, public TargetedMovementGeneratorBase
@@ -38,22 +42,29 @@ class TRINITY_DLL_SPEC TargetedMovementGenerator
public:
TargetedMovementGenerator(Unit &target, float offset = 0, float angle = 0);
~TargetedMovementGenerator() {}
+
void Initialize(T &);
void Finalize(T &);
void Reset(T &);
bool Update(T &, const uint32 &);
MovementGeneratorType GetMovementGeneratorType() { return TARGETED_MOTION_TYPE; }
+
void MovementInform(T &);
+
Unit* GetTarget() const;
+
bool GetDestination(float &x, float &y, float &z) const
{
if(i_destinationHolder.HasArrived() || !i_destinationHolder.HasDestination()) return false;
i_destinationHolder.GetDestination(x,y,z);
return true;
}
+
void unitSpeedChanged() { i_recalculateTravel=true; }
private:
+
bool _setTargetLocation(T &);
+
float i_offset;
float i_angle;
DestinationHolder< Traveller<T> > i_destinationHolder;
diff --git a/src/game/TaxiHandler.cpp b/src/game/TaxiHandler.cpp
index df6bf3c28a0..45c3cbd871b 100644
--- a/src/game/TaxiHandler.cpp
+++ b/src/game/TaxiHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -29,13 +30,17 @@
#include "Path.h"
#include "WaypointMovementGenerator.h"
#include "DestinationHolderImp.h"
+
void WorldSession::HandleTaxiNodeStatusQueryOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_TAXINODE_STATUS_QUERY" );
+
uint64 guid;
+
recv_data >> guid;
SendTaxiStatus( guid );
}
+
void WorldSession::SendTaxiStatus( uint64 guid )
{
// cheating checks
@@ -45,22 +50,29 @@ void WorldSession::SendTaxiStatus( uint64 guid )
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(),GetPlayer( )->GetTeam());
+
// 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::HandleTaxiQueryAvailableNodes( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_TAXIQUERYAVAILABLENODES" );
+
uint64 guid;
recv_data >> guid;
+
// cheating checks
Creature *unit = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!unit)
@@ -68,69 +80,92 @@ void WorldSession::HandleTaxiQueryAvailableNodes( WorldPacket & recv_data )
sLog.outDebug( "WORLD: HandleTaxiQueryAvailableNodes - 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()->RemoveAurasByType(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(),GetPlayer( )->GetTeam());
+
if ( curloc == 0 )
return;
+
bool lastTaxiCheaterState = GetPlayer()->isTaxiCheater();
if(unit->GetEntry() == 29480) GetPlayer()->SetTaxiCheater(true); // Grimwing in Ebon Hold, special case. NOTE: Not perfect, Zul'Aman should not be included according to WoWhead, and I think taxicheat includes it.
+
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" );
+
GetPlayer()->SetTaxiCheater(lastTaxiCheaterState);
}
+
void WorldSession::SendDoFlight( uint32 mountDisplayId, uint32 path, uint32 pathNode )
{
// remove fake death
if (GetPlayer()->hasUnitState(UNIT_STAT_DIED))
GetPlayer()->RemoveAurasByType(SPELL_AURA_FEIGN_DEATH);
+
while(GetPlayer()->GetMotionMaster()->GetCurrentMovementGeneratorType()==FLIGHT_MOTION_TYPE)
GetPlayer()->GetMotionMaster()->MovementExpired(false);
+
if (mountDisplayId)
GetPlayer()->Mount( mountDisplayId );
+
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(),GetPlayer( )->GetTeam());
+
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::HandleActivateTaxiExpressOpcode ( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXIEXPRESS" );
+
uint64 guid;
uint32 node_count, _totalcost;
+
recv_data >> guid >> _totalcost >> node_count;
+
Creature *npc = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
if (!npc)
{
@@ -138,20 +173,26 @@ void WorldSession::HandleActivateTaxiExpressOpcode ( WorldPacket & recv_data )
return;
}
std::vector<uint32> 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, npc);
}
+
void WorldSession::HandleMoveSplineDoneOpcode(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
@@ -159,7 +200,9 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& /*recv_data*/)
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())
{
@@ -167,18 +210,22 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& /*recv_data*/)
{
// 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())
{
@@ -188,27 +235,35 @@ void WorldSession::HandleMoveSplineDoneOpcode(WorldPacket& /*recv_data*/)
_player->GetSession()->SendPacket( &data );
}
}
+
sLog.outDebug( "WORLD: Taxi has to go from %u to %u", sourcenode, destinationnode );
+
uint32 mountDisplayId = objmgr.GetTaxiMountDisplayId(sourcenode, GetPlayer()->GetTeam());
+
uint32 path, cost;
objmgr.GetTaxiPath( sourcenode, destinationnode, path, cost);
+
if(path && mountDisplayId)
SendDoFlight( mountDisplayId, path, 1 ); // skip start fly node
else
GetPlayer()->m_taxi.ClearTaxiDestinations(); // clear problematic path and next
return;
}
+
GetPlayer()->CleanupAfterTaxiFlight();
GetPlayer()->SetFallInformation(0, GetPlayer()->GetPositionZ());
if(GetPlayer()->pvpInfo.inHostileArea)
GetPlayer()->CastSpell(GetPlayer(), 2479, true);
}
+
void WorldSession::HandleActivateTaxiOpcode( WorldPacket & recv_data )
{
sLog.outDebug( "WORLD: Received CMSG_ACTIVATETAXI" );
+
uint64 guid;
std::vector<uint32> 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 = GetPlayer()->GetNPCIfCanInteractWith(guid, UNIT_NPC_FLAG_FLIGHTMASTER);
@@ -217,6 +272,7 @@ void WorldSession::HandleActivateTaxiOpcode( WorldPacket & recv_data )
sLog.outDebug( "WORLD: HandleActivateTaxiOpcode - Unit (GUID: %u) not found or you can't interact with it.", uint32(GUID_LOPART(guid)) );
return;
}
+
GetPlayer()->ActivateTaxiPathTo(nodes, npc);
}
diff --git a/src/game/TemporarySummon.cpp b/src/game/TemporarySummon.cpp
index 05ae270dabd..0b31eea9707 100644
--- a/src/game/TemporarySummon.cpp
+++ b/src/game/TemporarySummon.cpp
@@ -17,11 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "Log.h"
#include "ObjectAccessor.h"
#include "CreatureAI.h"
#include "ObjectMgr.h"
#include "TemporarySummon.h"
+
TempSummon::TempSummon(SummonPropertiesEntry const *properties, Unit *owner) :
Creature(), m_type(TEMPSUMMON_MANUAL_DESPAWN), m_timer(0), m_lifetime(0)
, m_Properties(properties)
@@ -29,13 +31,16 @@ Creature(), m_type(TEMPSUMMON_MANUAL_DESPAWN), m_timer(0), m_lifetime(0)
m_summonerGUID = owner ? owner->GetGUID() : 0;
m_unitTypeMask |= UNIT_MASK_SUMMON;
}
+
Unit* TempSummon::GetSummoner() const
{
return m_summonerGUID ? ObjectAccessor::GetUnit(*this, m_summonerGUID) : NULL;
}
+
void TempSummon::Update( uint32 diff )
{
Creature::Update(diff);
+
if (m_deathState == DEAD)
{
UnSummon();
@@ -52,6 +57,7 @@ void TempSummon::Update( uint32 diff )
UnSummon();
return;
}
+
m_timer -= diff;
break;
}
@@ -64,12 +70,15 @@ void TempSummon::Update( uint32 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)
@@ -79,6 +88,7 @@ void TempSummon::Update( uint32 diff )
UnSummon();
return;
}
+
m_timer -= diff;
}
break;
@@ -91,6 +101,7 @@ void TempSummon::Update( uint32 diff )
UnSummon();
return;
}
+
break;
}
case TEMPSUMMON_DEAD_DESPAWN:
@@ -110,6 +121,7 @@ void TempSummon::Update( uint32 diff )
UnSummon();
return;
}
+
if (!isInCombat())
{
if (m_timer <= diff)
@@ -132,6 +144,7 @@ void TempSummon::Update( uint32 diff )
UnSummon();
return;
}
+
if (!isInCombat() && isAlive() )
{
if (m_timer <= diff)
@@ -152,14 +165,19 @@ void TempSummon::Update( uint32 diff )
break;
}
}
+
void TempSummon::InitStats(uint32 duration)
{
assert(!isPet());
+
m_timer = duration;
m_lifetime = duration;
+
if(m_type == TEMPSUMMON_MANUAL_DESPAWN)
m_type = (duration == 0) ? TEMPSUMMON_DEAD_DESPAWN : TEMPSUMMON_TIMED_DESPAWN;
+
Unit *owner = GetSummoner();
+
if(owner && isTrigger() && m_spells[0])
{
setFaction(owner->getFaction());
@@ -167,8 +185,10 @@ void TempSummon::InitStats(uint32 duration)
if(owner->GetTypeId() == TYPEID_PLAYER)
m_ControlledByPlayer = true;
}
+
if(!m_Properties)
return;
+
if(owner)
{
if(uint32 slot = m_Properties->Slot)
@@ -182,11 +202,13 @@ void TempSummon::InitStats(uint32 duration)
owner->m_SummonSlot[slot] = GetGUID();
}
}
+
if(m_Properties->Faction)
setFaction(m_Properties->Faction);
else if(IsVehicle()) // properties should be vehicle
setFaction(owner->getFaction());
}
+
void TempSummon::InitSummon()
{
Unit* owner = GetSummoner();
@@ -198,10 +220,12 @@ void TempSummon::InitSummon()
AI()->IsSummonedBy(owner);
}
}
+
void TempSummon::SetTempSummonType(TempSummonType type)
{
m_type = type;
}
+
void TempSummon::UnSummon()
{
//assert(!isPet());
@@ -211,15 +235,19 @@ void TempSummon::UnSummon()
assert(!IsInWorld());
return;
}
+
Unit* owner = GetSummoner();
if(owner && owner->GetTypeId() == TYPEID_UNIT && ((Creature*)owner)->IsAIEnabled)
((Creature*)owner)->AI()->SummonedCreatureDespawn(this);
+
AddObjectToRemoveList();
}
+
void TempSummon::RemoveFromWorld()
{
if(!IsInWorld())
return;
+
if(m_Properties)
{
if(uint32 slot = m_Properties->Slot)
@@ -231,13 +259,17 @@ void TempSummon::RemoveFromWorld()
}
}
}
+
//if(GetOwnerGUID())
// sLog.outError("Unit %u has owner guid when removed from world", GetEntry());
+
Creature::RemoveFromWorld();
}
+
void TempSummon::SaveToDB()
{
}
+
Minion::Minion(SummonPropertiesEntry const *properties, Unit *owner) : TempSummon(properties, owner)
, m_owner(owner)
{
@@ -245,25 +277,33 @@ Minion::Minion(SummonPropertiesEntry const *properties, Unit *owner) : TempSummo
m_unitTypeMask |= UNIT_MASK_MINION;
m_followAngle = PET_FOLLOW_ANGLE;
}
+
void Minion::InitStats(uint32 duration)
{
TempSummon::InitStats(duration);
+
SetReactState(REACT_PASSIVE);
+
SetCreatorGUID(m_owner->GetGUID());
setFaction(m_owner->getFaction());
+
m_owner->SetMinion(this, true);
}
+
void Minion::RemoveFromWorld()
{
if(!IsInWorld())
return;
+
m_owner->SetMinion(this, false);
TempSummon::RemoveFromWorld();
}
+
bool Minion::IsGuardianPet() const
{
return isPet() || m_Properties && m_Properties->Category == SUMMON_CATEGORY_PET;
}
+
Guardian::Guardian(SummonPropertiesEntry const *properties, Unit *owner) : Minion(properties, owner)
, m_bonusdamage(0)
{
@@ -274,40 +314,50 @@ Guardian::Guardian(SummonPropertiesEntry const *properties, Unit *owner) : Minio
InitCharmInfo();
}
}
+
void Guardian::InitStats(uint32 duration)
{
Minion::InitStats(duration);
+
InitStatsForLevel(m_owner->getLevel());
+
if(m_owner->GetTypeId() == TYPEID_PLAYER && HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
m_charmInfo->InitCharmCreateSpells();
+
SetReactState(REACT_AGGRESSIVE);
}
+
void Guardian::InitSummon()
{
TempSummon::InitSummon();
+
if(m_owner->GetTypeId() == TYPEID_PLAYER
&& m_owner->GetMinionGUID() == GetGUID()
&& !m_owner->GetCharmGUID())
((Player*)m_owner)->CharmSpellInitialize();
}
+
Puppet::Puppet(SummonPropertiesEntry const *properties, Unit *owner) : Minion(properties, owner)
{
assert(owner->GetTypeId() == TYPEID_PLAYER);
m_owner = (Player*)owner;
m_unitTypeMask |= UNIT_MASK_PUPPET;
}
+
void Puppet::InitStats(uint32 duration)
{
Minion::InitStats(duration);
SetLevel(m_owner->getLevel());
SetReactState(REACT_PASSIVE);
}
+
void Puppet::InitSummon()
{
Minion::InitSummon();
if (!SetCharmedBy(m_owner, CHARM_TYPE_POSSESS))
assert(false);
}
+
void Puppet::Update(uint32 time)
{
Minion::Update(time);
@@ -321,10 +371,12 @@ void Puppet::Update(uint32 time)
}
}
}
+
void Puppet::RemoveFromWorld()
{
if(!IsInWorld())
return;
+
RemoveCharmedBy(NULL);
Minion::RemoveFromWorld();
}
diff --git a/src/game/TemporarySummon.h b/src/game/TemporarySummon.h
index 2ff56c6e74c..e67a5573ce2 100644
--- a/src/game/TemporarySummon.h
+++ b/src/game/TemporarySummon.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_TEMPSUMMON_H
#define TRINITYCORE_TEMPSUMMON_H
+
#include "Creature.h"
+
class TRINITY_DLL_SPEC TempSummon : public Creature
{
public:
@@ -34,6 +37,7 @@ class TRINITY_DLL_SPEC TempSummon : public Creature
void SaveToDB();
Unit* GetSummoner() const;
uint64 const& GetSummonerGUID() { return m_summonerGUID; }
+
const SummonPropertiesEntry * const m_Properties;
private:
TempSummonType m_type;
@@ -41,6 +45,7 @@ class TRINITY_DLL_SPEC TempSummon : public Creature
uint32 m_lifetime;
uint64 m_summonerGUID;
};
+
class Minion : public TempSummon
{
public:
@@ -56,6 +61,7 @@ class Minion : public TempSummon
Unit * const m_owner;
float m_followAngle;
};
+
class Guardian : public Minion
{
public:
@@ -63,6 +69,7 @@ class Guardian : public Minion
void InitStats(uint32 duration);
bool InitStatsForLevel(uint32 level);
void InitSummon();
+
bool UpdateStats(Stats stat);
bool UpdateAllStats();
void UpdateResistances(uint32 school);
@@ -71,11 +78,13 @@ class Guardian : public Minion
void UpdateMaxPower(Powers power);
void UpdateAttackPowerAndDamage(bool ranged = false);
void UpdateDamagePhysical(WeaponAttackType attType);
+
int32 GetBonusDamage() { return m_bonusdamage; }
void SetBonusDamage(int32 damage) { m_bonusdamage = damage; }
protected:
int32 m_bonusdamage;
};
+
class Puppet : public Minion
{
public:
@@ -87,5 +96,6 @@ class Puppet : public Minion
protected:
Player *m_owner;
};
+
#endif
diff --git a/src/game/ThreatManager.cpp b/src/game/ThreatManager.cpp
index 6e16125d348..11e4f2ab61e 100644
--- a/src/game/ThreatManager.cpp
+++ b/src/game/ThreatManager.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -25,21 +26,26 @@
#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 = pHatedUnit->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;
@@ -49,32 +55,40 @@ HostilReference::HostilReference(Unit* pUnit, ThreatManager *pThreatManager, flo
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(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent)
{
if(getSource())
getSource()->processThreatEvent(&pThreatRefStatusChangeEvent);
}
+
//============================================================
+
void HostilReference::addThreat(float pMod)
{
iThreat += pMod;
@@ -87,6 +101,7 @@ void HostilReference::addThreat(float pMod)
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_THREAT_CHANGE, this, pMod);
fireStatusChanged(event);
}
+
if(isValid() && pMod >= 0)
{
Unit* victim_owner = getTarget()->GetCharmerOrOwner();
@@ -94,12 +109,15 @@ void HostilReference::addThreat(float pMod)
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());
@@ -123,12 +141,15 @@ void HostilReference::updateOnlineStatus()
}
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)
@@ -136,37 +157,48 @@ void HostilReference::setOnlineOfflineState(bool pIsOnline)
iOnline = pIsOnline;
if(!iOnline)
setAccessibleState(false); // if not online that not accessable as well
+
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ONLINE_STATUS, this);
fireStatusChanged(event);
}
}
+
//============================================================
+
void HostilReference::setAccessibleState(bool pIsAccessible)
{
if(iAccessible != pIsAccessible)
{
iAccessible = pIsAccessible;
+
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_ASSECCIBLE_STATUS, this);
fireStatusChanged(event);
}
}
+
//============================================================
// prepare the reference for deleting
// this is called be the target
+
void HostilReference::removeReference()
{
invalidate();
+
ThreatRefStatusChangeEvent event(UEV_THREAT_REF_REMOVE_FROM_LIST, this);
fireStatusChanged(event);
}
+
//============================================================
+
Unit* HostilReference::getSourceUnit()
{
return (getSource()->getOwner());
}
+
//============================================================
//================ ThreatContainer ===========================
//============================================================
+
void ThreatContainer::clearReferences()
{
for(std::list<HostilReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
@@ -176,11 +208,13 @@ void ThreatContainer::clearReferences()
}
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<HostilReference*>::const_iterator i = iThreatList.begin(); i != iThreatList.end(); ++i)
{
@@ -190,10 +224,13 @@ HostilReference* ThreatContainer::getReferenceByTarget(Unit* pVictim)
break;
}
}
+
return result;
}
+
//============================================================
// Add the threat, if we find the reference
+
HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
{
HostilReference* ref = getReferenceByTarget(pVictim);
@@ -201,20 +238,26 @@ HostilReference* ThreatContainer::addThreat(Unit* pVictim, float pThreat)
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)
@@ -223,21 +266,27 @@ void ThreatContainer::update()
}
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;
bool noPriorityTargetFound = false;
+
std::list<HostilReference*>::const_iterator lastRef = iThreatList.end();
lastRef--;
+
for(std::list<HostilReference*>::const_iterator iter = iThreatList.begin(); iter != iThreatList.end() && !found;)
{
currentRef = (*iter);
+
Unit* target = currentRef->getTarget();
assert(target); // if the ref has status online the target must be there !
+
// some units are prefered in comparison to others
if(!noPriorityTargetFound && (target->IsImmunedToDamage(pAttacker->GetMeleeDamageSchoolMask()) || target->hasNegativeAuraWithInterruptFlag(AURA_INTERRUPT_FLAG_TAKE_DAMAGE)) )
{
@@ -257,6 +306,7 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe
continue;
}
}
+
if(pAttacker->canCreatureAttack(target)) // skip non attackable currently targets
{
if(pCurrentVictim) // select 1.3/1.1 better target in comparison current target
@@ -268,6 +318,7 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe
found = true;
break;
}
+
if (currentRef->getThreat() > 1.3f * pCurrentVictim->getThreat() ||
currentRef->getThreat() > 1.1f * pCurrentVictim->getThreat() &&
pAttacker->IsWithinMeleeRange(target))
@@ -286,15 +337,20 @@ HostilReference* ThreatContainer::selectNextVictim(Creature* pAttacker, HostilRe
}
if(!found)
currentRef = NULL;
+
return currentRef;
}
+
//============================================================
//=================== ThreatManager ==========================
//============================================================
+
ThreatManager::ThreatManager(Unit* owner) : iCurrentVictim(NULL), iOwner(owner), iUpdateTimer(THREAT_UPDATE_INTERVAL)
{
}
+
//============================================================
+
void ThreatManager::clearReferences()
{
iThreatContainer.clearReferences();
@@ -302,24 +358,32 @@ void ThreatManager::clearReferences()
iCurrentVictim = NULL;
iUpdateTimer = THREAT_UPDATE_INTERVAL;
}
+
//============================================================
+
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.)
+
// not to self
if (pVictim == getOwner())
return;
+
// not to GM
if(!pVictim || (pVictim->GetTypeId() == TYPEID_PLAYER && ((Player*)pVictim)->isGameMaster()) )
return;
+
// not to dead and not for dead
if(!pVictim->isAlive() || !getOwner()->isAlive() )
return;
+
assert(getOwner()->GetTypeId()== TYPEID_UNIT);
+
float threat = ThreatCalcHelper::calcThreat(pVictim, iOwner, pThreat, schoolMask, pThreatSpell);
+
// must check > 0.0f, otherwise dead loop
if(threat > 0.0f && pVictim->GetReducedThreatPercent())
{
@@ -328,14 +392,17 @@ void ThreatManager::addThreat(Unit* pVictim, float pThreat, SpellSchoolMask scho
if(Unit *unit = pVictim->GetMisdirectionTarget())
_addThreat(unit, reducedThreat);
}
+
_addThreat(pVictim, threat);
}
+
void ThreatManager::_addThreat(Unit *pVictim, float threat)
{
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
@@ -346,12 +413,16 @@ void ThreatManager::_addThreat(Unit *pVictim, float threat)
hostilReference->setOnlineOfflineState(false); // GM is always offline
}
}
+
//============================================================
+
void ThreatManager::modifyThreatPercent(Unit *pVictim, int32 pPercent)
{
iThreatContainer.modifyThreatPercent(pVictim, pPercent);
}
+
//============================================================
+
Unit* ThreatManager::getHostilTarget()
{
iThreatContainer.update();
@@ -359,7 +430,9 @@ Unit* ThreatManager::getHostilTarget()
setCurrentVictim(nextVictim);
return getCurrentVictim() != NULL ? getCurrentVictim()->getTarget() : NULL;
}
+
//============================================================
+
float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
{
float threat = 0.0f;
@@ -370,7 +443,9 @@ float ThreatManager::getThreat(Unit *pVictim, bool pAlsoSearchOfflineList)
threat = ref->getThreat();
return threat;
}
+
//============================================================
+
void ThreatManager::tauntApply(Unit* pTaunter)
{
HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
@@ -381,14 +456,18 @@ void ThreatManager::tauntApply(Unit* pTaunter)
ref->setTempThreat(getCurrentVictim()->getThreat());
}
}
+
//============================================================
+
void ThreatManager::tauntFadeOut(Unit *pTaunter)
{
HostilReference* ref = iThreatContainer.getReferenceByTarget(pTaunter);
if(ref)
ref->resetTempThreat();
}
+
//============================================================
+
void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
{
if (pHostilReference && pHostilReference != iCurrentVictim)
@@ -397,13 +476,17 @@ void ThreatManager::setCurrentVictim(HostilReference* pHostilReference)
}
iCurrentVictim = pHostilReference;
}
+
//============================================================
// The hated unit is gone, dead or deleted
// return true, if the event is consumed
+
void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent)
{
threatRefStatusChangeEvent->setThreatManager(this); // now we can set the threat manager
+
HostilReference* hostilReference = threatRefStatusChangeEvent->getReference();
+
switch(threatRefStatusChangeEvent->getType())
{
case UEV_THREAT_REF_THREAT_CHANGE:
@@ -444,6 +527,7 @@ void ThreatManager::processThreatEvent(ThreatRefStatusChangeEvent* threatRefStat
break;
}
}
+
bool ThreatManager::isNeedUpdateToClient(uint32 time)
{
if (isThreatListEmpty())
diff --git a/src/game/ThreatManager.h b/src/game/ThreatManager.h
index db527d27c42..1dba6277cfc 100644
--- a/src/game/ThreatManager.h
+++ b/src/game/ThreatManager.h
@@ -17,43 +17,60 @@
* 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 <list>
+
//==============================================================
+
class Unit;
class Creature;
class ThreatManager;
struct SpellEntry;
+
#define THREAT_UPDATE_INTERVAL 1 * IN_MILISECONDS // Server should send threat update to client periodically each second
+
//==============================================================
// 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 TRINITY_DLL_SPEC HostilReference : public Reference<Unit, ThreatManager>
{
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 accessible = false
bool isAccessible() 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)
@@ -61,31 +78,47 @@ class TRINITY_DLL_SPEC HostilReference : public Reference<Unit, ThreatManager>
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<Unit, ThreatManager>::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();
private:
// Inform the source, that the status of that reference was changed
void fireStatusChanged(ThreatRefStatusChangeEvent& pThreatRefStatusChangeEvent);
+
Unit* getSourceUnit();
private:
float iThreat;
@@ -94,8 +127,10 @@ class TRINITY_DLL_SPEC HostilReference : public Reference<Unit, ThreatManager>
bool iOnline;
bool iAccessible;
};
+
//==============================================================
class ThreatManager;
+
class TRINITY_DLL_SPEC ThreatContainer
{
private:
@@ -103,6 +138,7 @@ class TRINITY_DLL_SPEC ThreatContainer
bool iDirty;
protected:
friend class ThreatManager;
+
void remove(HostilReference* pRef) { iThreatList.remove(pRef); }
void addReference(HostilReference* pHostilReference) { iThreatList.push_back(pHostilReference); }
void clearReferences();
@@ -111,37 +147,63 @@ class TRINITY_DLL_SPEC ThreatContainer
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<HostilReference*>& getThreatList() { return iThreatList; }
};
+
//=================================================
+
class TRINITY_DLL_SPEC ThreatManager
{
public:
friend class HostilReference;
+
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();}
+
void processThreatEvent(ThreatRefStatusChangeEvent* threatRefStatusChangeEvent);
+
bool isNeedUpdateToClient(uint32 time);
+
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.
std::list<HostilReference*>& getThreatList() { return iThreatContainer.getThreatList(); }
@@ -150,12 +212,14 @@ class TRINITY_DLL_SPEC ThreatManager
ThreatContainer& getOfflineContainer() { return iThreatOfflineContainer; }
private:
void _addThreat(Unit *pVictim, float threat);
+
HostilReference* iCurrentVictim;
Unit* iOwner;
uint32 iUpdateTimer;
ThreatContainer iThreatContainer;
ThreatContainer iThreatOfflineContainer;
};
+
//=================================================
#endif
diff --git a/src/game/TicketHandler.cpp b/src/game/TicketHandler.cpp
index 091b26519b9..2ac0c601edc 100644
--- a/src/game/TicketHandler.cpp
+++ b/src/game/TicketHandler.cpp
@@ -17,12 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "Language.h"
#include "WorldPacket.h"
#include "Common.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "World.h"
+
void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
{
if(GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID()))
@@ -32,9 +34,11 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
SendPacket( &data );
return;
}
+
uint32 map;
float x, y, z;
std::string ticketText, ticketText2;
+
WorldPacket data(SMSG_GMTICKET_CREATE, 4);
recv_data >> map;
recv_data >> x;
@@ -42,7 +46,8 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
recv_data >> z;
recv_data >> ticketText;
recv_data >> ticketText2;
- GM_Ticket *ticket = new GM_Ticket;
+
+ GM_Ticket *ticket = new GM_Ticket;
ticket->name = GetPlayer()->GetName();
ticket->guid = objmgr.GenerateGMTicketId();
ticket->playerGuid = GetPlayer()->GetGUID();
@@ -56,16 +61,23 @@ void WorldSession::HandleGMTicketCreateOpcode( WorldPacket & recv_data )
ticket->closed = 0;
ticket->assignedToGM = 0;
ticket->comment = "";
+
objmgr.AddOrUpdateGMTicket(*ticket, true);
+
data << uint32(2);
SendPacket(&data);
+
sWorld.SendGMText(LANG_COMMAND_TICKETNEW, GetPlayer()->GetName(), ticket->guid);
+
}
+
void WorldSession::HandleGMTicketUpdateOpcode( WorldPacket & recv_data)
{
WorldPacket data(SMSG_GMTICKET_UPDATETEXT, 4);
+
std::string message;
recv_data >> message;
+
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if(!ticket)
{
@@ -73,44 +85,57 @@ void WorldSession::HandleGMTicketUpdateOpcode( WorldPacket & recv_data)
SendPacket(&data);
return;
}
+
ticket->message = message;
ticket->timestamp = time(NULL);
+
objmgr.AddOrUpdateGMTicket(*ticket);
+
data << uint32(2);
SendPacket(&data);
+
sWorld.SendGMText(LANG_COMMAND_TICKETUPDATED, GetPlayer()->GetName(), ticket->guid);
+
}
+
void WorldSession::HandleGMTicketDeleteOpcode( WorldPacket & /*recv_data*/)
{
GM_Ticket* ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
+
if(ticket)
{
WorldPacket data(SMSG_GMTICKET_DELETETICKET, 4);
data << uint32(9);
SendPacket(&data);
+
sWorld.SendGMText(LANG_COMMAND_TICKETPLAYERABANDON, GetPlayer()->GetName(), ticket->guid );
objmgr.RemoveGMTicket(ticket, GetPlayer()->GetGUID(), false);
SendGMTicketGetTicket(0x0A, 0);
}
}
+
void WorldSession::HandleGMTicketGetTicketOpcode( WorldPacket & /*recv_data*/)
{
WorldPacket data( SMSG_QUERY_TIME_RESPONSE, 4+4 );
data << (uint32)time(NULL);
data << (uint32)0;
SendPacket( &data );
+
GM_Ticket *ticket = objmgr.GetGMTicketByPlayer(GetPlayer()->GetGUID());
if(ticket)
SendGMTicketGetTicket(0x06, ticket->message.c_str());
else
SendGMTicketGetTicket(0x0A, 0);
+
}
+
void WorldSession::HandleGMTicketSystemStatusOpcode( WorldPacket & /*recv_data*/)
{
WorldPacket data(SMSG_GMTICKET_SYSTEMSTATUS, 4);
data << uint32(1);
SendPacket(&data);
}
+
void WorldSession::SendGMTicketGetTicket(uint32 status, char const* text)
{
int len = text ? strlen(text) : 0;
diff --git a/src/game/Totem.cpp b/src/game/Totem.cpp
index 5a4a0d8714a..f841ac2fd9c 100644
--- a/src/game/Totem.cpp
+++ b/src/game/Totem.cpp
@@ -17,6 +17,7 @@
* 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 "Log.h"
@@ -24,12 +25,14 @@
#include "Player.h"
#include "ObjectMgr.h"
#include "SpellMgr.h"
+
Totem::Totem(SummonPropertiesEntry const *properties, Unit *owner) : Minion(properties, owner)
{
m_unitTypeMask |= UNIT_MASK_TOTEM;
m_duration = 0;
m_type = TOTEM_PASSIVE;
}
+
void Totem::Update( uint32 time )
{
if (!m_owner->isAlive() || !isAlive())
@@ -37,6 +40,7 @@ void Totem::Update( uint32 time )
UnSummon(); // remove self
return;
}
+
if (m_duration <= time)
{
UnSummon(); // remove self
@@ -44,11 +48,14 @@ void Totem::Update( uint32 time )
}
else
m_duration -= time;
+
Creature::Update( time );
}
+
void Totem::InitStats(uint32 duration)
{
Minion::InitStats(duration);
+
CreatureInfo const *cinfo = GetCreatureInfo();
if(m_owner->GetTypeId() == TYPEID_PLAYER && cinfo)
{
@@ -58,6 +65,7 @@ void Totem::InitStats(uint32 duration)
display_id = minfo->modelid;
SetDisplayId(display_id);
}
+
// Get spell casted by totem
SpellEntry const * totemSpell = sSpellStore.LookupEntry(GetSpell());
if (totemSpell)
@@ -66,27 +74,36 @@ void Totem::InitStats(uint32 duration)
if (GetSpellCastTime(totemSpell))
m_type = TOTEM_ACTIVE;
}
+
if(GetEntry() == SENTRY_TOTEM_ENTRY)
SetReactState(REACT_AGGRESSIVE);
+
m_duration = duration;
+
SetLevel(m_owner->getLevel());
}
+
void Totem::InitSummon()
{
WorldPacket data(SMSG_GAMEOBJECT_SPAWN_ANIM_OBSOLETE, 8);
data << GetGUID();
SendMessageToSet(&data, true);
+
if(m_type == TOTEM_PASSIVE)
- CastSpell(this, GetSpell(), true);
+ CastSpell(this, GetSpell(), true);
+
// Some totems can have both instant effect and passive spell
if (GetSpell(1))
CastSpell(this, GetSpell(1), true);
}
+
void Totem::UnSummon()
{
SendObjectDeSpawnAnim(GetGUID());
+
CombatStop();
RemoveAurasDueToSpell(GetSpell());
+
// clear owenr's totem slot
for(int i = SUMMON_SLOT_TOTEM; i < MAX_TOTEM_SLOT; ++i)
{
@@ -96,7 +113,9 @@ void Totem::UnSummon()
break;
}
}
+
m_owner->RemoveAurasDueToSpell(GetSpell());
+
//remove aura all party members too
Group *pGroup = NULL;
if (m_owner->GetTypeId() == TYPEID_PLAYER)
@@ -113,8 +132,10 @@ void Totem::UnSummon()
}
}
}
+
AddObjectToRemoveList();
}
+
bool Totem::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const
{
// TODO: possibly all negative auras immune?
diff --git a/src/game/Totem.h b/src/game/Totem.h
index 1a1d28f2dc6..6dbd699f1d2 100644
--- a/src/game/Totem.h
+++ b/src/game/Totem.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_TOTEM_H
#define TRINITYCORE_TOTEM_H
+
#include "TemporarySummon.h"
+
enum TotemType
{
TOTEM_PASSIVE = 0,
TOTEM_ACTIVE = 1,
TOTEM_STATUE = 2 // copied straight from MaNGOS, may need more implementation to work
};
+
#define SENTRY_TOTEM_ENTRY 3968
+
class Totem : public Minion
{
public:
@@ -39,6 +44,7 @@ class Totem : public Minion
uint32 GetSpell(uint8 slot=0) const { return m_spells[slot]; }
uint32 GetTotemDuration() const { return m_duration; }
TotemType GetTotemType() const { return m_type; }
+
bool UpdateStats(Stats /*stat*/) { return true; }
bool UpdateAllStats() { return true; }
void UpdateResistances(uint32 /*school*/) {}
@@ -47,7 +53,9 @@ class Totem : public Minion
void UpdateMaxPower(Powers /*power*/) {}
void UpdateAttackPowerAndDamage(bool /*ranged*/ ) {}
void UpdateDamagePhysical(WeaponAttackType /*attType*/) {}
+
bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const;
+
protected:
TotemType m_type;
uint32 m_duration;
diff --git a/src/game/TotemAI.cpp b/src/game/TotemAI.cpp
index 5dfbcf6bfdd..08c70308d1c 100644
--- a/src/game/TotemAI.cpp
+++ b/src/game/TotemAI.cpp
@@ -17,51 +17,65 @@
* 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 "DBCStores.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) : CreatureAI(c), i_victimGuid(0)
{
assert(c->isTotem());
}
+
void
TotemAI::MoveInLineOfSight(Unit *)
{
}
+
void TotemAI::EnterEvadeMode()
{
m_creature->CombatStop(true);
}
+
void
TotemAI::UpdateAI(const uint32 /*diff*/)
{
if (((Totem*)m_creature)->GetTotemType() != TOTEM_ACTIVE)
return;
+
if (!m_creature->isAlive() || m_creature->IsNonMeleeSpellCasted(false))
return;
+
// Search spell
SpellEntry const *spellInfo = sSpellStore.LookupEntry(((Totem*)m_creature)->GetSpell());
if (!spellInfo)
return;
+
// Get spell range
SpellRangeEntry const* srange = sSpellRangeStore.LookupEntry(spellInfo->rangeIndex);
float max_range = GetSpellMaxRangeForHostile(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(*m_creature, i_victimGuid) : NULL;
+
// Search victim if no, not attackable, or out of range, or friendly (possible in case duel end)
if( !victim ||
!victim->isTargetableForAttack() || !m_creature->IsWithinDistInMap(victim, max_range) ||
@@ -72,11 +86,13 @@ TotemAI::UpdateAI(const uint32 /*diff*/)
Trinity::UnitLastSearcher<Trinity::NearestAttackableUnitInObjectRangeCheck> checker(m_creature, victim, u_check);
m_creature->VisitNearbyObject(max_range, checker);
}
+
// If have target
if (victim)
{
// remember
i_victimGuid = victim->GetGUID();
+
// attack
m_creature->SetInFront(victim); // client change orientation by self
m_creature->CastSpell(victim, ((Totem*)m_creature)->GetSpell(), false);
@@ -84,6 +100,7 @@ TotemAI::UpdateAI(const uint32 /*diff*/)
else
i_victimGuid = 0;
}
+
void
TotemAI::AttackStart(Unit *)
{
diff --git a/src/game/TotemAI.h b/src/game/TotemAI.h
index 4f652f23cce..003f5d5ca13 100644
--- a/src/game/TotemAI.h
+++ b/src/game/TotemAI.h
@@ -17,21 +17,29 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TOTEMAI_H
#define TRINITY_TOTEMAI_H
+
#include "CreatureAI.h"
#include "Timer.h"
+
class Creature;
class Totem;
+
class TRINITY_DLL_DECL TotemAI : public CreatureAI
{
public:
+
explicit TotemAI(Creature *c);
+
void MoveInLineOfSight(Unit *);
void AttackStart(Unit *);
void EnterEvadeMode();
+
void UpdateAI(const uint32);
static int Permissible(const Creature *);
+
private:
uint64 i_victimGuid;
};
diff --git a/src/game/TradeHandler.cpp b/src/game/TradeHandler.cpp
index e7c87cef3e6..b4cc62ba39a 100644
--- a/src/game/TradeHandler.cpp
+++ b/src/game/TradeHandler.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -28,6 +29,7 @@
#include "Item.h"
#include "SocialMgr.h"
#include "Language.h"
+
enum TradeStatus
{
TRADE_STATUS_BUSY = 0,
@@ -54,9 +56,11 @@ enum TradeStatus
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:
@@ -86,34 +90,42 @@ void WorldSession::SendTradeStatus(uint32 status)
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?)
@@ -121,10 +133,13 @@ void WorldSession::SendUpdateTrade()
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
@@ -158,8 +173,10 @@ void WorldSession::SendUpdateTrade()
}
SendPacket(&data);
}
+
//==============================================================
// transfer the items to the players
+
void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
{
for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
@@ -184,6 +201,7 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
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);
}
@@ -198,6 +216,7 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
hisItems[i]->GetProto()->Name1,hisItems[i]->GetEntry(),hisItems[i]->GetCount(),
_player->GetName(),_player->GetSession()->GetAccountId());
}
+
// store
_player->MoveItemToInventory( playerDst, hisItems[i], true, true);
}
@@ -228,14 +247,18 @@ void WorldSession::moveItems(Item* myItems[], Item* hisItems[])
}
}
}
+
//==============================================================
+
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() )
{
@@ -244,6 +267,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
_player->acceptTrade = false;
return;
}
+
// not accept case incorrect money amount
if( _player->pTrader->tradeGold > _player->pTrader->GetMoney() )
{
@@ -252,6 +276,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
_player->pTrader->acceptTrade = false;
return;
}
+
// not accept if some items now can't be trade (cheating)
for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
@@ -278,11 +303,13 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
}
}
}
+
_player->acceptTrade = true;
if (_player->pTrader->acceptTrade )
{
// inform partner client
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_TRADE_ACCEPT);
+
// store items in local list and set 'in-trade' flag
for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
@@ -303,15 +330,18 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
hisItems[i]->SetInTrade();
}
}
+
// test if item will fit in each inventory
hisCanCompleteTrade = (_player->pTrader->CanStoreItems( myItems,TRADE_SLOT_TRADED_COUNT )== EQUIP_ERR_OK);
myCanCompleteTrade = (_player->CanStoreItems( hisItems,TRADE_SLOT_TRADED_COUNT ) == EQUIP_ERR_OK);
+
// clear 'in-trade' flag
for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
if(myItems[i]) myItems[i]->SetInTrade(false);
if(hisItems[i]) hisItems[i]->SetInTrade(false);
}
+
// in case of missing space report error
if(!myCanCompleteTrade)
{
@@ -329,6 +359,7 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
_player->pTrader->GetSession()->SendTradeStatus(TRADE_STATUS_BACK_TO_TRADE);
return;
}
+
// execute trade: 1. remove
for(int i=0; i<TRADE_SLOT_TRADED_COUNT; ++i)
{
@@ -343,8 +374,10 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
_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))
{
@@ -363,20 +396,25 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
_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;
}
@@ -385,139 +423,175 @@ void WorldSession::HandleAcceptTradeOpcode(WorldPacket& /*recvPacket*/)
_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_LOGGEDIN_OR_RECENTLY_LOGGOUT
_player->TradeCancel(true);
}
+
void WorldSession::HandleInitiateTradeOpcode(WorldPacket& recvPacket)
{
if (GetPlayer()->pTrader)
return;
+
uint64 ID;
+
if (!GetPlayer()->isAlive())
{
SendTradeStatus(TRADE_STATUS_YOU_DEAD);
return;
}
+
if (GetPlayer()->hasUnitState(UNIT_STAT_STUNNED))
{
SendTradeStatus(TRADE_STATUS_YOU_STUNNED);
return;
}
+
if (isLogingOut())
{
SendTradeStatus(TRADE_STATUS_YOU_LOGOUT);
return;
}
+
if (GetPlayer()->isInFlight())
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
+
recvPacket >> ID;
+
Player* pOther = ObjectAccessor::FindPlayer( ID );
+
if (!pOther)
{
SendTradeStatus(TRADE_STATUS_NO_TARGET);
return;
}
+
if (pOther == GetPlayer() || pOther->pTrader)
{
SendTradeStatus(TRADE_STATUS_BUSY);
return;
}
+
if (!pOther->isAlive())
{
SendTradeStatus(TRADE_STATUS_TARGET_DEAD);
return;
}
+
if (pOther->isInFlight())
{
SendTradeStatus(TRADE_STATUS_TARGET_TO_FAR);
return;
}
+
if (pOther->hasUnitState(UNIT_STAT_STUNNED))
{
SendTradeStatus(TRADE_STATUS_TARGET_STUNNED);
return;
}
+
if (pOther->GetSession()->isLogingOut())
{
SendTradeStatus(TRADE_STATUS_TARGET_LOGOUT);
return;
}
+
if (pOther->GetSocial()->HasIgnore(GetPlayer()->GetGUIDLow()))
{
SendTradeStatus(TRADE_STATUS_IGNORE_YOU);
return;
}
+
if(!sWorld.getConfig(CONFIG_ALLOW_TWO_SIDE_TRADE) && pOther->GetTeam() !=_player->GetTeam() )
{
SendTradeStatus(TRADE_STATUS_WRONG_FACTION);
return;
}
+
if (!pOther->IsWithinDistInMap(_player,10.0f,false))
{
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)
{
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)
{
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()))
@@ -525,7 +599,9 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
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)
{
@@ -536,19 +612,26 @@ void WorldSession::HandleSetTradeItemOpcode(WorldPacket& recvPacket)
return;
}
}
+
_player->tradeItems[tradeSlot] = pos;
+
_player->pTrader->GetSession()->SendUpdateTrade();
}
+
void WorldSession::HandleClearTradeItemOpcode(WorldPacket& recvPacket)
{
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
index d55e50986b4..25248b7b9d2 100644
--- a/src/game/Transports.cpp
+++ b/src/game/Transports.cpp
@@ -17,51 +17,70 @@
* 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 "DBCStores.h"
#include "ProgressBar.h"
+
#include "World.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<uint32> mapsUsed;
+
if(!t->GenerateWaypoints(goinfo->moTransport.taxiPathId, mapsUsed))
// skip transports with empty waypoints list
{
@@ -69,26 +88,34 @@ void MapManager::LoadTransports()
delete t;
continue;
}
+
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<uint32>::const_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:
t->SetMap(MapManager::Instance().CreateMap(mapid, t, 0));
+
//t->GetMap()->Add<GameObject>((GameObject *)t);
++count;
} while(result->NextRow());
delete result;
+
sLog.outString();
sLog.outString( ">> Loaded %u transports", count );
+
// check transport data DB integrity
result = WorldDatabase.Query("SELECT gameobject.guid,gameobject.id,transports.name FROM gameobject,transports WHERE gameobject.id = transports.entry");
if(result) // wrong data found
@@ -96,52 +123,69 @@ void MapManager::LoadTransports()
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()
{
m_updateFlag = (UPDATEFLAG_TRANSPORT | UPDATEFLAG_HIGHGUID | UPDATEFLAG_HAS_POSITION | UPDATEFLAG_ROTATION);
}
+
bool Transport::Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags)
{
Relocate(x,y,z,ang);
// instance id and phaseMask isn't set to values different from std.
+
if(!IsPositionValid())
{
sLog.outError("Transport (GUID: %u) not created. Suggested coordinates isn't valid (X: %f Y: %f)",
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(GAMEOBJECT_FLAGS, MAKE_PAIR32(0x28, 0x64));
SetUInt32Value(GAMEOBJECT_LEVEL, m_period);
SetEntry(goinfo->id);
+
SetUInt32Value(GAMEOBJECT_DISPLAYID, goinfo->displayId);
+
SetGoState(GO_STATE_READY);
SetGoType(GameobjectTypes(goinfo->type));
+
SetGoAnimProgress(animprogress);
if(dynflags)
SetUInt32Value(GAMEOBJECT_DYNAMIC, MAKE_PAIR32(0, dynflags));
+
SetName(goinfo->name);
+
return true;
}
+
struct keyFrame
{
keyFrame(float _x, float _y, float _z, uint32 _mapid, int _actionflag, int _delay)
@@ -149,6 +193,7 @@ struct keyFrame
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;
@@ -160,12 +205,15 @@ struct keyFrame
float distFromPrev;
float tFrom, tTo;
};
+
bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
{
TransportPath path;
objmgr.GetTransportPathNodes(pathid, path);
+
if (path.Empty())
return false;
+
std::vector<keyFrame> keyFrames;
int mapChange = 0;
mapids.clear();
@@ -189,14 +237,17 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
--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)
{
@@ -219,6 +270,7 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
lastStop = i;
}
}
+
float tmpDist = 0;
for (size_t i = 0; i < keyFrames.size(); ++i)
{
@@ -229,6 +281,7 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
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();
@@ -237,37 +290,45 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
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, 0);
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)))
{
@@ -275,23 +336,27 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
{
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, i);
if (teleport)
m_WayPoints[t] = pos;
}
+
if (tFrom < tTo) // caught in tFrom dock's "gravitational pull"
{
if (tFrom <= 30000)
@@ -320,17 +385,21 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
}
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, i);
+
// sLog.outString("T: %d, x: %f, y: %f, z: %f, t:%d", t, pos.x, pos.y, pos.z, teleport);
/*
if(keyFrames[i+1].delay > 5)
@@ -338,18 +407,25 @@ bool Transport::GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids)
*/
//if (teleport)
m_WayPoints[t] = pos;
+
t += keyFrames[i + 1].delay * 1000;
// sLog.outString("------");
}
+
uint32 timer = t;
+
// sLog.outDetail(" Generated %lu waypoints, total time %u.", (unsigned long)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::const_iterator Transport::GetNextWayPoint()
{
WayPointMap::const_iterator iter = m_curr;
@@ -358,64 +434,77 @@ Transport::WayPointMap::const_iterator Transport::GetNextWayPoint()
iter = m_WayPoints.begin();
return iter;
}
+
void Transport::TeleportTransport(uint32 newMapid, float x, float y, float z)
{
Map const* oldMap = GetMap();
Relocate(x, y, z);
+
for(PlayerSet::const_iterator itr = m_passengers.begin(); itr != m_passengers.end();)
{
Player *plr = *itr;
++itr;
+
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);
}
+
//we need to create and save new Map object with 'newMapid' because if not done -> lead to invalid Map object reference...
//player far teleport would try to create same instance, but we need it NOW for transport...
//correct me if I'm wrong O.o
//yes, you're right
+
ResetMap();
Map * newMap = MapManager::Instance().CreateMap(newMapid, this, 0);
SetMap(newMap);
assert (GetMap());
+
if(oldMap != newMap)
{
UpdateForMap(oldMap);
UpdateForMap(newMap);
}
}
+
bool Transport::AddPassenger(Player* passenger)
{
if(m_passengers.insert(passenger).second)
sLog.outDetail("Player %s boarded transport %s.", passenger->GetName(), GetName());
return true;
}
+
bool Transport::RemovePassenger(Player* passenger)
{
if (m_passengers.erase(passenger))
sLog.outDetail("Player %s removed from transport %s.", passenger->GetName(), GetName());
return true;
}
+
void Transport::CheckForEvent(uint32 entry, uint32 wp_id)
{
uint32 key = entry*100+wp_id;
if(objmgr.TransportEventMap.find(key) != objmgr.TransportEventMap.end())
GetMap()->ScriptsStart(sEventScripts, objmgr.TransportEventMap[key], this, NULL);
}
+
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)
{
@@ -454,20 +543,26 @@ void Transport::Update(uint32 /*p_time*/)
//(*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());
+
if ((sLog.getLogFilter() & LOG_FILTER_TRANSPORT_MOVES)==0)
sLog.outDetail("%s moved to %d %f %f %f %d", this->m_name.c_str(), m_curr->second.id, m_curr->second.x, m_curr->second.y, m_curr->second.z, m_curr->second.mapid);
+
//Transport Event System
CheckForEvent(this->GetEntry(), m_curr->second.id);
}
}
+
void Transport::UpdateForMap(Map const* targetMap)
{
Map::PlayerList const& pl = targetMap->GetPlayers();
if(pl.isEmpty())
return;
+
if(GetMapId()==targetMap->GetId())
{
for(Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
@@ -488,6 +583,7 @@ void Transport::UpdateForMap(Map const* targetMap)
BuildOutOfRangeUpdateBlock(&transData);
WorldPacket out_packet;
transData.BuildPacket(&out_packet);
+
for(Map::PlayerList::const_iterator itr = pl.begin(); itr != pl.end(); ++itr)
if(this != itr->getSource()->GetTransport())
itr->getSource()->SendDirectMessage(&out_packet);
diff --git a/src/game/Transports.h b/src/game/Transports.h
index d60fece4738..e05a6006971 100644
--- a/src/game/Transports.h
+++ b/src/game/Transports.h
@@ -17,12 +17,16 @@
* 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 <map>
#include <set>
#include <string>
+
class TransportPath
{
public:
@@ -33,32 +37,40 @@ class TransportPath
uint32 actionFlag;
uint32 delay;
};
+
void SetLength(const unsigned int sz)
{
i_nodes.resize( sz );
}
+
unsigned int Size(void) const { return i_nodes.size(); }
bool Empty(void) const { return i_nodes.empty(); }
void Resize(unsigned int sz) { i_nodes.resize(sz); }
void Clear(void) { i_nodes.clear(); }
PathNode* GetNodes(void) { return static_cast<PathNode *>(&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<PathNode> i_nodes;
};
+
class Transport : public GameObject
{
public:
explicit Transport();
+
bool Create(uint32 guidlow, uint32 mapid, float x, float y, float z, float ang, uint32 animprogress, uint32 dynflags);
bool GenerateWaypoints(uint32 pathid, std::set<uint32> &mapids);
void Update(uint32 p_time);
bool AddPassenger(Player* passenger);
bool RemovePassenger(Player* passenger);
void CheckForEvent(uint32 entry, uint32 wp_id);
+
typedef std::set<Player*> PlayerSet;
PlayerSet const& GetPassengers() const { return m_passengers; }
+
private:
struct WayPoint
{
@@ -72,16 +84,21 @@ class Transport : public GameObject
bool teleport;
uint32 id;
};
+
typedef std::map<uint32, WayPoint> WayPointMap;
+
WayPointMap::const_iterator m_curr;
WayPointMap::const_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);
void UpdateForMap(Map const* map);
diff --git a/src/game/Traveller.h b/src/game/Traveller.h
index c77be90e38a..279af30428a 100644
--- a/src/game/Traveller.h
+++ b/src/game/Traveller.h
@@ -17,15 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TRAVELLER_H
#define TRINITY_TRAVELLER_H
+
#include "Creature.h"
#include "Player.h"
#include <cassert>
+
/** 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<class T>
struct TRINITY_DLL_DECL Traveller
{
@@ -38,19 +42,23 @@ struct TRINITY_DLL_DECL Traveller
new (this) Traveller(obj);
return *this;
}
+
operator T&(void) { return i_traveller; }
operator const T&(void) { return i_traveller; }
float GetPositionX() const { return i_traveller.GetPositionX(); }
float GetPositionY() const { return i_traveller.GetPositionY(); }
float GetPositionZ() const { return i_traveller.GetPositionZ(); }
T& GetTraveller(void) { return i_traveller; }
+
float Speed(void) { assert(false); return 0.0f; }
float GetMoveDestinationTo(float x, float y, float z);
uint32 GetTotalTrevelTimeTo(float x, float y, float z);
+
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) {}
};
+
template<class T>
inline uint32 Traveller<T>::GetTotalTrevelTimeTo(float x, float y, float z)
{
@@ -62,6 +70,7 @@ inline uint32 Traveller<T>::GetTotalTrevelTimeTo(float x, float y, float z)
speed *= 0.001f; // speed is in seconds so convert from second to millisecond
return static_cast<uint32>(dist/speed);
}
+
// specialization for creatures
template<>
inline float Traveller<Creature>::Speed()
@@ -75,29 +84,34 @@ inline float Traveller<Creature>::Speed()
else
return i_traveller.GetSpeed(MOVE_RUN);
}
+
template<>
inline void Traveller<Creature>::Relocation(float x, float y, float z, float orientation)
{
i_traveller.GetMap()->CreatureRelocation(&i_traveller, x, y, z, orientation);
}
+
template<>
inline float Traveller<Creature>::GetMoveDestinationTo(float x, float y, float z)
{
float dx = x - GetPositionX();
float dy = y - GetPositionY();
float dz = z - GetPositionZ();
+
//if(i_traveller.HasUnitMovementFlag(MOVEMENTFLAG_FLYING))
return sqrt((dx*dx) + (dy*dy) + (dz*dz));
//else //Walking on the ground
// return sqrt((dx*dx) + (dy*dy));
}
+
template<>
inline void Traveller<Creature>::MoveTo(float x, float y, float z, uint32 t)
{
//i_traveller.AI_SendMoveToPacket(x, y, z, t, i_traveller.GetUnitMovementFlags(), 0);
i_traveller.SendMonsterMove(x, y, z, t);
}
+
// specialization for players
template<>
inline float Traveller<Player>::Speed()
@@ -109,28 +123,33 @@ inline float Traveller<Player>::Speed()
else
return i_traveller.GetSpeed(i_traveller.m_movementInfo.HasMovementFlag(MOVEMENTFLAG_WALK_MODE) ? MOVE_WALK : MOVE_RUN);
}
+
template<>
inline float Traveller<Player>::GetMoveDestinationTo(float x, float y, float z)
{
float dx = x - GetPositionX();
float dy = y - GetPositionY();
float dz = z - GetPositionZ();
+
//if (i_traveller.isInFlight())
return sqrt((dx*dx) + (dy*dy) + (dz*dz));
//else //Walking on the ground
// return sqrt((dx*dx) + (dy*dy));
}
+
template<>
inline void Traveller<Player>::Relocation(float x, float y, float z, float orientation)
{
i_traveller.GetMap()->PlayerRelocation(&i_traveller, x, y, z, orientation);
}
+
template<>
inline void Traveller<Player>::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, t);
}
+
typedef Traveller<Creature> CreatureTraveller;
typedef Traveller<Player> PlayerTraveller;
#endif
diff --git a/src/game/Unit.cpp b/src/game/Unit.cpp
index 4885d38585e..94a88fba710 100644
--- a/src/game/Unit.cpp
+++ b/src/game/Unit.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -53,7 +54,9 @@
#include "Vehicle.h"
#include "Transports.h"
#include "ScriptCalls.h"
+
#include <math.h>
+
float baseMoveSpeed[MAX_MOVE_TYPE] =
{
2.5f, // MOVE_WALK
@@ -77,6 +80,7 @@ float playerBaseMoveSpeed[MAX_MOVE_TYPE] = {
4.5f, // MOVE_FLIGHT_BACK
3.14f // MOVE_PITCH_RATE
};
+
// Used for prepare can/can`t triggr aura
static bool InitTriggerAuraData();
// Define can trigger auras
@@ -85,6 +89,7 @@ static bool isTriggerAura[TOTAL_AURAS];
static bool isNonTriggerAura[TOTAL_AURAS];
// Prepare lists
static bool procPrepared = InitTriggerAuraData();
+
Unit::Unit()
: WorldObject(), i_motionMaster(this), m_ThreatManager(this), m_HostilRefManager(this)
, m_NotifyListPos(-1), m_Notified(false), IsAIEnabled(false), NeedChangeAI(false)
@@ -94,36 +99,47 @@ Unit::Unit()
{
m_objectType |= TYPEMASK_UNIT;
m_objectTypeId = TYPEID_UNIT;
+
m_updateFlag = (UPDATEFLAG_HIGHGUID | UPDATEFLAG_LIVING | UPDATEFLAG_HAS_POSITION);
+
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_canDualWield = false;
+
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(uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
m_SummonSlot[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;
+
m_AurasUpdateIterator = m_Auras.end();
m_Visibility = VISIBILITY_ON;
+
m_interruptMask = 0;
m_detectInvisibilityMask = 0;
m_invisibilityMask = 0;
m_transform = 0;
m_ShapeShiftFormSpellId = 0;
m_canModifyStats = false;
+
for (uint8 i = 0; i < MAX_SPELL_IMMUNITY; ++i)
m_spellImmune[i].clear();
for (uint8 i = 0; i < UNIT_MOD_END; ++i)
@@ -135,6 +151,7 @@ Unit::Unit()
}
// implement 50% base damage from offhand
m_auraModifiersGroup[UNIT_MOD_DAMAGE_OFFHAND][TOTAL_PCT] = 0.5f;
+
for (uint8 i = 0; i < MAX_ATTACK; ++i)
{
m_weaponDamage[i][MINDAMAGE] = BASE_MINDAMAGE;
@@ -142,27 +159,33 @@ Unit::Unit()
}
for (uint8 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 (uint8 i = 0; i < MAX_SPELL_SCHOOL; ++i)
m_threatModifier[i] = 1.0f;
m_isSorted = true;
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
m_speed_rate[i] = 1.0f;
+
m_charmInfo = NULL;
//m_unit_movement_flags = 0;
m_reducedThreatPercent = 0;
m_misdirectionTargetGUID = 0;
+
// remove aurastates allowing special moves
for(uint8 i = 0; i < MAX_REACTIVE; ++i)
m_reactiveTimer[i] = 0;
}
+
Unit::~Unit()
{
// set current spells as deletable
@@ -174,16 +197,20 @@ Unit::~Unit()
m_currentSpells[i] = NULL;
}
}
+
RemoveAllGameObjects();
RemoveAllDynObjects();
_DeleteAuras();
+
if(m_charmInfo) delete m_charmInfo;
if(m_vehicleKit) delete m_vehicleKit;
+
assert(!m_attacking);
assert(m_attackers.empty());
assert(m_sharedVision.empty());
assert(m_Controlled.empty());
}
+
void Unit::Update( uint32 p_time )
{
/*if(p_time > m_AurasCheck)
@@ -192,16 +219,20 @@ void Unit::Update( uint32 p_time )
_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 );
+
// If this is set during update SetCantProc(false) call is missing somewhere in the code
// Having this would prevent spells from being proced, so let's crash
assert(!m_procDeep);
+
if (CanHaveThreatList() && getThreatManager().isNeedUpdateToClient(p_time))
SendThreatListUpdate();
+
// update combat timer only for players and pets (only pets with PetAI)
if (isInCombat() && (GetTypeId() == TYPEID_PLAYER || (((Creature *)this)->isPet() && IsControlledByPlayer())))
{
@@ -217,6 +248,7 @@ void Unit::Update( uint32 p_time )
m_CombatTimer -= p_time;
}
}
+
//not implemented before 3.0.2
//if(!hasUnitState(UNIT_STAT_CASTING))
{
@@ -227,13 +259,17 @@ void Unit::Update( uint32 p_time )
if(uint32 off_att = getAttackTimer(OFF_ATTACK))
setAttackTimer(OFF_ATTACK, (p_time >= off_att ? 0 : off_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);
ModifyAuraState(AURA_STATE_HEALTH_ABOVE_75_PERCENT, GetHealth() > GetMaxHealth()*0.75f);
+
i_motionMaster.UpdateMotion(p_time);
}
+
bool Unit::haveOffhandWeapon() const
{
if(GetTypeId() == TYPEID_PLAYER)
@@ -241,12 +277,14 @@ bool Unit::haveOffhandWeapon() const
else
return m_canDualWield;
}
+
void Unit::SendMonsterMoveWithSpeedToCurrentDestination(Player* player)
{
float x, y, z;
if(GetMotionMaster()->GetDestination(x, y, z))
SendMonsterMoveWithSpeed(x, y, z, 0, player);
}
+
void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime, Player* player)
{
if (!transitTime)
@@ -265,6 +303,7 @@ void Unit::SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTim
//float orientation = (float)atan2((double)dy, (double)dx);
SendMonsterMove(x, y, z, transitTime, player);
}
+
void Unit::SendMonsterStop()
{
WorldPacket data( SMSG_MONSTER_MOVE, (17 + GetPackGUID().size()) );
@@ -274,35 +313,45 @@ void Unit::SendMonsterStop()
data << getMSTime();
data << uint8(1);
SendMessageToSet(&data, true);
+
clearUnitState(UNIT_STAT_MOVE);
}
+
void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 Time, Player* player)
{
WorldPacket data( SMSG_MONSTER_MOVE, 1+12+4+1+4+4+4+12+GetPackGUID().size());
data.append(GetPackGUID());
+
data << uint8(0); // new in 3.1
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << getMSTime();
+
data << uint8(0);
data << uint32((GetUnitMovementFlags() & MOVEMENTFLAG_LEVITATING) ? MOVEFLAG_FLY : MOVEFLAG_WALK);
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 );
+
addUnitState(UNIT_STAT_MOVE);
}
+
void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 MoveFlags, uint32 time, float speedZ, Player *player)
{
WorldPacket data( SMSG_MONSTER_MOVE, 12+4+1+4+4+4+12+GetPackGUID().size());
data.append(GetPackGUID());
+
data << uint8(0); // new in 3.1
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << getMSTime();
+
data << uint8(0);
data << MoveFlags;
+
if(MoveFlags & MOVEFLAG_JUMP)
{
data << time;
@@ -311,21 +360,26 @@ void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 M
}
else
data << time;
+
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::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint8 type, uint32 MovementFlags, uint32 Time, Player* player)
{
float moveTime = Time;
+
WorldPacket data( SMSG_MONSTER_MOVE, (41 + GetPackGUID().size()) );
data.append(GetPackGUID());
data << uint8(0); // new in 3.1
data << GetPositionX() << GetPositionY() << GetPositionZ();
data << uint32(getMSTime());
+
data << uint8(type); // unknown
switch(type)
{
@@ -346,21 +400,28 @@ void Unit::SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 M
data << float(0); // facing angle
break;
}
+
data << uint32(MovementFlags);
+
if(MovementFlags & MONSTER_MOVE_WALK)
moveTime *= 1.05f;
+
data << uint32(moveTime); // 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 traveltime = uint32(path.GetTotalLength(start, end) * 32);
+
uint32 pathSize = end - start;
+
WorldPacket data( SMSG_MONSTER_MOVE, (GetPackGUID().size()+1+4+4+4+4+1+4+4+4+pathSize*4*3) );
data.append(GetPackGUID());
data << uint8(0);
@@ -377,6 +438,7 @@ void Unit::SendMonsterMoveByPath(Path const& path, uint32 start, uint32 end)
//MONSTER_MOVE_SPLINE_FLY
addUnitState(UNIT_STAT_MOVE);
}
+
void Unit::SendMonsterMoveTransport(Unit *vehicleOwner)
{
WorldPacket data(SMSG_MONSTER_MOVE_TRANSPORT, GetPackGUID().size()+vehicleOwner->GetPackGUID().size());
@@ -397,32 +459,42 @@ void Unit::SendMonsterMoveTransport(Unit *vehicleOwner)
data << uint32(0);//GetTransOffsetZ();
SendMessageToSet(&data, true);
}
+
void Unit::resetAttackTimer(WeaponAttackType type)
{
m_attackTimer[type] = uint32(GetAttackTime(type) * m_modAttackSpeedPct[type]);
}
+
bool Unit::IsWithinCombatRange(const Unit *obj, 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 = GetCombatReach() + obj->GetCombatReach();
float maxdist = dist2compare + sizefactor;
+
return distsq < maxdist * maxdist;
}
+
bool Unit::IsWithinMeleeRange(const Unit *obj, float dist) 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 = GetMeleeReach() + obj->GetMeleeReach();
float maxdist = dist + sizefactor;
+
return distsq < maxdist * maxdist;
}
+
void Unit::GetRandomContactPoint( const Unit* obj, float &x, float &y, float &z, float distance2dMin, float distance2dMax ) const
{
float combat_reach = GetCombatReach();
@@ -438,10 +510,12 @@ void Unit::GetRandomContactPoint( const Unit* obj, float &x, float &y, float &z,
GetNearPoint(obj,x,y,z,obj->GetCombatReach(), distance2dMin+(distance2dMax-distance2dMin)*rand_norm()
, GetAngle(obj) + (attacker_number ? (M_PI/2 - M_PI * rand_norm()) * (float)attacker_number / combat_reach * 0.3 : 0));
}
+
void Unit::RemoveMovementImpairingAuras()
{
RemoveAurasWithMechanic((1<<MECHANIC_SNARE)|(1<<MECHANIC_ROOT));
return;
+
for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
{
if(spellmgr.GetSpellCustomAttr(iter->second->GetId()) & SPELL_ATTR_CU_MOVEMENT_IMPAIR)
@@ -450,10 +524,12 @@ void Unit::RemoveMovementImpairingAuras()
++iter;
}
}
+
void Unit::RemoveAurasWithInterruptFlags(uint32 flag, uint32 except)
{
if(!(m_interruptMask & flag))
return;
+
// interrupt auras
for (AuraList::iterator iter = m_interruptableAuras.begin(); iter != m_interruptableAuras.end();)
{
@@ -467,14 +543,17 @@ void Unit::RemoveAurasWithInterruptFlags(uint32 flag, uint32 except)
iter = m_interruptableAuras.begin();
}
}
+
// interrupt channeled spell
if(Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
if(spell->getState() == SPELL_STATE_CASTING
&& (spell->m_spellInfo->ChannelInterruptFlags & flag)
&& spell->m_spellInfo->Id != except)
InterruptNonMeleeSpells(false);
+
UpdateInterruptMask();
}
+
void Unit::RemoveAurasWithFamily(uint32 family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, uint64 casterGUID)
{
for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
@@ -491,6 +570,7 @@ void Unit::RemoveAurasWithFamily(uint32 family, uint32 familyFlag1, uint32 famil
++iter;
}
}
+
void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemode, uint32 except)
{
for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
@@ -506,15 +586,18 @@ void Unit::RemoveAurasWithMechanic(uint32 mechanic_mask, AuraRemoveMode removemo
++iter;
}
}
+
void Unit::UpdateInterruptMask()
{
m_interruptMask = 0;
for(AuraList::const_iterator i = m_interruptableAuras.begin(); i != m_interruptableAuras.end(); ++i)
m_interruptMask |= (*i)->GetSpellProto()->AuraInterruptFlags;
+
if(Spell* spell = m_currentSpells[CURRENT_CHANNELED_SPELL])
if(spell->getState() == SPELL_STATE_CASTING)
m_interruptMask |= spell->m_spellInfo->ChannelInterruptFlags;
}
+
bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const
{
if(!HasAuraType(auraType)) return false;
@@ -525,6 +608,7 @@ bool Unit::HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint
return true;
return false;
}
+
void Unit::DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb)
{
if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
@@ -534,6 +618,7 @@ void Unit::DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb)
damage = 0;
return;
}
+
//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 && IsControlledByPlayer() && pVictim->IsControlledByPlayer())
@@ -546,16 +631,20 @@ void Unit::DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb)
damage = 0;
}
}
+
uint32 originalDamage = damage;
+
//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)->IsAIEnabled )
// ((Creature *)pVictim)->AI()->DamageTaken(this, damage);
+
if(absorb && originalDamage > damage)
absorb += (originalDamage - damage);
}
+
uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage, DamageEffectType damagetype, SpellSchoolMask damageSchoolMask, SpellEntry const *spellProto, bool durabilityLoss)
{
if(pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsAIEnabled)
@@ -567,13 +656,16 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
// interrupting auras with AURA_INTERRUPT_FLAG_DAMAGE before checking !damage (absorbed damage breaks that type of auras)
pVictim->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_TAKE_DAMAGE, spellProto ? spellProto->Id : 0);
}
+
if(!damage)
{
// Rage from absorbed damage
if(cleanDamage && cleanDamage->absorbed_damage && pVictim->getPowerType() == POWER_RAGE)
pVictim->RewardRage(cleanDamage->absorbed_damage, 0, false);
+
return 0;
}
+
// no xp,health if type 8 /critters/
if(pVictim->GetTypeId() != TYPEID_PLAYER && pVictim->GetCreatureType() == CREATURE_TYPE_CRITTER)
{
@@ -582,9 +674,11 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
{
pVictim->setDeathState(JUST_DIED);
pVictim->SetHealth(0);
+
CreatureInfo const* cInfo = ((Creature*)pVictim)->GetCreatureInfo();
if(cInfo && cInfo->lootid)
pVictim->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
+
// some critters required for quests (need normal entry instead possible heroic in any cases)
if(GetTypeId() == TYPEID_PLAYER)
if(CreatureInfo const* normalInfo = objmgr.GetCreatureTemplate(pVictim->GetEntry()))
@@ -592,11 +686,15 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
}
else
pVictim->ModifyHealth(- (int32)damage);
+
return damage;
}
+
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))
@@ -604,13 +702,16 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
// 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;
}
+
// Rage from Damage made (only from direct weapon damage)
if(cleanDamage && damagetype==DIRECT_DAMAGE && this != pVictim && getPowerType() == POWER_RAGE)
{
uint32 weaponSpeedHitFactor;
uint32 rage_damage = damage + cleanDamage->absorbed_damage;
+
switch(cleanDamage->attackType)
{
case BASE_ATTACK:
@@ -619,7 +720,9 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 7);
else
weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
+
RewardRage(rage_damage, weaponSpeedHitFactor, true);
+
break;
}
case OFF_ATTACK:
@@ -628,16 +731,20 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 3.5f);
else
weaponSpeedHitFactor = uint32(GetAttackTime(cleanDamage->attackType)/1000.0f * 1.75f);
+
RewardRage(rage_damage, weaponSpeedHitFactor, true);
+
break;
}
case RANGED_ATTACK:
break;
}
}
+
if (GetTypeId() == TYPEID_PLAYER && this != pVictim)
{
Player *killer = ((Player*)this);
+
// in bg, count dmg if victim is also a player
if (pVictim->GetTypeId()==TYPEID_PLAYER)
{
@@ -647,25 +754,32 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
bg->UpdatePlayerScore(killer, SCORE_DAMAGE_DONE, damage);
}
}
+
killer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_DAMAGE_DONE, damage, 0, pVictim);
killer->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_DEALT, damage);
}
+
if (pVictim->GetTypeId() == TYPEID_PLAYER)
((Player*)pVictim)->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HIT_RECEIVED, damage);
- else if(!pVictim->IsControlledByPlayer())
+ else if(!pVictim->IsControlledByPlayer())
{
//!pVictim->HasFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_OTHER_TAGGER)
if(!((Creature*)pVictim)->hasLootRecipient())
((Creature*)pVictim)->SetLootRecipient(this);
+
if(IsControlledByPlayer())
((Creature*)pVictim)->LowerPlayerDamageReq(health < damage ? health : damage);
}
+
if (health <= damage)
{
DEBUG_LOG("DealDamage: victim just died");
+
if (pVictim->GetTypeId() == TYPEID_PLAYER)
((Player*)pVictim)->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, health);
+
Kill(pVictim, durabilityLoss);
+
//Hook for OnPVPKill Event
if (pVictim->GetTypeId() == TYPEID_PLAYER && this->GetTypeId() == TYPEID_PLAYER)
{
@@ -683,9 +797,12 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
else // if (health <= damage)
{
DEBUG_LOG("DealDamageAlive");
+
if (pVictim->GetTypeId() == TYPEID_PLAYER)
((Player*)pVictim)->UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_DAMAGE_RECEIVED, damage);
+
pVictim->ModifyHealth(- (int32)damage);
+
if(damagetype == DIRECT_DAMAGE || damagetype == SPELL_DIRECT_DAMAGE)
{
//TODO: This is from procflag, I do not know which spell needs this
@@ -709,12 +826,14 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
((Player*)pVictim)->DurabilityPointLossForEquipSlot(slot);
}
}
+
// Rage from damage received
if(this != pVictim && pVictim->getPowerType() == POWER_RAGE)
{
uint32 rage_damage = damage + (cleanDamage ? cleanDamage->absorbed_damage : 0);
pVictim->RewardRage(rage_damage, 0, false);
}
+
if(GetTypeId()==TYPEID_PLAYER)
{
// random durability for items (HIT DONE)
@@ -724,6 +843,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
((Player*)this)->DurabilityPointLossForEquipSlot(slot);
}
}
+
if (damagetype != NODAMAGE && damage)// && pVictim->GetTypeId() == TYPEID_PLAYER)
{
if(pVictim != this && pVictim->GetTypeId() == TYPEID_PLAYER) // does not support creature push_back
@@ -742,6 +862,7 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
}
}
}
+
if(Spell* spell = pVictim->m_currentSpells[CURRENT_CHANNELED_SPELL])
{
if(spell->getState() == SPELL_STATE_CASTING)
@@ -753,38 +874,50 @@ uint32 Unit::DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDa
}
}
}
+
// 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, SPELL_ID_DUEL_BEG, 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(CurrentSpellTypes(i),false);
}
+
void Unit::CastSpell(Unit* Victim, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, 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,triggeredByAura, originalCaster);
}
+
void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster)
{
if(!spellInfo)
@@ -792,9 +925,11 @@ void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, I
sLog.outError("CastSpell: unknown spell by caster: %s %u)", (GetTypeId()==TYPEID_PLAYER ? "player (GUID:" : "creature (Entry:"),(GetTypeId()==TYPEID_PLAYER ? GetGUIDLow() : GetEntry()));
return;
}
+
if (!originalCaster && GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem() && IsControlledByPlayer())
if (Unit * owner = GetOwner())
originalCaster=owner->GetGUID();
+
SpellCastTargets targets;
uint32 targetMask = spellInfo->Targets;
//if(targetMask & (TARGET_FLAG_UNIT|TARGET_FLAG_UNK2))
@@ -818,6 +953,7 @@ void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, I
}
}
targets.setUnitTarget(Victim);
+
if(targetMask & (TARGET_FLAG_SOURCE_LOCATION|TARGET_FLAG_DEST_LOCATION))
{
if(!Victim)
@@ -827,14 +963,19 @@ void Unit::CastSpell(Unit* Victim,SpellEntry const *spellInfo, bool triggered, I
}
targets.setDst(Victim);
}
+
if (castItem)
DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
if(!originalCaster && triggeredByAura)
originalCaster = triggeredByAura->GetCasterGUID();
+
Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
spell->m_CastItem = castItem;
spell->prepare(&targets, triggeredByAura);
}
+
void Unit::CastCustomSpell(Unit* target, uint32 spellId, int32 const* bp0, int32 const* bp1, int32 const* bp2, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster)
{
CustomSpellValues values;
@@ -843,12 +984,14 @@ void Unit::CastCustomSpell(Unit* target, uint32 spellId, int32 const* bp0, int32
if(bp2) values.AddSpellMod(SPELLVALUE_BASE_POINT2, *bp2);
CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
}
+
void Unit::CastCustomSpell(uint32 spellId, SpellValueMod mod, int32 value, Unit* target, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster)
{
CustomSpellValues values;
values.AddSpellMod(mod, value);
CastCustomSpell(spellId, values, target, triggered, castItem, triggeredByAura, originalCaster);
}
+
void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit* Victim, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId );
@@ -857,8 +1000,10 @@ void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit*
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;
}
+
SpellCastTargets targets;
uint32 targetMask = spellInfo->Targets;
+
//check unit target
for(uint8 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
@@ -874,6 +1019,7 @@ void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit*
}
}
targets.setUnitTarget(Victim);
+
//check destination
if(targetMask & (TARGET_FLAG_SOURCE_LOCATION|TARGET_FLAG_DEST_LOCATION))
{
@@ -884,32 +1030,43 @@ void Unit::CastCustomSpell(uint32 spellId, CustomSpellValues const &value, Unit*
}
targets.setDst(Victim);
}
+
if(!originalCaster && triggeredByAura)
originalCaster = triggeredByAura->GetCasterGUID();
+
Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
if(castItem)
{
DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
spell->m_CastItem = castItem;
}
+
for(CustomSpellValues::const_iterator itr = value.begin(); itr != value.end(); ++itr)
spell->SetSpellValue(itr->first, itr->second);
+
spell->prepare(&targets, triggeredByAura);
}
+
// used for scripting
void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster, Unit* OriginalVictim)
{
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;
}
+
if (castItem)
DEBUG_LOG("WORLD: cast Item spellId - %i", spellInfo->Id);
+
if(!originalCaster && triggeredByAura)
originalCaster = triggeredByAura->GetCasterGUID();
+
Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
SpellCastTargets targets;
targets.setDst(x, y, z);
if(OriginalVictim)
@@ -917,32 +1074,41 @@ void Unit::CastSpell(float x, float y, float z, uint32 spellId, bool triggered,
spell->m_CastItem = castItem;
spell->prepare(&targets, triggeredByAura);
}
+
// used for scripting
void Unit::CastSpell(GameObject *go, uint32 spellId, bool triggered, Item *castItem, AuraEffect* triggeredByAura, uint64 originalCaster)
{
if(!go)
return;
+
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;
}
+
if(!(spellInfo->Targets & ( TARGET_FLAG_OBJECT | TARGET_FLAG_OBJECT_UNK)))
{
sLog.outError("CastSpell: spell id %i by caster: %s %u) is not gameobject spell", spellId,(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 && triggeredByAura)
originalCaster = triggeredByAura->GetCasterGUID();
+
Spell *spell = new Spell(this, spellInfo, triggered, originalCaster );
+
SpellCastTargets targets;
targets.setGOTarget(go);
spell->m_CastItem = castItem;
spell->prepare(&targets, triggeredByAura);
}
+
// Obsolete func need remove, here only for comotability vs another patches
uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage)
{
@@ -955,18 +1121,22 @@ uint32 Unit::SpellNonMeleeDamageLog(Unit *pVictim, uint32 spellID, uint32 damage
DealSpellDamage(&damageInfo, true);
return damageInfo.damage;
}
+
void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType, bool crit)
{
if (damage < 0)
return;
+
if(spellInfo->AttributesEx4 & SPELL_ATTR_EX4_FIXED_DAMAGE)
{
damageInfo->damage = damage;
return;
}
+
Unit *pVictim = damageInfo->target;
if(!pVictim || !pVictim->isAlive())
return;
+
SpellSchoolMask damageSchoolMask = SpellSchoolMask(damageInfo->schoolMask);
uint32 crTypeMask = pVictim->GetCreatureTypeMask();
// Check spell crit chance
@@ -985,15 +1155,18 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama
// Get blocked status
blocked = isSpellBlocked(pVictim, spellInfo, attackType);
}
+
if (crit)
{
damageInfo->HitInfo|= SPELL_HIT_TYPE_CRIT;
+
// Calculate crit bonus
uint32 crit_bonus = damage;
// Apply crit_damage bonus for melee spells
if(Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_CRIT_DAMAGE_BONUS, crit_bonus);
damage += crit_bonus;
+
// Apply SPELL_AURA_MOD_ATTACKER_RANGED_CRIT_DAMAGE or SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE
int32 critPctDamageMod=0;
if(attackType == RANGED_ATTACK)
@@ -1005,8 +1178,10 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama
}
// Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
critPctDamageMod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
+
if (critPctDamageMod!=0)
damage = int32((damage) * float((100.0f + critPctDamageMod)/100.0f));
+
// Resilience - reduce crit damage
if (pVictim->GetTypeId()==TYPEID_PLAYER)
damage -= ((Player*)pVictim)->GetMeleeCritDamageReduction(damage);
@@ -1040,8 +1215,10 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama
}
break;
}
+
if( damageSchoolMask & SPELL_SCHOOL_MASK_NORMAL )
damage = CalcArmorReducedDamage(pVictim, damage, spellInfo, attackType);
+
// Calculate absorb resist
if(damage > 0)
{
@@ -1052,21 +1229,27 @@ void Unit::CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 dama
damage = 0;
damageInfo->damage = damage;
}
+
void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss)
{
if (damageInfo==0)
return;
+
Unit *pVictim = damageInfo->target;
+
if(!pVictim)
return;
+
if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
return;
+
SpellEntry const *spellProto = sSpellStore.LookupEntry(damageInfo->SpellID);
if (spellProto == NULL)
{
sLog.outDebug("Unit::DealSpellDamage have wrong damageInfo->SpellID: %u", damageInfo->SpellID);
return;
}
+
//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 && IsControlledByPlayer() && pVictim->IsControlledByPlayer())
@@ -1075,10 +1258,12 @@ void Unit::DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss)
if(area && area->IsSanctuary()) // sanctuary
return;
}
+
// Call default DealDamage
CleanDamage cleanDamage(damageInfo->cleanDamage, damageInfo->absorb, BASE_ATTACK, MELEE_HIT_NORMAL);
DealDamage(pVictim, damageInfo->damage, &cleanDamage, SPELL_DIRECT_DAMAGE, SpellSchoolMask(damageInfo->schoolMask), spellProto, durabilityLoss);
}
+
//TODO for melee need create structure as in
void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType)
{
@@ -1091,16 +1276,19 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
damageInfo->absorb = 0;
damageInfo->resist = 0;
damageInfo->blocked_amount = 0;
+
damageInfo->TargetState = 0;
damageInfo->HitInfo = 0;
damageInfo->procAttacker = PROC_FLAG_NONE;
damageInfo->procVictim = PROC_FLAG_NONE;
damageInfo->procEx = PROC_EX_NONE;
damageInfo->hitOutCome = MELEE_HIT_EVADE;
+
if(!pVictim)
return;
if(!this->isAlive() || !pVictim->isAlive())
return;
+
// Select HitInfo/procAttacker/procVictim flag based on attack type
switch (attackType)
{
@@ -1122,11 +1310,13 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
default:
break;
}
+
// Physical Immune check
if(damageInfo->target->IsImmunedToDamage(SpellSchoolMask(damageInfo->damageSchoolMask)))
{
damageInfo->HitInfo |= HITINFO_NORMALSWING;
damageInfo->TargetState = VICTIMSTATE_IS_IMMUNE;
+
damageInfo->procEx |=PROC_EX_IMMUNE;
damageInfo->damage = 0;
damageInfo->cleanDamage = 0;
@@ -1138,19 +1328,23 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
// Calculate armor reduction
damageInfo->damage = CalcArmorReducedDamage(damageInfo->target, damage, NULL , damageInfo->attackType);
damageInfo->cleanDamage += damage - damageInfo->damage;
+
damageInfo->hitOutCome = RollMeleeOutcomeAgainst(damageInfo->target, damageInfo->attackType);
+
// Disable parry or dodge for ranged attack
if(damageInfo->attackType == RANGED_ATTACK)
{
if (damageInfo->hitOutCome == MELEE_HIT_PARRY) damageInfo->hitOutCome = MELEE_HIT_NORMAL;
if (damageInfo->hitOutCome == MELEE_HIT_DODGE) damageInfo->hitOutCome = MELEE_HIT_MISS;
}
+
switch(damageInfo->hitOutCome)
{
case MELEE_HIT_EVADE:
{
damageInfo->HitInfo |= HITINFO_MISS|HITINFO_SWINGNOHITSOUND;
damageInfo->TargetState = VICTIMSTATE_EVADES;
+
damageInfo->procEx|=PROC_EX_EVADE;
damageInfo->damage = 0;
damageInfo->cleanDamage = 0;
@@ -1160,6 +1354,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
{
damageInfo->HitInfo |= HITINFO_MISS;
damageInfo->TargetState = VICTIMSTATE_NORMAL;
+
damageInfo->procEx|=PROC_EX_MISS;
damageInfo->damage = 0;
damageInfo->cleanDamage = 0;
@@ -1173,6 +1368,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
{
damageInfo->HitInfo |= HITINFO_CRITICALHIT;
damageInfo->TargetState = VICTIMSTATE_NORMAL;
+
damageInfo->procEx|=PROC_EX_CRITICAL_HIT;
// Crit bonus calc
damageInfo->damage += damageInfo->damage;
@@ -1185,11 +1381,14 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
mod += damageInfo->target->GetTotalAuraModifier(SPELL_AURA_MOD_ATTACKER_MELEE_CRIT_DAMAGE);
mod += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_DAMAGE_BONUS_MELEE);
}
+
uint32 crTypeMask = damageInfo->target->GetCreatureTypeMask();
+
// Increase crit damage from SPELL_AURA_MOD_CRIT_PERCENT_VERSUS
mod += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_CRIT_PERCENT_VERSUS, crTypeMask);
if (mod!=0)
damageInfo->damage = int32((damageInfo->damage) * float((100.0f + mod)/100.0f));
+
// Resilience - reduce crit damage
if (pVictim->GetTypeId()==TYPEID_PLAYER)
{
@@ -1205,6 +1404,7 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
damageInfo->cleanDamage += damageInfo->damage;
damageInfo->damage = 0;
break;
+
case MELEE_HIT_DODGE:
damageInfo->TargetState = VICTIMSTATE_DODGE;
damageInfo->procEx|=PROC_EX_DODGE;
@@ -1253,8 +1453,10 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
break;
}
default:
+
break;
}
+
// Calculate absorb resist
if(int32(damageInfo->damage) > 0)
{
@@ -1269,18 +1471,23 @@ void Unit::CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *da
}
if (damageInfo->resist)
damageInfo->HitInfo|=HITINFO_RESIST;
+
}
else // Impossible get negative result but....
damageInfo->damage = 0;
}
+
void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
{
if (damageInfo==0) return;
Unit *pVictim = damageInfo->target;
+
if(!pVictim)
return;
+
if (!pVictim->isAlive() || pVictim->hasUnitState(UNIT_STAT_UNATTACKABLE) || pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode())
return;
+
//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 && IsControlledByPlayer() && pVictim->IsControlledByPlayer())
@@ -1289,11 +1496,13 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
if(area && area->IsSanctuary()) // sanctuary
return;
}
+
// Hmmmm dont like this emotes client must by self do all animations
if (damageInfo->HitInfo&HITINFO_CRITICALHIT)
pVictim->HandleEmoteCommand(EMOTE_ONESHOT_WOUNDCRITICAL);
if(damageInfo->blocked_amount && damageInfo->TargetState!=VICTIMSTATE_BLOCKS)
pVictim->HandleEmoteCommand(EMOTE_ONESHOT_PARRYSHIELD);
+
if(damageInfo->TargetState == VICTIMSTATE_PARRY)
{
// Get attack timers
@@ -1329,9 +1538,11 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
}
}
}
+
// Call default DealDamage
CleanDamage cleanDamage(damageInfo->cleanDamage,damageInfo->absorb,damageInfo->attackType,damageInfo->hitOutCome);
DealDamage(pVictim, damageInfo->damage, &cleanDamage, DIRECT_DAMAGE, SpellSchoolMask(damageInfo->damageSchoolMask), NULL, durabilityLoss);
+
// If this is a creature and it attacks from behind it has a probability to daze it's victim
if( (damageInfo->hitOutCome==MELEE_HIT_CRIT || damageInfo->hitOutCome==MELEE_HIT_CRUSHING || damageInfo->hitOutCome==MELEE_HIT_NORMAL || damageInfo->hitOutCome==MELEE_HIT_GLANCING) &&
GetTypeId() != TYPEID_PLAYER && !((Creature*)this)->IsControlledByPlayer() && !pVictim->HasInArc(M_PI, this)
@@ -1340,19 +1551,26 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
// -probability is between 0% and 40%
// 20% base chance
float Probability = 20.0f;
+
//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.5f;
+
uint32 VictimDefense=pVictim->GetDefenseSkillValue();
uint32 AttackerMeleeSkill=GetUnitMeleeSkill();
+
Probability *= AttackerMeleeSkill/(float)VictimDefense;
+
if(Probability > 40.0f)
Probability = 40.0f;
+
if(roll_chance_f(Probability))
CastSpell(pVictim, 1604, true);
}
+
if(GetTypeId() == TYPEID_PLAYER)
((Player *)this)->CastItemCombatSpell(pVictim, damageInfo->attackType, damageInfo->procVictim, damageInfo->procEx);
+
// Do effect if any damage done to target
if (damageInfo->damage)
{
@@ -1373,7 +1591,9 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
//uint32 resist;
//CalcAbsorbResist(pVictim, SpellSchools(spellProto->School), SPELL_DIRECT_DAMAGE, damage, &absorb, &resist);
//damage-=absorb + resist;
+
pVictim->DealDamageMods(this,damage,NULL);
+
WorldPacket data(SMSG_SPELLDAMAGESHIELD,(8+8+4+4+4+4));
data << uint64(pVictim->GetGUID());
data << uint64(GetGUID());
@@ -1382,7 +1602,9 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
data << uint32(0); // Overkill
data << uint32(i_spellProto->SchoolMask);
pVictim->SendMessageToSet(&data, true );
+
pVictim->DealDamage(this, damage, 0, SPELL_DIRECT_DAMAGE, GetSpellSchoolMask(i_spellProto), i_spellProto, true);
+
if (pVictim->m_removedAurasCount > removedAuras)
{
removedAuras = pVictim->m_removedAurasCount;
@@ -1393,6 +1615,7 @@ void Unit::DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss)
}
}
+
void Unit::HandleEmoteCommand(uint32 anim_id)
{
WorldPacket data( SMSG_EMOTE, 4 + 8 );
@@ -1400,15 +1623,19 @@ void Unit::HandleEmoteCommand(uint32 anim_id)
data << uint64(GetGUID());
SendMessageToSet(&data, true);
}
+
uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType)
{
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(spellInfo)
if(Player *modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellInfo->Id, SPELLMOD_IGNORE_ARMOR, armor);
+
AuraEffectList const& ResIgnoreAurasAb = GetAurasByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST);
for(AuraEffectList::const_iterator j = ResIgnoreAurasAb.begin();j != ResIgnoreAurasAb.end(); ++j)
{
@@ -1416,12 +1643,14 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEnt
&& (*j)->isAffectedOnSpell(spellInfo))
armor= int32(float(armor) * (float(100-(*j)->GetAmount())/100.0f));
}
+
AuraEffectList const& ResIgnoreAuras = GetAurasByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST);
for(AuraEffectList::const_iterator j = ResIgnoreAuras.begin();j != ResIgnoreAuras.end(); ++j)
{
if( (*j)->GetMiscValue() & SPELL_SCHOOL_MASK_NORMAL)
armor= int32(float(armor) * (float(100-(*j)->GetAmount())/100.0f));
}
+
if ( GetTypeId() == TYPEID_PLAYER )
{
AuraEffectList const& ResIgnoreAuras = GetAurasByType(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT);
@@ -1433,10 +1662,12 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEnt
armor= int32(float(armor) * (float(100-(*itr)->GetAmount())/100.0f));
continue;
}
+
// item dependent spell - check curent weapons
for(int i = 0; i < MAX_ATTACK; ++i)
{
Item *weapon = ((Player *)this)->GetWeaponForAttack(WeaponAttackType(i));
+
if(weapon && weapon->IsFitToSpellRequirements((*itr)->GetSpellProto()))
{
armor= int32(float(armor) * (float(100-(*itr)->GetAmount())/100.0f));
@@ -1445,6 +1676,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEnt
}
}
}
+
// Apply Player CR_ARMOR_PENETRATION rating
if (GetTypeId()==TYPEID_PLAYER)
{
@@ -1452,7 +1684,7 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEnt
if (getLevel()<60)
maxArmorPen=400+85*pVictim->getLevel();
else
- maxArmorPen=400+85*pVictim->getLevel()+4.5*85*(pVictim->getLevel()-59);
+ maxArmorPen=400+85*pVictim->getLevel()+4.5*85*(pVictim->getLevel()-59);
// Cap armor penetration to this number
maxArmorPen = std::min(((armor+maxArmorPen)/3),armor);
// Figure out how much armor do we ignore
@@ -1462,24 +1694,32 @@ uint32 Unit::CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEnt
}
// Ignore enemy armor by SPELL_AURA_MOD_TARGET_ARMOR_PCT
//armor *= 1.0f - GetTotalAuraModifier(SPELL_AURA_MOD_ARMOR_PENETRATION_PCT) / 100.0f;
+
if (armor < 0.0f)
armor = 0.0f;
+
float levelModifier = getLevel();
if (levelModifier > 59)
levelModifier = levelModifier + (4.5f * (levelModifier-59));
+
float tmpvalue = 0.1f * armor / (8.5f * levelModifier + 40);
tmpvalue = tmpvalue/(1.0f + tmpvalue);
+
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, SpellEntry const *spellInfo)
{
if(!pVictim || !pVictim->isAlive() || !damage)
return;
+
// Magic damage, check for resists
if ((schoolMask & SPELL_SCHOOL_MASK_NORMAL)==0)
{
@@ -1487,6 +1727,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
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;
@@ -1510,6 +1751,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
*resist += uint32(damage * m / 4);
if(*resist > damage)
*resist = damage;
+
AuraEffectList const& ResIgnoreAurasAb = GetAurasByType(SPELL_AURA_MOD_ABILITY_IGNORE_TARGET_RESIST);
for(AuraEffectList::const_iterator j = ResIgnoreAurasAb.begin();j != ResIgnoreAurasAb.end(); ++j)
{
@@ -1517,6 +1759,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
&& (*j)->isAffectedOnSpell(spellInfo))
*resist= int32(float(*resist) * (float(100-(*j)->GetAmount())/100.0f));
}
+
AuraEffectList const& ResIgnoreAuras = GetAurasByType(SPELL_AURA_MOD_IGNORE_TARGET_RESIST);
for(AuraEffectList::const_iterator j = ResIgnoreAuras.begin();j != ResIgnoreAuras.end(); ++j)
{
@@ -1526,6 +1769,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
}
else
*resist = 0;
+
int32 RemainingDamage = damage - *resist;
int32 TotalAbsorb = RemainingDamage;
// Get unit state (need for some absorb check)
@@ -1542,9 +1786,12 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
{
if (!((*i)->GetMiscValue() & schoolMask))
continue;
+
SpellEntry const* spellProto = (*i)->GetSpellProto();
+
// Max Amount can be absorbed by this aura
int32 currentAbsorb = (*i)->GetAmount();
+
// Found empty aura (impossible but..)
if (currentAbsorb <=0)
{
@@ -1644,6 +1891,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
preventDeathAmount = (*i)->GetAmount();
continue;
}
+
// Power Word: Shield
if (spellProto->SpellFamilyFlags.IsEqual(0x1, 0, 0x400))
{
@@ -1659,7 +1907,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
{
case 5065: // Rank 1
case 5064: // Rank 2
- triggeredSpells.push_back(TriggeredSpellInfo(33619, pVictim, this,
+ triggeredSpells.push_back(TriggeredSpellInfo(33619, pVictim, this,
std::min(RemainingDamage, currentAbsorb) * aurEff->GetAmount() / 100, *i));
break;
default:
@@ -1690,9 +1938,11 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
if(Unit *caster = (*i)->GetCaster())
{
uint32 absorbed = uint32( currentAbsorb * caster->GetArmor() * 0.01f );
+
// Glyph of Unbreakable Armor
if (AuraEffect *aurEff = caster->GetAuraEffect(58635, 0))
absorbed += uint32( absorbed * aurEff->GetAmount() / 100 );
+
RemainingDamage -= absorbed;
}
continue;
@@ -1715,6 +1965,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
int32 canabsorb = caster->GetHealth();
if (canabsorb < absorbed)
absorbed = canabsorb;
+
RemainingDamage -= absorbed;
triggeredSpells.push_back(TriggeredSpellInfo(0, this, caster, absorbed, *i));
}
@@ -1727,17 +1978,21 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
default:
break;
}
+
// currentAbsorb - damage can be absorbed by shield
// If need absorb less damage
if (RemainingDamage < currentAbsorb)
currentAbsorb = RemainingDamage;
+
RemainingDamage -= currentAbsorb;
+
// Reduce shield amount
(*i)->SetAmount((*i)->GetAmount() -currentAbsorb);
// Need remove it later
if ((*i)->GetAmount()<=0)
existExpired = true;
}
+
for(TriggeredSpellInfoVct::const_iterator itr = triggeredSpells.begin(); itr != triggeredSpells.end(); ++itr)
{
if(itr->spell)
@@ -1751,6 +2006,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
itr->source->DealDamage(itr->target, damage, NULL, damagetype, schoolMask, 0, false);
}
}
+
// Remove all expired absorb auras
if (existExpired)
{
@@ -1768,35 +2024,44 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
}
}
}
+
// absorb by mana cost
AuraEffectList const& vManaShield = pVictim->GetAurasByType(SPELL_AURA_MANA_SHIELD);
for(AuraEffectList::const_iterator i = vManaShield.begin(), next; i != vManaShield.end() && RemainingDamage > 0; i = next)
{
next = i; ++next;
+
// check damage school mask
if(((*i)->GetMiscValue() & schoolMask)==0)
continue;
+
int32 currentAbsorb;
if (RemainingDamage >= (*i)->GetAmount())
currentAbsorb = (*i)->GetAmount();
else
currentAbsorb = RemainingDamage;
+
float manaMultiplier = (*i)->GetSpellProto()->EffectMultipleValue[(*i)->GetEffIndex()];
if(Player *modOwner = pVictim->GetSpellModOwner())
modOwner->ApplySpellMod((*i)->GetId(), SPELLMOD_MULTIPLE_VALUE, manaMultiplier);
+
int32 maxAbsorb = int32(pVictim->GetPower(POWER_MANA) / manaMultiplier);
if (currentAbsorb > maxAbsorb)
currentAbsorb = maxAbsorb;
+
(*i)->SetAmount((*i)->GetAmount()-currentAbsorb);
if((*i)->GetAmount() <= 0)
{
pVictim->RemoveAura((*i)->GetParentAura(), AURA_REMOVE_BY_ENEMY_SPELL);
next = vManaShield.begin();
}
+
int32 manaReduction = int32(currentAbsorb * manaMultiplier);
pVictim->ApplyPowerMod(POWER_MANA, manaReduction, false);
+
RemainingDamage -= currentAbsorb;
}
+
// only split damage if not damaging yourself
if(pVictim != this)
{
@@ -1804,47 +2069,63 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
for(AuraEffectList::const_iterator i = vSplitDamageFlat.begin(), next; i != vSplitDamageFlat.end() && RemainingDamage >= 0; i = next)
{
next = i; ++next;
+
// check damage school mask
if(((*i)->GetMiscValue() & 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)->GetAmount())
currentAbsorb = (*i)->GetAmount();
else
currentAbsorb = RemainingDamage;
+
RemainingDamage -= currentAbsorb;
+
uint32 splitted = currentAbsorb;
uint32 splitted_absorb = 0;
DealDamageMods(caster,splitted,&splitted_absorb);
+
SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, splitted_absorb, 0, false, 0, false);
+
CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
}
+
AuraEffectList const& vSplitDamagePct = pVictim->GetAurasByType(SPELL_AURA_SPLIT_DAMAGE_PCT);
for(AuraEffectList::const_iterator i = vSplitDamagePct.begin(), next; i != vSplitDamagePct.end() && RemainingDamage >= 0; i = next)
{
next = i; ++next;
+
// check damage school mask
if(((*i)->GetMiscValue() & 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;
+
uint32 splitted = uint32(RemainingDamage * (*i)->GetAmount() / 100.0f);
+
RemainingDamage -= int32(splitted);
+
uint32 split_absorb = 0;
DealDamageMods(caster,splitted,&split_absorb);
+
SendSpellNonMeleeDamageLog(caster, (*i)->GetSpellProto()->Id, splitted, schoolMask, split_absorb, 0, false, 0, false);
+
CleanDamage cleanDamage = CleanDamage(splitted, 0, BASE_ATTACK, MELEE_HIT_NORMAL);
DealDamage(caster, splitted, &cleanDamage, DIRECT_DAMAGE, schoolMask, (*i)->GetSpellProto(), false);
}
}
+
TotalAbsorb = (TotalAbsorb - RemainingDamage > 0) ? TotalAbsorb - RemainingDamage : 0;
// TODO: School should be checked for absorbing auras or for attacks?
int32 auraAbsorbMod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_TARGET_ABSORB_SCHOOL);
@@ -1855,8 +2136,10 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
&& (*i)->isAffectedOnSpell(spellInfo))
auraAbsorbMod = (*i)->GetAmount();
}
+
// Ignore absorb - add reduced amount again to damage
RemainingDamage += auraAbsorbMod * TotalAbsorb / 100;
+
// Apply death prevention spells effects
if (preventDeathSpell && RemainingDamage >= pVictim->GetHealth())
{
@@ -1889,7 +2172,9 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
}
}
}
+
*absorb = RemainingDamage > 0 ? (damage - RemainingDamage - *resist) : (damage - *resist);
+
if (*absorb)
{
// Incanter's Absorption
@@ -1902,6 +2187,7 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
for(AuraMap::const_iterator iter = range.first; iter != range.second; ++iter)
if (AuraEffect const * bonusEff = iter->second->GetPartAura(0))
current_dmg += bonusEff->GetAmount();
+
int32 new_dmg = (int32)*absorb * aurEff->GetAmount() / 100;
int32 max_dmg = (int32)pVictim->GetMaxHealth() * 5 / 100;
// Do not apply more auras if more than 5% hp
@@ -1912,14 +2198,18 @@ void Unit::CalcAbsorbResist(Unit *pVictim,SpellSchoolMask schoolMask, DamageEffe
}
}
}
+
void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool extra )
{
if(hasUnitState(UNIT_STAT_CANNOT_AUTOATTACK) || HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PACIFIED) )
return;
+
if (!pVictim->isAlive())
return;
+
CombatStart(pVictim);
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MELEE_ATTACK);
+
uint32 hitInfo;
if (attType == BASE_ATTACK)
hitInfo = HITINFO_NORMALSWING2;
@@ -1927,75 +2217,98 @@ void Unit::AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType, bool ex
hitInfo = HITINFO_LEFTSWING;
else
return; // ignore ranged case
+
// melee attack spell casted at main hand attack only
if (attType == BASE_ATTACK && m_currentSpells[CURRENT_MELEE_SPELL])
{
m_currentSpells[CURRENT_MELEE_SPELL]->cast();
return;
}
+
// attack can be redirected to another target
pVictim = SelectMagnetTarget(pVictim);
+
CalcDamageInfo damageInfo;
CalculateMeleeDamage(pVictim, 0, &damageInfo, attType);
// Send log damage message to client
DealDamageMods(pVictim,damageInfo.damage,&damageInfo.absorb);
SendAttackStateUpdate(&damageInfo);
+
ProcDamageAndSpell(damageInfo.target, damageInfo.procAttacker, damageInfo.procVictim, damageInfo.procEx, damageInfo.damage, damageInfo.attackType);
DealMeleeDamage(&damageInfo,true);
+
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(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
else
DEBUG_LOG("AttackerStateUpdate: (NPC) %u attacked %u (TypeId: %u) for %u dmg, absorbed %u, blocked %u, resisted %u.",
GetGUIDLow(), pVictim->GetGUIDLow(), pVictim->GetTypeId(), damageInfo.damage, damageInfo.absorb, damageInfo.blocked_amount, damageInfo.resist);
+
// if damage pVictim call AI reaction
//if(pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->AI())
// ((Creature*)pVictim)->AI()->AttackedBy(this);
+
}
+
MeleeHitOutcome Unit::RollMeleeOutcomeAgainst(const Unit *pVictim, WeaponAttackType attType) const
{
// This is only wrapper
+
// Miss chance based on melee
//float miss_chance = MeleeMissChanceCalc(pVictim, attType);
float miss_chance = MeleeSpellMissChance(pVictim, attType, int32(GetWeaponSkillValue(attType,pVictim)) - int32(pVictim->GetDefenseSkillValue(this)), 0);
+
// 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));
}
+
MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttackType attType, int32 crit_chance, int32 miss_chance, int32 dodge_chance, int32 parry_chance, int32 block_chance) 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 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) && !pVictim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
{
@@ -2006,9 +2319,11 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
// 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)*100;
dodge_chance = int32 (float (dodge_chance) * GetTotalAuraMultiplier(SPELL_AURA_MOD_ENEMY_DODGE));
+
tmp = dodge_chance;
if ( (tmp > 0) // check if unit _can_ dodge
&& ((tmp -= skillBonus) > 0)
@@ -2018,7 +2333,9 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
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) && !pVictim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
{
@@ -2029,6 +2346,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
// 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 tmp2 = int32(parry_chance);
@@ -2040,6 +2358,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
return MELEE_HIT_PARRY;
}
}
+
if(pVictim->GetTypeId()==TYPEID_PLAYER || !(((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK) )
{
tmp = block_chance;
@@ -2052,8 +2371,10 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
}
}
}
+
// Critical chance
tmp = crit_chance;
+
if (tmp > 0 && roll < (sum += tmp))
{
DEBUG_LOG ("RollMeleeOutcomeAgainst: CRIT <%d, %d)", sum-tmp, sum);
@@ -2062,6 +2383,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
else
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 &&
(GetTypeId() == TYPEID_PLAYER || ((Creature*)this)->isPet()) &&
@@ -2072,6 +2394,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
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))
@@ -2080,6 +2403,7 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
return MELEE_HIT_GLANCING;
}
}
+
// mobs can score crushing blows if they're 4 or more levels above victim
if (getLevelForTarget(pVictim) >= pVictim->getLevelForTarget(this) + 4 &&
// can be from by creature (if can) or from controlled player that considered as creature
@@ -2104,12 +2428,15 @@ MeleeHitOutcome Unit::RollMeleeOutcomeAgainst (const Unit *pVictim, WeaponAttack
}
}
}
+
DEBUG_LOG ("RollMeleeOutcomeAgainst: NORMAL");
return MELEE_HIT_NORMAL;
}
+
uint32 Unit::CalculateDamage (WeaponAttackType attType, bool normalized, bool addTotalPct)
{
float min_damage, max_damage;
+
if (GetTypeId()==TYPEID_PLAYER && (normalized || !addTotalPct))
((Player*)this)->CalculateMinMaxDamage(attType,normalized,addTotalPct,min_damage, max_damage);
else
@@ -2135,47 +2462,60 @@ uint32 Unit::CalculateDamage (WeaponAttackType attType, bool normalized, bool ad
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::SendMeleeAttackStart(Unit* pVictim)
{
WorldPacket data( SMSG_ATTACKSTART, 8 + 8 );
data << uint64(GetGUID());
data << uint64(pVictim->GetGUID());
+
SendMessageToSet(&data, true);
DEBUG_LOG( "WORLD: Sent SMSG_ATTACKSTART" );
}
+
void Unit::SendMeleeAttackStop(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);*/
}
+
bool Unit::isSpellBlocked(Unit *pVictim, SpellEntry const * /*spellProto*/, WeaponAttackType attackType)
{
if (pVictim->HasInArc(M_PI,this) || pVictim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
@@ -2191,10 +2531,12 @@ bool Unit::isSpellBlocked(Unit *pVictim, SpellEntry const * /*spellProto*/, Weap
return false;
}
*/
+
// Check creatures flags_extra for disable block
if(pVictim->GetTypeId()==TYPEID_UNIT &&
((Creature*)pVictim)->GetCreatureInfo()->flags_extra & CREATURE_FLAG_EXTRA_NO_BLOCK )
return false;
+
float blockChance = pVictim->GetUnitBlockChance();
blockChance += (int32(GetWeaponSkillValue(attackType)) - int32(pVictim->GetMaxSkillValueForLevel()))*0.04f;
if (roll_chance_f(blockChance))
@@ -2202,18 +2544,21 @@ bool Unit::isSpellBlocked(Unit *pVictim, SpellEntry const * /*spellProto*/, Weap
}
return false;
}
+
bool Unit::isBlockCritical()
{
if (roll_chance_i(GetTotalAuraModifier(SPELL_AURA_MOD_BLOCK_CRIT_CHANCE)))
return true;
return false;
}
+
// 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);
@@ -2221,23 +2566,29 @@ bool Unit::isBlockCritical()
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;
@@ -2246,6 +2597,7 @@ bool Unit::isBlockCritical()
return miss_chance;
}*/
+
int32 Unit::GetMechanicResistChance(const SpellEntry *spell)
{
if(!spell)
@@ -2265,29 +2617,36 @@ int32 Unit::GetMechanicResistChance(const SpellEntry *spell)
}
return resist_mech;
}
+
// Melee based spells hit result calculations
SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
{
WeaponAttackType attType = BASE_ATTACK;
- // Check damage class instead of attack type to correctly handle judgements
+
+ // Check damage class instead of attack type to correctly handle judgements
// - they are meele, but can't be dodged/parried/deflected because of ranged dmg class
if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED)
attType = RANGED_ATTACK;
+
int32 attackerWeaponSkill;
// skill value for these spells (for example judgements) is 5* level
if (spell->DmgClass == SPELL_DAMAGE_CLASS_RANGED && !IsRangedWeaponSpell(spell))
attackerWeaponSkill = getLevel() * 5;
// bonus from skills is 0.04% per skill Diff
- else
+ else
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->Id)*100.0f);
// Roll miss
uint32 tmp = missChance;
if (roll < tmp)
return SPELL_MISS_MISS;
+
// Chance resist mechanic (select max value from every mechanic spell effect)
int32 resist_mech = 0;
// Get effects mechanic and chance
@@ -2305,17 +2664,21 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
tmp += resist_mech;
if (roll < tmp)
return SPELL_MISS_RESIST;
+
bool canDodge = true;
bool canParry = true;
bool canBlock = spell->AttributesEx3 & SPELL_ATTR_EX3_BLOCKABLE_SPELL;
+
// Same spells cannot be parry/dodge
if (spell->Attributes & SPELL_ATTR_IMPOSSIBLE_DODGE_PARRY_BLOCK)
return SPELL_MISS_NONE;
+
// Chance resist mechanic
int32 resist_chance = pVictim->GetMechanicResistChance(spell)*100;
tmp += resist_chance;
if (roll < tmp)
return SPELL_MISS_RESIST;
+
// Ranged attacks can only miss, resist and deflect
if (attType == RANGED_ATTACK)
{
@@ -2329,6 +2692,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
}
return SPELL_MISS_NONE;
}
+
// Check for attack from behind
if (!pVictim->HasInArc(M_PI,this) && !pVictim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
{
@@ -2365,6 +2729,7 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
break;
}
}
+
if (canDodge)
{
// Roll dodge
@@ -2377,10 +2742,12 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
dodgeChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
if (dodgeChance < 0)
dodgeChance = 0;
+
tmp += dodgeChance;
if (roll < tmp)
return SPELL_MISS_DODGE;
}
+
if (canParry)
{
// Roll parry
@@ -2390,37 +2757,45 @@ SpellMissInfo Unit::MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell)
parryChance-=int32(((Player*)this)->GetExpertiseDodgeOrParryReduction(attType) * 100.0f);
if (parryChance < 0)
parryChance = 0;
+
tmp += parryChance;
if (roll < tmp)
return SPELL_MISS_PARRY;
}
+
if (canBlock)
{
int32 blockChance = int32(pVictim->GetUnitBlockChance()*100.0f) - skillDiff * 4;
if ( blockChance < 0 )
blockChance = 0;
tmp += blockChance;
+
if (roll < tmp)
return SPELL_MISS_BLOCK;
}
+
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() && pVictim->GetTypeId() != TYPEID_PLAYER)
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);
@@ -2434,26 +2809,36 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
// 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);
+
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 > 10000) HitChance = 10000;
+
int32 tmp = 10000 - HitChance;
+
uint32 rand = urand(0,10000);
+
if (rand < tmp)
return SPELL_MISS_MISS;
+
// Chance resist mechanic (select max value from every mechanic spell effect)
int32 resist_chance = pVictim->GetMechanicResistChance(spell);
tmp += resist_chance;
+
// Chance resist debuff
tmp -= pVictim->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_DEBUFF_RESISTANCE, int32(spell->Dispel));
+
// Roll chance
if (rand < tmp)
return SPELL_MISS_RESIST;
+
// cast by caster in front of victim
if (pVictim->HasInArc(M_PI,this) || pVictim->HasAuraType(SPELL_AURA_IGNORE_HIT_DIRECTION))
{
@@ -2462,8 +2847,10 @@ SpellMissInfo Unit::MagicSpellHitResult(Unit *pVictim, SpellEntry const *spell)
if (rand < tmp)
return SPELL_MISS_DEFLECT;
}
+
return SPELL_MISS_NONE;
}
+
// Calculate spell hit result can be:
// Every spell can: Evade/Immune/Reflect/Sucesful hit
// For melee based spells:
@@ -2477,9 +2864,11 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
// Return evade for units in evade mode
if (pVictim->GetTypeId()==TYPEID_UNIT && ((Creature*)pVictim)->IsInEvadeMode() && this != pVictim)
return SPELL_MISS_EVADE;
+
// Check for immune
if (pVictim->IsImmunedToSpell(spell))
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)
@@ -2488,8 +2877,10 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
// Check for immune
if (pVictim->IsImmunedToDamage(spell))
return SPELL_MISS_IMMUNE;
+
if(this == pVictim)
return SPELL_MISS_NONE;
+
// Try victim reflect spell
if (CanReflect)
{
@@ -2505,6 +2896,7 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
return SPELL_MISS_REFLECT;
}
}
+
switch (spell->DmgClass)
{
case SPELL_DAMAGE_CLASS_RANGED:
@@ -2517,12 +2909,15 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
}
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)
{
@@ -2540,21 +2935,26 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
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)
{
@@ -2563,21 +2963,26 @@ SpellMissInfo Unit::SpellHitResult(Unit *pVictim, SpellEntry const *spell, bool
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)
@@ -2592,6 +2997,7 @@ uint32 Unit::GetDefenseSkillValue(Unit const* target) const
else
return GetUnitMeleeSkill(target);
}
+
float Unit::GetUnitDodgeChance() const
{
if(hasUnitState(UNIT_STAT_STUNNED))
@@ -2610,11 +3016,14 @@ float Unit::GetUnitDodgeChance() const
}
}
}
+
float Unit::GetUnitParryChance() const
{
if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
return 0.0f;
+
float chance = 0.0f;
+
if(GetTypeId() == TYPEID_PLAYER)
{
Player const* player = (Player const*)this;
@@ -2623,6 +3032,7 @@ float Unit::GetUnitParryChance() const
Item *tmpitem = player->GetWeaponForAttack(BASE_ATTACK,true);
if(!tmpitem)
tmpitem = player->GetWeaponForAttack(OFF_ATTACK,true);
+
if(tmpitem)
chance = GetFloatValue(PLAYER_PARRY_PERCENTAGE);
}
@@ -2635,12 +3045,15 @@ float Unit::GetUnitParryChance() const
chance += GetTotalAuraModifier(SPELL_AURA_MOD_PARRY_PERCENT);
}
}
+
return chance > 0.0f ? chance : 0.0f;
}
+
float Unit::GetUnitBlockChance() const
{
if ( IsNonMeleeSpellCasted(false) || hasUnitState(UNIT_STAT_STUNNED))
return 0.0f;
+
if(GetTypeId() == TYPEID_PLAYER)
{
Player const* player = (Player const*)this;
@@ -2665,9 +3078,11 @@ float Unit::GetUnitBlockChance() const
}
}
}
+
float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVictim) const
{
float crit;
+
if(GetTypeId() == TYPEID_PLAYER)
{
switch(attackType)
@@ -2693,12 +3108,15 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVict
crit += GetTotalAuraModifier(SPELL_AURA_MOD_WEAPON_CRIT_PERCENT);
crit += GetTotalAuraModifier(SPELL_AURA_MOD_CRIT_PCT);
}
+
// 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)
{
@@ -2707,25 +3125,32 @@ float Unit::GetUnitCriticalChance(WeaponAttackType attackType, const Unit *pVict
else
crit -= ((Player*)pVictim)->GetRatingBonusValue(CR_CRIT_TAKEN_MELEE);
}
+
// Apply crit chance from defence skill
crit += (int32(GetMaxSkillValueForLevel(pVictim)) - int32(pVictim->GetDefenseSkillValue(this))) * 0.04f;
+
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(IsInFeralForm())
return GetMaxSkillValueForLevel(); // always maximized SKILL_FERAL_COMBAT in fact
+
// weapon 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->IsControlledByPlayer())
? ((Player*)this)->GetMaxSkillValue(skill)
@@ -2743,6 +3168,7 @@ uint32 Unit::GetWeaponSkillValue (WeaponAttackType attType, Unit const* target)
value = GetUnitMeleeSkill(target);
return value;
}
+
void Unit::_DeleteAuras()
{
while(!m_removedAuras.empty())
@@ -2752,10 +3178,12 @@ void Unit::_DeleteAuras()
// sLog.outDebug("Aura %d is deleted from unit %d", Aur->GetId(), GetGUIDLow());
}
}
+
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)
{
@@ -2765,6 +3193,7 @@ void Unit::_UpdateSpells( uint32 time )
m_currentSpells[i] = NULL; // remove pointer
}
}
+
// update auras
// m_AurasUpdateIterator can be updated in inderect called code at aura remove to skip next planned to update but removed auras
for (m_AurasUpdateIterator = m_Auras.begin(); m_AurasUpdateIterator != m_Auras.end();)
@@ -2773,6 +3202,7 @@ void Unit::_UpdateSpells( uint32 time )
++m_AurasUpdateIterator; // need shift to next for allow update if need into aura update
i_aura->Update(time);
}
+
// remove expired auras
for(AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end();)
{
@@ -2781,7 +3211,9 @@ void Unit::_UpdateSpells( uint32 time )
else
++i;
}
+
_DeleteAuras();
+
if(!m_gameObj.empty())
{
GameObjectList::iterator itr;
@@ -2799,6 +3231,7 @@ void Unit::_UpdateSpells( uint32 time )
}
}
}
+
void Unit::_UpdateAutoRepeatSpell()
{
//check "realtime" interrupts
@@ -2810,10 +3243,12 @@ void Unit::_UpdateAutoRepeatSpell()
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))
{
@@ -2823,20 +3258,27 @@ void Unit::_UpdateAutoRepeatSpell()
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
+
CurrentSpellTypes CSpellType = pSpell->GetCurrentContainer();
+
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)
{
@@ -2844,6 +3286,7 @@ void Unit::SetCurrentCastedSpell( Spell * pSpell )
{
// generic spells always break channeled not delayed spells
InterruptSpell(CURRENT_CHANNELED_SPELL,false);
+
// autorepeat breaking
if ( m_currentSpells[CURRENT_AUTOREPEAT_SPELL] )
{
@@ -2854,17 +3297,20 @@ void Unit::SetCurrentCastedSpell( Spell * pSpell )
}
addUnitState(UNIT_STAT_CASTING);
} 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->Id != SPELL_ID_AUTOSHOT )
InterruptSpell(CURRENT_AUTOREPEAT_SPELL);
addUnitState(UNIT_STAT_CASTING);
} break;
+
case CURRENT_AUTOREPEAT_SPELL:
{
// only Auto Shoot does not break anything
@@ -2877,22 +3323,28 @@ void Unit::SetCurrentCastedSpell( Spell * pSpell )
// 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]->SetReferencedFromCurrent(false);
+
// set new current spell
m_currentSpells[CSpellType] = pSpell;
pSpell->SetReferencedFromCurrent(true);
+
pSpell->m_selfContainer = &(m_currentSpells[pSpell->GetCurrentContainer()]);
}
+
void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool withInstant)
{
assert(spellType < CURRENT_MAX_SPELL);
+
//sLog.outDebug("Interrupt spell for unit %u.", GetEntry());
Spell *spell = m_currentSpells[spellType];
if(spell
@@ -2902,31 +3354,39 @@ void Unit::InterruptSpell(CurrentSpellTypes spellType, bool withDelayed, bool wi
// for example, do not let self-stun aura interrupt itself
if(!spell->IsInterruptable())
return;
+
m_currentSpells[spellType] = NULL;
+
// send autorepeat cancel message for autorepeat spells
if (spellType == CURRENT_AUTOREPEAT_SPELL)
{
if(GetTypeId() == TYPEID_PLAYER)
((Player*)this)->SendAutoRepeatCancel(this);
}
+
if (spell->getState() != SPELL_STATE_FINISHED)
spell->cancel();
spell->SetReferencedFromCurrent(false);
}
}
+
void Unit::FinishSpell(CurrentSpellTypes spellType, bool ok /*= true*/)
{
Spell* spell = m_currentSpells[spellType];
if (!spell)
return;
+
if (spellType == CURRENT_CHANNELED_SPELL)
spell->SendChannelUpdate(0);
+
spell->finish(ok);
}
+
bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skipAutorepeat, bool isAutoshoot) 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) &&
@@ -2945,20 +3405,25 @@ bool Unit::IsNonMeleeSpellCasted(bool withDelayed, bool skipChanneled, bool skip
// 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, bool withInstant)
{
// 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))
InterruptSpell(CURRENT_GENERIC_SPELL,withDelayed,withInstant);
+
// 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))
InterruptSpell(CURRENT_AUTOREPEAT_SPELL,withDelayed,withInstant);
+
// 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))
InterruptSpell(CURRENT_CHANNELED_SPELL,true,true);
}
+
Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
{
for (uint32 i = 0; i < CURRENT_MAX_SPELL; i++)
@@ -2966,20 +3431,24 @@ Spell* Unit::FindCurrentSpellBySpellId(uint32 spell_id) const
return m_currentSpells[i];
return NULL;
}
+
int32 Unit::GetCurrentSpellCastTime(uint32 spell_id) const
{
if (Spell const * spell = FindCurrentSpellBySpellId(spell_id))
return spell->GetCastTime();
return 0;
}
+
bool Unit::isInFrontInMap(Unit const* target, float distance, float arc) const
{
return IsWithinDistInMap(target, distance) && HasInArc( arc, target );
}
+
bool Unit::isInBackInMap(Unit const* target, float distance, float arc) const
{
return IsWithinDistInMap(target, distance) && !HasInArc( 2 * M_PI - arc, target );
}
+
bool Unit::isInAccessiblePlaceFor(Creature const* c) const
{
if(IsInWater())
@@ -2987,57 +3456,74 @@ bool Unit::isInAccessiblePlaceFor(Creature const* c) const
else
return c->canWalk() || c->canFly();
}
+
bool Unit::IsInWater() const
{
return GetBaseMap()->IsInWater(GetPositionX(),GetPositionY(), GetPositionZ());
}
+
bool Unit::IsUnderWater() const
{
return GetBaseMap()->IsUnderWater(GetPositionX(),GetPositionY(),GetPositionZ());
}
+
void Unit::DeMorph()
{
SetDisplayId(GetNativeDisplayId());
}
+
int32 Unit::GetTotalAuraModifier(AuraType auratype) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
modifier += (*i)->GetAmount();
+
return modifier;
}
+
float Unit::GetTotalAuraMultiplier(AuraType auratype) const
{
float multiplier = 1.0f;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
multiplier *= (100.0f + (*i)->GetAmount())/100.0f;
+
return multiplier;
}
+
int32 Unit::GetMaxPositiveAuraModifier(AuraType auratype)
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
if ((*i)->GetAmount() > modifier)
modifier = (*i)->GetAmount();
}
+
return modifier;
}
+
int32 Unit::GetMaxNegativeAuraModifier(AuraType auratype) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
if ((*i)->GetAmount() < modifier)
modifier = (*i)->GetAmount();
+
return modifier;
}
+
int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
@@ -3046,9 +3532,11 @@ int32 Unit::GetTotalAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask)
}
return modifier;
}
+
float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask) const
{
float multiplier = 1.0f;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
@@ -3057,31 +3545,39 @@ float Unit::GetTotalAuraMultiplierByMiscMask(AuraType auratype, uint32 misc_mask
}
return multiplier;
}
+
int32 Unit::GetMaxPositiveAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
if ((*i)->GetMiscValue()& misc_mask && (*i)->GetAmount() > modifier)
modifier = (*i)->GetAmount();
}
+
return modifier;
}
+
int32 Unit::GetMaxNegativeAuraModifierByMiscMask(AuraType auratype, uint32 misc_mask) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
if ((*i)->GetMiscValue()& misc_mask && (*i)->GetAmount() < modifier)
modifier = (*i)->GetAmount();
}
+
return modifier;
}
+
int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
@@ -3090,9 +3586,11 @@ int32 Unit::GetTotalAuraModifierByMiscValue(AuraType auratype, int32 misc_value)
}
return modifier;
}
+
float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_value) const
{
float multiplier = 1.0f;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
@@ -3101,28 +3599,35 @@ float Unit::GetTotalAuraMultiplierByMiscValue(AuraType auratype, int32 misc_valu
}
return multiplier;
}
+
int32 Unit::GetMaxPositiveAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
if ((*i)->GetMiscValue()== misc_value && (*i)->GetAmount() > modifier)
modifier = (*i)->GetAmount();
}
+
return modifier;
}
+
int32 Unit::GetMaxNegativeAuraModifierByMiscValue(AuraType auratype, int32 misc_value) const
{
int32 modifier = 0;
+
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
for(AuraEffectList::const_iterator i = mTotalAuraList.begin();i != mTotalAuraList.end(); ++i)
{
if ((*i)->GetMiscValue()== misc_value && (*i)->GetAmount() < modifier)
modifier = (*i)->GetAmount();
}
+
return modifier;
}
+
bool Unit::AddAura(Aura *Aur, bool handleEffects)
{
// aura doesn't apply effects-return
@@ -3131,7 +3636,9 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
delete Aur;
return false;
}
+
SpellEntry const* aurSpellInfo = Aur->GetSpellProto();
+
// ghost spell check, allow apply any auras at player loading in ghost mode (will be cleanup after load)
if( !isAlive() && !IsDeathPersistentSpell(aurSpellInfo) &&
//Aur->GetId() != 2584 && // Waiting to Resurrect (not have death persistence flag)
@@ -3140,6 +3647,7 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
delete Aur;
return false;
}
+
if(Aur->GetTarget() != this)
{
sLog.outError("Aura (spell %u) add to aura list of %s (lowguid: %u) but Aura target is %s (lowguid: %u)",
@@ -3148,7 +3656,9 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
delete Aur;
return false;
}
+
uint32 aurId = aurSpellInfo->Id;
+
// passive and persistent and Incanter's Absorption auras can stack with themselves any number of times
if (!Aur->IsPassive() && !Aur->IsPersistent() && aurId != 44413)
{
@@ -3161,6 +3671,7 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
if (stackAmount > aurSpellInfo->StackAmount)
stackAmount = aurSpellInfo->StackAmount;
Aur->SetStackAmount(stackAmount, false);
+
// spell is triggered with only stackamount change but no amount change
switch(aurId)
{
@@ -3188,20 +3699,23 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
case 64821: // Fuse Armor (Razorscale)
if(stackAmount == aurSpellInfo->StackAmount)
CastSpell(this, 64774, true, NULL, NULL, Aur->GetCasterGUID());
- break;
+ break;
}
}
+
// Use the new one to replace the old one
// This is the only place where AURA_REMOVE_BY_STACK should be used
RemoveAura(foundAura, AURA_REMOVE_BY_STACK);
}
}
+
// passive auras not stacable with other ranks
if (!RemoveNoStackAurasDueToAura(Aur))
{
delete Aur;
return false; // couldn't 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 (Aur->IsSingleTarget())
{
@@ -3210,6 +3724,7 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
{
Unit* caster = Aur->GetCaster();
assert(caster);
+
bool restart = false;
AuraList& scAuras = caster->GetSingleCastAuras();
for(AuraList::iterator itr = scAuras.begin(); itr != scAuras.end(); ++itr)
@@ -3222,6 +3737,7 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
break;
}
}
+
if(!restart)
{
// done
@@ -3230,8 +3746,10 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
}
}
}
+
// add aura, register in lists and arrays
Aur->_AddAura();
+
//*****************************************************
// Update target aura state flag
//*****************************************************
@@ -3240,25 +3758,34 @@ bool Unit::AddAura(Aura *Aur, bool handleEffects)
m_auraStateAuras.insert(AuraStateAurasMap::value_type(aState, Aur));
ModifyAuraState(aState, true);
}
+
m_Auras.insert(AuraMap::value_type(aurId, Aur));
+
if(aurSpellInfo->AuraInterruptFlags)
{
m_interruptableAuras.push_back(Aur);
AddInterruptMask(aurSpellInfo->AuraInterruptFlags);
}
+
if (handleEffects)
Aur->HandleEffects(true);
+
sLog.outDebug("Aura %u now is in use", aurId);
return true;
}
+
bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
{
SpellEntry const* spellProto = Aur->GetSpellProto();
+
uint32 spellId = Aur->GetId();
+
// passive spell special case (only non stackable with ranks)
if(IsPassiveSpell(spellId) && IsPassiveSpellStackableWithRanks(spellProto))
return true;
+
//bool linked = spellmgr.GetSpellCustomAttr(spellId) & SPELL_ATTR_CU_LINK_AURA? true : false;
+
bool remove = false;
for(AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
{
@@ -3267,18 +3794,22 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
remove = false;
i = m_Auras.begin();
}
+
SpellEntry const* i_spellProto = i->second->GetSpellProto();
uint32 i_spellId = i_spellProto->Id;
bool sameCaster = Aur->GetCasterGUID() == (*i).second->GetCasterGUID();
+
if(IsPassiveSpell(i_spellId))
{
// passive non-stackable spells not stackable only for same caster
if(!sameCaster)
continue;
+
// passive non-stackable spells not stackable only with another rank of same spell
if (!spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
continue;
}
+
bool is_triggered_by_spell = false;
// prevent triggering aura of removing aura that triggered it
// prevent triggered aura of removing aura that triggering it (triggered effect early some aura of parent spell
@@ -3291,7 +3822,9 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
break;
}
}
+
// check if they can stack
+
/*// Dont remove by stack with linked auras
// Not needed for now
if(sameCaster && linked)
@@ -3304,10 +3837,13 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
break;
}
}*/
+
if (is_triggered_by_spell)
continue;
+
if(spellmgr.CanAurasStack(spellProto, i_spellProto, sameCaster))
continue;
+
//some spells should be not removed by lower rank of them (totem, paladin aura)
if (!sameCaster
&&(Aur->IsAreaAura())
@@ -3315,6 +3851,7 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
&&(spellmgr.IsRankSpellDueToSpell(spellProto, i_spellId))
&&(IsHigherHankOfSpell(spellId,i_spellId)))
return false;
+
// Remove all auras by aura caster
RemoveAura(i, AURA_REMOVE_BY_DEFAULT);
if(i == m_Auras.end())
@@ -3323,6 +3860,7 @@ bool Unit::RemoveNoStackAurasDueToAura(Aura *Aur)
}
return true;
}
+
void Unit::RemoveAura(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode)
{
for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);)
@@ -3336,6 +3874,7 @@ void Unit::RemoveAura(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode)
++iter;
}
}
+
void Unit::RemoveAura(Aura * aur ,AuraRemoveMode mode)
{
// no need to remove
@@ -3352,6 +3891,7 @@ void Unit::RemoveAura(Aura * aur ,AuraRemoveMode mode)
++iter;
}
}
+
void Unit::RemoveAurasDueToSpell(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode)
{
for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);)
@@ -3365,6 +3905,7 @@ void Unit::RemoveAurasDueToSpell(uint32 spellId, uint64 caster ,AuraRemoveMode r
++iter;
}
}
+
void Unit::RemoveAuraFromStack(uint32 spellId, uint64 caster ,AuraRemoveMode removeMode)
{
for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);)
@@ -3378,11 +3919,13 @@ void Unit::RemoveAuraFromStack(uint32 spellId, uint64 caster ,AuraRemoveMode rem
++iter;
}
}
+
inline void Unit::RemoveAuraFromStack(AuraMap::iterator &iter,AuraRemoveMode removeMode)
{
if (iter->second->modStackAmount(-1))
RemoveAura(iter, removeMode);
}
+
void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit *dispeler)
{
for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);)
@@ -3391,6 +3934,7 @@ void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit
if (casterGUID == aur->GetCasterGUID())
{
RemoveAuraFromStack(iter, AURA_REMOVE_BY_ENEMY_SPELL);
+
// Unstable Affliction (crash if before removeaura?)
if (aur->GetSpellProto()->SpellFamilyName == SPELLFAMILY_WARLOCK && (aur->GetSpellProto()->SpellFamilyFlags[1] & 0x0100))
{
@@ -3407,10 +3951,12 @@ void Unit::RemoveAurasDueToSpellByDispel(uint32 spellId, uint64 casterGUID, Unit
++iter;
}
}
+
void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit *stealer)
{
if (casterGUID == stealer->GetGUID())
return;
+
for(AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);)
{
Aura * aur = iter->second;
@@ -3427,6 +3973,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit
int32 dur = 2*MINUTE*IN_MILISECONDS < aur->GetAuraDuration() ? 2*MINUTE*IN_MILISECONDS : aur->GetAuraDuration();
Aura * new_aur = new Aura(aur->GetSpellProto(),aur->GetEffectMask(), stealer, stealer, stealer);
new_aur->SetLoadedState(aur->GetCasterGUID(), dur, dur, aur->GetAuraCharges(), aur->GetStackAmount(), &damage[0]);
+
// Unregister _before_ adding to stealer
aur->UnregisterSingleCastAura();
// strange but intended behaviour: Stolen single target auras won't be treated as single targeted
@@ -3439,6 +3986,7 @@ void Unit::RemoveAurasDueToSpellBySteal(uint32 spellId, uint64 casterGUID, Unit
++iter;
}
}
+
void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId)
{
for (AuraMap::iterator iter = m_Auras.lower_bound(spellId); iter != m_Auras.upper_bound(spellId);)
@@ -3452,6 +4000,7 @@ void Unit::RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId)
++iter;
}
}
+
void Unit::RemoveAurasByType(AuraType auraType, uint64 casterGUID, Aura * except, bool negative, bool positive)
{
for (AuraEffectList::const_iterator iter = m_modAuras[auraType].begin(); iter != m_modAuras[auraType].end();)
@@ -3468,6 +4017,7 @@ void Unit::RemoveAurasByType(AuraType auraType, uint64 casterGUID, Aura * except
}
}
}
+
void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase)
{
// single target auras from other casters
@@ -3489,6 +4039,7 @@ void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase)
else
++iter;
}
+
// single target auras at other targets
AuraList& scAuras = GetSingleCastAuras();
for (AuraList::iterator iter = scAuras.begin(); iter != scAuras.end();)
@@ -3504,31 +4055,43 @@ void Unit::RemoveNotOwnSingleTargetAuras(uint32 newPhase)
}
}
}
+
void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
{
Aura* Aur = i->second;
+
// if unit currently update aura list then make safe update iterator shift to next
if (m_AurasUpdateIterator == i)
++m_AurasUpdateIterator;
+
// 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_removedAurasCount;
+
if(Aur->IsPersistent())
if(DynamicObject *dynObj = ObjectAccessor::GetObjectInWorld(Aur->GetSourceGUID(), (DynamicObject*)NULL))
dynObj->RemoveAffected(this);
+
Aur->UnregisterSingleCastAura();
+
if(Aur->GetSpellProto()->AuraInterruptFlags)
{
m_interruptableAuras.remove(Aur);
UpdateInterruptMask();
}
+
Aur->SetRemoveMode(mode);
+
sLog.outDebug("Aura %u now is remove mode %d", Aur->GetId(), mode);
Aur->HandleEffects(false);
+
// set aura to be removed during unit::_updatespells
m_removedAuras.push_back(Aur);
+
Aur->_RemoveAura();
+
bool auraStateFound = false;
if (AuraState auraState = GetSpellAuraState(Aur->GetSpellProto()))
{
@@ -3550,22 +4113,26 @@ void Unit::RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode)
if (!auraStateFound)
ModifyAuraState(auraState, false);
}
+
// Remove totem at next update if totem looses its aura
if (Aur->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE && GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem()&& ((TempSummon*)this)->GetSummonerGUID()==Aur->GetCasterGUID())
{
if (((Totem*)this)->GetSpell()==Aur->GetId() && ((Totem*)this)->GetTotemType()==TOTEM_PASSIVE)
((Totem*)this)->setDeathState(JUST_DIED);
}
+
// only way correctly remove all auras from list
//if(removedAuras != m_removedAurasCount) new aura may be casted
i = m_Auras.begin();
}
+
void Unit::RemoveAllAuras()
{
AuraMap::iterator iter = m_Auras.begin();
while (!m_Auras.empty())
RemoveAura(iter);
}
+
void Unit::RemoveAllAuras(uint64 casterGUID, Aura * except /*=NULL*/, bool negative /*=true*/, bool positive /*=true*/)
{
for(AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end();)
@@ -3577,6 +4144,7 @@ void Unit::RemoveAllAuras(uint64 casterGUID, Aura * except /*=NULL*/, bool negat
++iter;
}
}
+
void Unit::RemoveArenaAuras(bool onleave)
{
// in join, remove positive buffs, on end, remove negative
@@ -3592,6 +4160,7 @@ void Unit::RemoveArenaAuras(bool onleave)
++iter;
}
}
+
void Unit::RemoveAllAurasOnDeath()
{
// used just after dieing to remove all visible auras
@@ -3604,6 +4173,7 @@ void Unit::RemoveAllAurasOnDeath()
++iter;
}
}
+
void Unit::DelayAura(uint32 spellId, uint64 caster, int32 delaytime)
{
if (Aura * aur = GetAura(spellId, caster))
@@ -3617,16 +4187,19 @@ void Unit::DelayAura(uint32 spellId, uint64 caster, int32 delaytime)
sLog.outDebug("Aura %u partially interrupted on unit %u, new duration: %u ms",aur->GetId(), GetGUIDLow(), aur->GetAuraDuration());
}
}
+
void Unit::_RemoveAllAuraMods()
{
for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
(*i).second->ApplyAllModifiers(false);
}
+
void Unit::_ApplyAllAuraMods()
{
for (AuraMap::iterator i = m_Auras.begin(); i != m_Auras.end(); ++i)
(*i).second->ApplyAllModifiers(true);
}
+
bool Unit::HasAuraTypeWithMiscvalue(AuraType auratype, uint32 miscvalue) const
{
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
@@ -3635,6 +4208,7 @@ bool Unit::HasAuraTypeWithMiscvalue(AuraType auratype, uint32 miscvalue) const
return true;
return false;
}
+
bool Unit::HasAuraTypeWithValue(AuraType auratype, uint32 value) const
{
AuraEffectList const& mTotalAuraList = GetAurasByType(auratype);
@@ -3644,19 +4218,23 @@ bool Unit::HasAuraTypeWithValue(AuraType auratype, uint32 value) const
return false;
}
+
bool Unit::HasAuraType(AuraType auraType) const
{
return (!m_modAuras[auraType].empty());
}
+
bool Unit::HasAura(uint32 spellId, uint64 caster) const
{
//Special case for non existing spell
if (spellId==61988)
return HasAura(61987, caster) || HasAura(25771, caster);
+
if (Aura * aur = GetAura(spellId, caster))
return true;
return false;
}
+
bool Unit::HasAura(Aura * aur) const
{
// no need to find aura
@@ -3671,12 +4249,14 @@ bool Unit::HasAura(Aura * aur) const
}
return false;
}
+
bool Unit::HasAuraEffect(uint32 spellId, uint8 effIndex, uint64 caster) const
{
if (Aura * aur = GetAura(spellId, caster))
return aur->HasEffect(effIndex);
return false;
}
+
Aura * Unit::GetAura(uint32 spellId, uint64 caster) const
{
if (!caster)
@@ -3693,12 +4273,14 @@ Aura * Unit::GetAura(uint32 spellId, uint64 caster) const
return NULL;
}
}
+
AuraEffect * Unit::GetAuraEffect(uint32 spellId, uint8 effIndex, uint64 caster) const
{
if (Aura * aur = GetAura(spellId, caster))
return aur->GetPartAura(effIndex);
return false;
}
+
AuraEffect* Unit::GetAura(AuraType type, uint32 family, uint32 familyFlag1, uint32 familyFlag2, uint32 familyFlag3, uint64 casterGUID)
{
AuraEffectList const& auras = GetAurasByType(type);
@@ -3714,6 +4296,7 @@ AuraEffect* Unit::GetAura(AuraType type, uint32 family, uint32 familyFlag1, uint
}
return NULL;
}
+
AuraEffect * Unit::IsScriptOverriden(SpellEntry const * spell, int32 script) const
{
AuraEffectList const& auras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
@@ -3725,6 +4308,7 @@ AuraEffect * Unit::IsScriptOverriden(SpellEntry const * spell, int32 script) con
}
return NULL;
}
+
uint32 Unit::GetDiseasesByCaster(uint64 casterGUID, bool remove)
{
static const AuraType diseaseAuraTypes[] =
@@ -3733,6 +4317,7 @@ uint32 Unit::GetDiseasesByCaster(uint64 casterGUID, bool remove)
SPELL_AURA_MOD_MECHANIC_DAMAGE_TAKEN_PERCENT, // Crypt Fever and Ebon Plague
SPELL_AURA_NONE
};
+
uint32 diseases=0;
for(AuraType const* itr = &diseaseAuraTypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
{
@@ -3743,6 +4328,7 @@ uint32 Unit::GetDiseasesByCaster(uint64 casterGUID, bool remove)
&& (*i)->GetCasterGUID()==casterGUID)
{
++diseases;
+
if (remove)
{
RemoveAura((*i)->GetId(), (*i)->GetCasterGUID());
@@ -3755,6 +4341,7 @@ uint32 Unit::GetDiseasesByCaster(uint64 casterGUID, bool remove)
}
return diseases;
}
+
uint32 Unit::GetDoTsByCaster(uint64 casterGUID) const
{
static const AuraType diseaseAuraTypes[] =
@@ -3763,6 +4350,7 @@ uint32 Unit::GetDoTsByCaster(uint64 casterGUID) const
SPELL_AURA_PERIODIC_DAMAGE_PERCENT,
SPELL_AURA_NONE
};
+
uint32 dots=0;
for(AuraType const* itr = &diseaseAuraTypes[0]; itr && itr[0] != SPELL_AURA_NONE; ++itr)
{
@@ -3776,10 +4364,12 @@ uint32 Unit::GetDoTsByCaster(uint64 casterGUID) const
}
return dots;
}
+
void Unit::AddDynObject(DynamicObject* dynObj)
{
m_dynObjGUIDs.push_back(dynObj->GetGUID());
}
+
void Unit::RemoveDynObject(uint32 spellid)
{
if(m_dynObjGUIDs.empty())
@@ -3800,6 +4390,7 @@ void Unit::RemoveDynObject(uint32 spellid)
++i;
}
}
+
void Unit::RemoveAllDynObjects()
{
while(!m_dynObjGUIDs.empty())
@@ -3810,6 +4401,7 @@ void Unit::RemoveAllDynObjects()
m_dynObjGUIDs.erase(m_dynObjGUIDs.begin());
}
}
+
DynamicObject * Unit::GetDynObject(uint32 spellId)
{
for (DynObjectGUIDs::iterator i = m_dynObjGUIDs.begin(); i != m_dynObjGUIDs.end();)
@@ -3820,24 +4412,29 @@ DynamicObject * Unit::GetDynObject(uint32 spellId)
i = m_dynObjGUIDs.erase(i);
continue;
}
+
if (dynObj->GetSpellId() == spellId)
return dynObj;
++i;
}
return NULL;
}
+
GameObject* Unit::GetGameObject(uint32 spellId) const
{
for (GameObjectList::const_iterator i = m_gameObj.begin(); i != m_gameObj.end(); ++i)
if ((*i)->GetSpellId() == spellId)
return *i;
+
return NULL;
}
+
void Unit::AddGameObject(GameObject* gameObj)
{
assert(gameObj && gameObj->GetOwnerGUID()==0);
m_gameObj.push_back(gameObj);
gameObj->SetOwnerGUID(GetGUID());
+
if ( GetTypeId()==TYPEID_PLAYER && gameObj->GetSpellId() )
{
SpellEntry const* createBySpell = sSpellStore.LookupEntry(gameObj->GetSpellId());
@@ -3847,10 +4444,13 @@ void Unit::AddGameObject(GameObject* gameObj)
((Player*)this)->AddSpellAndCategoryCooldowns(createBySpell,0,NULL,true);
}
}
+
void Unit::RemoveGameObject(GameObject* gameObj, bool del)
{
assert(gameObj && gameObj->GetOwnerGUID()==GetGUID());
+
gameObj->SetOwnerGUID(0);
+
for(uint32 i = 0; i < 4; ++i)
{
if(m_ObjectSlot[i] == gameObj->GetGUID())
@@ -3859,10 +4459,12 @@ void Unit::RemoveGameObject(GameObject* gameObj, bool del)
break;
}
}
+
// GO created by some spell
if (uint32 spellid = gameObj->GetSpellId())
{
RemoveAurasDueToSpell(spellid);
+
if (GetTypeId()==TYPEID_PLAYER)
{
SpellEntry const* createBySpell = sSpellStore.LookupEntry(spellid );
@@ -3872,13 +4474,16 @@ void Unit::RemoveGameObject(GameObject* gameObj, bool del)
((Player*)this)->SendCooldownEvent(createBySpell);
}
}
+
m_gameObj.remove(gameObj);
+
if(del)
{
gameObj->SetRespawnTime(0);
gameObj->Delete();
}
}
+
void Unit::RemoveGameObject(uint32 spellid, bool del)
{
if(m_gameObj.empty())
@@ -3895,12 +4500,14 @@ void Unit::RemoveGameObject(uint32 spellid, bool del)
(*i)->SetRespawnTime(0);
(*i)->Delete();
}
+
next = m_gameObj.erase(i);
}
else
++next;
}
}
+
void Unit::RemoveAllGameObjects()
{
// remove references to unit
@@ -3912,6 +4519,7 @@ void Unit::RemoveAllGameObjects()
i = m_gameObj.erase(i);
}
}
+
void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log)
{
WorldPacket data(SMSG_SPELLNONMELEEDAMAGELOG, (16+4+4+4+1+4+4+1+1+4+4+1)); // we guess size
@@ -3932,6 +4540,7 @@ void Unit::SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log)
data << uint8 (0); // flag to use extend data
SendMessageToSet( &data, true );
}
+
void Unit::SendSpellNonMeleeDamageLog(Unit *target, uint32 SpellID, uint32 Damage, SpellSchoolMask damageSchoolMask, uint32 AbsorbedDamage, uint32 Resist, bool PhysicalDamage, uint32 Blocked, bool CriticalHit)
{
SpellNonMeleeDamage log(this, target, SpellID, damageSchoolMask);
@@ -3945,6 +4554,7 @@ void Unit::SendSpellNonMeleeDamageLog(Unit *target, uint32 SpellID, uint32 Damag
log.HitInfo |= SPELL_HIT_TYPE_CRIT;
SendSpellNonMeleeDamageLog(&log);
}
+
void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procExtra, uint32 amount, WeaponAttackType attType, SpellEntry const *procSpell, SpellEntry const * procAura)
{
// Not much to do if no flags are set.
@@ -3955,9 +4565,11 @@ void Unit::ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVic
if(pVictim && pVictim->isAlive() && procVictim)
pVictim->ProcDamageAndSpellFor(true,this,procVictim, procExtra, attType, procSpell, amount, procAura);
}
+
void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo)
{
AuraEffect *aura = pInfo->auraEff;
+
WorldPacket data(SMSG_PERIODICAURALOG, 30);
data.append(GetPackGUID());
data.appendPackGUID(aura->GetCasterGUID());
@@ -3995,8 +4607,10 @@ void Unit::SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo)
sLog.outError("Unit::SendPeriodicAuraLog: unknown aura %u", uint32(aura->GetAuraName()));
return;
}
+
SendMessageToSet(&data, true);
}
+
void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
{
WorldPacket data(SMSG_SPELLLOGMISS, (4+8+1+4+8+1));
@@ -4010,9 +4624,11 @@ void Unit::SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo)
// end loop
SendMessageToSet(&data, true);
}
+
void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo)
{
sLog.outDebug("WORLD: Sending SMSG_ATTACKERSTATEUPDATE");
+
uint32 count = 1;
WorldPacket data(SMSG_ATTACKERSTATEUPDATE, 16 + 45); // we guess size
data << uint32(damageInfo->HitInfo);
@@ -4022,29 +4638,36 @@ void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo)
int32 overkill = damageInfo->damage - damageInfo->target->GetHealth();
data << uint32(overkill < 0 ? 0 : overkill); // Overkill
data << uint8(count); // Sub damage count
+
for(uint32 i = 0; i < count; ++i)
{
data << uint32(damageInfo->damageSchoolMask); // School of sub damage
data << float(damageInfo->damage); // sub damage
data << uint32(damageInfo->damage); // Sub Damage
}
+
if(damageInfo->HitInfo & (HITINFO_ABSORB | HITINFO_ABSORB2))
{
for(uint32 i = 0; i < count; ++i)
data << uint32(damageInfo->absorb); // Absorb
}
+
if(damageInfo->HitInfo & (HITINFO_RESIST | HITINFO_RESIST2))
{
for(uint32 i = 0; i < count; ++i)
data << uint32(damageInfo->resist); // Resist
}
+
data << uint8(damageInfo->TargetState);
data << uint32(0);
data << uint32(0);
+
if(damageInfo->HitInfo & HITINFO_BLOCK)
data << uint32(damageInfo->blocked_amount);
+
if(damageInfo->HitInfo & HITINFO_UNK3)
data << uint32(0);
+
if(damageInfo->HitInfo & HITINFO_UNK1)
{
data << uint32(0);
@@ -4063,8 +4686,10 @@ void Unit::SendAttackStateUpdate(CalcDamageInfo *damageInfo)
}
data << uint32(0);
}
+
SendMessageToSet( &data, true );
}
+
void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount)
{
CalcDamageInfo dmgInfo;
@@ -4079,14 +4704,18 @@ void Unit::SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType,
dmgInfo.blocked_amount = BlockedAmount;
SendAttackStateUpdate(&dmgInfo);
}
+
bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
{
SpellEntry const *hasteSpell = triggeredByAura->GetSpellProto();
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
uint32 triggered_spell_id = 0;
Unit* target = pVictim;
int32 basepoints0 = 0;
+
switch(hasteSpell->SpellFamilyName)
{
case SPELLFAMILY_ROGUE:
@@ -4108,36 +4737,48 @@ bool Unit::HandleHasteAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
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 && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (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::HandleSpellCritChanceAuraProc(Unit *pVictim, uint32 /*damage*/, AuraEffect* triggeredByAura, SpellEntry const * /*procSpell*/, uint32 /*procFlag*/, uint32 /*procEx*/, uint32 cooldown)
{
SpellEntry const *triggeredByAuraSpell = triggeredByAura->GetSpellProto();
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
uint32 triggered_spell_id = 0;
Unit* target = pVictim;
int32 basepoints0 = 0;
+
switch(triggeredByAuraSpell->SpellFamilyName)
{
case SPELLFAMILY_MAGE:
@@ -4150,6 +4791,7 @@ bool Unit::HandleSpellCritChanceAuraProc(Unit *pVictim, uint32 /*damage*/, AuraE
Unit* caster = triggeredByAura->GetCaster();
if(!caster)
return false;
+
triggered_spell_id = 54648;
target = caster;
break;
@@ -4157,39 +4799,51 @@ bool Unit::HandleSpellCritChanceAuraProc(Unit *pVictim, uint32 /*damage*/, AuraE
}
}
}
+
// 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",triggeredByAuraSpell->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, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown)
{
SpellEntry const *dummySpell = triggeredByAura->GetSpellProto ();
uint32 effIndex = triggeredByAura->GetEffIndex();
int32 triggerAmount = triggeredByAura->GetAmount();
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
uint32 triggered_spell_id = 0;
Unit* target = pVictim;
int32 basepoints0 = 0;
uint64 originalCaster = 0;
+
// Master of subtlety (checked here because ranks have different spellfamilynames)
if (dummySpell->Id == 31223 || dummySpell->Id == 31221 || dummySpell->Id == 31222)
{
@@ -4216,7 +4870,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
target = owner;
triggered_spell_id = 50454;
break;
- }
+ }
return false;
}
// Improved Divine Spirit
@@ -4255,6 +4909,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
basepoints0 = triggerAmount*int32(damage)/100;
if(basepoints0 > GetMaxHealth()/2)
basepoints0 = GetMaxHealth()/2;
+
triggered_spell_id = 25997;
break;
}
@@ -4265,6 +4920,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
target = SelectNearbyTarget();
if(!target)
return false;
+
triggered_spell_id = 26654;
break;
}
@@ -4289,9 +4945,11 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell)
return false;
+
// find Mage Armor
if (!GetAura(SPELL_AURA_MOD_MANA_REGEN_INTERRUPT, SPELLFAMILY_MAGE, 0x10000000))
return false;
+
switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
{
case SPELL_SCHOOL_NORMAL:
@@ -4305,6 +4963,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
default:
return false;
}
+
target = this;
break;
}
@@ -4313,6 +4972,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell)
return false;
+
switch(GetFirstSchoolInMask(GetSpellSchoolMask(procSpell)))
{
case SPELL_SCHOOL_NORMAL:
@@ -4326,6 +4986,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
default:
return false;
}
+
target = this;
break;
}
@@ -4336,6 +4997,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
target = GetOwner();
if(!target)
return false;
+
triggered_spell_id = 34650;
break;
}
@@ -4345,6 +5007,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// Cast finish spell at last charge
if (triggeredByAura->GetParentAura()->GetAuraCharges() > 1)
return false;
+
target = this;
triggered_spell_id = 33494;
break;
@@ -4359,6 +5022,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
basepoints0 = 3 * damage; // 300%
if (basepoints0 < 0)
return false;
+
triggered_spell_id = 31285;
target = this;
break;
@@ -4379,6 +5043,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER || !this->isAlive())
return false;
+
// Select class defined buff
switch (getClass())
{
@@ -4414,6 +5079,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
default:
return false;
}
+
target = this;
if (roll_chance_i(10))
((Player*)this)->Say("This is Madness!", LANG_UNIVERSAL);
@@ -4432,6 +5098,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER)
return false;
+
// Get Aldor reputation rank
if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
{
@@ -4456,6 +5123,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
if(IsFriendlyTo(target))
return false;
}
+
triggered_spell_id = 45429;
break;
}
@@ -4468,6 +5136,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER)
return false;
+
// Get Aldor reputation rank
if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
{
@@ -4490,6 +5159,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER)
return false;
+
// Get Aldor reputation rank
if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
{
@@ -4513,6 +5183,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER)
return false;
+
// Get Aldor reputation rank
if (((Player *)this)->GetReputationRank(932) == REP_EXALTED)
{
@@ -4564,6 +5235,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// Roll chane
if (!roll_chance_i(triggerAmount))
return false;
+
// Remove any stun effect on target
pVictim->RemoveAurasWithMechanic(1<<MECHANIC_STUN, AURA_REMOVE_BY_ENEMY_SPELL);
return true;
@@ -4578,6 +5250,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if (getPowerType() != POWER_MANA)
return false;
+
// mana reward
basepoints0 = (triggerAmount * GetMaxPower(POWER_MANA) / 100);
target = this;
@@ -4589,11 +5262,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell)
return false;
+
// mana cost save
int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100;
basepoints0 = cost * triggerAmount/100;
if( basepoints0 <=0 )
return false;
+
target = this;
triggered_spell_id = 29077;
break;
@@ -4607,11 +5282,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
CastSpell(target, triggered_spell_id, true, 0, triggeredByAura);
return true;
}
+
// Arcane Potency
if (dummySpell->SpellIconID == 2120)
{
if(!procSpell)
return false;
+
target = this;
switch (dummySpell->Id)
{
@@ -4623,6 +5300,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
}
break;
}
+
// Hot Streak
if (dummySpell->SpellIconID == 2999)
{
@@ -4631,6 +5309,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
AuraEffect *counter = triggeredByAura->GetParentAura()->GetPartAura(1);
if (!counter)
return true;
+
// Count spell criticals in a row in second aura
if (procEx & PROC_EX_CRITICAL_HIT)
{
@@ -4649,6 +5328,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell)
return false;
+
int32 cost = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100;
basepoints0 = cost * triggerAmount/100;
if( basepoints0 <=0 )
@@ -4662,6 +5342,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER)
return false;
+
target = this;
triggered_spell_id = 37436;
break;
@@ -4700,6 +5381,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (IG)",dummySpell->Id);
return false;
}
+
triggered_spell_id = 12654;
break;
}
@@ -4712,6 +5394,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
RemoveAurasDueToSpell(28682); //-> remove Combustion auras
return true; // charge counting (will removed)
}
+
CastSpell(this, 28682, true, castItem, triggeredByAura);
return (procEx & PROC_EX_CRITICAL_HIT);// charge update only at crit hits, no hidden cooldowns
}
@@ -4720,6 +5403,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId() != TYPEID_PLAYER)
return false;
+
SpellCooldowns const SpellCDs = ((Player*)this)->GetSpellCooldowns();
// remove cooldowns on all ranks of Frost Nova
for(SpellCooldowns::const_iterator itr = SpellCDs.begin(); itr != SpellCDs.end(); itr++)
@@ -4745,6 +5429,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
target = SelectNearbyTarget();
if(!target)
return false;
+
triggered_spell_id = 26654;
break;
}
@@ -4757,12 +5442,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
break;
}
}
+
// Retaliation
if(dummySpell->SpellFamilyFlags[1] & 0x8)
{
// check attack comes not from behind
if (!HasInArc(M_PI, pVictim))
return false;
+
triggered_spell_id = 22858;
break;
}
@@ -4775,6 +5462,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// Need stun or root mechanic
if (!(GetAllSpellMechanicMask(procSpell) & ((1<<MECHANIC_ROOT)|(1<<MECHANIC_STUN))))
return false;
+
switch (dummySpell->Id)
{
case 29838: triggered_spell_id=29842; break;
@@ -4784,6 +5472,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
sLog.outError("Unit::HandleDummyAuraProc: non handled spell id: %u (SW)",dummySpell->Id);
return false;
}
+
target = this;
break;
}
@@ -4809,13 +5498,16 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
// 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!)
if(Unit* caster = GetUnit(*this, casterGuid))
caster->CastSpell(this, 27285, true, castItem);
return true; // no hidden cooldown
}
+
// Damage counting
triggeredByAura->SetAmount(triggeredByAura->GetAmount() - damage);
return true;
@@ -4828,8 +5520,10 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
// 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!)
if(Unit* caster = GetUnit(*this, casterGuid))
caster->CastSpell(this, 32865, true, castItem);
@@ -4936,6 +5630,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
target = GetGuardianPet();
if(!target)
return false;
+
// heal amount
basepoints0 = damage * triggerAmount/100;
triggered_spell_id = 37382;
@@ -4957,11 +5652,14 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!pVictim || !pVictim->isAlive())
return false;
+
if (effIndex!=0)
return false;
+
// pVictim is caster of aura
if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
return false;
+
// Energize 0.25% of max. mana
pVictim->CastSpell(pVictim,57669,true,castItem,triggeredByAura);
return true; // no hidden cooldown
@@ -4980,9 +5678,11 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!pVictim || !pVictim->isAlive())
return false;
+
// pVictim is caster of aura
if(triggeredByAura->GetCasterGUID() != pVictim->GetGUID())
return false;
+
// heal amount
int32 team = triggerAmount*damage/500;
int32 self = triggerAmount*damage/100 - team;
@@ -5000,6 +5700,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
triggered_spell_id = 40440;
else
return false;
+
target = this;
break;
}
@@ -5014,9 +5715,11 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
case 55680:
{
triggered_spell_id = 56161;
+
SpellEntry const* GoPoH = sSpellStore.LookupEntry(triggered_spell_id);
if(!GoPoH)
return false;
+
int EffIndex = 0;
for(uint8 i = 0; i < MAX_SPELL_EFFECTS; i++)
{
@@ -5029,6 +5732,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
int32 tickcount = GetSpellMaxDuration(GoPoH) / GoPoH->EffectAmplitude[EffIndex];
if(!tickcount)
return false;
+
basepoints0 = damage * triggerAmount / tickcount / 100;
break;
}
@@ -5038,6 +5742,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if (!roll_chance_i(triggerAmount))
return false;
+
RemoveMovementImpairingAuras();
break;
}
@@ -5054,6 +5759,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
multiplier -= 0.5f;
else if (dummySpell->Id == 47537)
multiplier += 0.5f;
+
basepoints0 = (multiplier * GetMaxPower(POWER_MANA) / 100);
triggered_spell_id = 47755;
target = this;
@@ -5062,6 +5768,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if (!roll_chance_i(triggerAmount))
return false;
+
switch(pVictim->getPowerType())
{
target = pVictim;
@@ -5084,6 +5791,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
return false;
if(!target->IsFriendlyTo(this))
return false;
+
basepoints0 = int32(target->GetMaxHealth() * triggerAmount / 100);
triggered_spell_id = 56131;
break;
@@ -5102,6 +5810,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell || (GetSpellSchoolMask(procSpell) & (SPELL_SCHOOL_MASK_FROST | SPELL_SCHOOL_MASK_SHADOW))==0 )
return false;
+
// heal amount
basepoints0 = damage * triggerAmount/100;
target = this;
@@ -5135,6 +5844,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if (procSpell->SpellIconID != 62)
return false;
+
int32 mana_perc = triggeredByAura->GetSpellProto()->EffectBasePoints[triggeredByAura->GetEffIndex()]+1;
basepoints0 = uint32((GetPower(POWER_MANA) * mana_perc / 100) / 10);
triggered_spell_id = 54833;
@@ -5227,6 +5937,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
case 40442:
{
float chance;
+
// Starfire
if( procSpell->SpellFamilyFlags[0] & 0x4 )
{
@@ -5247,8 +5958,10 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
}
else
return false;
+
if (!roll_chance_f(chance))
return false;
+
target = this;
break;
}
@@ -5337,6 +6050,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// 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;
}
@@ -5364,10 +6078,12 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell)
return false;
+
// energy cost save
basepoints0 = procSpell->manaCost * triggerAmount/100;
if(basepoints0 <= 0)
return false;
+
target = this;
triggered_spell_id = 31663;
break;
@@ -5381,11 +6097,13 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!procSpell)
return false;
+
// mana cost save
int32 mana = procSpell->manaCost + procSpell->ManaCostPercentage * GetCreateMana() / 100;
basepoints0 = mana * 40/100;
if(basepoints0 <= 0)
return false;
+
target = this;
triggered_spell_id = 34720;
break;
@@ -5403,6 +6121,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
int32 chance = triggeredByAura->GetSpellProto()->EffectBasePoints[triggeredByAura->GetEffIndex()];
if(!roll_chance_i(chance))
return false;
+
triggered_spell_id = 24406;
break;
}
@@ -5541,6 +6260,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(!pVictim)
return false;
+
// Set class defined buff
switch (pVictim->getClass())
{
@@ -5571,6 +6291,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(effIndex != 0) // effect 1,2 used by seal unleashing code
return false;
+
triggered_spell_id = 31803;
break;
}
@@ -5579,6 +6300,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(effIndex != 0) // effect 1,2 used by seal unleashing code
return false;
+
triggered_spell_id = 53742;
break;
}
@@ -5589,9 +6311,11 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// if healed by another unit (pVictim)
if(this == pVictim)
return false;
+
// heal amount
basepoints0 = triggerAmount*damage/100;
target = this;
+
if(basepoints0)
triggered_spell_id = 31786;
break;
@@ -5619,7 +6343,9 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if( !procSpell )
return false;
+
float chance;
+
// Flash of light/Holy light
if( procSpell->SpellFamilyFlags[0] & 0xC0000000)
{
@@ -5634,8 +6360,10 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
}
else
return false;
+
if (!roll_chance_f(chance))
return false;
+
break;
}
// Glyph of Divinity
@@ -5692,6 +6420,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if( !pVictim )
return false;
+
// Set class defined buff
switch (pVictim->getClass())
{
@@ -5729,13 +6458,17 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
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;
+
if(triggeredByAura->GetParentAura() && castItem->GetGUID() != triggeredByAura->GetParentAura()->GetCastItemGUID())
return false;
+
// Now amount of extra power stored in 1 effect of Enchant spell
// Get it by item enchant id
uint32 spellId;
@@ -5756,13 +6489,16 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
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, 1, windfurySpellEntry->EffectBasePoints[1], pVictim);
+
// Main-Hand case
if ( castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && isAttackReady(BASE_ATTACK) )
{
@@ -5779,12 +6515,15 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
}
else
return false;
+
// 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
@@ -5792,6 +6531,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if( !procSpell )
return false;
+
float chance;
if (procSpell->SpellFamilyFlags[0] & 0x1)
{
@@ -5810,8 +6550,10 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
}
else
return false;
+
if (!roll_chance_f(chance))
return false;
+
target = this;
break;
}
@@ -5871,6 +6613,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
float chance = triggerAmount;
if (!roll_chance_f(chance))
return false;
+
triggered_spell_id = 63685;
break;
}
@@ -5880,9 +6623,11 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// Earthbind Totem summon only
if(procSpell->Id != 2484)
return false;
+
float chance = triggerAmount;
if (!roll_chance_f(chance))
return false;
+
triggered_spell_id = 64695;
break;
}
@@ -5901,6 +6646,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
originalCaster = triggeredByAura->GetCasterGUID();
target = this;
basepoints0 = triggerAmount;
+
// Glyph of Earth Shield
if (AuraEffect* aur = GetAuraEffect(63279,0))
{
@@ -5915,33 +6661,43 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
if(GetTypeId()!=TYPEID_PLAYER)
return false;
+
if(!castItem || !castItem->IsEquipped())
return false;
+
// firehit = dummySpell->EffectBasePoints[0] / ((4*19.25) * 1.3);
float fire_onhit = dummySpell->EffectBasePoints[0] / 100.0;
+
float add_spellpower = SpellBaseDamageBonus(SPELL_SCHOOL_MASK_FIRE)
+ SpellBaseDamageBonusForVictim(SPELL_SCHOOL_MASK_FIRE, pVictim);
+
// 1.3speed = 5%, 2.6speed = 10%, 4.0 speed = 15%, so, 1.0speed = 3.84%
add_spellpower= add_spellpower / 100.0 * 3.84;
+
// Enchant on Off-Hand and ready?
if ( castItem->GetSlot() == EQUIPMENT_SLOT_OFFHAND && isAttackReady(OFF_ATTACK))
{
float BaseWeaponSpeed = GetAttackTime(OFF_ATTACK)/1000.0;
+
// Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed
basepoints0 = int32( (fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed) );
triggered_spell_id = 10444;
}
+
// Enchant on Main-Hand and ready?
else if ( castItem->GetSlot() == EQUIPMENT_SLOT_MAINHAND && isAttackReady(BASE_ATTACK))
{
float BaseWeaponSpeed = GetAttackTime(BASE_ATTACK)/1000.0;
+
// Value1: add the tooltip damage by swingspeed + Value2: add spelldmg by swingspeed
basepoints0 = int32( (fire_onhit * BaseWeaponSpeed) + (add_spellpower * BaseWeaponSpeed) );
triggered_spell_id = 10444;
}
+
// If not ready, we should return, shouldn't we?!
else
return false;
+
CastCustomSpell(pVictim,triggered_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
return true;
}
@@ -5966,25 +6722,33 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
{
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 spell;
if (procSpell->SpellFamilyFlags[0] & 0x2)
spell = 45297;
else
spell = 45284;
uint32 spellId = spellmgr.GetSpellWithRank(spell, spellmgr.GetSpellRank(procSpell->Id));
+
// Remove cooldown (Chain Lightning - have Category Recovery time)
if (procSpell->SpellFamilyFlags[0] & 0x2)
((Player*)this)->RemoveSpellCooldown(spellId);
+
// do not reduce damage-spells have correct basepoints
int32 mod = 0;
+
// Apply spellmod
CastCustomSpell(this, 39805, NULL, &mod, NULL, true, castItem, triggeredByAura);
+
CastSpell(pVictim, spellId, true, castItem, triggeredByAura);
+
if( cooldown && GetTypeId()==TYPEID_PLAYER )
((Player*)this)->AddSpellCooldown(dummySpell->Id,0,time(NULL) + cooldown);
+
return true;
}
// Static Shock
@@ -6093,13 +6857,16 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
// check if shown in spell book
if(!itr->second->active || itr->second->disabled || itr->second->state == PLAYERSPELL_REMOVED)
continue;
+
SpellEntry const *spellProto = sSpellStore.LookupEntry(itr->first);
if (!spellProto)
continue;
+
if (spellProto->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT
&& spellProto->SpellFamilyFlags[0] & 0x2000)
{
SpellChainNode const* newChain = spellmgr.GetSpellChainNode(itr->first);
+
// No chain entry or entry lower than found entry
if (!chain || !newChain || (chain->rank < newChain->rank))
{
@@ -6136,6 +6903,7 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
}
else
continue;
+
basepoints0 = CalculateSpellDamage(procSpell,i,procSpell->EffectBasePoints[i],this) * 0.4f;
CastCustomSpell(this,triggered_spell_id,&basepoints0,NULL,NULL,true,NULL,triggeredByAura);
}
@@ -6166,26 +6934,34 @@ bool Unit::HandleDummyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* trigger
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 && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (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, originalCaster);
else
CastSpell(target,triggered_spell_id,true,castItem,triggeredByAura, originalCaster);
+
if( cooldown && GetTypeId()==TYPEID_PLAYER )
((Player*)this)->AddSpellCooldown(triggered_spell_id,0,time(NULL) + cooldown);
+
return true;
}
bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown)
@@ -6193,11 +6969,14 @@ bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect*
SpellEntry const *dummySpell = triggeredByAura->GetSpellProto ();
uint32 effIndex = triggeredByAura->GetEffIndex();
int32 triggerAmount = triggeredByAura->GetAmount();
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
uint32 triggered_spell_id = 0;
Unit* target = pVictim;
int32 basepoints0 = 0;
+
switch(dummySpell->SpellFamilyName)
{
case SPELLFAMILY_HUNTER:
@@ -6217,22 +6996,27 @@ bool Unit::HandleObsModEnergyAuraProc(Unit *pVictim, uint32 damage, AuraEffect*
// processed charge only counting case
if(!triggered_spell_id)
return true;
+
SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
// Try handle unknown trigger spells
if(!triggerEntry)
{
sLog.outError("Unit::HandleObsModEnergyAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id);
return false;
}
+
// default case
if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (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;
@@ -6242,11 +7026,14 @@ bool Unit::HandleModDamagePctTakenAuraProc(Unit *pVictim, uint32 damage, AuraEff
SpellEntry const *dummySpell = triggeredByAura->GetSpellProto ();
uint32 effIndex = triggeredByAura->GetEffIndex();
int32 triggerAmount = triggeredByAura->GetAmount();
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
uint32 triggered_spell_id = 0;
Unit* target = pVictim;
int32 basepoints0 = 0;
+
switch(dummySpell->SpellFamilyName)
{
case SPELLFAMILY_PALADIN:
@@ -6267,30 +7054,39 @@ bool Unit::HandleModDamagePctTakenAuraProc(Unit *pVictim, uint32 damage, AuraEff
// processed charge only counting case
if(!triggered_spell_id)
return true;
+
SpellEntry const* triggerEntry = sSpellStore.LookupEntry(triggered_spell_id);
+
if(!triggerEntry)
{
sLog.outError("Unit::HandleModDamagePctTakenAuraProc: Spell %u have not existed triggered spell %u",dummySpell->Id,triggered_spell_id);
return false;
}
+
// default case
if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (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;
}
+
// Used in case when access to whole aura is needed
// All procs should be handled like this...
bool Unit::HandleAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, SpellEntry const * procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown, bool * handled)
{
SpellEntry const *dummySpell = triggeredByAura->GetSpellProto();
+
switch(dummySpell->SpellFamilyName)
{
case SPELLFAMILY_DEATHKNIGHT:
@@ -6316,10 +7112,12 @@ bool Unit::HandleAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, S
// Reset amplitude - set death rune remove timer to 30s
aurEff->ResetPeriodicTimer();
uint32 runesLeft;
+
if (dummySpell->SpellIconID == 2622)
runesLeft = 2;
else
runesLeft = 1;
+
for (uint8 i=0;i<MAX_RUNES && runesLeft;++i)
{
if (dummySpell->SpellIconID == 2622)
@@ -6336,6 +7134,7 @@ bool Unit::HandleAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, S
}
if (((Player*)this)->GetRuneCooldown(i) != RUNE_COOLDOWN)
continue;
+
--runesLeft;
// Mark aura as used
aurEff->SetAmount(aurEff->GetAmount() | (1<<i));
@@ -6345,6 +7144,7 @@ bool Unit::HandleAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, S
}
return false;
}
+
switch(dummySpell->Id)
{
// Hungering Cold aura drop
@@ -6360,20 +7160,27 @@ bool Unit::HandleAuraProc(Unit *pVictim, uint32 damage, Aura* triggeredByAura, S
}
return false;
}
+
bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlags, uint32 procEx, uint32 cooldown)
{
// Get triggered aura spell info
SpellEntry const* auraSpellInfo = triggeredByAura->GetSpellProto();
+
// Basepoints of trigger aura
int32 triggerAmount = triggeredByAura->GetAmount();
+
// Set trigger spell id, target, custom basepoints
uint32 trigger_spell_id = auraSpellInfo->EffectTriggerSpell[triggeredByAura->GetEffIndex()];
+
Unit* target = NULL;
int32 basepoints0 = 0;
+
if(triggeredByAura->GetAuraName() == SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE)
basepoints0 = triggerAmount;
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
- ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+ ? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
// Try handle unknown trigger spells
if (sSpellStore.LookupEntry(trigger_spell_id) == NULL)
{
@@ -6570,10 +7377,12 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
return false;
// stacking
CastSpell(this, 37658, true, NULL, triggeredByAura);
+
Aura * dummy = GetAura(37658);
// release at 3 aura in stack (cont contain in basepoint of trigger aura)
if(!dummy || dummy->GetStackAmount() < triggerAmount)
return false;
+
RemoveAurasDueToSpell(37658);
trigger_spell_id = 37661;
target = pVictim;
@@ -6586,11 +7395,13 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
return false;
// stacking
CastSpell(this, 54842, true, NULL, triggeredByAura);
+
// counting
Aura * dummy = GetAura(54842);
// release at 3 aura in stack (cont contain in basepoint of trigger aura)
if(!dummy || dummy->GetStackAmount() < triggerAmount)
return false;
+
RemoveAurasDueToSpell(54842);
trigger_spell_id = 54843;
target = pVictim;
@@ -6677,8 +7488,10 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
// Check health condition - should drop to less 30% (damage deal after this!)
if (!(10*(int32(GetHealth() - damage)) < 3 * GetMaxHealth()))
return false;
+
if(pVictim && pVictim->isAlive())
pVictim->getThreatManager().modifyThreatPercent(this,-10);
+
basepoints0 = triggerAmount * GetMaxHealth() / 100;
trigger_spell_id = 31616;
target = this;
@@ -6713,6 +7526,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
{
if (GetTypeId() != TYPEID_PLAYER)
return false;
+
trigger_spell_id = 50475;
basepoints0 = damage * triggerAmount / 100;
}
@@ -6722,6 +7536,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
break;
}
}
+
// All ok. Check current trigger spell
SpellEntry const* triggerEntry = sSpellStore.LookupEntry(trigger_spell_id);
if ( triggerEntry == NULL )
@@ -6730,9 +7545,11 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
// sLog.outError("Unit::HandleProcTriggerSpell: Spell %u have 0 in EffectTriggered[%d], not handled custom case?",auraSpellInfo->Id,triggeredByAura->GetEffIndex());
return false;
}
+
// not allow proc extra attack spell at extra attack
if( m_extraAttacks && IsSpellHaveEffect(triggerEntry, SPELL_EFFECT_ADD_EXTRA_ATTACKS) )
return false;
+
// Custom requirements (not listed in procEx) Warning! damage dealing after this
// Custom triggered spells
switch (auraSpellInfo->Id)
@@ -6746,13 +7563,14 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
trigger_spell_id = 26470;
break;
}
- // Deflection
+ // Deflection
case 52420:
{
if(GetHealth()*100 / GetMaxHealth() >= 35)
return false;
break;
}
+
// Cheat Death
case 28845:
{
@@ -6767,6 +7585,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
// whenever you deal damage to a target who is below 20% health.
if (pVictim->GetHealth() > pVictim->GetMaxHealth() / 5)
return false;
+
target = this;
trigger_spell_id = 22588;
}
@@ -6814,10 +7633,12 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
return false;
break;
}
+
// Blade Barrier
if (auraSpellInfo->SpellFamilyName == SPELLFAMILY_DEATHKNIGHT && auraSpellInfo->SpellIconID == 85)
if (this->GetTypeId() != TYPEID_PLAYER || !((Player*)this)->IsBaseRuneSlotsOnCooldown(RUNE_BLOOD))
return false;
+
// Custom basepoints/target for exist spell
// dummy basepoints or other customs
switch(trigger_spell_id)
@@ -6833,9 +7654,12 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
target = triggeredByAura->GetParentAura()->GetCaster();
if(!target)
return false;
+
if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)target)->HasSpellCooldown(trigger_spell_id))
return false;
+
target->CastSpell(target,trigger_spell_id,true,castItem,triggeredByAura);
+
if( cooldown && GetTypeId()==TYPEID_PLAYER )
((Player*)this)->AddSpellCooldown(trigger_spell_id,0,time(NULL) + cooldown);
return true;
@@ -6921,7 +7745,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
case 53817:
{
// have rank dependent proc chance, ignore too often cases
- // PPM = 2.5 * (rank of talent),
+ // PPM = 2.5 * (rank of talent),
uint32 rank = spellmgr.GetSpellRank(auraSpellInfo->Id);
// 5 rank -> 100% 4 rank -> 80% and etc from full rate
if(!roll_chance_i(20*rank))
@@ -6933,6 +7757,7 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
{
if (procSpell == 0 || !(procEx & (PROC_EX_NORMAL_HIT|PROC_EX_CRITICAL_HIT)) || this == pVictim)
return false;
+
// Need stun, fear or silence mechanic
if (!(GetAllSpellMechanicMask(procSpell) & ((1<<MECHANIC_SILENCE)|(1<<MECHANIC_STUN)|(1<<MECHANIC_FEAR))))
return false;
@@ -6979,30 +7804,41 @@ bool Unit::HandleProcTriggerSpell(Unit *pVictim, uint32 damage, AuraEffect* trig
break;
}
}
+
if( cooldown && GetTypeId()==TYPEID_PLAYER && ((Player*)this)->HasSpellCooldown(trigger_spell_id))
return false;
+
// try detect target manually if not set
if ( target == NULL )
target = !(procFlags & (PROC_FLAG_SUCCESSFUL_POSITIVE_MAGIC_SPELL | PROC_FLAG_SUCCESSFUL_POSITIVE_SPELL_HIT)) && IsPositiveSpell(trigger_spell_id) ? this : pVictim;
+
// default case
if((!target && !spellmgr.IsSrcTargetSpell(triggerEntry)) || (target && target!=this && !target->isAlive()))
return false;
+
if(basepoints0)
CastCustomSpell(target,trigger_spell_id,&basepoints0,NULL,NULL,true,castItem,triggeredByAura);
else
CastSpell(target,trigger_spell_id,true,castItem,triggeredByAura);
+
if( cooldown && GetTypeId()==TYPEID_PLAYER )
((Player*)this)->AddSpellCooldown(trigger_spell_id,0,time(NULL) + cooldown);
+
return true;
}
+
bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraEffect *triggeredByAura, SpellEntry const *procSpell, uint32 cooldown)
{
int32 scriptId = triggeredByAura->GetMiscValue();
+
if(!pVictim || !pVictim->isAlive())
return false;
+
Item* castItem = triggeredByAura->GetParentAura()->GetCastItemGUID() && GetTypeId()==TYPEID_PLAYER
? ((Player*)this)->GetItemByGuid(triggeredByAura->GetParentAura()->GetCastItemGUID()) : NULL;
+
uint32 triggered_spell_id = 0;
+
switch(scriptId)
{
case 836: // Improved Blizzard (Rank 1)
@@ -7031,6 +7867,7 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraE
// Chance 50%
if (!roll_chance_i(50))
return false;
+
switch (pVictim->getPowerType())
{
case POWER_MANA: triggered_spell_id = 28722; break;
@@ -7063,26 +7900,35 @@ bool Unit::HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraE
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())
@@ -7098,6 +7944,7 @@ void Unit::setPowerType(Powers new_powertype)
((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_POWER_TYPE);
}
}
+
switch(new_powertype)
{
default:
@@ -7121,12 +7968,14 @@ void Unit::setPowerType(Powers new_powertype)
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)
@@ -7138,6 +7987,7 @@ FactionTemplateEntry const* Unit::getFactionTemplateEntry() const
}
return entry;
}
+
bool Unit::IsHostileTo(Unit const* unit) const
{
if(!unit)
@@ -7145,60 +7995,78 @@ 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->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY))
return false;
+
// PvP FFA state
if(pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP) && pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_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)
{
@@ -7207,6 +8075,7 @@ bool Unit::IsHostileTo(Unit const* unit) const
{
if(ReputationRank const* force =((Player*)tester)->GetReputationMgr().GetForcedRankIfAny(target_faction))
return *force <= 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(FactionState const* factionState = ((Player*)tester)->GetReputationMgr().GetState(raw_target_faction))
@@ -7221,74 +8090,95 @@ bool Unit::IsHostileTo(Unit const* unit) const
{
if(ReputationRank const* force = ((Player*)target)->GetReputationMgr().GetForcedRankIfAny(tester_faction))
return *force <= 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)->GetReputationMgr().GetRank(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->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY) && pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_SANCTUARY))
return true;
+
// PvP FFA state
if(pTester->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_FFA_PVP) && pTarget->HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_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)
{
@@ -7297,6 +8187,7 @@ bool Unit::IsFriendlyTo(Unit const* unit) const
{
if(ReputationRank const* force =((Player*)tester)->GetReputationMgr().GetForcedRankIfAny(target_faction))
return *force >= 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(FactionState const* factionState = ((Player*)tester)->GetReputationMgr().GetState(raw_target_faction))
@@ -7311,45 +8202,57 @@ bool Unit::IsFriendlyTo(Unit const* unit) const
{
if(ReputationRank const* force =((Player*)target)->GetReputationMgr().GetForcedRankIfAny(tester_faction))
return *force >= 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)->GetReputationMgr().GetRank(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 || !my_faction->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 || !my_faction->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->IsInWorld() || !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)
{
@@ -7361,9 +8264,11 @@ bool Unit::Attack(Unit *victim, bool meleeAttack)
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))
RemoveAurasByType(SPELL_AURA_MOD_UNATTACKABLE);
+
if (m_attacking)
{
if (m_attacking == victim)
@@ -7386,22 +8291,29 @@ bool Unit::Attack(Unit *victim, bool meleeAttack)
}
return false;
}
+
//switch target
InterruptSpell(CURRENT_MELEE_SPELL);
if(!meleeAttack)
clearUnitState(UNIT_STAT_MELEE_ATTACKING);
}
+
if(m_attacking)
m_attacking->_removeAttacker(this);
+
m_attacking = victim;
m_attacking->_addAttacker(this);
+
//Set our target
SetUInt64Value(UNIT_FIELD_TARGET, victim->GetGUID());
+
if(meleeAttack)
addUnitState(UNIT_STAT_MELEE_ATTACKING);
+
// set position before any AI calls/assistance
//if(GetTypeId()==TYPEID_UNIT)
// ((Creature*)this)->SetCombatStartPosition(GetPositionX(), GetPositionY(), GetPositionZ());
+
if(GetTypeId()==TYPEID_UNIT)
{
// should not let player enter combat by right clicking target
@@ -7409,69 +8321,92 @@ bool Unit::Attack(Unit *victim, bool meleeAttack)
if(victim->GetTypeId() == TYPEID_PLAYER)
victim->SetInCombatWith(this);
AddThreat(victim, 0.0f);
+
WorldPacket data(SMSG_AI_REACTION, 12);
data << uint64(GetGUID());
data << uint32(AI_REACTION_AGGRO); // Aggro sound
((WorldObject*)this)->SendMessageToSet(&data, true);
+
((Creature*)this)->CallAssistance();
}
+
// delay offhand weapon attack to next attack time
if(haveOffhandWeapon())
resetAttackTimer(OFF_ATTACK);
+
if(meleeAttack)
SendMeleeAttackStart(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);
+
// reset only at real combat stop
if(GetTypeId()==TYPEID_UNIT )
{
((Creature*)this)->SetNoCallAssistance(false);
((Creature*)this)->SetNoSearchAssistance(false);
}
+
SendMeleeAttackStop(victim);
+
return true;
}
+
void Unit::CombatStop(bool includingCast)
{
if (includingCast && IsNonMeleeSpellCasted(false))
InterruptNonMeleeSpells(false);
+
AttackStop();
RemoveAllAttackers();
if( GetTypeId()==TYPEID_PLAYER )
((Player*)this)->SendAttackSwingCancelAttack(); // melee and ranged forced attack cancel
ClearInCombat();
}
+
void Unit::CombatStopWithPets(bool includingCast)
{
CombatStop(includingCast);
+
for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
(*itr)->CombatStop(includingCast);
}
+
bool Unit::isAttackingPlayer() const
{
if(hasUnitState(UNIT_STAT_ATTACK_PLAYER))
return true;
+
for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
if((*itr)->isAttackingPlayer())
return true;
+
for(uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
if(m_SummonSlot[i])
if(Creature *summon = GetMap()->GetCreature(m_SummonSlot[i]))
if(summon->isAttackingPlayer())
return true;
+
return false;
}
+
void Unit::RemoveAllAttackers()
{
while (!m_attackers.empty())
@@ -7484,6 +8419,7 @@ void Unit::RemoveAllAttackers()
}
}
}
+
void Unit::ModifyAuraState(AuraState flag, bool apply)
{
if (apply)
@@ -7510,6 +8446,7 @@ void Unit::ModifyAuraState(AuraState flag, bool apply)
if (HasFlag(UNIT_FIELD_AURASTATE,1<<(flag-1)))
{
RemoveFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
+
if (flag != AURA_STATE_ENRAGE) // enrage aura state triggering continues auras
{
Unit::AuraMap& tAuras = GetAuras();
@@ -7527,6 +8464,7 @@ void Unit::ModifyAuraState(AuraState flag, bool apply)
}
}
}
+
uint32 Unit::BuildAuraStateUpdateForTarget(Unit * target) const
{
uint32 auraStates = GetUInt32Value(UNIT_FIELD_AURASTATE) &~(PER_CASTER_AURA_STATE_MASK);
@@ -7540,6 +8478,7 @@ uint32 Unit::BuildAuraStateUpdateForTarget(Unit * target) const
}
return auraStates;
}
+
bool Unit::HasAuraState(AuraState flag, SpellEntry const *spellProto, Unit const * Caster) const
{
if (Caster)
@@ -7561,8 +8500,10 @@ bool Unit::HasAuraState(AuraState flag, SpellEntry const *spellProto, Unit const
return false;
}
}
+
return HasFlag(UNIT_FIELD_AURASTATE, 1<<(flag-1));
}
+
Unit *Unit::GetOwner(bool inWorld) const
{
if(uint64 ownerid = GetOwnerGUID())
@@ -7573,19 +8514,23 @@ Unit *Unit::GetOwner(bool inWorld) const
}
return NULL;
}
+
Unit *Unit::GetCharmer() const
{
if(uint64 charmerid = GetCharmerGUID())
return ObjectAccessor::GetUnit(*this, charmerid);
return NULL;
}
+
Player* Unit::GetCharmerOrOwnerPlayerOrPlayerItself() const
{
uint64 guid = GetCharmerOrOwnerGUID();
if(IS_PLAYER_GUID(guid))
return ObjectAccessor::GetPlayer(*this, guid);
+
return GetTypeId()==TYPEID_PLAYER ? (Player*)this : NULL;
}
+
Minion *Unit::GetFirstMinion() const
{
if(uint64 pet_guid = GetMinionGUID())
@@ -7593,11 +8538,14 @@ Minion *Unit::GetFirstMinion() const
if(Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid))
if(pet->HasUnitTypeMask(UNIT_MASK_MINION))
return (Minion*)pet;
+
sLog.outError("Unit::GetFirstMinion: Minion %u not exist.",GUID_LOPART(pet_guid));
const_cast<Unit*>(this)->SetMinionGUID(0);
}
+
return NULL;
}
+
Guardian* Unit::GetGuardianPet() const
{
if(uint64 pet_guid = GetPetGUID())
@@ -7605,25 +8553,32 @@ Guardian* Unit::GetGuardianPet() const
if(Creature* pet = ObjectAccessor::GetCreatureOrPetOrVehicle(*this, pet_guid))
if(pet->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
return (Guardian*)pet;
+
sLog.outCrash("Unit::GetGuardianPet: Guardian " I64FMT " not exist.", pet_guid);
const_cast<Unit*>(this)->SetPetGUID(0);
}
+
return NULL;
}
+
Unit* Unit::GetCharm() const
{
if(uint64 charm_guid = GetCharmGUID())
{
if(Unit* pet = ObjectAccessor::GetUnit(*this, charm_guid))
return pet;
+
sLog.outError("Unit::GetCharm: Charmed creature %u not exist.",GUID_LOPART(charm_guid));
const_cast<Unit*>(this)->SetUInt64Value(UNIT_FIELD_CHARM, 0);
}
+
return NULL;
}
+
void Unit::SetMinion(Minion *minion, bool apply)
{
sLog.outDebug("SetMinion %u for %u, apply %u", minion->GetEntry(), GetEntry(), apply);
+
if(apply)
{
if(!minion->AddUInt64Value(UNIT_FIELD_SUMMONEDBY, GetGUID()))
@@ -7631,12 +8586,15 @@ void Unit::SetMinion(Minion *minion, bool apply)
sLog.outCrash("SetMinion: Minion %u is not the minion of owner %u", minion->GetEntry(), GetEntry());
return;
}
+
m_Controlled.insert(minion);
+
if(GetTypeId() == TYPEID_PLAYER)
{
minion->m_ControlledByPlayer = true;
minion->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
}
+
// Can only have one pet. If a new one is summoned, dismiss the old one.
if(minion->IsGuardianPet())
{
@@ -7659,6 +8617,7 @@ void Unit::SetMinion(Minion *minion, bool apply)
SetMinionGUID(0);
}
}
+
if(minion->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
{
if(AddUInt64Value(UNIT_FIELD_SUMMON, minion->GetGUID()))
@@ -7667,17 +8626,21 @@ void Unit::SetMinion(Minion *minion, bool apply)
}
//else if(minion->m_Properties && minion->m_Properties->Type == SUMMON_TYPE_MINIPET)
// AddUInt64Value(UNIT_FIELD_CRITTER, minion->GetGUID());
+
// PvP, FFAPvP
minion->SetByteValue(UNIT_FIELD_BYTES_2, 1, GetByteValue(UNIT_FIELD_BYTES_2, 1));
+
// FIXME: hack, speed must be set only at follow
if(GetTypeId() == TYPEID_PLAYER && minion->isPet())
for(uint8 i = 0; i < MAX_MOVE_TYPE; ++i)
minion->SetSpeed(UnitMoveType(i), m_speed_rate[i], true);
+
// Ghoul pets have energy instead of mana (is anywhere better place for this code?)
if (minion->IsPetGhoul())
{
minion->setPowerType(POWER_ENERGY);
}
+
if (GetTypeId() == TYPEID_PLAYER)
{
// Send infinity cooldown - client does that automatically but after relog cooldown needs to be set again
@@ -7695,10 +8658,13 @@ void Unit::SetMinion(Minion *minion, bool apply)
sLog.outCrash("SetMinion: Minion %u is not the minion of owner %u", minion->GetEntry(), GetEntry());
return;
}
+
m_Controlled.erase(minion);
+
if(minion->IsGuardianPet())
if(GetPetGUID() == minion->GetGUID())
SetPetGUID(0);
+
if (GetTypeId() == TYPEID_PLAYER)
{
SpellEntry const *spellInfo = sSpellStore.LookupEntry(minion->GetUInt32Value(UNIT_CREATED_BY_SPELL));
@@ -7708,6 +8674,7 @@ void Unit::SetMinion(Minion *minion, bool apply)
((Player*)this)->SendCooldownEvent(spellInfo);
}
}
+
//if(minion->HasUnitTypeMask(UNIT_MASK_GUARDIAN))
{
if(RemoveUInt64Value(UNIT_FIELD_SUMMON, minion->GetGUID()))
@@ -7719,6 +8686,7 @@ void Unit::SetMinion(Minion *minion, bool apply)
//if(GetCharmGUID() == (*itr)->GetGUID())
if(GetGUID() == (*itr)->GetCharmerGUID())
continue;
+
//assert((*itr)->GetOwnerGUID() == GetGUID());
if((*itr)->GetOwnerGUID() != GetGUID())
{
@@ -7727,8 +8695,10 @@ void Unit::SetMinion(Minion *minion, bool apply)
assert(false);
}
assert((*itr)->GetTypeId() == TYPEID_UNIT);
+
if(!(*itr)->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
continue;
+
if(AddUInt64Value(UNIT_FIELD_SUMMON, (*itr)->GetGUID()))
{
//show another pet bar if there is no charm bar
@@ -7744,10 +8714,11 @@ void Unit::SetMinion(Minion *minion, bool apply)
}
}
}
- //else if(minion->m_Properties && minion->m_Properties->Type == SUMMON_TYPE_MINIPET)
+ //else if(minion->m_Properties && minion->m_Properties->Type == SUMMON_TYPE_MINIPET)
// RemoveUInt64Value(UNIT_FIELD_CRITTER, minion->GetGUID());
}
}
+
void Unit::GetAllMinionsByEntry(std::list<Creature*>& Minions, uint32 entry)
{
for(Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();)
@@ -7759,6 +8730,7 @@ void Unit::GetAllMinionsByEntry(std::list<Creature*>& Minions, uint32 entry)
Minions.push_back((Creature*)unit);
}
}
+
void Unit::RemoveAllMinionsByEntry(uint32 entry)
{
for(Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end();)
@@ -7771,6 +8743,7 @@ void Unit::RemoveAllMinionsByEntry(uint32 entry)
// i think this is safe because i have never heard that a despawned minion will trigger a same minion
}
}
+
void Unit::SetCharm(Unit* charm, bool apply)
{
if(apply)
@@ -7779,21 +8752,26 @@ void Unit::SetCharm(Unit* charm, bool apply)
{
if(!AddUInt64Value(UNIT_FIELD_CHARM, charm->GetGUID()))
sLog.outCrash("Player %s is trying to charm unit %u, but it already has a charmed unit %u", GetName(), charm->GetEntry(), GetCharmGUID());
+
charm->m_ControlledByPlayer = true;
// TODO: maybe we can use this flag to check if controlled by player
charm->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
}
else
charm->m_ControlledByPlayer = false;
+
// PvP, FFAPvP
charm->SetByteValue(UNIT_FIELD_BYTES_2, 1, GetByteValue(UNIT_FIELD_BYTES_2, 1));
+
if(!charm->AddUInt64Value(UNIT_FIELD_CHARMEDBY, GetGUID()))
sLog.outCrash("Unit %u is being charmed, but it already has a charmer %u", charm->GetEntry(), charm->GetCharmerGUID());
+
if(charm->HasUnitMovementFlag(MOVEMENTFLAG_WALK_MODE))
{
charm->RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
charm->SendMovementFlagUpdate();
}
+
m_Controlled.insert(charm);
}
else
@@ -7803,8 +8781,10 @@ void Unit::SetCharm(Unit* charm, bool apply)
if(!RemoveUInt64Value(UNIT_FIELD_CHARM, charm->GetGUID()))
sLog.outCrash("Player %s is trying to uncharm unit %u, but it has another charmed unit %u", GetName(), charm->GetEntry(), GetCharmGUID());
}
+
if(!charm->RemoveUInt64Value(UNIT_FIELD_CHARMEDBY, GetGUID()))
sLog.outCrash("Unit %u is being uncharmed, but it has another charmer %u", charm->GetEntry(), charm->GetCharmerGUID());
+
if(charm->GetTypeId() == TYPEID_PLAYER)
{
charm->m_ControlledByPlayer = true;
@@ -7823,46 +8803,59 @@ void Unit::SetCharm(Unit* charm, bool apply)
charm->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
charm->SetByteValue(UNIT_FIELD_BYTES_2, 1, 0);
}
+
if(charm->GetTypeId() == TYPEID_PLAYER
|| !((Creature*)charm)->HasUnitTypeMask(UNIT_MASK_MINION)
|| charm->GetOwnerGUID() != GetGUID())
m_Controlled.erase(charm);
}
}
+
int32 Unit::DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical)
{
int32 gain = pVictim->ModifyHealth(int32(addhealth));
+
Unit* unit = this;
+
if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem())
unit = GetOwner();
+
if (unit->GetTypeId()==TYPEID_PLAYER)
{
// overheal = addhealth - gain
unit->SendHealSpellLog(pVictim, spellProto->Id, addhealth, addhealth - gain, critical);
+
if (BattleGround *bg = ((Player*)unit)->GetBattleGround())
bg->UpdatePlayerScore((Player*)unit, SCORE_HEALING_DONE, gain);
+
// use the actual gain, as the overheal shall not be counted, skip gain 0 (it ignored anyway in to criteria)
if (gain)
((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HEALING_DONE, gain, 0, pVictim);
+
((Player*)unit)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEAL_CASTED, addhealth);
}
+
if (pVictim->GetTypeId()==TYPEID_PLAYER)
{
((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_TOTAL_HEALING_RECEIVED, gain);
((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_HIGHEST_HEALING_RECEIVED, addhealth);
}
+
return gain;
}
+
Unit* Unit::SelectMagnetTarget(Unit *victim, SpellEntry const *spellInfo)
{
if(!victim)
return NULL;
+
// Magic case
if(spellInfo && (spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE || spellInfo->DmgClass == SPELL_DAMAGE_CLASS_MAGIC))
{
//I am not sure if this should be redirected.
if(spellInfo->DmgClass == SPELL_DAMAGE_CLASS_NONE)
return victim;
+
Unit::AuraEffectList const& magnetAuras = victim->GetAurasByType(SPELL_AURA_SPELL_MAGNET);
for(Unit::AuraEffectList::const_iterator itr = magnetAuras.begin(); itr != magnetAuras.end(); ++itr)
if(Unit* magnet = (*itr)->GetParentAura()->GetUnitSource())
@@ -7885,8 +8878,10 @@ Unit* Unit::SelectMagnetTarget(Unit *victim, SpellEntry const *spellInfo)
return magnet;
}
}
+
return victim;
}
+
Unit* Unit::GetFirstControlled() const
{
//Sequence: charmed, pet, other guardians
@@ -7898,11 +8893,13 @@ Unit* Unit::GetFirstControlled() const
}
return unit;
}
+
void Unit::RemoveAllControlled()
{
//possessed pet and vehicle
if(GetTypeId() == TYPEID_PLAYER)
((Player*)this)->StopCastingCharm();
+
while(!m_Controlled.empty())
{
Unit *target = *m_Controlled.begin();
@@ -7927,6 +8924,7 @@ void Unit::RemoveAllControlled()
if(GetCharmGUID())
sLog.outCrash("Unit %u is not able to release its charm " I64FMT, GetEntry(), GetCharmGUID());
}
+
Unit* Unit::GetNextRandomRaidMemberOrPet(float radius)
{
Player* player = NULL;
@@ -7935,6 +8933,7 @@ Unit* Unit::GetNextRandomRaidMemberOrPet(float radius)
// Should we enable this also for charmed units?
else if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->isPet())
player=(Player*)GetOwner();
+
if (!player)
return NULL;
Group *pGroup = player->GetGroup();
@@ -7951,27 +8950,34 @@ Unit* Unit::GetNextRandomRaidMemberOrPet(float radius)
// We are owner now, return pet
return IsWithinDistInMap(pet, radius) ? pet : NULL;
}
+
std::vector<Unit*> nearMembers;
//reserve place for players and pets because resizing vector every unit push is unefficient (vector is reallocated then)
nearMembers.reserve(pGroup->GetMembersCount()*2);
+
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 && Target->isAlive() && IsWithinDistInMap(Target, radius) &&
!IsHostileTo(Target) )
nearMembers.push_back(Target);
+
// Push player's pet to vector
Unit * pet = Target->GetGuardianPet();
if (pet && pet !=this && pet->isAlive() && IsWithinDistInMap(pet, radius) &&
!IsHostileTo(pet) )
nearMembers.push_back(pet);
}
+
if (nearMembers.empty())
return NULL;
+
uint32 randTarget = urand(0,nearMembers.size()-1);
return nearMembers[randTarget];
}
+
/*
Player * Unit::GetMoverSource() const
{
@@ -7983,6 +8989,7 @@ Player * Unit::GetMoverSource() const
return NULL;
}
*/
+
//only called in Player::SetSeer
void Unit::AddPlayerToVision(Player* plr)
{
@@ -7993,6 +9000,7 @@ void Unit::AddPlayerToVision(Player* plr)
}
m_sharedVision.push_back(plr);
}
+
//only called in Player::SetSeer
void Unit::RemovePlayerFromVision(Player* plr)
{
@@ -8003,10 +9011,12 @@ void Unit::RemovePlayerFromVision(Player* plr)
SetWorldObject(false);
}
}
+
void Unit::RemoveBindSightAuras()
{
RemoveAurasByType(SPELL_AURA_BIND_SIGHT);
}
+
void Unit::RemoveCharmAuras()
{
RemoveAurasByType(SPELL_AURA_MOD_CHARM);
@@ -8014,17 +9024,20 @@ void Unit::RemoveCharmAuras()
RemoveAurasByType(SPELL_AURA_MOD_POSSESS);
RemoveAurasByType(SPELL_AURA_AOE_CHARM);
}
+
void Unit::UnsummonAllTotems()
{
for (int8 i = 0; i < MAX_SUMMON_SLOT; ++i)
{
if(!m_SummonSlot[i])
continue;
+
Creature *OldTotem = GetMap()->GetCreature(m_SummonSlot[i]);
if(OldTotem && OldTotem->isSummon())
((TempSummon*)OldTotem)->UnSummon();
}
}
+
void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, bool critical)
{
// we guess size
@@ -8038,6 +9051,7 @@ void Unit::SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32
data << uint8(0); // unused in client?
SendMessageToSet(&data, true);
}
+
void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype)
{
WorldPacket data(SMSG_SPELLENERGIZELOG, (8+8+4+4+4+1));
@@ -8048,32 +9062,38 @@ void Unit::SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, Po
data << uint32(Damage);
SendMessageToSet(&data, true);
}
+
void Unit::EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype)
{
SendEnergizeSpellLog(pVictim, SpellID, Damage, powertype);
// needs to be called after sending spell log
pVictim->ModifyPower(powertype, Damage);
}
+
uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 pdamage, DamageEffectType damagetype, uint32 stack)
{
if(!spellProto || !pVictim || damagetype==DIRECT_DAMAGE )
return pdamage;
+
// For totems get damage bonus from owner
if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem())
{
if(Unit* owner = GetOwner())
return owner->SpellDamageBonus(pVictim, spellProto, pdamage, damagetype);
}
+
// Taken/Done total percent damage auras
float DoneTotalMod = 1.0f;
float TakenTotalMod = 1.0f;
float ApCoeffMod = 1.0f;
int32 DoneTotal = 0;
int32 TakenTotal = 0;
+
// ..done
// Pet damage
if( GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isPet() )
DoneTotalMod *= ((Creature*)this)->GetSpellDamageMod(((Creature*)this)->GetCreatureInfo()->rank);
+
AuraEffectList const& mModDamagePercentDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_DONE);
for(AuraEffectList::const_iterator i = mModDamagePercentDone.begin(); i != mModDamagePercentDone.end(); ++i)
{
@@ -8086,6 +9106,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
}
}
+
uint32 creatureTypeMask = pVictim->GetCreatureTypeMask();
// Add flat bonus from spell damage versus
DoneTotal += GetTotalAuraModifierByMiscMask(SPELL_AURA_MOD_FLAT_SPELL_DAMAGE_VERSUS, creatureTypeMask);
@@ -8093,6 +9114,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
for(AuraEffectList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
if(creatureTypeMask & uint32((*i)->GetMiscValue()))
DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
+
// done scripted mod (take it from owner)
Unit *owner = GetOwner();
if (!owner) owner = this;
@@ -8211,6 +9233,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
}
}
}
+
// Custom scripted damage
switch(spellProto->SpellFamilyName)
{
@@ -8221,6 +9244,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
if (pVictim->HasAuraState(AURA_STATE_FROZEN, spellProto, this))
DoneTotalMod *= 3.0f;
}
+
// Torment the weak
if (spellProto->SpellFamilyFlags[0]&0x20200021 || spellProto->SpellFamilyFlags[1]& 0x9000)
if(pVictim->HasAuraType(SPELL_AURA_MOD_DECREASE_SPEED))
@@ -8245,6 +9269,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
DoneTotalMod *= (aurEff->GetAmount() + 100.0f) / 100.f;
}
break;
+
case SPELLFAMILY_PALADIN:
// Judgement of Vengeance/Judgement of Corruption
if((spellProto->SpellFamilyFlags[1] & 0x400000) && spellProto->SpellIconID==2292)
@@ -8282,20 +9307,24 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
if (spellProto->SpellFamilyFlags[0] & 0x2)
if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 2721, 0))
DoneTotalMod *= (100.0f + aurEff->GetAmount()) / 100.0f;
+
// Glacier Rot
if (spellProto->SpellFamilyFlags[0] & 0x2 || spellProto->SpellFamilyFlags[1] & 0x6)
if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DEATHKNIGHT, 196, 0))
DoneTotalMod *= (100.0f + aurEff->GetAmount()) / 100.0f;
+
// This is not a typo - Impurity has SPELLFAMILY_DRUID
if (AuraEffect * aurEff = GetDummyAura(SPELLFAMILY_DRUID, 1986, 0))
ApCoeffMod *= (100.0f + aurEff->GetAmount()) / 100.0f;
break;
}
+
// ..taken
AuraEffectList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
for(AuraEffectList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
if( (*i)->GetMiscValue() & GetSpellSchoolMask(spellProto) )
TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
+
// .. taken pct: dummy auras
if (pVictim->GetTypeId() == TYPEID_PLAYER)
{
@@ -8308,11 +9337,13 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
TakenTotalMod *= (mod+100.0f)/100.0f;
}
}
+
// From caster spells
AuraEffectList const& mOwnerTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_FROM_CASTER);
for(AuraEffectList::const_iterator i = mOwnerTaken.begin(); i != mOwnerTaken.end(); ++i)
if( (*i)->GetCasterGUID() == GetGUID() && (*i)->isAffectedOnSpell(spellProto))
TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
+
// Mod damage from spell mechanic
if (uint32 mechanicMask = GetAllSpellMechanicMask(spellProto))
{
@@ -8321,6 +9352,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
if(mechanicMask & uint32(1<<((*i)->GetMiscValue())))
TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
}
+
// Taken/Done fixed damage bonus auras
int32 DoneAdvertisedBenefit = SpellBaseDamageBonus(GetSpellSchoolMask(spellProto));
int32 TakenAdvertisedBenefit = SpellBaseDamageBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
@@ -8328,6 +9360,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
// note that their spell damage is just gain of their own auras
if (HasUnitTypeMask(UNIT_MASK_GUARDIAN))
DoneAdvertisedBenefit += ((Guardian*)this)->GetBonusDamage();
+
// Check for table values
float coeff = 0;
SpellBonusEntry const* bonus = spellmgr.GetSpellBonusData(spellProto->Id);
@@ -8388,6 +9421,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
}
// 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(uint8 j = 0; j < MAX_SPELL_EFFECTS; ++j)
{
@@ -8403,6 +9437,7 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
else
coeff = DotFactor;
}
+
float coeff2 = CalculateLevelPenalty(spellProto) * stack;
if(spellProto->SpellFamilyName) //TODO: fix this
TakenTotal+= TakenAdvertisedBenefit * coeff * coeff2;
@@ -8414,16 +9449,21 @@ uint32 Unit::SpellDamageBonus(Unit *pVictim, SpellEntry const *spellProto, uint3
}
DoneTotal += DoneAdvertisedBenefit * coeff * coeff2;
}
+
float tmpDamage = (pdamage + DoneTotal) * DoneTotalMod;
// apply spellmod to Done damage (flat and pct)
if(Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, tmpDamage);
+
tmpDamage = (tmpDamage + TakenTotal) * TakenTotalMod;
+
return tmpDamage > 0 ? uint32(tmpDamage) : 0;
}
+
int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask)
{
int32 DoneAdvertisedBenefit = 0;
+
// ..done
AuraEffectList const& mDamageDone = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE);
for(AuraEffectList::const_iterator i = mDamageDone.begin();i != mDamageDone.end(); ++i)
@@ -8433,10 +9473,12 @@ int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask)
(*i)->GetSpellProto()->EquippedItemInventoryTypeMask == 0 )
// 0 == any inventory type (not wand then)
DoneAdvertisedBenefit += (*i)->GetAmount();
+
if (GetTypeId() == TYPEID_PLAYER)
{
// Base value
DoneAdvertisedBenefit +=((Player*)this)->GetBaseSpellPowerBonus();
+
// Damage bonus from stats
AuraEffectList const& mDamageDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_DAMAGE_OF_STAT_PERCENT);
for(AuraEffectList::const_iterator i = mDamageDoneOfStatPercent.begin();i != mDamageDoneOfStatPercent.end(); ++i)
@@ -8453,30 +9495,37 @@ int32 Unit::SpellBaseDamageBonus(SpellSchoolMask schoolMask)
for(AuraEffectList::const_iterator i =mDamageDonebyAP.begin();i != mDamageDonebyAP.end(); ++i)
if ((*i)->GetMiscValue() & schoolMask)
DoneAdvertisedBenefit += int32(GetTotalAttackPowerValue(BASE_ATTACK) * (*i)->GetAmount() / 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
AuraEffectList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
for(AuraEffectList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
if(creatureTypeMask & uint32((*i)->GetMiscValue()))
TakenAdvertisedBenefit += (*i)->GetAmount();
+
// ..taken
AuraEffectList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
for(AuraEffectList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
if(((*i)->GetMiscValue() & schoolMask) != 0)
TakenAdvertisedBenefit += (*i)->GetAmount();
+
return TakenAdvertisedBenefit;
}
+
bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType) const
{
// not critting spell
if((spellProto->AttributesEx2 & SPELL_ATTR_EX2_CANT_CRIT))
return false;
+
float crit_chance = 0.0f;
switch(spellProto->DmgClass)
{
@@ -8639,11 +9688,13 @@ bool Unit::isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolM
// 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::SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim)
{
// Calculate critical bonus
@@ -8659,18 +9710,23 @@ uint32 Unit::SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damag
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::SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim)
{
// Calculate critical bonus
@@ -8686,32 +9742,40 @@ uint32 Unit::SpellCriticalHealingBonus(SpellEntry const *spellProto, uint32 dama
crit_bonus = damage / 2; // for spells is 50%
break;
}
+
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;
+
damage = int32(float(damage) * GetTotalAuraMultiplier(SPELL_AURA_MOD_CRITICAL_HEALING_AMOUNT));
+
return damage;
}
+
uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint32 healamount, DamageEffectType damagetype, uint32 stack)
{
// For totems get healing bonus from owner (statue isn't totem in fact)
if( GetTypeId()==TYPEID_UNIT && ((Creature*)this)->isTotem())
if(Unit* owner = GetOwner())
return owner->SpellHealingBonus(pVictim, spellProto, healamount, damagetype, stack);
+
// Healing Done
// Taken/Done total percent damage auras
float DoneTotalMod = 1.0f;
float TakenTotalMod = 1.0f;
int32 DoneTotal = 0;
int32 TakenTotal = 0;
+
// Healing done percent
AuraEffectList const& mHealingDonePct = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE_PERCENT);
for(AuraEffectList::const_iterator i = mHealingDonePct.begin();i != mHealingDonePct.end(); ++i)
DoneTotalMod *= (100.0f + (*i)->GetAmount()) / 100.0f;
+
// done scripted mod (take it from owner)
Unit *owner = GetOwner();
if (!owner) owner = this;
@@ -8772,10 +9836,13 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
break;
}
}
+
// Taken/Done fixed damage bonus auras
int32 DoneAdvertisedBenefit = SpellBaseHealingBonus(GetSpellSchoolMask(spellProto));
int32 TakenAdvertisedBenefit = SpellBaseHealingBonusForVictim(GetSpellSchoolMask(spellProto), pVictim);
+
bool scripted = false;
+
for (uint8 i=0;i<MAX_SPELL_EFFECTS;++i)
{
switch (spellProto->EffectApplyAuraName[i])
@@ -8787,6 +9854,7 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
break;
}
}
+
// Check for table values
SpellBonusEntry const* bonus = !scripted ? spellmgr.GetSpellBonusData(spellProto->Id) : NULL;
float coeff = 0;
@@ -8836,6 +9904,7 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
coeff = 0.0f;
}
}
+
// Default calculation
if (DoneAdvertisedBenefit || TakenAdvertisedBenefit)
{
@@ -8889,6 +9958,7 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
// As wowwiki says: C = (Cast Time / 3.5) * 1.88 (for healing spells)
coeff = (CastingTime / 3500.0f) * DotFactor * 1.88f;
}
+
factorMod *= CalculateLevelPenalty(spellProto)* stack;
TakenTotal += TakenAdvertisedBenefit * coeff * factorMod;
if(Player* modOwner = GetSpellModOwner())
@@ -8899,11 +9969,13 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
}
DoneTotal += DoneAdvertisedBenefit * coeff * factorMod;
}
+
// use float as more appropriate for negative values and percent applying
float heal = (healamount + DoneTotal)*DoneTotalMod;
// apply spellmod to Done amount
if(Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id, damagetype == DOT ? SPELLMOD_DOT : SPELLMOD_DAMAGE, heal);
+
// Nourish cast
if (spellProto->SpellFamilyName == SPELLFAMILY_DRUID && spellProto->SpellFamilyFlags[1] & 0x2000000)
{
@@ -8912,7 +9984,9 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
//increase healing by 20%
TakenTotalMod *= 1.2f;
}
+
// Taken mods
+
// Healing Wave
if (spellProto->SpellFamilyName == SPELLFAMILY_SHAMAN && spellProto->SpellFamilyFlags[0] & 0x40)
{
@@ -8921,42 +9995,53 @@ uint32 Unit::SpellHealingBonus(Unit *pVictim, SpellEntry const *spellProto, uint
if(HealingWay)
TakenTotalMod *= (HealingWay->GetAmount() + 100.0f) / 100.0f;
}
+
// Healing taken percent
float minval = pVictim->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
if(minval)
TakenTotalMod *= (100.0f + minval) / 100.0f;
+
float maxval = pVictim->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HEALING_PCT);
if(maxval)
TakenTotalMod *= (100.0f + maxval) / 100.0f;
+
if(damagetype==DOT)
{
// Healing over time taken percent
float minval_hot = pVictim->GetMaxNegativeAuraModifier(SPELL_AURA_MOD_HOT_PCT);
if(minval_hot)
TakenTotalMod *= (100.0f + minval_hot) / 100.0f;
+
float maxval_hot = pVictim->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_HOT_PCT);
if(maxval_hot)
TakenTotalMod *= (100.0f + maxval_hot) / 100.0f;
}
+
AuraEffectList const& mHealingGet= pVictim->GetAurasByType(SPELL_AURA_MOD_HEALING_RECEIVED);
for(AuraEffectList::const_iterator i = mHealingGet.begin(); i != mHealingGet.end(); ++i)
if (GetGUID()==(*i)->GetCasterGUID() && (*i)->isAffectedOnSpell(spellProto) )
TakenTotalMod *= ((*i)->GetAmount() + 100.0f) / 100.0f;
+
heal = (heal + TakenTotal) * TakenTotalMod;
+
return heal < 0 ? 0 : uint32(heal);
}
+
int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask)
{
int32 AdvertisedBenefit = 0;
+
AuraEffectList const& mHealingDone = GetAurasByType(SPELL_AURA_MOD_HEALING_DONE);
for(AuraEffectList::const_iterator i = mHealingDone.begin();i != mHealingDone.end(); ++i)
if(((*i)->GetMiscValue() & schoolMask) != 0)
AdvertisedBenefit += (*i)->GetAmount();
+
// Healing bonus of spirit, intellect and strength
if (GetTypeId() == TYPEID_PLAYER)
{
// Base value
AdvertisedBenefit +=((Player*)this)->GetBaseSpellPowerBonus();
+
// Healing bonus from stats
AuraEffectList const& mHealingDoneOfStatPercent = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_STAT_PERCENT);
for(AuraEffectList::const_iterator i = mHealingDoneOfStatPercent.begin();i != mHealingDoneOfStatPercent.end(); ++i)
@@ -8965,6 +10050,7 @@ int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask)
Stats usedStat = Stats((*i)->GetSpellProto()->EffectMiscValue[(*i)->GetEffIndex()]);
AdvertisedBenefit += int32(GetStat(usedStat) * (*i)->GetAmount() / 100.0f);
}
+
// ... and attack power
AuraEffectList const& mHealingDonebyAP = GetAurasByType(SPELL_AURA_MOD_SPELL_HEALING_OF_ATTACK_POWER);
for(AuraEffectList::const_iterator i = mHealingDonebyAP.begin();i != mHealingDonebyAP.end(); ++i)
@@ -8973,6 +10059,7 @@ int32 Unit::SpellBaseHealingBonus(SpellSchoolMask schoolMask)
}
return AdvertisedBenefit;
}
+
int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVictim)
{
int32 AdvertisedBenefit = 0;
@@ -8982,6 +10069,7 @@ int32 Unit::SpellBaseHealingBonusForVictim(SpellSchoolMask schoolMask, Unit *pVi
AdvertisedBenefit += (*i)->GetAmount();
return AdvertisedBenefit;
}
+
bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask)
{
//If m_immuneToSchool type contain this school type, IMMUNE damage.
@@ -8989,13 +10077,16 @@ bool Unit::IsImmunedToDamage(SpellSchoolMask shoolMask)
for (SpellImmuneList::const_iterator itr = schoolList.begin(); itr != schoolList.end(); ++itr)
if(itr->type & shoolMask)
return true;
+
//If m_immuneToDamage type contain magic, IMMUNE damage.
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
if(itr->type & shoolMask)
return true;
+
return false;
}
+
bool Unit::IsImmunedToDamage(SpellEntry const* spellInfo)
{
uint32 shoolMask = GetSpellSchoolMask(spellInfo);
@@ -9007,22 +10098,27 @@ bool Unit::IsImmunedToDamage(SpellEntry const* spellInfo)
if(itr->type & shoolMask &&!IsDispelableBySpell(spellInfo, itr->spellId))
return true;
}
+
//If m_immuneToDamage type contain magic, IMMUNE damage.
SpellImmuneList const& damageList = m_spellImmune[IMMUNITY_DAMAGE];
for (SpellImmuneList::const_iterator itr = damageList.begin(); itr != damageList.end(); ++itr)
if(itr->type & shoolMask)
return true;
+
return false;
}
+
bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo)
{
if (!spellInfo)
return false;
+
// Single spell immunity.
SpellImmuneList const& idList = m_spellImmune[IMMUNITY_ID];
for(SpellImmuneList::const_iterator itr = idList.begin(); itr != idList.end(); ++itr)
if(itr->type == spellInfo->Id)
return true;
+
if(spellInfo->Dispel)
{
SpellImmuneList const& dispelList = m_spellImmune[IMMUNITY_DISPEL];
@@ -9030,6 +10126,7 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo)
if(itr->type == spellInfo->Dispel)
return true;
}
+
if(spellInfo->Mechanic)
{
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
@@ -9037,6 +10134,7 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo)
if(itr->type == spellInfo->Mechanic)
return true;
}
+
if(spellInfo->Id != 42292 && spellInfo->Id !=59752)
{
SpellImmuneList const& schoolList = m_spellImmune[IMMUNITY_SCHOOL];
@@ -9046,8 +10144,10 @@ bool Unit::IsImmunedToSpell(SpellEntry const* spellInfo)
&& !IsDispelableBySpell(spellInfo, itr->spellId))
return true;
}
+
return false;
}
+
bool Unit::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const
{
if (!spellInfo)
@@ -9058,6 +10158,7 @@ bool Unit::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) con
for (SpellImmuneList::const_iterator itr = effectList.begin(); itr != effectList.end(); ++itr)
if(itr->type == effect)
return true;
+
if(uint32 mechanic = spellInfo->EffectMechanic[index])
{
SpellImmuneList const& mechanicList = m_spellImmune[IMMUNITY_MECHANIC];
@@ -9065,6 +10166,7 @@ bool Unit::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) con
if(itr->type == spellInfo->EffectMechanic[index])
return true;
}
+
if(uint32 aura = spellInfo->EffectApplyAuraName[index])
{
SpellImmuneList const& list = m_spellImmune[IMMUNITY_STATE];
@@ -9079,12 +10181,15 @@ bool Unit::IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) con
!IsPositiveEffect(spellInfo->Id, index)) // Harmful
return true;
}
+
return false;
}
+
bool Unit::IsDamageToThreatSpell(SpellEntry const * spellInfo) const
{
if(!spellInfo)
return false;
+
switch(spellInfo->SpellFamilyName)
{
case SPELLFAMILY_WARLOCK:
@@ -9100,30 +10205,39 @@ bool Unit::IsDamageToThreatSpell(SpellEntry const * spellInfo) const
return true;
break;
}
+
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
AuraEffectList const& mDamageDoneCreature = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_CREATURE);
for(AuraEffectList::const_iterator i = mDamageDoneCreature.begin();i != mDamageDoneCreature.end(); ++i)
if(creatureTypeMask & uint32((*i)->GetMiscValue()))
DoneFlatBenefit += (*i)->GetAmount();
+
// ..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)
AuraEffectList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_RANGED_ATTACK_POWER_VERSUS);
for(AuraEffectList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
@@ -9133,12 +10247,14 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT
else
{
APbonus += pVictim->GetTotalAuraModifier(SPELL_AURA_MELEE_ATTACK_POWER_ATTACKER_BONUS);
+
// ..done (base at attack power and creature type)
AuraEffectList const& mCreatureAttackPower = GetAurasByType(SPELL_AURA_MOD_MELEE_ATTACK_POWER_VERSUS);
for(AuraEffectList::const_iterator i = mCreatureAttackPower.begin();i != mCreatureAttackPower.end(); ++i)
if(creatureTypeMask & uint32((*i)->GetMiscValue()))
APbonus += (*i)->GetAmount();
}
+
if (APbonus!=0) // Can be negative
{
bool normalized = false;
@@ -9156,32 +10272,40 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT
// This is not a typo - Impurity has SPELLFAMILY_DRUID
if (AuraEffect const * aurEff = GetDummyAura(SPELLFAMILY_DRUID, 1986, 0))
APbonus *= (100.0f + aurEff->GetAmount()) / 100.0f;
+
DoneFlatBenefit += int32(APbonus/14.0f * GetAPMultiplier(attType,normalized));
}
+
// ..taken
AuraEffectList const& mDamageTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_TAKEN);
for(AuraEffectList::const_iterator i = mDamageTaken.begin();i != mDamageTaken.end(); ++i)
if((*i)->GetMiscValue() & GetMeleeDamageSchoolMask())
TakenFlatBenefit += (*i)->GetAmount();
+
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.0f;
float TakenTotalMod = 1.0f;
+
// ..done
// SPELL_AURA_MOD_DAMAGE_PERCENT_DONE included in weapon damage
// SPELL_AURA_MOD_OFFHAND_DAMAGE_PCT included in weapon damage
+
AuraEffectList const& mDamageDoneVersus = GetAurasByType(SPELL_AURA_MOD_DAMAGE_DONE_VERSUS);
for(AuraEffectList::const_iterator i = mDamageDoneVersus.begin();i != mDamageDoneVersus.end(); ++i)
if(creatureTypeMask & uint32((*i)->GetMiscValue()))
DoneTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
+
// ..taken
AuraEffectList const& mModDamagePercentTaken = pVictim->GetAurasByType(SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN);
for(AuraEffectList::const_iterator i = mModDamagePercentTaken.begin(); i != mModDamagePercentTaken.end(); ++i)
if((*i)->GetMiscValue() & GetMeleeDamageSchoolMask())
TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
+
// .. taken pct (special attacks)
if (spellProto)
{
@@ -9197,6 +10321,7 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT
TakenTotalMod *= ((*i)->GetAmount()+100.0f)/100.0f;
}
}
+
// .. taken pct: dummy auras
AuraEffectList const& mDummyAuras = pVictim->GetAurasByType(SPELL_AURA_DUMMY);
for(AuraEffectList::const_iterator i = mDummyAuras.begin(); i != mDummyAuras.end(); ++i)
@@ -9217,6 +10342,7 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT
break;
}
}
+
// .. taken pct: class scripts
AuraEffectList const& mclassScritAuras = GetAurasByType(SPELL_AURA_OVERRIDE_CLASS_SCRIPTS);
for(AuraEffectList::const_iterator i = mclassScritAuras.begin(); i != mclassScritAuras.end(); ++i)
@@ -9232,12 +10358,14 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT
sLog.outError("Spell structure of DD (%u) changed.",(*i)->GetId());
continue;
}
+
// effect 0 have expected value but in negative state
TakenTotalMod *= (-eff0->GetAmount()+100.0f)/100.0f;
}
break;
}
}
+
if(attType != RANGED_ATTACK)
{
AuraEffectList const& mModMeleeDamageTakenPercent = pVictim->GetAurasByType(SPELL_AURA_MOD_MELEE_DAMAGE_TAKEN_PCT);
@@ -9250,17 +10378,22 @@ void Unit::MeleeDamageBonus(Unit *pVictim, uint32 *pdamage,WeaponAttackType attT
for(AuraEffectList::const_iterator i = mModRangedDamageTakenPercent.begin(); i != mModRangedDamageTakenPercent.end(); ++i)
TakenTotalMod *= ((*i)->GetAmount()+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)
@@ -9290,10 +10423,13 @@ void Unit::ApplySpellImmune(uint32 spellId, uint32 op, uint32 type, bool apply)
}
}
}
+
}
+
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)
{
// Create dispel mask by dispel type
@@ -9313,6 +10449,7 @@ void Unit::ApplySpellDispelImmunity(const SpellEntry * spellProto, DispelType ty
}
}
}
+
float Unit::GetWeaponProcChance() const
{
// normalized proc chance for weapon attack speed
@@ -9323,6 +10460,7 @@ float Unit::GetWeaponProcChance() const
return (GetAttackTime(OFF_ATTACK) * 1.6f / 1000.0f);
return 0;
}
+
float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellEntry * spellProto) const
{
// proc per minute chance calculation
@@ -9331,14 +10469,19 @@ float Unit::GetPPMProcChance(uint32 WeaponSpeed, float PPM, const SpellEntry * s
if (spellProto)
if(Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_PROC_PER_MINUTE,PPM);
+
return uint32((WeaponSpeed * PPM) / 600.0f); // result is chance in percents (probability = Speed_in_sec * (PPM / 60))
}
+
void Unit::Mount(uint32 mount)
{
RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_MOUNT);
+
if(mount)
SetUInt32Value(UNIT_FIELD_MOUNTDISPLAYID, mount);
+
SetFlag( UNIT_FIELD_FLAGS, UNIT_FLAG_MOUNT );
+
// unsummon pet
if(GetTypeId() == TYPEID_PLAYER)
{
@@ -9353,19 +10496,24 @@ void Unit::Mount(uint32 mount)
((Player*)this)->UnsummonPetTemporaryIfAny();
}
}
+
}
+
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)
- {
+ {
if(Pet *pPet = ((Player*)this)->GetPet())
{
if(pPet->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED) && !pPet->hasUnitState(UNIT_STAT_STUNNED))
@@ -9375,6 +10523,7 @@ void Unit::Unmount()
((Player*)this)->ResummonPetTemporaryUnSummonedIfAny();
}
}
+
void Unit::SetInCombatWith(Unit* enemy)
{
Unit* eOwner = enemy->GetCharmerOrOwnerOrSelf();
@@ -9383,6 +10532,7 @@ void Unit::SetInCombatWith(Unit* enemy)
SetInCombatState(true,enemy);
return;
}
+
//check for duel
if(eOwner->GetTypeId() == TYPEID_PLAYER && ((Player*)eOwner)->duel)
{
@@ -9395,23 +10545,27 @@ void Unit::SetInCombatWith(Unit* enemy)
}
SetInCombatState(false,enemy);
}
+
void Unit::CombatStart(Unit* target, bool initialAggro)
{
if (initialAggro)
{
if(!target->IsStandState()/* && !target->hasUnitState(UNIT_STAT_STUNNED)*/)
target->SetStandState(UNIT_STAND_STATE_STAND);
+
if(!target->isInCombat() && target->GetTypeId() != TYPEID_PLAYER
&& !((Creature*)target)->HasReactState(REACT_PASSIVE) && ((Creature*)target)->IsAIEnabled)
{
((Creature*)target)->AI()->AttackStart(this);
}
+
SetInCombatWith(target);
target->SetInCombatWith(this);
}
Unit *who = target->GetCharmerOrOwnerOrSelf();
if(who->GetTypeId() == TYPEID_PLAYER)
SetContestedPvP((Player*)who);
+
Player *me = GetCharmerOrOwnerPlayerOrPlayerItself();
if(me && who->IsPvP()
&& (who->GetTypeId() != TYPEID_PLAYER
@@ -9421,22 +10575,28 @@ void Unit::CombatStart(Unit* target, bool initialAggro)
me->RemoveAurasWithInterruptFlags(AURA_INTERRUPT_FLAG_ENTER_PVP_COMBAT);
}
}
+
void Unit::SetInCombatState(bool PvP, Unit* enemy)
{
// only alive units can be in combat
if(!isAlive())
return;
+
if(PvP)
m_CombatTimer = 5000;
+
if(isInCombat() || hasUnitState(UNIT_STAT_EVADE))
return;
+
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
if(GetTypeId() != TYPEID_PLAYER)
{
// Set home position at place of engaging combat for escorted creatures
if(( ((Creature*)this)->IsAIEnabled && ((Creature*)this)->AI()->IsEscorted() ) ||
((Creature*)this)->GetMotionMaster()->GetCurrentMovementGeneratorType() == WAYPOINT_MOTION_TYPE)
((Creature*)this)->SetHomePosition(GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation());
+
if(enemy)
{
if(((Creature*)this)->IsAIEnabled)
@@ -9444,6 +10604,7 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
if(((Creature*)this)->GetFormation())
((Creature*)this)->GetFormation()->MemberAttackStart((Creature*)this, enemy);
}
+
if(((Creature*)this)->isPet())
{
UpdateSpeed(MOVE_RUN, true);
@@ -9451,16 +10612,19 @@ void Unit::SetInCombatState(bool PvP, Unit* enemy)
UpdateSpeed(MOVE_FLIGHT, true);
}
}
+
for(Unit::ControlList::iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
{
(*itr)->SetInCombatState(PvP, enemy);
(*itr)->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
}
}
+
void Unit::ClearInCombat()
{
m_CombatTimer = 0;
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IN_COMBAT);
+
// Player's state will be cleared in Player::UpdateContestedPvP
if(GetTypeId() != TYPEID_PLAYER)
{
@@ -9470,6 +10634,7 @@ void Unit::ClearInCombat()
}
else
((Player*)this)->UpdatePotionCooldown();
+
if(GetTypeId() != TYPEID_PLAYER && ((Creature*)this)->isPet())
{
if(Unit *owner = GetOwner())
@@ -9481,16 +10646,20 @@ void Unit::ClearInCombat()
}
else if(!isCharmed())
return;
+
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PET_IN_COMBAT);
}
+
//TODO: remove this function
bool Unit::isTargetableForAttack() const
{
return isAttackableByAOE() && !hasUnitState(UNIT_STAT_DIED);
}
+
bool Unit::canAttack(Unit const* target, bool force) const
{
assert(target);
+
if(force)
{
if(IsFriendlyTo(target))
@@ -9498,45 +10667,61 @@ bool Unit::canAttack(Unit const* target, bool force) const
}
else if(!IsHostileTo(target))
return false;
+
//if(m_Vehicle && m_Vehicle == target->m_Vehicle)
// return true;
+
if(!target->isAttackableByAOE() || target->hasUnitState(UNIT_STAT_DIED))
return false;
+
// shaman totem quests: spell 8898, shaman can detect elementals but elementals cannot see shaman
if(m_invisibilityMask || target->m_invisibilityMask)
if(!canDetectInvisibilityOf(target) && !target->canDetectInvisibilityOf(this))
return false;
+
if(target->GetVisibility() == VISIBILITY_GROUP_STEALTH && !canDetectStealthOf(target, GetDistance(target)))
return false;
+
if(m_vehicle)
- if(IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))
+ if(IsOnVehicle(target) || m_vehicle->GetBase()->IsOnVehicle(target))
return false;
+
return true;
}
+
bool Unit::isAttackableByAOE() const
{
if(!isAlive())
return false;
+
if(HasFlag(UNIT_FIELD_FLAGS,
UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_OOC_NOT_ATTACKABLE))
return false;
+
if(GetTypeId()==TYPEID_PLAYER && ((Player *)this)->isGameMaster())
return false;
+
return !hasUnitState(UNIT_STAT_UNATTACKABLE);
}
+
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);
@@ -9547,20 +10732,27 @@ int32 Unit::ModifyHealth(int32 dVal)
SetHealth(maxHealth);
gain = maxHealth - curHealth;
}
+
return gain;
}
+
int32 Unit::GetHealthGain(int32 dVal)
{
int32 gain = 0;
+
if(dVal==0)
return 0;
+
int32 curHealth = (int32)GetHealth();
+
int32 val = dVal + curHealth;
if(val <= 0)
{
return -curHealth;
}
+
int32 maxHealth = (int32)GetMaxHealth();
+
if(val < maxHealth)
{
gain = dVal;
@@ -9569,21 +10761,28 @@ int32 Unit::GetHealthGain(int32 dVal)
{
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);
@@ -9594,18 +10793,23 @@ int32 Unit::ModifyPower(Powers power, int32 dVal)
SetPower(power,maxPower);
gain = maxPower - curPower;
}
+
return gain;
}
+
bool Unit::isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const
{
if(!u || !IsInMap(u))
return false;
+
return u->canSeeOrDetect(this, detect, inVisibleList, is3dDistance);
}
+
bool Unit::canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList, bool is3dDistance) const
{
return true;
}
+
bool Unit::canDetectInvisibilityOf(Unit const* u) const
{
if(m_invisibilityMask & u->m_invisibilityMask) // same group
@@ -9614,18 +10818,21 @@ bool Unit::canDetectInvisibilityOf(Unit const* u) const
for(AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
if((*iter)->GetCasterGUID()==GetGUID())
return true;
+
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::AuraEffectList const& iAuras = u->GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
for(Unit::AuraEffectList::const_iterator itr = iAuras.begin(); itr != iAuras.end(); ++itr)
if(((*itr)->GetMiscValue())==i && invLevel < (*itr)->GetAmount())
invLevel = (*itr)->GetAmount();
+
// find invisibility detect level
uint32 detectLevel = 0;
if(i==6 && GetTypeId()==TYPEID_PLAYER) // special drunk detection case
@@ -9639,12 +10846,15 @@ bool Unit::canDetectInvisibilityOf(Unit const* u) const
if(((*itr)->GetMiscValue())==i && detectLevel < (*itr)->GetAmount())
detectLevel = (*itr)->GetAmount();
}
+
if(invLevel <= detectLevel)
return true;
}
}
+
return false;
}
+
bool Unit::canDetectStealthOf(Unit const* target, float distance) const
{
if(hasUnitState(UNIT_STAT_STUNNED))
@@ -9655,10 +10865,12 @@ bool Unit::canDetectStealthOf(Unit const* target, float distance) const
return false;
if(HasAuraType(SPELL_AURA_DETECT_STEALTH))
return true;
+
AuraEffectList const& auras = target->GetAurasByType(SPELL_AURA_MOD_STALKED); // Hunter mark
for(AuraEffectList::const_iterator iter = auras.begin(); iter != auras.end(); ++iter)
if((*iter)->GetCasterGUID()==GetGUID())
return true;
+
//Visible distance based on stealth value (stealth rank 4 300MOD, 10.5 - 3 = 7.5)
float visibleDistance = 10.5f - target->GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH) / 100.0f;
//Visible distance is modified by -Level Diff (every level diff = 1.0f in visible distance)
@@ -9667,20 +10879,26 @@ bool Unit::canDetectStealthOf(Unit const* target, float distance) const
//based on wowwiki every 5 mod we have 1 more level diff in calculation
visibleDistance += (float)(GetTotalAuraModifier(SPELL_AURA_MOD_DETECT) - target->GetTotalAuraModifier(SPELL_AURA_MOD_STEALTH_LEVEL)) / 5.0f;
visibleDistance = visibleDistance > MAX_PLAYER_STEALTH_DETECT_RANGE ? MAX_PLAYER_STEALTH_DETECT_RANGE : visibleDistance;
+
return distance < visibleDistance;
}
+
void Unit::SetVisibility(UnitVisibility x)
{
m_Visibility = x;
+
SetToNotify();
+
if(x == VISIBILITY_GROUP_STEALTH)
DestroyForNearbyPlayers();
}
+
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)
{
// Only apply debuffs
@@ -9717,10 +10935,13 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
{
main_speed_mod = GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED);
stack_bonus = GetTotalAuraMultiplier(SPELL_AURA_MOD_VEHICLE_SPEED_ALWAYS);
+
// for some spells this mod is applied on vehicle owner
uint32 owner_speed_mod = 0;
+
if (Unit * owner = GetCharmer())
uint32 owner_speed_mod = owner->GetMaxPositiveAuraModifier(SPELL_AURA_MOD_INCREASE_VEHICLE_FLIGHT_SPEED);
+
main_speed_mod = main_speed_mod>owner_speed_mod ? main_speed_mod : owner_speed_mod;
}
else if (IsMounted())
@@ -9730,7 +10951,9 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
}
else // Use not mount (shapeshift for example) auras (should stack)
main_speed_mod = GetTotalAuraModifier(SPELL_AURA_MOD_INCREASE_FLIGHT_SPEED);
+
non_stack_bonus = (100.0 + GetMaxPositiveAuraModifier(SPELL_AURA_MOD_FLIGHT_SPEED_NOT_STACK))/100.0f;
+
// Update speed for vehicle if available
if (GetTypeId()==TYPEID_PLAYER && GetVehicle())
GetVehicleBase()->UpdateSpeed(MOVE_FLIGHT, true);
@@ -9740,9 +10963,12 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
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:
@@ -9752,6 +10978,7 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
// Set creature speed rate from CreatureInfo
if (GetTypeId() == TYPEID_UNIT)
speed *= ((Creature*)this)->GetCreatureInfo()->speed;
+
// 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))
@@ -9766,25 +10993,32 @@ void Unit::UpdateSpeed(UnitMoveType mtype, bool forced)
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]*(IsControlledByPlayer() ? playerBaseMoveSpeed[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();
+
WorldPacket data;
if(!forced)
{
@@ -9821,6 +11055,7 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
sLog.outError("Unit::SetSpeed: Unsupported move type (%d), data not sent to client.",mtype);
return;
}
+
data.append(GetPackGUID());
data << uint32(0); // movement flags
data << uint16(0); // unk flags
@@ -9840,10 +11075,12 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
// 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];
+
if(!isInCombat())
if(Pet* pet = ((Player*)this)->GetPet())
pet->SetSpeed(mtype, m_speed_rate[mtype], forced);
}
+
switch(mtype)
{
case MOVE_WALK:
@@ -9885,6 +11122,7 @@ void Unit::SetSpeed(UnitMoveType mtype, float rate, bool forced)
SendMessageToSet( &data, true );
}
}
+
void Unit::SetHover(bool on)
{
if(on)
@@ -9892,6 +11130,7 @@ void Unit::SetHover(bool on)
else
RemoveAurasDueToSpell(11010);
}
+
void Unit::setDeathState(DeathState s)
{
if (s != ALIVE && s!= JUST_ALIVED)
@@ -9900,13 +11139,16 @@ void Unit::setDeathState(DeathState s)
DeleteThreatList();
getHostilRefManager().deleteReferences();
ClearComboPointHolders(); // any combo points pointed to unit lost at it death
+
if(IsNonMeleeSpellCasted(false))
InterruptNonMeleeSpells(false);
+
UnsummonAllTotems();
RemoveAllControlled();
RemoveAllAurasOnDeath();
ExitVehicle();
}
+
if (s == JUST_DIED)
{
ModifyAuraState(AURA_STATE_HEALTHLESS_20_PERCENT, false);
@@ -9925,6 +11167,7 @@ void Unit::setDeathState(DeathState s)
{
RemoveFlag (UNIT_FIELD_FLAGS, UNIT_FLAG_SKINNABLE); // clear skinnable for creature and player (at battleground)
}
+
if (m_deathState != ALIVE && s == ALIVE)
{
//_ApplyAllAuraMods();
@@ -9933,6 +11176,7 @@ void Unit::setDeathState(DeathState s)
}
m_deathState = s;
}
+
/*########################################
######## ########
######## AGGRO SYSTEM ########
@@ -9943,81 +11187,111 @@ 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;
+
// totems can not have threat list
if( ((Creature*)this)->isTotem() )
return false;
+
// vehicles can not have threat list
//if( ((Creature*)this)->IsVehicle() )
// return false;
+
// summons can not have a threat list, unless they are controlled by a creature
if( HasUnitTypeMask(UNIT_MASK_MINION | UNIT_MASK_GUARDIAN | UNIT_MASK_CONTROLABLE_GUARDIAN) && IS_PLAYER_GUID(((Pet*)this)->GetOwnerGUID()) )
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()
{
if(CanHaveThreatList() && !m_ThreatManager.isThreatListEmpty())
SendClearThreatListOpcode();
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;
+
if(((Creature*)this)->HasReactState(REACT_PASSIVE))
return;
+
Unit *target = getVictim();
if(target && target == taunter)
return;
+
SetInFront(taunter);
if (((Creature*)this)->IsAIEnabled)
((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;
+
if(((Creature*)this)->HasReactState(REACT_PASSIVE))
return;
+
Unit *target = getVictim();
if(!target || target != taunter)
return;
+
if(m_ThreatManager.isThreatListEmpty())
{
if(((Creature*)this)->IsAIEnabled)
((Creature*)this)->AI()->EnterEvadeMode();
return;
}
+
//m_ThreatManager.tauntFadeOut(taunter);
target = m_ThreatManager.getHostilTarget();
+
if (target && target != taunter)
{
SetInFront(target);
@@ -10025,18 +11299,22 @@ void Unit::TauntFadeOut(Unit *taunter)
((Creature*)this)->AI()->AttackStart(target);
}
}
+
//======================================================================
+
Unit* Creature::SelectVictim()
{
//function provides main threat functionality
//next-victim-selection algorithm and evade mode are called
//threat list sorting etc.
+
Unit* target = NULL;
// First checking if we have some taunt on us
const AuraEffectList& tauntAuras = GetAurasByType(SPELL_AURA_MOD_TAUNT);
if ( !tauntAuras.empty() )
{
Unit* caster;
+
// The last taunt aura caster is alive an we are happy to attack him
if ( (caster = tauntAuras.back()->GetCaster()) && caster->isAlive() )
return getVictim();
@@ -10044,6 +11322,7 @@ Unit* Creature::SelectVictim()
{
// We do not have last taunt aura caster but we have more taunt auras,
// so find first available target
+
// Auras are pushed_back, last caster will be on the end
AuraEffectList::const_iterator aura = --tauntAuras.end();
do
@@ -10060,6 +11339,7 @@ Unit* Creature::SelectVictim()
else
target = getVictim();
}
+
if (CanHaveThreatList())
{
if ( !target && !m_ThreatManager.isThreatListEmpty() )
@@ -10092,11 +11372,13 @@ Unit* Creature::SelectVictim()
}
else
return NULL;
+
if(target && _IsTargetAcceptable(target))
{
SetInFront(target);
return target;
}
+
// 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
@@ -10107,14 +11389,17 @@ Unit* Creature::SelectVictim()
&& !((Creature*)(*itr))->HasUnitTypeMask(UNIT_MASK_CONTROLABLE_GUARDIAN))
return NULL;
}
+
// TODO: a vehicle may eat some mob, so mob should not evade
if(GetVehicle())
return NULL;
+
// search nearby enemy before enter evade mode
if(HasReactState(REACT_AGGRESSIVE))
if(target = SelectNearestTarget())
if(_IsTargetAcceptable(target))
return target;
+
if(m_invisibilityMask)
{
Unit::AuraEffectList const& iAuras = GetAurasByType(SPELL_AURA_MOD_INVISIBILITY);
@@ -10126,13 +11411,17 @@ Unit* Creature::SelectVictim()
}
return NULL;
}
+
// enter in evade mode in other case
AI()->EnterEvadeMode();
+
return NULL;
}
+
//======================================================================
//======================================================================
//======================================================================
+
int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_index, int32 effBasePoints, Unit const* /*target*/)
{
int32 level = int32(getLevel());
@@ -10141,14 +11430,17 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde
else if (level < (int32)spellProto->baseLevel)
level = (int32)spellProto->baseLevel;
level-= (int32)spellProto->spellLevel;
+
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);
+
// range can have possitive and negative values, so order its for irand
int32 randvalue = int32(spellProto->EffectBaseDice[effect_index]) >= randomPoints
? irand(randomPoints, int32(spellProto->EffectBaseDice[effect_index]))
: irand(int32(spellProto->EffectBaseDice[effect_index]), randomPoints);
+
int32 value = basePoints + randvalue;
//random damage
//if(comboDamage != 0 && unitPlayer /*&& target && (target->GetGUID() == unitPlayer->GetComboTarget())*/)
@@ -10156,6 +11448,7 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde
if(uint8 comboPoints = m_movedPlayer->GetComboPoints())
if(float comboDamage = spellProto->EffectPointsPerComboPoint[effect_index])
value += (int32)(comboDamage * comboPoints);
+
if(Player* modOwner = GetSpellModOwner())
{
modOwner->ApplySpellMod(spellProto->Id,SPELLMOD_ALL_EFFECTS, value);
@@ -10172,6 +11465,7 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde
break;
}
}
+
if(!basePointsPerLevel && (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 &&
@@ -10182,32 +11476,42 @@ int32 Unit::CalculateSpellDamage(SpellEntry const* spellProto, uint8 effect_inde
//there are many more: slow speed, -healing pct
value = int32(value*0.25f*exp(getLevel()*(70-spellProto->spellLevel)/1000.0f));
//value = int32(value * (int32)getLevel() / (int32)(spellProto->spellLevel ? spellProto->spellLevel : 1));
+
return value;
}
+
int32 Unit::CalcSpellDuration(SpellEntry const* spellProto)
{
uint8 comboPoints = m_movedPlayer ? m_movedPlayer->GetComboPoints() : 0;
+
int32 minduration = GetSpellDuration(spellProto);
int32 maxduration = GetSpellMaxDuration(spellProto);
+
int32 duration;
+
if(comboPoints && minduration != -1 && minduration != maxduration)
duration = minduration + int32((maxduration - minduration) * comboPoints / 5);
else
duration = minduration;
+
return duration;
}
+
int32 Unit::ModSpellDuration(SpellEntry const* spellProto, Unit const* target, int32 duration, bool positive)
{
//don't mod permament auras duration
if (duration<0)
return duration;
+
//cut duration only of negative effects
if (!positive)
{
int32 mechanic = GetAllSpellMechanicMask(spellProto);
+
int32 durationMod;
int32 durationMod_always = 0;
int32 durationMod_not_stack = 0;
+
for (uint8 i = 1;i<=MECHANIC_ENRAGED;++i)
{
if (!(mechanic & 1<<i))
@@ -10222,26 +11526,32 @@ int32 Unit::ModSpellDuration(SpellEntry const* spellProto, Unit const* target, i
if (new_durationMod_not_stack < durationMod_not_stack)
durationMod_not_stack = new_durationMod_not_stack;
}
+
// Select strongest negative mod
if (durationMod_always > durationMod_not_stack)
durationMod = durationMod_not_stack;
else
durationMod = durationMod_always;
+
if (durationMod != 0)
duration = int32( float (duration) * float(100.0f+durationMod) /100.0f);
+
// there are only negative mods currently
durationMod_always =target->GetTotalAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL, spellProto->Dispel);
durationMod_not_stack=target->GetMaxNegativeAuraModifierByMiscValue(SPELL_AURA_MOD_AURA_DURATION_BY_DISPEL_NOT_STACK, spellProto->Dispel);
+
durationMod=0;
if (durationMod_always > durationMod_not_stack)
durationMod += durationMod_not_stack;
else
durationMod += durationMod_always;
+
if (durationMod != 0)
duration = int32( float (duration) * float(100.0f+durationMod) /100.0f);
}
//else positive mods here, there are no currently
//when there will be, change GetTotalAuraModifierByMiscValue to GetTotalPositiveAuraModifierByMiscValue
+
// Glyphs which increase duration of selfcasted buffs
if (target == this)
{
@@ -10273,6 +11583,7 @@ int32 Unit::ModSpellDuration(SpellEntry const* spellProto, Unit const* target, i
}
return duration>0 ? duration : 0;
}
+
void Unit::ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spell * spell)
{
if (!spellProto || castTime<0)
@@ -10280,6 +11591,7 @@ void Unit::ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spel
//called from caster
if(Player* modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellProto->Id, SPELLMOD_CASTING_TIME, castTime, spell);
+
if( !(spellProto->Attributes & (SPELL_ATTR_UNK4|SPELL_ATTR_TRADESPELL)) && spellProto->DmgClass == SPELL_DAMAGE_CLASS_MAGIC && spellProto->SpellFamilyName)
castTime = int32( float(castTime) * GetFloatValue(UNIT_MOD_CAST_SPEED));
else
@@ -10288,16 +11600,20 @@ void Unit::ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spel
castTime = int32 (float(castTime) * m_modAttackSpeedPct[RANGED_ATTACK]);
}
}
+
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)
{
@@ -10312,6 +11628,7 @@ DiminishingLevels Unit::GetDiminishing(DiminishingGroup group)
}
return DIMINISHING_LEVEL_1;
}
+
void Unit::IncrDiminishing(DiminishingGroup group)
{
// Checking for existing in the table
@@ -10325,22 +11642,28 @@ void Unit::IncrDiminishing(DiminishingGroup group)
}
m_Diminishing.push_back(DiminishingReturn(group,getMSTime(),DIMINISHING_LEVEL_2));
}
+
void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Unit* caster,DiminishingLevels Level, int32 limitduration)
{
if(duration == -1 || group == DIMINISHING_NONE || caster->IsFriendlyTo(this) )
return;
+
// test pet/charm masters instead pets/charmeds
Unit const* targetOwner = GetCharmerOrOwner();
Unit const* casterOwner = caster->GetCharmerOrOwner();
+
// Duration of crowd control abilities on pvp target is limited by 10 sec. (2.2.0)
if(limitduration > 0 && duration > limitduration)
{
Unit const* target = targetOwner ? targetOwner : this;
Unit const* source = casterOwner ? casterOwner : caster;
+
if(target->GetTypeId() == TYPEID_PLAYER && source->GetTypeId() == TYPEID_PLAYER)
duration = limitduration;
}
+
float mod = 1.0f;
+
// Some diminishings applies to mobs too (for example, Stun)
if((GetDiminishingReturnsGroupType(group) == DRTYPE_PLAYER && (targetOwner ? (targetOwner->GetTypeId() == TYPEID_PLAYER) : (GetTypeId() == TYPEID_PLAYER))) || GetDiminishingReturnsGroupType(group) == DRTYPE_ALL)
{
@@ -10354,8 +11677,10 @@ void Unit::ApplyDiminishingToDuration(DiminishingGroup group, int32 &duration,Un
default: break;
}
}
+
duration = int32(duration * mod);
}
+
void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply )
{
// Checking for existing in the table
@@ -10363,6 +11688,7 @@ void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply )
{
if(i->DRGroup != group)
continue;
+
if(apply)
i->stack += 1;
else if(i->stack)
@@ -10375,6 +11701,7 @@ void Unit::ApplyDiminishingAura( DiminishingGroup group, bool apply )
break;
}
}
+
uint32 Unit::GetSpellMaxRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry)
{
if (!rangeEntry)
@@ -10405,22 +11732,27 @@ uint32 Unit::GetSpellRadiusForTarget(Unit* target,const SpellRadiusEntry * radiu
return radiusEntry->radiusHostile;
return radiusEntry->radiusFriend;
};
+
Unit* Unit::GetUnit(WorldObject& object, uint64 guid)
{
return ObjectAccessor::GetUnit(object,guid);
}
+
Player* Unit::GetPlayer(uint64 guid)
{
return ObjectAccessor::FindPlayer(guid);
}
+
Creature* Unit::GetCreature(WorldObject& object, uint64 guid)
{
return object.GetMap()->GetCreature(guid);
}
+
bool Unit::isVisibleForInState( Player const* u, bool inVisibleList ) const
{
return u->canSeeOrDetect(this, false, inVisibleList, false);
}
+
uint32 Unit::GetCreatureType() const
{
if(GetTypeId() == TYPEID_PLAYER)
@@ -10434,11 +11766,13 @@ uint32 Unit::GetCreatureType() const
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)
@@ -10446,7 +11780,9 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f
sLog.outError("ERROR in HandleStatModifier(): non existed UnitMods or wrong UnitModifierType!");
return false;
}
+
float val = 1.0f;
+
switch(modifierType)
{
case BASE_VALUE:
@@ -10457,14 +11793,18 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f
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:
@@ -10472,8 +11812,10 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f
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:
@@ -10481,22 +11823,28 @@ bool Unit::HandleStatModifier(UnitMods unitMod, UnitModifierType modifierType, f
case UNIT_MOD_HAPPINESS:
case UNIT_MOD_RUNE:
case UNIT_MOD_RUNIC_POWER: 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)
@@ -10504,22 +11852,29 @@ float Unit::GetModifierValue(UnitMods unitMod, UnitModifierType modifierType) co
sLog.outError("trial to access non existed 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)
@@ -10527,17 +11882,22 @@ float Unit::GetTotalAuraModValue(UnitMods unitMod) const
sLog.outError("trial to access non existed 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;
@@ -10546,14 +11906,18 @@ SpellSchools Unit::GetSpellSchoolByAuraGroup(UnitMods unitMod) const
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;
@@ -10561,11 +11925,14 @@ Stats Unit::GetStatByAuraGroup(UnitMods unitMod) const
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
{
switch(unitMod)
@@ -10579,8 +11946,10 @@ Powers Unit::GetPowerTypeByAuraGroup(UnitMods unitMod) const
case UNIT_MOD_RUNIC_POWER:return POWER_RUNIC_POWER;
default: return POWER_MANA;
}
+
return POWER_MANA;
}
+
float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const
{
if (attType == RANGED_ATTACK)
@@ -10598,19 +11967,24 @@ float Unit::GetTotalAttackPowerValue(WeaponAttackType attType) const
return ap * (1.0f + GetFloatValue(UNIT_FIELD_ATTACK_POWER_MULTIPLIER));
}
}
+
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)
{
if(getDeathState() == JUST_DIED)
@@ -10623,7 +11997,9 @@ void Unit::SetHealth(uint32 val)
if(maxHealth < val)
val = maxHealth;
}
+
SetUInt32Value(UNIT_FIELD_HEALTH, val);
+
// group update
if(GetTypeId() == TYPEID_PLAYER)
{
@@ -10641,11 +12017,14 @@ void Unit::SetHealth(uint32 val)
}
}
}
+
void Unit::SetMaxHealth(uint32 val)
{
if(!val) val = 1;
+
uint32 health = GetHealth();
SetUInt32Value(UNIT_FIELD_MAXHEALTH, val);
+
// group update
if(GetTypeId() == TYPEID_PLAYER)
{
@@ -10662,22 +12041,28 @@ void Unit::SetMaxHealth(uint32 val)
((Player*)owner)->SetGroupUpdateFlag(GROUP_UPDATE_FLAG_PET_MAX_HP);
}
}
+
if(val < health)
SetHealth(val);
}
+
void Unit::SetPower(Powers power, uint32 val)
{
if(GetPower(power) == val)
return;
+
uint32 maxPower = GetMaxPower(power);
if(maxPower < val)
val = maxPower;
+
SetStatInt32Value(UNIT_FIELD_POWER1 + power, val);
+
WorldPacket data(SMSG_POWER_UPDATE);
data.append(GetPackGUID());
data << uint8(power);
data << uint32(val);
SendMessageToSet(&data, GetTypeId() == TYPEID_PLAYER ? true : false);
+
// group update
if(GetTypeId() == TYPEID_PLAYER)
{
@@ -10693,15 +12078,18 @@ void Unit::SetPower(Powers power, uint32 val)
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)
{
@@ -10718,12 +12106,15 @@ void Unit::SetMaxPower(Powers power, uint32 val)
((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)
{
@@ -10741,9 +12132,11 @@ void Unit::ApplyPowerMod(Powers power, uint32 val, bool apply)
}
}
}
+
void Unit::ApplyMaxPowerMod(Powers power, uint32 val, bool apply)
{
ApplyModUInt32Value(UNIT_FIELD_MAXPOWER1+power, val, apply);
+
// group update
if(GetTypeId() == TYPEID_PLAYER)
{
@@ -10761,6 +12154,7 @@ void Unit::ApplyMaxPowerMod(Powers power, uint32 val, bool apply)
}
}
}
+
uint32 Unit::GetCreatePowers( Powers power ) const
{
// POWER_FOCUS and POWER_HAPPINESS only have hunter pet
@@ -10775,8 +12169,10 @@ uint32 Unit::GetCreatePowers( Powers power ) const
case POWER_RUNE: return 0;
case POWER_HEALTH: return 0;
}
+
return 0;
}
+
void Unit::AddToWorld()
{
if(!IsInWorld())
@@ -10788,27 +12184,34 @@ void Unit::AddToWorld()
SetToNotify();
}
}
+
void Unit::RemoveFromWorld()
{
// cleanup
assert(GetGUID());
+
if(IsInWorld())
{
if(IsVehicle())
GetVehicleKit()->Uninstall();
+
RemoveCharmAuras();
RemoveBindSightAuras();
RemoveNotOwnSingleTargetAuras();
+
ExitVehicle();
UnsummonAllTotems();
RemoveAllControlled();
+
if(m_NotifyListPos >= 0)
GetMap()->RemoveUnitFromNotify(this);
+
if(GetCharmerGUID())
{
sLog.outCrash("Unit %u has charmer guid when removed from world", GetEntry());
assert(false);
}
+
if(Unit *owner = GetOwner())
{
if(owner->m_Controlled.find(this) != owner->m_Controlled.end())
@@ -10817,12 +12220,15 @@ void Unit::RemoveFromWorld()
assert(false);
}
}
+
WorldObject::RemoveFromWorld();
}
}
+
void Unit::CleanupsBeforeDelete()
{
assert(GetGUID());
+
//A unit may be in removelist and not in world, but it is still in grid
//and may have some references during delete
RemoveAllAuras();
@@ -10835,13 +12241,16 @@ void Unit::CleanupsBeforeDelete()
RemoveAllGameObjects();
RemoveAllDynObjects();
GetMotionMaster()->Clear(false); // remove different non-standard movement generators.
+
if(IsInWorld())
RemoveFromWorld();
}
+
void Unit::UpdateCharmAI()
{
if(GetTypeId() == TYPEID_PLAYER)
return;
+
if(i_disabledAI) // disabled AI must be primary AI
{
if(!isCharmed())
@@ -10863,30 +12272,38 @@ void Unit::UpdateCharmAI()
}
}
}
+
CharmInfo* Unit::InitCharmInfo()
{
if(!m_charmInfo)
m_charmInfo = new CharmInfo(this);
+
return m_charmInfo;
}
+
void Unit::DeleteCharmInfo()
{
if(!m_charmInfo)
return;
+
delete m_charmInfo;
m_charmInfo = NULL;
}
+
CharmInfo::CharmInfo(Unit* unit)
: m_unit(unit), m_CommandState(COMMAND_FOLLOW), m_petnumber(0), m_barInit(false)
{
for(uint8 i = 0; i < MAX_SPELL_CHARM; ++i)
m_charmspells[i].SetActionAndType(0,ACT_DISABLED);
+
if(m_unit->GetTypeId() == TYPEID_UNIT)
{
m_oldReactState = ((Creature*)m_unit)->GetReactState();
((Creature*)m_unit)->SetReactState(REACT_PASSIVE);
}
+
}
+
CharmInfo::~CharmInfo()
{
if(m_unit->GetTypeId() == TYPEID_UNIT)
@@ -10894,18 +12311,22 @@ CharmInfo::~CharmInfo()
((Creature*)m_unit)->SetReactState(m_oldReactState);
}
}
+
void CharmInfo::InitPetActionBar()
{
// the first 3 SpellOrActions are attack, follow and stay
for(uint32 i = 0; i < ACTION_BAR_INDEX_PET_SPELL_START - ACTION_BAR_INDEX_START; ++i)
SetActionBar(ACTION_BAR_INDEX_START + i,COMMAND_ATTACK - i,ACT_COMMAND);
+
// middle 4 SpellOrActions are spells/special attacks/abilities
for(uint32 i = 0; i < ACTION_BAR_INDEX_PET_SPELL_END-ACTION_BAR_INDEX_PET_SPELL_START; ++i)
SetActionBar(ACTION_BAR_INDEX_PET_SPELL_START + i,0,ACT_PASSIVE);
+
// last 3 SpellOrActions are reactions
for(uint32 i = 0; i < ACTION_BAR_INDEX_END - ACTION_BAR_INDEX_PET_SPELL_END; ++i)
SetActionBar(ACTION_BAR_INDEX_PET_SPELL_END + i,COMMAND_ATTACK - i,ACT_REACTION);
}
+
void CharmInfo::InitEmptyActionBar(bool withAttack)
{
if(withAttack)
@@ -10915,6 +12336,7 @@ void CharmInfo::InitEmptyActionBar(bool withAttack)
for(uint32 x = ACTION_BAR_INDEX_START+1; x < ACTION_BAR_INDEX_END; ++x)
SetActionBar(x,0,ACT_PASSIVE);
}
+
void CharmInfo::InitPossessCreateSpells()
{
InitEmptyActionBar();
@@ -10933,6 +12355,7 @@ void CharmInfo::InitPossessCreateSpells()
}
}
}
+
void CharmInfo::InitCharmCreateSpells()
{
if(m_unit->GetTypeId() == TYPEID_PLAYER) //charmed players don't have spells
@@ -10940,18 +12363,22 @@ void CharmInfo::InitCharmCreateSpells()
InitEmptyActionBar();
return;
}
+
InitPetActionBar();
+
for(uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
{
uint32 spellId = ((Creature*)m_unit)->m_spells[x];
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(spellInfo && spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD)
spellId = 0;
+
if(!spellId)
{
m_charmspells[x].SetActionAndType(spellId,ACT_DISABLED);
continue;
}
+
if (IsPassiveSpell(spellId))
{
m_unit->CastSpell(m_unit, spellId, true);
@@ -10960,6 +12387,7 @@ void CharmInfo::InitCharmCreateSpells()
else
{
m_charmspells[x].SetActionAndType(spellId,ACT_DISABLED);
+
ActiveStates newstate;
if(spellInfo)
{
@@ -10971,6 +12399,7 @@ void CharmInfo::InitCharmCreateSpells()
for(uint32 i = 0; i < MAX_SPELL_EFFECTS && !autocast; ++i)
if(SpellTargetType[spellInfo->EffectImplicitTargetA[i]] == TARGET_TYPE_UNIT_TARGET)
autocast = true;
+
if(autocast)
{
newstate = ACT_ENABLED;
@@ -10980,13 +12409,16 @@ void CharmInfo::InitCharmCreateSpells()
newstate = ACT_DISABLED;
}
}
+
AddSpellToActionBar(spellId, newstate);
}
}
}
+
bool CharmInfo::AddSpellToActionBar(uint32 spell_id, ActiveStates newstate)
{
uint32 first_id = spellmgr.GetFirstSpellInChain(spell_id);
+
// new spell rank can be already listed
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
@@ -10999,6 +12431,7 @@ bool CharmInfo::AddSpellToActionBar(uint32 spell_id, ActiveStates newstate)
}
}
}
+
// or use empty slot in other case
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
@@ -11010,9 +12443,11 @@ bool CharmInfo::AddSpellToActionBar(uint32 spell_id, ActiveStates newstate)
}
return false;
}
+
bool CharmInfo::RemoveSpellFromActionBar(uint32 spell_id)
{
uint32 first_id = spellmgr.GetFirstSpellInChain(spell_id);
+
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
{
if (uint32 action = PetActionBar[i].GetAction())
@@ -11024,16 +12459,20 @@ bool CharmInfo::RemoveSpellFromActionBar(uint32 spell_id)
}
}
}
+
return false;
}
+
void CharmInfo::ToggleCreatureAutocast(uint32 spellid, bool apply)
{
if(IsPassiveSpell(spellid))
return;
+
for(uint32 x = 0; x < MAX_SPELL_CHARM; ++x)
if(spellid == m_charmspells[x].GetAction())
m_charmspells[x].SetType(apply ? ACT_ENABLED : ACT_DISABLED);
}
+
void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
{
m_petnumber = petnumber;
@@ -11042,12 +12481,16 @@ void CharmInfo::SetPetNumber(uint32 petnumber, bool statwindow)
else
m_unit->SetUInt32Value(UNIT_FIELD_PETNUMBER, 0);
}
+
void CharmInfo::LoadPetActionBar(const std::string& data )
{
InitPetActionBar();
+
Tokens tokens = StrSplit(data, " ");
+
if (tokens.size() != (ACTION_BAR_INDEX_PET_SPELL_END-ACTION_BAR_INDEX_PET_SPELL_START)*2)
return; // non critical, will reset to default
+
uint8 index;
Tokens::iterator iter;
for(iter = tokens.begin(), index = ACTION_BAR_INDEX_PET_SPELL_START; index < ACTION_BAR_INDEX_PET_SPELL_END; ++iter, ++index )
@@ -11056,7 +12499,9 @@ void CharmInfo::LoadPetActionBar(const std::string& data )
uint8 type = atol((*iter).c_str());
++iter;
uint32 action = atol((*iter).c_str());
+
PetActionBar[index].SetActionAndType(action,ActiveStates(type));
+
// check correctness
if(PetActionBar[index].IsActionBarForSpell())
{
@@ -11067,11 +12512,13 @@ void CharmInfo::LoadPetActionBar(const std::string& data )
}
}
}
+
void CharmInfo::BuildActionBar( WorldPacket* data )
{
for(uint32 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
*data << uint32(PetActionBar[i].packedData);
}
+
void CharmInfo::SetSpellAutocast( uint32 spell_id, bool state )
{
for(uint8 i = 0; i < MAX_UNIT_ACTION_BAR_INDEX; ++i)
@@ -11083,10 +12530,12 @@ void CharmInfo::SetSpellAutocast( uint32 spell_id, bool state )
}
}
}
+
bool Unit::isFrozen() const
{
return HasAuraState(AURA_STATE_FROZEN);
}
+
struct ProcTriggeredData
{
ProcTriggeredData(Aura* _aura)
@@ -11099,7 +12548,9 @@ struct ProcTriggeredData
Aura * aura;
uint32 effMask;
};
+
typedef std::list< ProcTriggeredData > ProcTriggeredList;
+
// List of auras that CAN be trigger but may not exist in spell_proc_event
// in most case need for drop charges
// in some types of aura need do additional check
@@ -11146,10 +12597,13 @@ bool InitTriggerAuraData()
isTriggerAura[SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE] = true;
isTriggerAura[SPELL_AURA_MOD_DAMAGE_FROM_CASTER] = true;
isTriggerAura[SPELL_AURA_MOD_SPELL_CRIT_CHANCE] = true;
+
isNonTriggerAura[SPELL_AURA_MOD_POWER_REGEN]=true;
isNonTriggerAura[SPELL_AURA_REDUCE_PUSHBACK]=true;
+
return true;
}
+
uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition)
{
uint32 procEx = PROC_EX_NONE;
@@ -11187,6 +12641,7 @@ uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missC
}
return procEx;
}
+
void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage , SpellEntry const * procAura)
{
// Player is loaded now - do not allow passive spell casts to proc
@@ -11257,6 +12712,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
}
}
}
+
ProcTriggeredList procTriggered;
// Fill procTriggered list
for(AuraMap::const_iterator itr = GetAuras().begin(); itr!= GetAuras().end(); ++itr)
@@ -11265,10 +12721,12 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
if (procAura && procAura->Id == itr->first)
continue;
ProcTriggeredData triggerData(itr->second);
+
// Defensive procs are active on absorbs (so absorption effects are not a hindrance)
bool active = (damage > 0) || ((procExtra & PROC_EX_ABSORB) && isVictim);
if(!IsTriggeredAtSpellProcEvent(pTarget, triggerData.aura, procSpell, procFlag, procExtra, attType, isVictim, active, triggerData.spellProcEvent))
continue;
+
for (uint8 i=0; i<MAX_SPELL_EFFECTS;++i)
{
if (AuraEffect * aurEff = itr->second->GetPartAura(i))
@@ -11285,27 +12743,34 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
if (triggerData.effMask)
procTriggered.push_front(triggerData);
}
+
// Nothing found
if (procTriggered.empty())
return;
+
if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC))
SetCantProc(true);
+
// Handle effects proceed this time
for(ProcTriggeredList::const_iterator i = procTriggered.begin(); i != procTriggered.end(); ++i)
{
// look for aura in auras list, it may be removed while proc event processing
if (!HasAura(i->aura))
continue;
+
bool useCharges= i->aura->GetAuraCharges()>0;
bool takeCharges = false;
SpellEntry const *spellInfo = i->aura->GetSpellProto();
uint32 Id = i->aura->GetId();
+
// For players set spell cooldown if need
uint32 cooldown = 0;
if (GetTypeId() == TYPEID_PLAYER && i->spellProcEvent && i->spellProcEvent->cooldown)
cooldown = i->spellProcEvent->cooldown;
+
if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DISABLE_PROC)
SetCantProc(true);
+
// This bool is needed till separate aura effect procs are still here
bool handled = false;
if (HandleAuraProc(pTarget, damage, i->aura, procSpell, procFlag, procExtra, cooldown, &handled))
@@ -11313,13 +12778,16 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), Id);
takeCharges = true;
}
+
if (!handled)
for (uint8 effIndex = 0; effIndex<MAX_SPELL_EFFECTS;++effIndex)
{
if (!(i->effMask & (1<<effIndex)))
continue;
+
AuraEffect *triggeredByAura = i->aura->GetPartAura(effIndex);
assert(triggeredByAura);
+
switch(triggeredByAura->GetAuraName())
{
case SPELL_AURA_PROC_TRIGGER_SPELL:
@@ -11378,6 +12846,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
{
sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)",
(isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
+
HandleAuraRaidProcFromChargeWithValue(triggeredByAura);
takeCharges=true;
break;
@@ -11386,6 +12855,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
{
sLog.outDebug("ProcDamageAndSpell: casting mending (triggered by %s dummy aura of spell %u)",
(isVictim?"a victim's":"an attacker's"),triggeredByAura->GetId());
+
HandleAuraRaidProcFromCharge(triggeredByAura);
takeCharges=true;
break;
@@ -11393,6 +12863,7 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
case SPELL_AURA_PROC_TRIGGER_SPELL_WITH_VALUE:
{
sLog.outDebug("ProcDamageAndSpell: casting spell %u (triggered with value by %s aura of spell %u)", spellInfo->Id,(isVictim?"a victim's":"an attacker's"), triggeredByAura->GetId());
+
if (HandleProcTriggerSpell(pTarget, damage, triggeredByAura, procSpell, procFlag, procExtra, cooldown))
takeCharges=true;
break;
@@ -11474,14 +12945,17 @@ void Unit::ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag
if (spellInfo->AttributesEx3 & SPELL_ATTR_EX3_DISABLE_PROC)
SetCantProc(false);
}
+
// Cleanup proc requirements
if (procExtra & (PROC_EX_INTERNAL_TRIGGERED | PROC_EX_INTERNAL_CANT_PROC))
SetCantProc(false);
}
+
SpellSchoolMask Unit::GetMeleeDamageSchoolMask() const
{
return SPELL_SCHOOL_MASK_NORMAL;
}
+
Player* Unit::GetSpellModOwner() const
{
if(GetTypeId()==TYPEID_PLAYER)
@@ -11494,14 +12968,17 @@ Player* Unit::GetSpellModOwner() const
}
return NULL;
}
+
///----------Pet responses methods-----------------
void Unit::SendPetCastFail(uint32 spellid, SpellCastResult msg)
{
if(msg == SPELL_CAST_OK)
return;
+
Unit *owner = GetCharmerOrOwner();
if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
return;
+
WorldPacket data(SMSG_PET_CAST_FAILED, 1 + 4 + 1);
data << uint8(0); // cast count?
data << uint32(spellid);
@@ -11510,57 +12987,70 @@ void Unit::SendPetCastFail(uint32 spellid, SpellCastResult msg)
// uint32 for some reason
((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::SendPetAIReaction(uint64 guid)
{
Unit* owner = GetOwner();
if(!owner || owner->GetTypeId() != TYPEID_PLAYER)
return;
+
WorldPacket data(SMSG_AI_REACTION, 8 + 4);
data << uint64(guid);
data << uint32(AI_REACTION_AGGRO);
((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 example 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());
+
SendMonsterStop();
+
// update position and orientation;
WorldPacket data;
BuildHeartBeatMsg(&data);
SendMessageToSet(&data,false);
}
+
void Unit::SendMovementFlagUpdate()
{
WorldPacket data;
BuildHeartBeatMsg(&data);
SendMessageToSet(&data, false);
}
+
bool Unit::IsSitState() const
{
uint8 s = getStandState();
@@ -11569,16 +13059,20 @@ bool Unit::IsSitState() const
s == UNIT_STAND_STATE_SIT_MEDIUM_CHAIR || s == UNIT_STAND_STATE_SIT_HIGH_CHAIR ||
s == UNIT_STAND_STATE_SIT;
}
+
bool Unit::IsStandState() const
{
uint8 s = getStandState();
return !IsSitState() && s != UNIT_STAND_STATE_SLEEP && s != UNIT_STAND_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);
@@ -11586,13 +13080,16 @@ void Unit::SetStandState(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);
@@ -11603,11 +13100,13 @@ void Unit::SetDisplayId(uint32 modelId)
((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;
@@ -11615,10 +13114,12 @@ void Unit::ClearComboPointHolders()
m_ComboPointHolders.erase(lowguid); // or remove manually
}
}
+
void Unit::ClearAllReactives()
{
for(uint8 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))
@@ -11626,16 +13127,20 @@ void Unit::ClearAllReactives()
if(getClass() == CLASS_WARRIOR && GetTypeId() == TYPEID_PLAYER)
((Player*)this)->ClearComboPoints();
}
+
void Unit::UpdateReactives( uint32 p_time )
{
for(uint8 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:
@@ -11660,15 +13165,18 @@ void Unit::UpdateReactives( uint32 p_time )
}
}
}
+
Unit* Unit::SelectNearbyTarget(float dist) const
{
std::list<Unit *> targets;
Trinity::AnyUnfriendlyUnitInObjectRangeCheck u_check(this, this, dist);
Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> searcher(this, targets, u_check);
VisitNearbyObject(dist, searcher);
+
// remove current target
if(getVictim())
targets.remove(getVictim());
+
// remove not LoS targets
for(std::list<Unit *>::iterator tIter = targets.begin(); tIter != targets.end();)
{
@@ -11681,16 +13189,20 @@ Unit* Unit::SelectNearbyTarget(float dist) const
else
++tIter;
}
+
// no appropriate targets
if(targets.empty())
return NULL;
+
// select random
uint32 rIdx = urand(0,targets.size()-1);
std::list<Unit *>::const_iterator tcIter = targets.begin();
for(uint32 i = 0; i < rIdx; ++i)
++tcIter;
+
return *tcIter;
}
+
bool Unit::hasNegativeAuraWithInterruptFlag(uint32 flag)
{
for (AuraMap::iterator iter = m_Auras.begin(); iter != m_Auras.end(); ++iter)
@@ -11700,6 +13212,7 @@ bool Unit::hasNegativeAuraWithInterruptFlag(uint32 flag)
}
return false;
}
+
void Unit::ApplyAttackTimePercentMod( WeaponAttackType att,float val, bool apply )
{
float remainingTimePct = (float)m_attackTimer[att] / (GetAttackTime(att) * m_modAttackSpeedPct[att]);
@@ -11715,6 +13228,7 @@ void Unit::ApplyAttackTimePercentMod( WeaponAttackType att,float val, bool apply
}
m_attackTimer[att] = uint32(GetAttackTime(att) * m_modAttackSpeedPct[att] * remainingTimePct);
}
+
void Unit::ApplyCastTimePercentMod(float val, bool apply )
{
if(val > 0)
@@ -11722,19 +13236,24 @@ void Unit::ApplyCastTimePercentMod(float val, bool apply )
else
ApplyPercentModFloatValue(UNIT_MOD_CAST_SPEED,-val,apply);
}
+
uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectType damagetype, uint32 CastingTime )
{
// Not apply this to creature casted spells with casttime==0
if(CastingTime==0 && GetTypeId()==TYPEID_UNIT && !((Creature*)this)->isPet())
return 3500;
+
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<MAX_SPELL_EFFECTS;i++)
{
switch ( spellProto->Effect[i] )
@@ -11764,9 +13283,11 @@ uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectT
default:
break;
}
+
if(IsAreaEffectTarget[spellProto->EffectImplicitTargetA[i]] || IsAreaEffectTarget[spellProto->EffectImplicitTargetB[i]])
AreaEffect = true;
}
+
// Combined Spells with Both Over Time and Direct Damage
if ( overTime > 0 && CastingTime > 0 && DirectDamage )
{
@@ -11776,6 +13297,7 @@ uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectT
if (OriginalCastTime < 1500) OriginalCastTime = 1500;
// Portion to Over Time
float PtOT = (overTime / 15000.0f) / ((overTime / 15000.0f) + (OriginalCastTime / 3500.0f));
+
if ( damagetype == DOT )
CastingTime = uint32(CastingTime * PtOT);
else if ( PtOT < 1.0f )
@@ -11783,9 +13305,11 @@ uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectT
else
CastingTime = 0;
}
+
// Area Effect Spells receive only half of bonus
if ( AreaEffect )
CastingTime /= 2;
+
// -5% of total per any additional effect
for ( uint8 i=0; i<effects; ++i)
{
@@ -11799,8 +13323,10 @@ uint32 Unit::GetCastingTimeForBonus( SpellEntry const *spellProto, DamageEffectT
break;
}
}
+
return CastingTime;
}
+
void Unit::UpdateAuraForGroup(uint8 slot)
{
if(slot >= MAX_AURAS) // slot not found, return
@@ -11828,13 +13354,16 @@ void Unit::UpdateAuraForGroup(uint8 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:
@@ -11850,6 +13379,7 @@ float Unit::GetAPMultiplier(WeaponAttackType attType, bool normalized)
return Weapon->GetProto()->SubClass==ITEM_SUBCLASS_WEAPON_DAGGER ? 1.7 : 2.4;
}
}
+
AuraEffect* Unit::GetAuraEffect(AuraType type, SpellFamilyNames name, uint32 iconId, uint8 effIndex) const
{
Unit::AuraEffectList const& mDummy = GetAurasByType(type);
@@ -11864,15 +13394,19 @@ AuraEffect* Unit::GetAuraEffect(AuraType type, SpellFamilyNames name, uint32 ico
}
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))
{
@@ -11888,56 +13422,72 @@ void Unit::SetContestedPvP(Player *attackedPlayer)
SetVisibility(GetVisibility());
}
}
+
void Unit::AddPetAura(PetAura const* petSpell)
{
if(GetTypeId() != TYPEID_PLAYER)
return;
+
m_petAuras.insert(petSpell);
if(Pet* pet = ((Player*)this)->GetPet())
pet->CastPetAura(petSpell);
}
+
void Unit::RemovePetAura(PetAura const* petSpell)
{
if(GetTypeId() != TYPEID_PLAYER)
return;
+
m_petAuras.erase(petSpell);
if(Pet* pet = ((Player*)this)->GetPet())
pet->RemoveAurasDueToSpell(petSpell->GetAura(pet->GetEntry()));
}
+
Pet* Unit::CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id)
{
if(GetTypeId()!=TYPEID_PLAYER)
return NULL;
+
Pet* pet = new Pet((Player*)this, HUNTER_PET);
+
if(!pet->CreateBaseAtCreature(creatureTarget))
{
delete pet;
return NULL;
}
+
pet->SetCreatorGUID(GetGUID());
pet->setFaction(getFaction());
pet->SetUInt32Value(UNIT_CREATED_BY_SPELL, spell_id);
+
if(GetTypeId()==TYPEID_PLAYER)
pet->SetUInt32Value(UNIT_FIELD_FLAGS, UNIT_FLAG_PVP_ATTACKABLE);
+
uint32 level = (creatureTarget->getLevel() < (getLevel() - 5)) ? (getLevel() - 5) : creatureTarget->getLevel();
+
if(!pet->InitStatsForLevel(level))
{
sLog.outError("Pet::InitStatsForLevel() failed for creature (Entry: %u)!",creatureTarget->GetEntry());
delete pet;
return NULL;
}
+
pet->GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true);
// this enables pet details window (Shift+P)
pet->InitPetCreateSpells();
//pet->InitLevelupSpellsForLevel();
pet->SetHealth(pet->GetMaxHealth());
+
return pet;
}
+
bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry const* procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const *& spellProcEvent )
{
SpellEntry const* spellProto = aura->GetSpellProto ();
+
// Get proc Event Entry
spellProcEvent = spellmgr.GetSpellProcEvent(spellProto->Id);
+
// Get EventProcFlag
uint32 EventProcFlag;
if (spellProcEvent && spellProcEvent->procFlags) // if exist get custom spellProcEvent->procFlags
@@ -11947,12 +13497,14 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co
// Continue if no trigger exist
if (!EventProcFlag)
return false;
+
// Additional checks for triggered spells (ignore trap casts)
if (procExtra & PROC_EX_INTERNAL_TRIGGERED && !(procFlag & PROC_FLAG_ON_TRAP_ACTIVATION))
{
if (!(spellProto->AttributesEx3 & SPELL_ATTR_EX3_CAN_PROC_TRIGGERED))
return false;
}
+
// Check spellProcEvent data requirements
if(!spellmgr.IsSpellProcEventCanTriggeredBy(spellProcEvent, EventProcFlag, procSpell, procFlag, procExtra, active))
return false;
@@ -11968,9 +13520,10 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co
}
// Aura added by spell can`t trigger from self (prevent drop charges/do triggers)
// But except periodic and kill triggers (can triggered from self)
- if(procSpell && procSpell->Id == spellProto->Id
+ if(procSpell && procSpell->Id == spellProto->Id
&& !(spellProto->procFlags&(PROC_FLAG_ON_TAKE_PERIODIC | PROC_FLAG_KILL)))
return false;
+
// Check if current equipment allows aura to proc
if(!isVictim && GetTypeId() == TYPEID_PLAYER)
{
@@ -11983,8 +13536,10 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co
item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_OFFHAND);
else
item = ((Player*)this)->GetUseableItemByPos(INVENTORY_SLOT_BAG_0, EQUIPMENT_SLOT_RANGED);
+
if (((Player*)this)->IsInFeralForm())
return false;
+
if(!item || item->IsBroken() || item->GetProto()->Class != ITEM_CLASS_WEAPON || !((1<<item->GetProto()->SubClass) & spellProto->EquippedItemSubClassMask))
return false;
}
@@ -12022,6 +13577,7 @@ bool Unit::IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura * aura, SpellEntry co
}
return roll_chance_f(chance);
}
+
bool Unit::HandleAuraRaidProcFromChargeWithValue( AuraEffect* triggeredByAura )
{
// aura can be deleted at casts
@@ -12029,6 +13585,7 @@ bool Unit::HandleAuraRaidProcFromChargeWithValue( AuraEffect* triggeredByAura )
uint32 effIdx = triggeredByAura->GetEffIndex();
int32 heal = triggeredByAura->GetAmount();
uint64 caster_guid = triggeredByAura->GetCasterGUID();
+
//Currently only Prayer Of Mending
if (!(spellProto->SpellFamilyName==SPELLFAMILY_PRIEST && spellProto->SpellFamilyFlags[1] & 0x20))
{
@@ -12037,8 +13594,10 @@ bool Unit::HandleAuraRaidProcFromChargeWithValue( AuraEffect* triggeredByAura )
}
// jumps
int32 jumps = triggeredByAura->GetParentAura()->GetAuraCharges()-1;
+
// current aura expire
triggeredByAura->GetParentAura()->SetAuraCharges(1); // will removed at next charges decrease
+
// next target selection
if(jumps > 0 && IS_PLAYER_GUID(caster_guid))
{
@@ -12047,9 +13606,11 @@ bool Unit::HandleAuraRaidProcFromChargeWithValue( AuraEffect* triggeredByAura )
radius = GetSpellRadiusForTarget(triggeredByAura->GetCaster(), sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
else
radius = GetSpellMaxRangeForTarget(triggeredByAura->GetCaster() ,sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
+
if(Player* caster = ((Player*)triggeredByAura->GetCaster()))
{
caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
+
if (Unit* target= GetNextRandomRaidMemberOrPet(radius))
{
CastCustomSpell(target,spellProto->Id,&heal,NULL,NULL,true,NULL,triggeredByAura,caster->GetGUID());
@@ -12059,6 +13620,7 @@ bool Unit::HandleAuraRaidProcFromChargeWithValue( AuraEffect* triggeredByAura )
}
}
}
+
// heal
CastCustomSpell(this,33110,&heal,NULL,NULL,true,NULL,NULL,caster_guid);
return true;
@@ -12067,6 +13629,7 @@ bool Unit::HandleAuraRaidProcFromCharge( AuraEffect* triggeredByAura )
{
// aura can be deleted at casts
SpellEntry const* spellProto = triggeredByAura->GetSpellProto();
+
uint32 damageSpellId;
switch(spellProto->Id)
{
@@ -12084,12 +13647,16 @@ bool Unit::HandleAuraRaidProcFromCharge( AuraEffect* triggeredByAura )
sLog.outError("Unit::HandleAuraRaidProcFromCharge, received not handled spell: %u", spellProto->Id);
return false;
}
+
uint64 caster_guid = triggeredByAura->GetCasterGUID();
uint32 effIdx = triggeredByAura->GetEffIndex();
+
// jumps
int32 jumps = triggeredByAura->GetParentAura()->GetAuraCharges()-1;
+
// current aura expire
triggeredByAura->GetParentAura()->SetAuraCharges(1); // will removed at next charges decrease
+
// next target selection
if(jumps > 0 && IS_PLAYER_GUID(caster_guid))
{
@@ -12098,9 +13665,11 @@ bool Unit::HandleAuraRaidProcFromCharge( AuraEffect* triggeredByAura )
radius = GetSpellRadiusForTarget(triggeredByAura->GetCaster(), sSpellRadiusStore.LookupEntry(spellProto->EffectRadiusIndex[effIdx]));
else
radius = GetSpellMaxRangeForTarget(triggeredByAura->GetCaster() ,sSpellRangeStore.LookupEntry(spellProto->rangeIndex));
+
if(Player* caster = ((Player*)triggeredByAura->GetCaster()))
{
caster->ApplySpellMod(spellProto->Id, SPELLMOD_RADIUS, radius,NULL);
+
if (Unit* target= GetNextRandomRaidMemberOrPet(radius))
{
CastSpell(target, spellProto, true,NULL,triggeredByAura,caster_guid);
@@ -12109,32 +13678,42 @@ bool Unit::HandleAuraRaidProcFromCharge( AuraEffect* triggeredByAura )
}
}
}
+
CastSpell(this, damageSpellId, true,NULL,triggeredByAura,caster_guid);
+
return true;
}
/*-----------------------TRINITY-----------------------------*/
+
void Unit::SetToNotify()
{
// it is called somewhere when obj is not in world (crash when log in instance)
if(m_NotifyListPos < 0)
GetMap()->AddUnitToNotify(this);
}
+
void Unit::Kill(Unit *pVictim, bool durabilityLoss)
{
// Prevent killing unit twice (and giving reward from kill twice)
if (!pVictim->GetHealth())
return;
+
// Inform pets (if any) when player kills target)
if (this->GetTypeId() == TYPEID_PLAYER && ((Player*)this)->GetPet())
{
Pet *pPet = ((Player *)this)->GetPet();
+
if (pPet && pPet->isAlive() && pPet->isControlled())
pPet->AI()->KilledUnit(pVictim);
}
+
//sLog.outError("%u kill %u", GetEntry(), pVictim->GetEntry());
+
pVictim->SetHealth(0);
+
// find player: owner of controlled `this` or `this` itself maybe
Player *player = GetCharmerOrOwnerPlayerOrPlayerItself();
+
bool bRewardIsAllowed = true;
if(pVictim->GetTypeId() == TYPEID_UNIT)
{
@@ -12142,6 +13721,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
if(!bRewardIsAllowed)
((Creature*)pVictim)->SetLootRecipient(NULL);
}
+
if(bRewardIsAllowed && pVictim->GetTypeId() == TYPEID_UNIT && ((Creature*)pVictim)->GetLootRecipient())
player = ((Creature*)pVictim)->GetLootRecipient();
// Reward player, his pets, and group/raid members
@@ -12153,8 +13733,10 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
else
player->ProcDamageAndSpell(pVictim, PROC_FLAG_NONE, PROC_FLAG_KILLED,PROC_EX_NONE, 0);
}
+
// Proc auras on death - must be before aura/combat remove
pVictim->ProcDamageAndSpell(NULL, PROC_FLAG_DEATH, PROC_FLAG_NONE, PROC_EX_NONE, 0, BASE_ATTACK, 0);
+
// if talent known but not triggered (check priest class for speedup check)
bool SpiritOfRedemption = false;
if(pVictim->GetTypeId()==TYPEID_PLAYER && pVictim->getClass()==CLASS_PRIEST)
@@ -12172,6 +13754,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
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,*itr);
SpiritOfRedemption = true;
@@ -12179,11 +13762,13 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
}
}
}
+
if(!SpiritOfRedemption)
{
DEBUG_LOG("SET JUST_DIED");
pVictim->setDeathState(JUST_DIED);
}
+
// 10% durability loss on death
// clean InHateListOf
if (pVictim->GetTypeId() == TYPEID_PLAYER)
@@ -12191,6 +13776,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
// remember victim PvP death for corpse type and corpse reclaim delay
// at original death (not at SpiritOfRedemtionTalent timeout)
((Player*)pVictim)->SetPvPDeath(player!=NULL);
+
// only if not player and not controlled by player pet. And not at BG
if ( (durabilityLoss && !player && !((Player*)pVictim)->InBattleGround()) || ( player && sWorld.getConfig(CONFIG_DURABILITY_LOSS_IN_PVP) ) )
{
@@ -12203,6 +13789,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
// Call KilledUnit for creatures
if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsAIEnabled)
((Creature*)this)->AI()->KilledUnit(pVictim);
+
// last damage from non duel opponent or opponent controlled creature
if(((Player*)pVictim)->duel)
{
@@ -12215,6 +13802,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
{
DEBUG_LOG("DealDamageNotPlayer");
Creature *cVictim = (Creature*)pVictim;
+
if(!cVictim->isPet())
{
cVictim->DeleteThreatList();
@@ -12222,18 +13810,22 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
if (cInfo && (cInfo->lootid || cInfo->maxgold > 0))
cVictim->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
}
+
// Call KilledUnit for creatures, this needs to be called after the lootable flag is set
if (GetTypeId() == TYPEID_UNIT && ((Creature*)this)->IsAIEnabled)
((Creature*)this)->AI()->KilledUnit(pVictim);
+
// Call creature just died function
if (cVictim->IsAIEnabled)
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())
@@ -12252,14 +13844,17 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
}
}
}
+
// outdoor pvp things, do these after setting the death state, else the player activity notify won't work... doh...
// handle player kill only if not suicide (spirit of redemption for example)
if(player && this != pVictim)
if(OutdoorPvP * pvp = player->GetOutdoorPvP())
pvp->HandleKill(player, pVictim);
+
//if(pVictim->GetTypeId() == TYPEID_PLAYER)
// if(OutdoorPvP * pvp = ((Player*)pVictim)->GetOutdoorPvP())
// pvp->HandlePlayerActivityChanged((Player*)pVictim);
+
// battleground things (do this at the end, so the death state flag will be properly set to handle in the bg->handlekill)
if(player && player->InBattleGround())
{
@@ -12271,6 +13866,7 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
bg->HandleKillUnit((Creature*)pVictim, player);
}
}
+
// achievement stuff
if (pVictim->GetTypeId() == TYPEID_PLAYER)
{
@@ -12280,13 +13876,16 @@ void Unit::Kill(Unit *pVictim, bool durabilityLoss)
((Player*)pVictim)->GetAchievementMgr().UpdateAchievementCriteria(ACHIEVEMENT_CRITERIA_TYPE_KILLED_BY_PLAYER, 1, ((Player*)this)->GetTeam());
}
}
+
void Unit::SetControlled(bool apply, UnitState state)
{
if(apply)
{
if(hasUnitState(state))
return;
+
addUnitState(state);
+
switch(state)
{
case UNIT_STAT_STUNNED:
@@ -12322,13 +13921,16 @@ void Unit::SetControlled(bool apply, UnitState state)
else SetFeared(false); break;
default: return;
}
+
clearUnitState(state);
+
if(hasUnitState(UNIT_STAT_STUNNED))
SetStunned(true);
else
{
if(hasUnitState(UNIT_STAT_ROOT))
SetRooted(true);
+
if(hasUnitState(UNIT_STAT_CONFUSED))
SetConfused(true);
else if(hasUnitState(UNIT_STAT_FLEEING))
@@ -12336,6 +13938,7 @@ void Unit::SetControlled(bool apply, UnitState state)
}
}
}
+
void Unit::SetStunned(bool apply)
{
if(apply)
@@ -12344,11 +13947,13 @@ void Unit::SetStunned(bool apply)
SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
CastStop();
AddUnitMovementFlag(MOVEMENTFLAG_ROOT);
+
// Creature specific
if(GetTypeId() != TYPEID_PLAYER)
((Creature*)this)->StopMoving();
else
SetStandState(UNIT_STAND_STATE_STAND);
+
WorldPacket data(SMSG_FORCE_MOVE_ROOT, 8);
data.append(GetPackGUID());
data << uint32(0);
@@ -12358,29 +13963,35 @@ void Unit::SetStunned(bool apply)
{
if(isAlive() && getVictim())
SetUInt64Value(UNIT_FIELD_TARGET, getVictim()->GetGUID());
+
// don't remove UNIT_FLAG_STUNNED for pet when owner is mounted (disabled pet's interface)
Unit *pOwner = GetOwner();
if(!pOwner || (pOwner->GetTypeId() == TYPEID_PLAYER && !((Player *)pOwner)->IsMounted()))
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_STUNNED);
+
if(!hasUnitState(UNIT_STAT_ROOT)) // prevent allow move if have also root effect
{
WorldPacket data(SMSG_FORCE_MOVE_UNROOT, 8+4);
data.append(GetPackGUID());
data << uint32(0);
SendMessageToSet(&data,true);
+
RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT);
}
}
}
+
void Unit::SetRooted(bool apply)
{
if(apply)
{
AddUnitMovementFlag(MOVEMENTFLAG_ROOT);
+
WorldPacket data(SMSG_FORCE_MOVE_ROOT, 10);
data.append(GetPackGUID());
data << (uint32)2;
SendMessageToSet(&data,true);
+
if(GetTypeId() != TYPEID_PLAYER)
((Creature *)this)->StopMoving();
}
@@ -12392,15 +14003,18 @@ void Unit::SetRooted(bool apply)
data.append(GetPackGUID());
data << (uint32)2;
SendMessageToSet(&data,true);
+
RemoveUnitMovementFlag(MOVEMENTFLAG_ROOT);
}
}
}
+
void Unit::SetFeared(bool apply)
{
if(apply)
{
SetUInt64Value(UNIT_FIELD_TARGET, 0);
+
Unit *caster = NULL;
Unit::AuraEffectList const& fearAuras = GetAurasByType(SPELL_AURA_MOD_FEAR);
if(!fearAuras.empty())
@@ -12419,9 +14033,11 @@ void Unit::SetFeared(bool apply)
SetUInt64Value(UNIT_FIELD_TARGET, getVictim()->GetGUID());
}
}
+
if (GetTypeId() == TYPEID_PLAYER)
((Player*)this)->SetClientControl(this, !apply);
}
+
void Unit::SetConfused(bool apply)
{
if(apply)
@@ -12439,58 +14055,72 @@ void Unit::SetConfused(bool apply)
SetUInt64Value(UNIT_FIELD_TARGET, getVictim()->GetGUID());
}
}
+
if(GetTypeId() == TYPEID_PLAYER)
((Player*)this)->SetClientControl(this, !apply);
}
+
bool Unit::SetCharmedBy(Unit* charmer, CharmType type)
{
if(!charmer)
return false;
+
assert(type != CHARM_TYPE_POSSESS || charmer->GetTypeId() == TYPEID_PLAYER);
assert((type == CHARM_TYPE_VEHICLE) == IsVehicle());
+
sLog.outDebug("SetCharmedBy: charmer %u, charmed %u, type %u.", charmer->GetEntry(), GetEntry(), (uint32)type);
+
if(this == charmer)
{
sLog.outCrash("Unit::SetCharmedBy: Unit %u is trying to charm itself!", GetEntry());
return false;
}
+
//if(hasUnitState(UNIT_STAT_UNATTACKABLE))
// return false;
+
if(GetTypeId() == TYPEID_PLAYER && ((Player*)this)->GetTransport())
{
sLog.outCrash("Unit::SetCharmedBy: Player on transport is trying to charm %u", GetEntry());
return false;
}
+
// Already charmed
if(GetCharmerGUID())
{
sLog.outCrash("Unit::SetCharmedBy: %u has already been charmed but %u is trying to charm it!", GetEntry(), charmer->GetEntry());
return false;
}
+
CastStop();
CombatStop(); //TODO: CombatStop(true) may cause crash (interrupt spells)
DeleteThreatList();
+
// Charmer stop charming
if(charmer->GetTypeId() == TYPEID_PLAYER)
{
((Player*)charmer)->StopCastingCharm();
((Player*)charmer)->StopCastingBindSight();
}
+
// Charmed stop charming
if(GetTypeId() == TYPEID_PLAYER)
{
((Player*)this)->StopCastingCharm();
((Player*)this)->StopCastingBindSight();
}
+
// StopCastingCharm may remove a possessed pet?
if(!IsInWorld())
{
sLog.outCrash("Unit::SetCharmedBy: %u is not in world but %u is trying to charm it!", GetEntry(), charmer->GetEntry());
return false;
}
+
// Set charmed
setFaction(charmer->getFaction());
charmer->SetCharm(this, true);
+
if(GetTypeId() == TYPEID_UNIT)
{
((Creature*)this)->AI()->OnCharmed(true);
@@ -12502,6 +14132,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type)
((Player*)this)->ToggleAFK();
((Player*)this)->SetClientControl(this, 0);
}
+
// Pets already have a properly initialized CharmInfo, don't overwrite it.
if(type != CHARM_TYPE_VEHICLE && !GetCharmInfo())
{
@@ -12511,6 +14142,7 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type)
else
charmInfo->InitCharmCreateSpells();
}
+
if(charmer->GetTypeId() == TYPEID_PLAYER)
{
switch(type)
@@ -12537,9 +14169,11 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type)
{
//to prevent client crash
SetByteValue(UNIT_FIELD_BYTES_0, 1, (uint8)CLASS_MAGE);
+
//just to enable stat window
if(GetCharmInfo())
GetCharmInfo()->SetPetNumber(objmgr.GeneratePetNumber(), true);
+
//if charmed two demons the same session, the 2nd gets the 1st one's name
SetUInt32Value(UNIT_FIELD_PET_NAME_TIMESTAMP, time(NULL));
}
@@ -12553,10 +14187,12 @@ bool Unit::SetCharmedBy(Unit* charmer, CharmType type)
}
return true;
}
+
void Unit::RemoveCharmedBy(Unit *charmer)
{
if(!isCharmed())
return;
+
if(!charmer)
charmer = GetCharmer();
if(charmer != GetCharmer()) // one aura overrides another?
@@ -12566,6 +14202,7 @@ void Unit::RemoveCharmedBy(Unit *charmer)
// assert(false);
return;
}
+
CharmType type;
if(hasUnitState(UNIT_STAT_POSSESSED))
type = CHARM_TYPE_POSSESS;
@@ -12573,17 +14210,20 @@ void Unit::RemoveCharmedBy(Unit *charmer)
type = CHARM_TYPE_VEHICLE;
else
type = CHARM_TYPE_CHARM;
+
CastStop();
CombatStop(); //TODO: CombatStop(true) may cause crash (interrupt spells)
getHostilRefManager().deleteReferences();
DeleteThreatList();
RestoreFaction();
GetMotionMaster()->InitDefault();
+
if(type == CHARM_TYPE_POSSESS)
{
clearUnitState(UNIT_STAT_POSSESSED);
RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
}
+
if(GetTypeId() == TYPEID_UNIT)
{
((Creature*)this)->AI()->OnCharmed(false);
@@ -12601,12 +14241,16 @@ void Unit::RemoveCharmedBy(Unit *charmer)
}
else
((Player*)this)->SetClientControl(this, 1);
+
// If charmer still exists
if(!charmer)
return;
+
assert(type != CHARM_TYPE_POSSESS || charmer->GetTypeId() == TYPEID_PLAYER);
assert(type != CHARM_TYPE_VEHICLE || GetTypeId() == TYPEID_UNIT && IsVehicle());
+
charmer->SetCharm(this, false);
+
if(charmer->GetTypeId() == TYPEID_PLAYER)
{
switch(type)
@@ -12639,12 +14283,14 @@ void Unit::RemoveCharmedBy(Unit *charmer)
break;
}
}
+
//a guardian should always have charminfo
if(charmer->GetTypeId() == TYPEID_PLAYER && this != charmer->GetFirstControlled())
((Player*)charmer)->SendRemoveControlBar();
else if(GetTypeId() == TYPEID_PLAYER || GetTypeId() == TYPEID_UNIT && !((Creature*)this)->isGuardian())
DeleteCharmInfo();
}
+
void Unit::RestoreFaction()
{
if(GetTypeId() == TYPEID_PLAYER)
@@ -12659,6 +14305,7 @@ void Unit::RestoreFaction()
return;
}
}
+
if(CreatureInfo const *cinfo = ((Creature*)this)->GetCreatureInfo()) // normal creature
{
FactionTemplateEntry const *faction = getFactionTemplateEntry();
@@ -12666,20 +14313,24 @@ void Unit::RestoreFaction()
}
}
}
+
bool Unit::CreateVehicleKit(uint32 id)
{
VehicleEntry const *vehInfo = sVehicleStore.LookupEntry(id);
if(!vehInfo)
return false;
+
m_vehicleKit = new Vehicle(this, vehInfo);
m_updateFlag |= UPDATEFLAG_VEHICLE;
m_unitTypeMask |= UNIT_MASK_VEHICLE;
return true;
}
+
Unit *Unit::GetVehicleBase() const
{
return m_vehicle ? m_vehicle->GetBase() : NULL;
}
+
Creature *Unit::GetVehicleCreatureBase() const
{
Unit *veh = GetVehicleBase();
@@ -12687,47 +14338,57 @@ Creature *Unit::GetVehicleCreatureBase() const
return (Creature*)veh;
return NULL;
}
+
bool Unit::IsInPartyWith(Unit const *unit) const
{
if(this == unit)
return true;
+
const Unit *u1 = GetCharmerOrOwnerOrSelf();
const Unit *u2 = unit->GetCharmerOrOwnerOrSelf();
if(u1 == u2)
return true;
+
if(u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER)
return ((Player*)u1)->IsInSameGroupWith((Player*)u2);
else
return false;
}
+
bool Unit::IsInRaidWith(Unit const *unit) const
{
if(this == unit)
return true;
+
const Unit *u1 = GetCharmerOrOwnerOrSelf();
const Unit *u2 = unit->GetCharmerOrOwnerOrSelf();
if(u1 == u2)
return true;
+
if(u1->GetTypeId() == TYPEID_PLAYER && u2->GetTypeId() == TYPEID_PLAYER)
return ((Player*)u1)->IsInSameRaidWith((Player*)u2);
else
return false;
}
+
void Unit::GetRaidMember(std::list<Unit*> &nearMembers, float radius)
{
Player *owner = GetCharmerOrOwnerPlayerOrPlayerItself();
if(!owner)
return;
+
Group *pGroup = owner->GetGroup();
if(pGroup)
{
for(GroupReference *itr = pGroup->GetFirstMember(); itr != NULL; itr = itr->next())
{
Player* Target = itr->getSource();
+
if( Target && !IsHostileTo(Target) )
{
if(Target->isAlive() && IsWithinDistInMap(Target, radius) )
nearMembers.push_back(Target);
+
if(Guardian* pet = Target->GetGuardianPet())
if(pet->isAlive() && IsWithinDistInMap(pet, radius) )
nearMembers.push_back(pet);
@@ -12743,23 +14404,28 @@ void Unit::GetRaidMember(std::list<Unit*> &nearMembers, float radius)
nearMembers.push_back(pet);
}
}
+
void Unit::GetPartyMemberInDist(std::list<Unit*> &TagUnitMap, float radius)
{
Unit *owner = GetCharmerOrOwnerOrSelf();
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();
+
// IsHostileTo check duel and controlled by enemy
if( Target && Target->GetSubGroup()==subgroup && !IsHostileTo(Target) )
{
if(Target->isAlive() && IsWithinDistInMap(Target, radius) )
TagUnitMap.push_back(Target);
+
if(Guardian* pet = Target->GetGuardianPet())
if(pet->isAlive() && IsWithinDistInMap(pet, radius) )
TagUnitMap.push_back(pet);
@@ -12775,23 +14441,28 @@ void Unit::GetPartyMemberInDist(std::list<Unit*> &TagUnitMap, float radius)
TagUnitMap.push_back(pet);
}
}
+
void Unit::GetPartyMembers(std::list<Unit*> &TagUnitMap)
{
Unit *owner = GetCharmerOrOwnerOrSelf();
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();
+
// IsHostileTo check duel and controlled by enemy
if( Target && Target->GetSubGroup()==subgroup && !IsHostileTo(Target) )
{
if(Target->isAlive() && IsInMap(Target) )
TagUnitMap.push_back(Target);
+
if(Guardian* pet = Target->GetGuardianPet())
if(pet->isAlive() && IsInMap(Target) )
TagUnitMap.push_back(pet);
@@ -12807,6 +14478,7 @@ void Unit::GetPartyMembers(std::list<Unit*> &TagUnitMap)
TagUnitMap.push_back(pet);
}
}
+
void Unit::HandleAuraEffect(AuraEffect * aureff, bool apply)
{
if (aureff->GetParentAura()->IsRemoved())
@@ -12815,6 +14487,7 @@ void Unit::HandleAuraEffect(AuraEffect * aureff, bool apply)
{
if (aureff->IsApplied())
return;
+
aureff->SetApplied(true);
m_modAuras[aureff->GetAuraName()].push_back(aureff);
aureff->ApplyModifier(true, true);
@@ -12823,25 +14496,32 @@ void Unit::HandleAuraEffect(AuraEffect * aureff, bool apply)
{
if (!aureff->IsApplied())
return;
+
aureff->SetApplied(false);
// remove from list before mods removing (prevent cyclic calls, mods added before including to aura list - use reverse order)
m_modAuras[aureff->GetAuraName()].remove(aureff);
aureff->ApplyModifier(false, true);
+
// Remove all triggered by aura spells vs unlimited duration
aureff->CleanupTriggeredSpells();
}
}
+
void Unit::AddAura(uint32 spellId, Unit *target)
{
if(!target || !target->isAlive())
return;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(spellId);
if(!spellInfo)
return;
+
if (target->IsImmunedToSpell(spellInfo))
return;
+
uint8 eff_mask=0;
Unit * source = this;
+
for(uint32 i = 0; i < MAX_SPELL_EFFECTS; ++i)
{
if(spellInfo->Effect[i] == SPELL_EFFECT_APPLY_AURA || IsAreaAuraEffect(spellInfo->Effect[i]))
@@ -12849,17 +14529,21 @@ void Unit::AddAura(uint32 spellId, Unit *target)
// Area auras applied as linked should have target as source (otherwise they'll be removed after first aura update)
if (spellInfo->Effect[i] != SPELL_EFFECT_APPLY_AURA)
source = target;
+
if(target->IsImmunedToSpellEffect(spellInfo, i))
continue;
eff_mask|=1<<i;
}
}
+
if (!eff_mask)
return;
+
// Because source is not give, use caster as source
Aura *Aur = new Aura(spellInfo, eff_mask, target, source, this);
target->AddAura(Aur);
}
+
void Unit::SetAuraStack(uint32 spellId, Unit *target, uint32 stack)
{
Aura *aur = target->GetAura(spellId, GetGUID());
@@ -12871,13 +14555,16 @@ void Unit::SetAuraStack(uint32 spellId, Unit *target, uint32 stack)
if(aur && stack)
aur->SetStackAmount(stack);
}
+
// This function is only used for area aura and creature addon
// it should be removed in the future
Aura * Unit::AddAuraEffect(const SpellEntry * spellInfo, uint8 effIndex, WorldObject *source, Unit* caster, int32 * basePoints)
{
// can't do that for passive auras - they stack from same caster so there is no way to get exact aura which should get effect
//assert (!IsPassiveSpell(spellInfo));
+
sLog.outDebug("AddAuraEffect: spell id: %u, effect index: %u", spellInfo->Id, (uint32)effIndex);
+
Aura *aur = GetAura(spellInfo->Id, caster->GetGUID());
// without this it may crash when shaman refresh totem? source is NULL
if(aur && aur->GetSourceGUID() != source->GetGUID())
@@ -12885,6 +14572,7 @@ Aura * Unit::AddAuraEffect(const SpellEntry * spellInfo, uint8 effIndex, WorldOb
RemoveAura(aur);
aur = NULL;
}
+
if(aur)
{
if(AuraEffect *aurEffect = CreateAuraEffect(aur, effIndex, basePoints))
@@ -12901,17 +14589,20 @@ Aura * Unit::AddAuraEffect(const SpellEntry * spellInfo, uint8 effIndex, WorldOb
}
else
aur = new Aura(spellInfo, 1<<effIndex, this, source, caster, NULL, NULL);
+
if(!AddAura(aur))
return NULL;
}
return aur;
}
+
// 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(const Unit *pVictim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const
{
// 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);
@@ -12923,24 +14614,29 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType,
HitChance = 95.0f;
else
HitChance = 76.0f;
+
// 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(spellId)
{
if(Player *modOwner = GetSpellModOwner())
modOwner->ApplySpellMod(spellId, 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;
int32 diff = -skillDiff;
@@ -12948,6 +14644,7 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType,
miss_chance += diff > 0 ? diff * 0.04 : diff * 0.02;
else
miss_chance += diff > 10 ? 2 + (diff - 10) * 0.4 : diff * 0.1;
+
// Limit miss chance from 0 to 60%
if (miss_chance < 0.0f)
return 0.0f;
@@ -12955,23 +14652,30 @@ float Unit::MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType,
return 60.0f;
return miss_chance;
}
+
void Unit::SetPhaseMask(uint32 newPhaseMask, bool update)
{
if(newPhaseMask==GetPhaseMask())
return;
+
if(IsInWorld())
RemoveNotOwnSingleTargetAuras(newPhaseMask); // we can lost access to caster or target
+
WorldObject::SetPhaseMask(newPhaseMask,update);
+
if(!IsInWorld())
return;
+
for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
if((*itr)->GetTypeId() == TYPEID_UNIT)
(*itr)->SetPhaseMask(newPhaseMask,true);
+
for(uint8 i = 0; i < MAX_SUMMON_SLOT; ++i)
if(m_SummonSlot[i])
if(Creature *summon = GetMap()->GetCreature(m_SummonSlot[i]))
summon->SetPhaseMask(newPhaseMask,true);
}
+
void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ)
{
Player *player = NULL;
@@ -12983,6 +14687,7 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ)
if(player && player->m_mover != this)
player = NULL;
}
+
if(!player)
{
GetMotionMaster()->MoveKnockbackFrom(x, y, speedXY, speedZ);
@@ -12991,6 +14696,7 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ)
{
float vcos, vsin;
GetSinCos(x, y, vsin, vcos);
+
WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
data.append(GetPackGUID());
data << uint32(0); // Sequence
@@ -12998,9 +14704,11 @@ void Unit::KnockbackFrom(float x, float y, float speedXY, float speedZ)
data << float(vsin); // y direction
data << float(speedXY); // Horizontal speed
data << float(-speedZ); // Z Movement speed (vertical)
+
player->GetSession()->SendPacket(&data);
}
}
+
void Unit::JumpTo(float speedXY, float speedZ, bool forward)
{
float angle = forward ? 0 : M_PI;
@@ -13012,6 +14720,7 @@ void Unit::JumpTo(float speedXY, float speedZ, bool forward)
{
float vcos = cos(angle+GetOrientation());
float vsin = sin(angle+GetOrientation());
+
WorldPacket data(SMSG_MOVE_KNOCK_BACK, (8+4+4+4+4+4));
data.append(GetPackGUID());
data << uint32(0); // Sequence
@@ -13019,9 +14728,11 @@ void Unit::JumpTo(float speedXY, float speedZ, bool forward)
data << float(vsin); // y direction
data << float(speedXY); // Horizontal speed
data << float(-speedZ); // Z Movement speed (vertical)
+
((Player*)this)->GetSession()->SendPacket(&data);
}
}
+
void Unit::JumpTo(WorldObject *obj, float speedZ)
{
float x, y, z;
@@ -13029,10 +14740,12 @@ void Unit::JumpTo(WorldObject *obj, float speedZ)
float speedXY = GetExactDist2d(x, y) * 10.0f / speedZ;
GetMotionMaster()->MoveJump(x, y, z, speedXY, speedZ);
}
+
void Unit::EnterVehicle(Vehicle *vehicle, int8 seatId)
{
if(!isAlive() || GetVehicleKit() == vehicle)
return;
+
if(m_vehicle)
{
if(m_vehicle == vehicle)
@@ -13050,6 +14763,7 @@ void Unit::EnterVehicle(Vehicle *vehicle, int8 seatId)
ExitVehicle();
}
}
+
if(GetTypeId() == TYPEID_PLAYER)
{
((Player*)this)->StopCastingCharm();
@@ -13057,6 +14771,7 @@ void Unit::EnterVehicle(Vehicle *vehicle, int8 seatId)
((Player*)this)->Unmount();
((Player*)this)->RemoveAurasByType(SPELL_AURA_MOUNTED);
}
+
assert(!m_vehicle);
m_vehicle = vehicle;
if(!m_vehicle->AddPassenger(this, seatId))
@@ -13064,9 +14779,11 @@ void Unit::EnterVehicle(Vehicle *vehicle, int8 seatId)
m_vehicle = NULL;
return;
}
+
SetControlled(true, UNIT_STAT_ROOT);
//movementInfo is set in AddPassenger
//packets are sent in AddPassenger
+
if(GetTypeId() == TYPEID_PLAYER)
{
//((Player*)this)->SetClientControl(vehicle, 1);
@@ -13074,10 +14791,12 @@ void Unit::EnterVehicle(Vehicle *vehicle, int8 seatId)
((Player*)this)->GetSession()->SendPacket(&data);
}
}
+
void Unit::ChangeSeat(int8 seatId, bool next)
{
if(!m_vehicle)
return;
+
if(seatId < 0)
{
seatId = m_vehicle->GetNextEmptySeat(GetTransSeat(), next);
@@ -13086,14 +14805,17 @@ void Unit::ChangeSeat(int8 seatId, bool next)
}
else if(seatId == GetTransSeat() || !m_vehicle->HasEmptySeat(seatId))
return;
+
m_vehicle->RemovePassenger(this);
if(!m_vehicle->AddPassenger(this, seatId))
assert(false);
}
+
void Unit::ExitVehicle()
{
if(!m_vehicle)
return;
+
Unit *vehicleBase = m_vehicle->GetBase();
const AuraEffectList &modAuras = vehicleBase->GetAurasByType(SPELL_AURA_CONTROL_VEHICLE);
for(AuraEffectList::const_iterator itr = modAuras.begin(); itr != modAuras.end(); ++itr)
@@ -13104,14 +14826,20 @@ void Unit::ExitVehicle()
break; // there should be no case that a vehicle has two auras for one source
}
}
+
if(!m_vehicle)
return;
+
//sLog.outError("exit vehicle");
+
m_vehicle->RemovePassenger(this);
+
// This should be done before dismiss, because there may be some aura removal
Vehicle *vehicle = m_vehicle;
m_vehicle = NULL;
+
SetControlled(false, UNIT_STAT_ROOT);
+
RemoveUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
m_movementInfo.t_x = 0;
m_movementInfo.t_y = 0;
@@ -13119,7 +14847,9 @@ void Unit::ExitVehicle()
m_movementInfo.t_o = 0;
m_movementInfo.t_time = 0;
m_movementInfo.t_seat = 0;
+
Relocate(vehicle->GetBase());
+
//Send leave vehicle, not correct
if(GetTypeId() == TYPEID_PLAYER)
{
@@ -13130,10 +14860,12 @@ void Unit::ExitVehicle()
WorldPacket data;
BuildHeartBeatMsg(&data);
SendMessageToSet(&data, false);
+
if(vehicle->GetBase()->HasUnitTypeMask(UNIT_MASK_MINION))
if(((Minion*)vehicle->GetBase())->GetOwner() == this)
vehicle->Dismiss();
}
+
void Unit::BuildMovementPacket(ByteBuffer *data) const
{
switch(GetTypeId())
@@ -13152,6 +14884,7 @@ void Unit::BuildMovementPacket(ByteBuffer *data) const
}
break;
}
+
*data << uint32(GetUnitMovementFlags()); // movement flags
*data << uint16(m_movementInfo.unk1); // 2.3.0
*data << uint32(getMSTime()); // time
@@ -13159,6 +14892,7 @@ void Unit::BuildMovementPacket(ByteBuffer *data) const
*data << GetPositionY();
*data << GetPositionZ();
*data << GetOrientation();
+
// 0x00000200
if(GetUnitMovementFlags() & MOVEMENTFLAG_ONTRANSPORT)
{
@@ -13178,11 +14912,14 @@ void Unit::BuildMovementPacket(ByteBuffer *data) const
*data << uint32(GetTransTime());
*data << uint8 (GetTransSeat());
}
+
// 0x02200000
if((GetUnitMovementFlags() & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING))
|| (m_movementInfo.unk1 & 0x20))
*data << (float)m_movementInfo.s_pitch;
+
*data << (uint32)m_movementInfo.fallTime;
+
// 0x00001000
if(GetUnitMovementFlags() & MOVEMENTFLAG_JUMPING)
{
@@ -13191,15 +14928,18 @@ void Unit::BuildMovementPacket(ByteBuffer *data) const
*data << (float)m_movementInfo.j_cosAngle;
*data << (float)m_movementInfo.j_xyspeed;
}
+
// 0x04000000
if(GetUnitMovementFlags() & MOVEMENTFLAG_SPLINE)
*data << (float)m_movementInfo.u_unk1;
+
/*if(GetTypeId() == TYPEID_PLAYER)
{
sLog.outString("Send MovementInfo:");
OutMovementInfo();
}*/
}
+
void Unit::OutMovementInfo() const
{
sLog.outString("MovementInfo for %u: Flag %u, Unk1 %u, Time %u, Pos %f %f %f %f, Fall %u", GetEntry(), m_movementInfo.flags, (uint32)m_movementInfo.unk1, m_movementInfo.time, GetPositionX(), GetPositionY(), GetPositionZ(), GetOrientation(), m_movementInfo.fallTime);
@@ -13212,6 +14952,7 @@ void Unit::OutMovementInfo() const
if(m_movementInfo.flags & MOVEMENTFLAG_SPLINE)
sLog.outString("Spline: %f", m_movementInfo.u_unk1);
}
+
void Unit::SetFlying(bool apply)
{
if(apply)
@@ -13225,6 +14966,7 @@ void Unit::SetFlying(bool apply)
RemoveUnitMovementFlag(MOVEMENTFLAG_FLY_MODE + MOVEMENTFLAG_FLYING);
}
}
+
void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool casting /*= false*/ )
{
if(GetTypeId() == TYPEID_PLAYER)
@@ -13244,10 +14986,13 @@ void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool ca
data << uint32 (0);
// Other information here: jumping angle etc
SendMessageToSet(&data, false);*/
+
// FIXME: this interrupts spell visual
DestroyForNearbyPlayers();
+
GetMap()->CreatureRelocation((Creature*)this, x, y, z, orientation);
//ObjectAccessor::UpdateObjectVisibility(this);
+
//WorldPacket data;
// Work strange for many spells: triggered active mover set for targeted player to creature
//BuildTeleportAckMsg(&data, x, y, z, orientation);
@@ -13255,6 +15000,7 @@ void Unit::NearTeleportTo( float x, float y, float z, float orientation, bool ca
//SendMessageToSet(&data, false);
}
}
+
void Unit::SendThreatListUpdate()
{
if (uint32 count = getThreatManager().getThreatList().size())
@@ -13273,6 +15019,7 @@ void Unit::SendThreatListUpdate()
}
}
+
void Unit::SendChangeCurrentVictimOpcode(HostilReference* pHostilReference)
{
if (uint32 count = getThreatManager().getThreatList().size())
@@ -13291,6 +15038,7 @@ void Unit::SendChangeCurrentVictimOpcode(HostilReference* pHostilReference)
SendMessageToSet(&data, false);
}
}
+
void Unit::SendClearThreatListOpcode()
{
sLog.outDebug( "WORLD: Send SMSG_THREAT_CLEAR Message" );
@@ -13298,6 +15046,7 @@ void Unit::SendClearThreatListOpcode()
data.append(GetPackGUID());
SendMessageToSet(&data, false);
}
+
void Unit::SendRemoveFromThreatListOpcode(HostilReference* pHostilReference)
{
sLog.outDebug( "WORLD: Send SMSG_THREAT_REMOVE Message" );
@@ -13306,47 +15055,60 @@ void Unit::SendRemoveFromThreatListOpcode(HostilReference* pHostilReference)
data.appendPackGUID(pHostilReference->getUnitGuid());
SendMessageToSet(&data, false);
}
+
void Unit::RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker )
{
float addRage;
+
float rageconversion = ((0.0091107836 * getLevel()*getLevel())+3.225598133*getLevel())+4.2652911;
+
// Unknown if correct, but lineary adjust rage conversion above level 70
if (getLevel() > 70)
rageconversion += 13.27f*(getLevel()-70);
+
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))
addRage *= 2.0;
}
+
addRage *= sWorld.getRate(RATE_POWER_RAGE_INCOME);
+
ModifyPower(POWER_RAGE, uint32(addRage*10));
}
+
void Unit::OutDebugInfo() const
{
sLog.outError("Unit::OutDebugInfo");
sLog.outString("GUID "UI64FMTD", entry %u, type %u, name %s", GetGUID(), GetEntry(), (uint32)GetTypeId(), GetName());
sLog.outString("OwnerGUID "UI64FMTD", MinionGUID "UI64FMTD", CharmerGUID "UI64FMTD", CharmedGUID "UI64FMTD, GetOwnerGUID(), GetMinionGUID(), GetCharmerGUID(), GetCharmGUID());
sLog.outString("In world %u, unit type mask %u", (uint32)(IsInWorld() ? 1 : 0), m_unitTypeMask);
+
sLog.outStringInLine("Summon Slot: ");
for(uint32 i = 0; i < MAX_SUMMON_SLOT; ++i)
sLog.outStringInLine(UI64FMTD", ", m_SummonSlot[i]);
sLog.outString();
+
sLog.outStringInLine("Controlled List: ");
for(ControlList::const_iterator itr = m_Controlled.begin(); itr != m_Controlled.end(); ++itr)
sLog.outStringInLine(UI64FMTD", ", (*itr)->GetGUID());
sLog.outString();
+
sLog.outStringInLine("Aura List: ");
for(AuraMap::const_iterator itr = m_Auras.begin(); itr != m_Auras.end(); ++itr)
sLog.outStringInLine("%u, ", itr->first);
sLog.outString();
+
if(IsVehicle())
{
sLog.outStringInLine("Passenger List: ");
@@ -13355,48 +15117,59 @@ void Unit::OutDebugInfo() const
sLog.outStringInLine(UI64FMTD", ", passenger->GetGUID());
sLog.outString();
}
+
if(GetVehicle())
sLog.outString("On vehicle %u.", GetVehicleBase()->GetEntry());
}
+
// MrSmite 09-05-2009 PetAI_v1.0
void CharmInfo::SetIsCommandAttack(bool val)
{
m_isCommandAttack = val;
}
+
bool CharmInfo::IsCommandAttack()
{
return m_isCommandAttack;
}
+
void CharmInfo::SaveStayPosition()
{
m_unit->GetPosition(m_stayX, m_stayY, m_stayZ);
}
+
void CharmInfo::GetStayPosition(float &x, float &y, float &z)
{
x = m_stayX;
y = m_stayY;
z = m_stayZ;
}
+
void CharmInfo::SetIsAtStay(bool val)
{
m_isAtStay = val;
}
+
bool CharmInfo::IsAtStay()
{
return m_isAtStay;
}
+
void CharmInfo::SetIsFollowing(bool val)
{
m_isFollowing = val;
}
+
bool CharmInfo::IsFollowing()
{
return m_isFollowing;
}
+
void CharmInfo::SetIsReturning(bool val)
{
m_isReturning = val;
}
+
bool CharmInfo::IsReturning()
{
return m_isReturning;
diff --git a/src/game/Unit.h b/src/game/Unit.h
index 7785e0ee0e9..efe615d5c17 100644
--- a/src/game/Unit.h
+++ b/src/game/Unit.h
@@ -17,8 +17,10 @@
* 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"
@@ -34,7 +36,9 @@
#include "DBCStructure.h"
#include <list>
+
#define WORLD_TRIGGER 12999
+
enum SpellInterruptFlags
{
SPELL_INTERRUPT_FLAG_MOVEMENT = 0x01, // why need this for instant?
@@ -44,6 +48,7 @@ enum SpellInterruptFlags
SPELL_INTERRUPT_FLAG_ABORT_ON_DMG = 0x10, // _complete_ interrupt on direct damage
//SPELL_INTERRUPT_UNK = 0x20 // unk, 564 of 727 spells having this spell start with "Glyph"
};
+
enum SpellChannelInterruptFlags
{
CHANNEL_FLAG_DAMAGE = 0x0002,
@@ -53,6 +58,7 @@ enum SpellChannelInterruptFlags
CHANNEL_FLAG_ONLY_IN_WATER = 0x0100,
CHANNEL_FLAG_DELAY = 0x4000
};
+
enum SpellAuraInterruptFlags
{
AURA_INTERRUPT_FLAG_HITBYSPELL = 0x00000001, // 0 removed when getting hit by a negative spell?
@@ -80,8 +86,10 @@ enum SpellAuraInterruptFlags
AURA_INTERRUPT_FLAG_TELEPORTED = 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
+
AURA_INTERRUPT_FLAG_NOT_VICTIM = (AURA_INTERRUPT_FLAG_HITBYSPELL | AURA_INTERRUPT_FLAG_TAKE_DAMAGE | AURA_INTERRUPT_FLAG_DIRECT_DAMAGE),
};
+
enum SpellModOp
{
SPELLMOD_DAMAGE = 0,
@@ -116,7 +124,9 @@ enum SpellModOp
SPELLMOD_CRIT_DAMAGE_BONUS_2 = 29, //one not used spell
SPELLMOD_SPELL_COST_REFUND_ON_FAIL = 30
};
+
#define MAX_SPELLMOD 32
+
enum SpellValueMod
{
SPELLVALUE_BASE_POINT0,
@@ -125,6 +135,7 @@ enum SpellValueMod
SPELLVALUE_RADIUS_MOD,
SPELLVALUE_MAX_TARGETS,
};
+
typedef std::pair<SpellValueMod, int32> CustomSpellValueMod;
class CustomSpellValues : public std::vector<CustomSpellValueMod>
{
@@ -134,13 +145,16 @@ class CustomSpellValues : public std::vector<CustomSpellValueMod>
push_back(std::make_pair(mod, value));
}
};
+
enum SpellFacingFlags
{
SPELL_FACING_FLAG_INFRONT = 0x0001
};
+
#define BASE_MINDAMAGE 1.0f
#define BASE_MAXDAMAGE 2.0f
#define BASE_ATTACK_TIME 2000
+
// byte value (UNIT_FIELD_BYTES_1,0)
enum UnitStandStateType
{
@@ -154,12 +168,14 @@ enum UnitStandStateType
UNIT_STAND_STATE_DEAD = 7,
UNIT_STAND_STATE_KNEEL = 8
};
+
// byte flag value (UNIT_FIELD_BYTES_1,2)
enum UnitStandFlags
{
UNIT_STAND_FLAGS_CREEP = 0x02,
UNIT_STAND_FLAGS_ALL = 0xFF
};
+
// byte flags value (UNIT_FIELD_BYTES_1,3)
enum UnitBytes1_Flags
{
@@ -167,6 +183,7 @@ enum UnitBytes1_Flags
UNIT_BYTE1_FLAG_UNTRACKABLE = 0x04,
UNIT_BYTE1_FLAG_ALL = 0xFF
};
+
// high byte (3 from 0..3) of UNIT_FIELD_BYTES_2
enum ShapeshiftForm
{
@@ -197,6 +214,7 @@ enum ShapeshiftForm
FORM_MOONKIN = 0x1F,
FORM_SPIRITOFREDEMPTION = 0x20
};
+
// low byte ( 0 from 0..3 ) of UNIT_FIELD_BYTES_2
enum SheathState
{
@@ -204,7 +222,9 @@ enum SheathState
SHEATH_STATE_MELEE = 1, // prepared melee weapon
SHEATH_STATE_RANGED = 2 // prepared ranged weapon
};
+
#define MAX_SHEATH_STATE 3
+
// byte (1 from 0..3) of UNIT_FIELD_BYTES_2
enum UnitBytes2_Flags
{
@@ -217,23 +237,27 @@ enum UnitBytes2_Flags
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 8
#define MAX_SPELL_CHARM 4
#define MAX_SPELL_VEHICLE 6
#define MAX_SPELL_POSSESS 8
#define MAX_SPELL_CONTROL_BAR 10
+
enum Swing
{
NOSWING = 0,
SINGLEHANDEDSWING = 1,
TWOHANDEDSWING = 2
};
+
enum VictimState
{
VICTIMSTATE_UNKNOWN1 = 0,
@@ -246,6 +270,7 @@ enum VictimState
VICTIMSTATE_IS_IMMUNE = 7,
VICTIMSTATE_DEFLECTS = 8
};
+
enum HitInfo
{
HITINFO_NORMALSWING = 0x00000000,
@@ -274,15 +299,18 @@ enum HitInfo
// 0x00400000
HITINFO_UNK3 = 0x00800000
};
+
//i would like to remove this: (it is defined in item.h
enum InventorySlot
{
NULL_BAG = 0,
NULL_SLOT = 255
};
+
struct FactionTemplateEntry;
struct SpellEntry;
struct SpellValue;
+
class Aura;
class AuraEffect;
class Creature;
@@ -298,12 +326,15 @@ class Guardian;
class UnitAI;
class Transport;
class Vehicle;
+
struct SpellImmune
{
uint32 type;
uint32 spellId;
};
+
typedef std::list<SpellImmune> SpellImmuneList;
+
enum UnitModifierType
{
BASE_VALUE = 0,
@@ -312,17 +343,20 @@ enum UnitModifierType
TOTAL_PCT = 3,
MODIFIER_TYPE_END = 4
};
+
enum WeaponDamageRange
{
MINDAMAGE,
MAXDAMAGE
};
+
enum DamageTypeToSchool
{
RESISTANCE,
DAMAGE_DEALT,
DAMAGE_TAKEN
};
+
enum AuraRemoveMode
{
AURA_REMOVE_BY_DEFAULT=0, // scripted remove, remove by stack with aura with different ids and sc aura remove
@@ -332,6 +366,7 @@ enum AuraRemoveMode
AURA_REMOVE_BY_EXPIRE, // dispel and absorb aura destroy
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.
@@ -368,6 +403,7 @@ enum UnitMods
UNIT_MOD_POWER_START = UNIT_MOD_MANA,
UNIT_MOD_POWER_END = UNIT_MOD_RUNIC_POWER + 1
};
+
enum BaseModGroup
{
CRIT_PERCENTAGE,
@@ -376,12 +412,15 @@ enum BaseModGroup
SHIELD_BLOCK_VALUE,
BASEMOD_END
};
+
enum BaseModType
{
FLAT_MOD,
PCT_MOD
};
+
#define MOD_END (PCT_MOD+1)
+
enum DeathState
{
ALIVE = 0,
@@ -391,6 +430,7 @@ enum DeathState
JUST_ALIVED = 4,
DEAD_FALLING= 5
};
+
enum UnitState
{
UNIT_STAT_DIED = 0x00000001,
@@ -425,6 +465,7 @@ enum UnitState
UNIT_STAT_CANNOT_TURN = (UNIT_STAT_LOST_CONTROL | UNIT_STAT_ROTATING),
UNIT_STAT_ALL_STATE = 0xffffffff //(UNIT_STAT_STOPPED | UNIT_STAT_MOVING | UNIT_STAT_IN_COMBAT | UNIT_STAT_IN_FLIGHT)
};
+
enum UnitMoveType
{
MOVE_WALK = 0,
@@ -437,9 +478,12 @@ enum UnitMoveType
MOVE_FLIGHT_BACK = 7,
MOVE_PITCH_RATE = 8
};
+
#define MAX_MOVE_TYPE 9
+
extern float baseMoveSpeed[MAX_MOVE_TYPE];
extern float playerBaseMoveSpeed[MAX_MOVE_TYPE];
+
enum WeaponAttackType
{
BASE_ATTACK = 0,
@@ -447,6 +491,7 @@ enum WeaponAttackType
RANGED_ATTACK = 2,
MAX_ATTACK
};
+
enum CombatRating
{
CR_WEAPON_SKILL = 0,
@@ -475,7 +520,9 @@ enum CombatRating
CR_EXPERTISE = 23,
CR_ARMOR_PENETRATION = 24
};
+
#define MAX_COMBAT_RATING 25
+
enum DamageEffectType
{
DIRECT_DAMAGE = 0, // used for normal weapon damage (not for class abilities or spells)
@@ -485,6 +532,7 @@ enum DamageEffectType
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
@@ -494,6 +542,7 @@ enum UnitVisibility
//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
{
@@ -530,6 +579,7 @@ enum UnitFlags
UNIT_FLAG_SHEATHE = 0x40000000,
UNIT_FLAG_UNK_31 = 0x80000000
};
+
// Value masks for UNIT_FIELD_FLAGS_2
enum UnitFlags2
{
@@ -542,6 +592,7 @@ enum UnitFlags2
UNIT_FLAG2_DISARM_RANGED = 0x00000400, //this does not disable ranged weapon display (maybe additional flag needed?)
UNIT_FLAG2_REGENERATE_POWER = 0x00000800
};
+
/// Non Player Character flags
enum NPCFlags
{
@@ -574,6 +625,7 @@ enum NPCFlags
UNIT_NPC_FLAG_GUARD = 0x10000000, // custom flag for guards
UNIT_NPC_FLAG_OUTDOORPVP = 0x20000000, // custom flag for outdoor pvp creatures
};
+
enum MoveFlags
{
MOVEFLAG_JUMP = 0x00000800,
@@ -582,6 +634,7 @@ enum MoveFlags
MOVEFLAG_GLIDE = 0x00003000, // dragon
MOVEFLAG_ENTER_TRANSPORT = 0x00800000,
};
+
enum MovementFlags
{
MOVEMENTFLAG_NONE = 0x00000000,
@@ -611,14 +664,16 @@ enum MovementFlags
MOVEMENTFLAG_WATERWALKING = 0x10000000, // prevent unit from falling through water
MOVEMENTFLAG_SAFE_FALL = 0x20000000, // active rogue safe fall spell (passive)
MOVEMENTFLAG_HOVER = 0x40000000, // hover, cannot jump
+
MOVEMENTFLAG_MOVING =
MOVEMENTFLAG_FORWARD |MOVEMENTFLAG_BACKWARD |MOVEMENTFLAG_STRAFE_LEFT|MOVEMENTFLAG_STRAFE_RIGHT|
MOVEMENTFLAG_PITCH_UP|MOVEMENTFLAG_PITCH_DOWN|MOVEMENTFLAG_FALL_DAMAGE|
MOVEMENTFLAG_JUMPING |MOVEMENTFLAG_FALLING |MOVEMENTFLAG_ASCEND |
MOVEMENTFLAG_SPLINE,
MOVEMENTFLAG_TURNING =
- MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT,
+ MOVEMENTFLAG_LEFT | MOVEMENTFLAG_RIGHT,
};
+
/*
enum MonsterMovementFlags
{
@@ -651,10 +706,12 @@ enum MonsterMovementFlags
MONSTER_MOVE_UNK11 = 0x10000000, // run
MONSTER_MOVE_UNK12 = 0x20000000, // run
MONSTER_MOVE_UNK13 = 0x40000000, // levitating
+
// masks
MONSTER_MOVE_SPLINE_FLY = 0x00003000, // fly by points
};
*/
+
struct MovementInfo
{
// common
@@ -675,6 +732,7 @@ struct MovementInfo
float j_zspeed, j_sinAngle, j_cosAngle, j_xyspeed;
// spline
float u_unk1;
+
MovementInfo()
{
flags = 0;
@@ -683,10 +741,12 @@ struct MovementInfo
x = y = z = o = t_x = t_y = t_z = t_o = s_pitch = j_zspeed = j_sinAngle = j_cosAngle = j_xyspeed = u_unk1 = 0.0f;
t_guid = 0;
}
+
uint32 GetMovementFlags() { return flags; }
void AddMovementFlag(uint32 flag) { flags |= flag; }
bool HasMovementFlag(uint32 flag) const { return flags & flag; }
};
+
enum UnitTypeMask
{
UNIT_MASK_NONE = 0x00000000,
@@ -701,6 +761,7 @@ enum UnitTypeMask
UNIT_MASK_CONTROLABLE_GUARDIAN = 0x00000100,
UNIT_MASK_ACCESSORY = 0x00000200,
};
+
enum DiminishingLevels
{
DIMINISHING_LEVEL_1 = 0,
@@ -708,30 +769,37 @@ enum DiminishingLevels
DIMINISHING_LEVEL_3 = 2,
DIMINISHING_LEVEL_IMMUNE = 3
};
+
struct DiminishingReturn
{
DiminishingReturn(DiminishingGroup group, uint32 t, uint32 count)
: DRGroup(group), stack(0), hitTime(t), hitCount(count)
{}
+
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
};
+
struct CleanDamage
{
CleanDamage(uint32 mitigated, uint32 absorbed, WeaponAttackType _attackType, MeleeHitOutcome _hitOutCome) :
mitigated_damage(mitigated), absorbed_damage(absorbed), attackType(_attackType), hitOutCome(_hitOutCome) {}
+
uint32 absorbed_damage;
uint32 mitigated_damage;
+
WeaponAttackType attackType;
MeleeHitOutcome hitOutCome;
};
+
// Struct for use in Unit::CalculateMeleeDamage
// Need create structure like in SMSG_ATTACKERSTATEUPDATE opcode
struct CalcDamageInfo
@@ -753,12 +821,14 @@ struct CalcDamageInfo
uint32 cleanDamage; // Used only for rage calculation
MeleeHitOutcome hitOutCome; // TODO: remove this field (need use TargetState)
};
+
// Spell damage info structure based on structure sending in SMSG_SPELLNONMELEEDAMAGELOG opcode
struct SpellNonMeleeDamage{
SpellNonMeleeDamage(Unit *_attacker, Unit *_target, uint32 _SpellID, uint32 _schoolMask)
: target(_target), attacker(_attacker), SpellID(_SpellID), damage(0), overkill(0), schoolMask(_schoolMask),
absorb(0), resist(0), physicalLog(false), unused(false), blocked(0), HitInfo(0), cleanDamage(0)
{}
+
Unit *target;
Unit *attacker;
uint32 SpellID;
@@ -774,10 +844,12 @@ struct SpellNonMeleeDamage{
// Used for help
uint32 cleanDamage;
};
+
struct SpellPeriodicAuraLogInfo
{
SpellPeriodicAuraLogInfo(AuraEffect *_auraEff, uint32 _damage, uint32 _overDamage, uint32 _absorb, uint32 _resist, float _multiplier, bool _critical)
: auraEff(_auraEff), damage(_damage), overDamage(_overDamage), absorb(_absorb), resist(_resist), multiplier(_multiplier), critical(_critical){}
+
AuraEffect *auraEff;
uint32 damage;
uint32 overDamage; // overkill/overheal
@@ -786,6 +858,7 @@ struct SpellPeriodicAuraLogInfo
float multiplier;
bool critical;
};
+
struct TriggeredSpellInfo
{
TriggeredSpellInfo(uint32 _spell, Unit *_source, Unit *_target, int32 _amount = 0, AuraEffect *_auraEff = NULL)
@@ -795,13 +868,18 @@ struct TriggeredSpellInfo
Unit *source, *target;
AuraEffect *auraEff;
};
+
typedef std::vector<TriggeredSpellInfo> TriggeredSpellInfoVct;
+
uint32 createProcExtendMask(SpellNonMeleeDamage *damageInfo, SpellMissInfo missCondition);
+
#define MAX_DECLINED_NAME_CASES 5
+
struct DeclinedName
{
std::string name[MAX_DECLINED_NAME_CASES];
};
+
enum CurrentSpellTypes
{
CURRENT_MELEE_SPELL = 0,
@@ -809,9 +887,11 @@ enum CurrentSpellTypes
CURRENT_AUTOREPEAT_SPELL = 2,
CURRENT_CHANNELED_SPELL = 3
};
+
#define CURRENT_FIRST_NON_MELEE_SPELL 1
#define CURRENT_MAX_SPELL 4
+
enum ActiveStates
{
ACT_PASSIVE = 0x01, // 0x01 - passive
@@ -821,12 +901,14 @@ enum ActiveStates
ACT_REACTION = 0x06, // 0x02 | 0x04
ACT_DECIDE = 0x00 // custom
};
+
enum ReactStates
{
REACT_PASSIVE = 0,
REACT_DEFENSIVE = 1,
REACT_AGGRESSIVE = 2
};
+
enum CommandStates
{
COMMAND_STAY = 0,
@@ -834,14 +916,18 @@ enum CommandStates
COMMAND_ATTACK = 2,
COMMAND_ABANDON = 3
};
+
#define UNIT_ACTION_BUTTON_ACTION(X) (uint32(X) & 0x00FFFFFF)
#define UNIT_ACTION_BUTTON_TYPE(X) ((uint32(X) & 0xFF000000) >> 24)
#define MAX_UNIT_ACTION_BUTTON_ACTION_VALUE (0x00FFFFFF+1)
#define MAKE_UNIT_ACTION_BUTTON(A,T) (uint32(A) | (uint32(T) << 24))
+
struct UnitActionBarEntry
{
UnitActionBarEntry() : packedData(uint32(ACT_DISABLED) << 24) {}
+
uint32 packedData;
+
// helper
ActiveStates GetType() const { return ActiveStates(UNIT_ACTION_BUTTON_TYPE(packedData)); }
uint32 GetAction() const { return UNIT_ACTION_BUTTON_ACTION(packedData); }
@@ -850,20 +936,25 @@ struct UnitActionBarEntry
ActiveStates Type = GetType();
return Type == ACT_DISABLED || Type == ACT_ENABLED || Type == ACT_PASSIVE;
}
+
void SetActionAndType(uint32 action, ActiveStates type)
{
packedData = MAKE_UNIT_ACTION_BUTTON(action,type);
}
+
void SetType(ActiveStates type)
{
packedData = MAKE_UNIT_ACTION_BUTTON(UNIT_ACTION_BUTTON_ACTION(packedData),type);
}
+
void SetAction(uint32 action)
{
packedData = (packedData & 0xFF000000) | UNIT_ACTION_BUTTON_ACTION(action);
}
};
+
typedef std::list<Player*> SharedVisionList;
+
enum CharmType
{
CHARM_TYPE_CHARM,
@@ -871,7 +962,9 @@ enum CharmType
CHARM_TYPE_VEHICLE,
CHARM_TYPE_CONVERT,
};
+
typedef UnitActionBarEntry CharmSpellEntry;
+
enum ActionBarIndex
{
ACTION_BAR_INDEX_START = 0,
@@ -879,7 +972,9 @@ enum ActionBarIndex
ACTION_BAR_INDEX_PET_SPELL_END = 7,
ACTION_BAR_INDEX_END = 10,
};
+
#define MAX_UNIT_ACTION_BAR_INDEX (ACTION_BAR_INDEX_END-ACTION_BAR_INDEX_START)
+
struct CharmInfo
{
public:
@@ -887,16 +982,19 @@ struct CharmInfo
~CharmInfo();
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_reactState = st; }
//ReactStates GetReactState() { return m_reactState; }
//bool HasReactState(ReactStates state) { return (m_reactState == state); }
+
void InitPossessCreateSpells();
void InitCharmCreateSpells();
void InitPetActionBar();
void InitEmptyActionBar(bool withAttack = true);
+
//return true if successful
bool AddSpellToActionBar(uint32 spellid, ActiveStates newstate = ACT_DECIDE);
bool RemoveSpellFromActionBar(uint32 spell_id);
@@ -908,8 +1006,11 @@ struct CharmInfo
PetActionBar[index].SetActionAndType(spellOrAction,type);
}
UnitActionBarEntry const* GetActionBarEntry(uint8 index) const { return &(PetActionBar[index]); }
+
void ToggleCreatureAutocast(uint32 spellid, bool apply);
+
CharmSpellEntry* GetCharmSpell(uint8 index) { return &(m_charmspells[index]); }
+
void SetIsCommandAttack(bool val);
bool IsCommandAttack();
void SetIsAtStay(bool val);
@@ -920,7 +1021,9 @@ struct CharmInfo
bool IsReturning();
void SaveStayPosition();
void GetStayPosition(float &x, float &y, float &z);
+
private:
+
Unit* m_unit;
UnitActionBarEntry PetActionBar[MAX_UNIT_ACTION_BAR_INDEX];
CharmSpellEntry m_charmspells[4];
@@ -928,8 +1031,10 @@ struct CharmInfo
//ReactStates m_reactState;
uint32 m_petnumber;
bool m_barInit;
+
//for restoration after charmed
ReactStates m_oldReactState;
+
bool m_isCommandAttack;
bool m_isAtStay;
bool m_isFollowing;
@@ -938,14 +1043,17 @@ struct CharmInfo
float m_stayY;
float m_stayZ;
};
+
// for clearing special attacks
#define REACTIVE_TIMER_START 4000
+
enum ReactiveType
{
REACTIVE_DEFENSE = 0,
REACTIVE_HUNTER_PARRY = 1,
REACTIVE_OVERPOWER = 2
};
+
#define MAX_REACTIVE 3
#define SUMMON_SLOT_PET 0
#define SUMMON_SLOT_TOTEM 1
@@ -953,10 +1061,13 @@ enum ReactiveType
#define SUMMON_SLOT_MINIPET 5
#define SUMMON_SLOT_QUEST 6
#define MAX_SUMMON_SLOT 7
+
// delay time next attack to prevent client attack animation problems
#define ATTACK_DISPLAY_DELAY 200
#define MAX_PLAYER_STEALTH_DETECT_RANGE 45.0f // max distance for detection targets by player
+
struct SpellProcEventEntry; // used only privately
+
class TRINITY_DLL_SPEC Unit : public WorldObject
{
public:
@@ -969,21 +1080,29 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
typedef std::list<Aura *> AuraList;
typedef std::list<DiminishingReturn> Diminishing;
typedef std::set<uint32> ComboPointHolderSet;
+
typedef std::map<uint8, Aura*> VisibleAuraMap;
+
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, int32 limitduration);
void ApplyDiminishingAura(DiminishingGroup group, bool apply);
void ClearDiminishings() { m_Diminishing.clear(); }
+
//target dependent range checks
uint32 GetSpellMaxRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry);
uint32 GetSpellMinRangeForTarget(Unit* target,const SpellRangeEntry * rangeEntry);
uint32 GetSpellRadiusForTarget(Unit* target,const SpellRadiusEntry * radiusEntry);
+
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]; }
@@ -998,6 +1117,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void GetRandomContactPoint( const Unit* target, float &x, float &y, float &z, float distance2dMin, float distance2dMax ) const;
uint32 m_extraAttacks;
bool m_canDualWield;
+
void _addAttacker(Unit *pAttacker) // must be called only from Unit::Attack(Unit*)
{
m_attackers.insert(pAttacker);
@@ -1010,8 +1130,10 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
{
if (getVictim() != NULL)
return getVictim();
+
if (!m_attackers.empty())
return *(m_attackers.begin());
+
return NULL;
}
bool Attack(Unit *victim, bool meleeAttack);
@@ -1021,12 +1143,14 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
AttackerSet const& getAttackers() const { return m_attackers; }
bool isAttackingPlayer() const;
Unit* getVictim() const { return m_attacking; }
+
void CombatStop(bool includingCast = false);
void CombatStopWithPets(bool includingCast = false);
Unit* SelectNearbyTarget(float dist = NOMINAL_MELEE_RANGE) const;
bool hasNegativeAuraWithInterruptFlag(uint32 flag);
void SendMeleeAttackStop(Unit* victim);
void SendMeleeAttackStart(Unit* pVictim);
+
void addUnitState(uint32 f) { m_state |= f; }
bool hasUnitState(const uint32 f) const { return (m_state & f); }
void clearUnitState(uint32 f) { m_state &= ~f; }
@@ -1035,6 +1159,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
return !hasUnitState(UNIT_STAT_CONFUSED | UNIT_STAT_FLEEING | UNIT_STAT_IN_FLIGHT |
UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED ) && GetOwnerGUID()==0;
}
+
uint32 HasUnitTypeMask(uint32 mask) const { return mask & m_unitTypeMask; }
void AddUnitTypeMask(uint32 mask) { m_unitTypeMask |= mask; }
bool isSummon() const { return m_unitTypeMask & UNIT_MASK_SUMMON; }
@@ -1043,6 +1168,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
bool isHunterPet() const{ return m_unitTypeMask & UNIT_MASK_HUNTER_PET; }
bool isTotem() const { return m_unitTypeMask & UNIT_MASK_TOTEM; }
bool IsVehicle() const { return m_unitTypeMask & UNIT_MASK_VEHICLE; }
+
uint32 getLevel() const { return GetUInt32Value(UNIT_FIELD_LEVEL); }
virtual uint32 getLevelForTarget(Unit const* /*target*/) const { return getLevel(); }
void SetLevel(uint32 lvl);
@@ -1051,18 +1177,22 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
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);
int32 GetHealthGain(int32 dVal);
+
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); }
@@ -1072,16 +1202,20 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
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
{
float f_BaseAttackTime = GetFloatValue(UNIT_FIELD_BASEATTACKTIME+att) / m_modAttackSpeedPct[att];
return (uint32)f_BaseAttackTime;
}
+
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);
+
SheathState GetSheath() const { return SheathState(GetByteValue(UNIT_FIELD_BYTES_2, 0)); }
virtual void SetSheath( SheathState sheathed ) { SetByteValue(UNIT_FIELD_BYTES_2, 0, sheathed); }
+
// faction template id
uint32 getFaction() const { return GetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE); }
void setFaction(uint32 faction) { SetUInt32Value(UNIT_FIELD_FACTIONTEMPLATE, faction ); }
@@ -1099,6 +1233,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
{
if(FactionTemplateEntry const* entry = getFactionTemplateEntry())
return entry->IsContestedGuardFaction();
+
return false;
}
bool IsPvP() const { return HasByteFlag(UNIT_FIELD_BYTES_2, 1, UNIT_BYTE2_FLAG_PVP); }
@@ -1115,34 +1250,45 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
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);
+
void SetStandFlags(uint8 flags) { SetByteFlag(UNIT_FIELD_BYTES_1, 2,flags); }
void RemoveStandFlags(uint8 flags) { RemoveByteFlag(UNIT_FIELD_BYTES_1, 2,flags); }
+
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; }
void DealDamageMods(Unit *pVictim, uint32 &damage, uint32* absorb);
uint32 DealDamage(Unit *pVictim, uint32 damage, CleanDamage const* cleanDamage = NULL, DamageEffectType damagetype = DIRECT_DAMAGE, SpellSchoolMask damageSchoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *spellProto = NULL, bool durabilityLoss = true);
void Kill(Unit *pVictim, bool durabilityLoss = true);
int32 DealHeal(Unit *pVictim, uint32 addhealth, SpellEntry const *spellProto, bool critical = false);
+
void ProcDamageAndSpell(Unit *pVictim, uint32 procAttacker, uint32 procVictim, uint32 procEx, uint32 amount, WeaponAttackType attType = BASE_ATTACK, SpellEntry const *procSpell = NULL, SpellEntry const * procAura = NULL);
void ProcDamageAndSpellFor( bool isVictim, Unit * pTarget, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, SpellEntry const * procSpell, uint32 damage , SpellEntry const * procAura = NULL);
+
void HandleEmoteCommand(uint32 anim_id);
void AttackerStateUpdate (Unit *pVictim, WeaponAttackType attType = BASE_ATTACK, bool extra = false );
+
//float MeleeMissChanceCalc(const Unit *pVictim, WeaponAttackType attType) const;
+
void CalculateMeleeDamage(Unit *pVictim, uint32 damage, CalcDamageInfo *damageInfo, WeaponAttackType attackType = BASE_ATTACK);
void DealMeleeDamage(CalcDamageInfo *damageInfo, bool durabilityLoss);
+
void CalculateSpellDamageTaken(SpellNonMeleeDamage *damageInfo, int32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType = BASE_ATTACK, bool crit = false);
void DealSpellDamage(SpellNonMeleeDamage *damageInfo, bool durabilityLoss);
+
float MeleeSpellMissChance(const Unit *pVictim, WeaponAttackType attType, int32 skillDiff, uint32 spellId) const;
SpellMissInfo MeleeSpellHitResult(Unit *pVictim, SpellEntry const *spell);
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;
@@ -1158,14 +1304,17 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
}
return true;
}
+
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 SpellEntry * spellProto) const;
+
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) 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 ); }
@@ -1189,29 +1338,37 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
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 CombatStart(Unit* target, bool initialAggro = true);
void SetInCombatState(bool PvP, Unit* enemy = NULL);
void SetInCombatWith(Unit* enemy);
void ClearInCombat();
uint32 GetCombatTimer() const { return m_CombatTimer; }
+
bool HasAuraTypeWithFamilyFlags(AuraType auraType, uint32 familyName, uint32 familyFlags) const;
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;
+
bool isTargetableForAttack() const;
bool isAttackableByAOE() const;
bool canAttack(Unit const* target, bool force = true) const;
virtual bool IsInWater() const;
virtual bool IsUnderWater() const;
bool isInAccessiblePlaceFor(Creature const* c) const;
+
void SendHealSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage, uint32 OverHeal, bool critical = false);
void SendEnergizeSpellLog(Unit *pVictim, uint32 SpellID, uint32 Damage,Powers powertype);
void EnergizeBySpell(Unit *pVictim, uint32 SpellID, uint32 Damage, Powers powertype);
@@ -1227,18 +1384,24 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void SetAuraStack(uint32 spellId, Unit *target, uint32 stack);
void HandleAuraEffect(AuraEffect * aureff, bool apply);
Aura *AddAuraEffect(const SpellEntry *spellInfo, uint8 effIndex, WorldObject *source, Unit *caster, int32 *basePoints = NULL);
+
bool IsDamageToThreatSpell(SpellEntry const * spellInfo) const;
+
void DeMorph();
+
void SendAttackStateUpdate(CalcDamageInfo *damageInfo);
void SendAttackStateUpdate(uint32 HitInfo, Unit *target, uint8 SwingType, SpellSchoolMask damageSchoolMask, uint32 Damage, uint32 AbsorbDamage, uint32 Resist, VictimState TargetState, uint32 BlockedAmount);
void SendSpellNonMeleeDamageLog(SpellNonMeleeDamage *log);
void SendSpellNonMeleeDamageLog(Unit *target,uint32 SpellID, uint32 Damage, SpellSchoolMask damageSchoolMask, uint32 AbsorbedDamage, uint32 Resist, bool PhysicalDamage, uint32 Blocked, bool CriticalHit = false);
void SendPeriodicAuraLog(SpellPeriodicAuraLogInfo *pInfo);
void SendSpellMiss(Unit *target, uint32 spellID, SpellMissInfo missInfo);
+
void NearTeleportTo(float x, float y, float z, float orientation, bool casting = false);
+
void KnockbackFrom(float x, float y, float speedXY, float speedZ);
void JumpTo(float speedXY, float speedZ, bool forward = true);
void JumpTo(WorldObject *obj, float speedZ);
+
void SendMonsterStop();
void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 Time, Player* player = NULL);
void SendMonsterMove(float NewPosX, float NewPosY, float NewPosZ, uint32 MoveFlags, uint32 time, float speedZ, Player *player = NULL);
@@ -1248,16 +1411,20 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void SendMonsterMoveWithSpeed(float x, float y, float z, uint32 transitTime = 0, Player* player = NULL);
void SendMonsterMoveWithSpeedToCurrentDestination(Player* player = NULL);
void SendMovementFlagUpdate();
+
void SendChangeCurrentVictimOpcode(HostilReference* pHostilReference);
void SendClearThreatListOpcode();
void SendRemoveFromThreatListOpcode(HostilReference* pHostilReference);
void SendThreatListUpdate();
+
void BuildHeartBeatMsg(WorldPacket *data) const;
void OutMovementInfo() const;
+
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 GetOwnerGUID() const { return GetUInt64Value(UNIT_FIELD_SUMMONEDBY); }
uint64 GetCreatorGUID() const { return GetUInt64Value(UNIT_FIELD_CREATEDBY); }
void SetCreatorGUID(uint64 creator) { SetUInt64Value(UNIT_FIELD_CREATEDBY, creator); }
@@ -1268,6 +1435,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
uint64 GetCharmGUID() const { return GetUInt64Value(UNIT_FIELD_CHARM); }
void SetPetGUID(uint64 guid) { m_SummonSlot[SUMMON_SLOT_PET] = guid; }
uint64 GetPetGUID() const { return m_SummonSlot[SUMMON_SLOT_PET]; }
+
bool IsControlledByPlayer() const { return m_ControlledByPlayer; }
uint64 GetCharmerOrOwnerGUID() const { return GetCharmerGUID() ? GetCharmerGUID() : GetOwnerGUID(); }
uint64 GetCharmerOrOwnerOrOwnGUID() const
@@ -1277,7 +1445,9 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
return GetGUID();
}
bool isCharmedOwnedByPlayerOrPlayer() const { return IS_PLAYER_GUID(GetCharmerOrOwnerOrOwnGUID()); }
+
Player* GetSpellModOwner() const;
+
Unit* GetOwner(bool inWorld = true) const;
Guardian *GetGuardianPet() const;
Minion *GetFirstMinion() const;
@@ -1288,9 +1458,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
{
if(Unit *u = GetCharmerOrOwner())
return u;
+
return (Unit*)this;
}
Player* GetCharmerOrOwnerPlayerOrPlayerItself() const;
+
void SetMinion(Minion *minion, bool apply);
void GetAllMinionsByEntry(std::list<Creature*>& Minions, uint32 entry);
void RemoveAllMinionsByEntry(uint32 entry);
@@ -1299,9 +1471,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
bool SetCharmedBy(Unit* charmer, CharmType type);
void RemoveCharmedBy(Unit* charmer);
void RestoreFaction();
+
ControlList m_Controlled;
Unit* GetFirstControlled() const;
void RemoveAllControlled();
+
bool isCharmed() const { return GetCharmerGUID() != 0; }
bool isPossessed() const { return hasUnitState(UNIT_STAT_POSSESSED); }
bool isPossessedByPlayer() const { return hasUnitState(UNIT_STAT_POSSESSED) && IS_PLAYER_GUID(GetCharmerGUID()); }
@@ -1313,6 +1487,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
return false;
}
bool isPossessing(Unit* u) const { return u->isPossessed() && GetCharmGUID() == u->GetGUID(); }
+
CharmInfo* GetCharmInfo() { return m_charmInfo; }
CharmInfo* InitCharmInfo();
void DeleteCharmInfo();
@@ -1325,8 +1500,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
bool HasSharedVision() const { return !m_sharedVision.empty(); }
void RemoveBindSightAuras();
void RemoveCharmAuras();
+
Pet* CreateTamedPetFrom(Creature* creatureTarget,uint32 spell_id = 0);
+
bool AddAura(Aura *aur, bool handleEffects = true);
+
void RemoveAura(AuraMap::iterator &i, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
void RemoveAura(uint32 spellId, uint64 caster = 0 ,AuraRemoveMode removeMode = AURA_REMOVE_BY_DEFAULT);
void RemoveAura(Aura * aur, AuraRemoveMode mode = AURA_REMOVE_BY_DEFAULT);
@@ -1338,6 +1516,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void RemoveAurasDueToItemSpell(Item* castItem,uint32 spellId);
void RemoveAurasByType(AuraType auraType, uint64 casterGUID = 0, Aura * except = NULL, bool negative = true, bool positive = true);
void RemoveNotOwnSingleTargetAuras(uint32 newPhase = 0x0);
+
void RemoveRankAurasDueToSpell(uint32 spellId);
bool RemoveNoStackAurasDueToAura(Aura *Aur);
void RemoveAurasWithInterruptFlags(uint32 flag, uint32 except = NULL);
@@ -1349,6 +1528,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void RemoveArenaAuras(bool onleave = false);
void RemoveAllAurasOnDeath();
void DelayAura(uint32 spellId, uint64 caster, 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); }
@@ -1373,37 +1553,47 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
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(CurrentSpellTypes spellType, bool withDelayed = true, bool withInstant = true);
void FinishSpell(CurrentSpellTypes spellType, bool ok = 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, bool isAutoshoot = false) const;
+
// set withDelayed to true to interrupt delayed spells too
// delayed+channeled spells are always interrupted
void InterruptNonMeleeSpells(bool withDelayed, uint32 spellid = 0, bool withInstant = true);
+
Spell* GetCurrentSpell(CurrentSpellTypes spellType) const { return m_currentSpells[spellType]; }
Spell* GetCurrentSpell(uint32 spellType) const { return m_currentSpells[spellType]; }
Spell* FindCurrentSpellBySpellId(uint32 spell_id) const;
int32 GetCurrentSpellCastTime(uint32 spell_id) const;
+
uint32 m_addDmgOnce;
uint64 m_SummonSlot[MAX_SUMMON_SLOT];
uint64 m_ObjectSlot[4];
uint32 m_detectInvisibilityMask;
uint32 m_invisibilityMask;
+
uint32 m_ShapeShiftFormSpellId;
ShapeshiftForm m_form;
bool IsInFeralForm() const { return m_form == FORM_CAT || m_form == FORM_BEAR || m_form == FORM_DIREBEAR; }
+
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; }
@@ -1426,6 +1616,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
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 isInFrontInMap(Unit const* target,float distance, float arc = M_PI) const;
void SetInFront(Unit const* target)
{
@@ -1433,22 +1624,27 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
SetOrientation(GetAngle(target));
}
bool isInBackInMap(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
virtual bool canSeeOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const = 0;
bool isVisibleForOrDetect(Unit const* u, bool detect, bool inVisibleList = false, bool is3dDistance = true) const;
bool canDetectInvisibilityOf(Unit const* u) const;
bool canDetectStealthOf(Unit const* u, float distance) const;
void SetPhaseMask(uint32 newPhaseMask, bool update);// overwrite WorldObject::SetPhaseMask
+
// 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 const* pl) const = 0;
+
AuraList & GetSingleCastAuras() { return m_scAuras; }
AuraList const& GetSingleCastAuras() const { return m_scAuras; }
SpellImmuneList m_spellImmune[MAX_SPELL_IMMUNITY];
+
// Threat related methods
bool CanHaveThreatList() const;
void AddThreat(Unit* pVictim, float threat, SpellSchoolMask schoolMask = SPELL_SCHOOL_MASK_NORMAL, SpellEntry const *threatSpell = NULL);
@@ -1460,6 +1656,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
void addHatedBy(HostilReference* pHostilReference) { m_HostilRefManager.insertFirst(pHostilReference); };
void removeHatedBy(HostilReference* /*pHostilReference*/ ) { /* nothing to do yet */ }
HostilRefManager& getHostilRefManager() { return m_HostilRefManager; }
+
VisibleAuraMap const *GetVisibleAuras() { return &m_visibleAuras; }
Aura * GetVisibleAura(uint8 slot)
{
@@ -1470,9 +1667,11 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
}
void SetVisibleAura(uint8 slot, Aura * aur){ m_visibleAuras[slot]=aur; }
void RemoveVisibleAura(uint8 slot){ m_visibleAuras.erase(slot); }
+
AuraMap & GetAuras() { return m_Auras; }
AuraMap const& GetAuras() const { return m_Auras; }
AuraEffectList const& GetAurasByType(AuraType type) const { return m_modAuras[type]; }
+
AuraEffect * GetAuraEffect(uint32 spellId, uint8 effIndex, uint64 casterGUID = 0) const;
Aura * GetAura(uint32 spellId, uint64 casterGUID = 0) const;
AuraEffect * GetAura(AuraType type, uint32 family, uint32 familyFlag1 , uint32 familyFlag2=0, uint32 familyFlag3=0, uint64 casterGUID=0);
@@ -1487,37 +1686,45 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
AuraEffect* GetAuraEffect(AuraType type, SpellFamilyNames name, uint32 iconId, uint8 effIndex) const;
uint32 GetDiseasesByCaster(uint64 casterGUID, bool remove = false);
uint32 GetDoTsByCaster(uint64 casterGUID) const;
+
int32 GetTotalAuraModifier(AuraType auratype) const;
float GetTotalAuraMultiplier(AuraType auratype) const;
int32 GetMaxPositiveAuraModifier(AuraType auratype);
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;
+
uint32 GetInterruptMask() const { return m_interruptMask; }
void AddInterruptMask(uint32 mask) { m_interruptMask |= mask; }
void UpdateInterruptMask();
+
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;}
+
DynamicObject* GetDynObject(uint32 spellId);
void AddDynObject(DynamicObject* dynObj);
void RemoveDynObject(uint32 spellid);
void RemoveDynObjectWithGUID(uint64 guid) { m_dynObjGUIDs.remove(guid); }
void RemoveAllDynObjects();
+
GameObject* GetGameObject(uint32 spellId) const;
void AddGameObject(GameObject* gameObj);
void RemoveGameObject(GameObject* gameObj, bool del);
void RemoveGameObject(uint32 spellid, bool del);
void RemoveAllGameObjects();
+
uint32 CalculateDamage(WeaponAttackType attType, bool normalized, bool addTotalPct);
float GetAPMultiplier(WeaponAttackType attType, bool normalized);
void ModifyAuraState(AuraState flag, bool apply);
@@ -1536,11 +1743,15 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
bool isSpellCrit(Unit *pVictim, SpellEntry const *spellProto, SpellSchoolMask schoolMask, WeaponAttackType attackType = BASE_ATTACK) const;
uint32 SpellCriticalDamageBonus(SpellEntry const *spellProto, uint32 damage, Unit *pVictim);
uint32 SpellCriticalHealingBonus(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);
@@ -1549,52 +1760,68 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
bool IsImmunedToDamage(SpellEntry const* spellInfo);
virtual bool IsImmunedToSpellEffect(SpellEntry const* spellInfo, uint32 index) const;
// redefined in Creature
+
uint32 CalcArmorReducedDamage(Unit* pVictim, const uint32 damage, SpellEntry const *spellInfo, WeaponAttackType attackType=MAX_ATTACK);
void CalcAbsorbResist(Unit *pVictim, SpellSchoolMask schoolMask, DamageEffectType damagetype, const uint32 damage, uint32 *absorb, uint32 *resist, SpellEntry const *spellInfo = NULL);
+
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);
float m_TempSpeed;
+
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 CalcSpellDuration(SpellEntry const* spellProto);
int32 ModSpellDuration(SpellEntry const* spellProto, Unit const* target, int32 duration, bool positive);
void ModSpellCastTime(SpellEntry const* spellProto, int32 & castTime, Spell * spell=NULL);
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);
static Player* GetPlayer(uint64 guid);
static Creature* GetCreature(WorldObject& object, uint64 guid);
+
MotionMaster* GetMotionMaster() { return &i_motionMaster; }
+
bool IsStopped() const { return !(hasUnitState(UNIT_STAT_MOVING)); }
void StopMoving();
+
void AddUnitMovementFlag(uint32 f) { m_movementInfo.flags |= f; }
void RemoveUnitMovementFlag(uint32 f) { m_movementInfo.flags &= ~f; }
uint32 HasUnitMovementFlag(uint32 f) const { return m_movementInfo.flags & f; }
uint32 GetUnitMovementFlags() const { return m_movementInfo.flags; }
void SetUnitMovementFlags(uint32 f) { m_movementInfo.flags = f; }
+
void SetControlled(bool apply, UnitState state);
+
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, SpellCastResult msg);
void SendPetActionFeedback (uint8 msg);
void SendPetTalk (uint32 pettalk);
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);
+
// proc trigger system
bool CanProc(){return !m_procDeep;}
void SetCantProc( bool apply)
@@ -1607,16 +1834,19 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
--m_procDeep;
}
}
+
// pet auras
typedef std::set<PetAura const*> PetAuraSet;
PetAuraSet m_petAuras;
void AddPetAura(PetAura const* petSpell);
void RemovePetAura(PetAura const* petSpell);
+
// relocation notification
void SetToNotify();
bool m_Notified;
int32 m_NotifyListPos;
float oldX, oldY;
+
void SetReducedThreatPercent(uint32 pct, uint64 guid)
{
m_reducedThreatPercent = pct;
@@ -1624,6 +1854,7 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
}
uint32 GetReducedThreatPercent() { return m_reducedThreatPercent; }
Unit *GetMisdirectionTarget() { return m_misdirectionTargetGUID ? GetUnit(*this, m_misdirectionTargetGUID) : NULL; }
+
bool IsAIEnabled, NeedChangeAI;
MovementInfo m_movementInfo;
bool CreateVehicleKit(uint32 id);
@@ -1638,70 +1869,101 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
float GetTransOffsetO() const { return m_movementInfo.t_o; }
uint32 GetTransTime() const { return m_movementInfo.t_time; }
int8 GetTransSeat() const { return m_movementInfo.t_seat; }
+
bool m_ControlledByPlayer;
+
void EnterVehicle(Unit *base, int8 seatId = -1) { EnterVehicle(base->GetVehicleKit(), seatId); }
void EnterVehicle(Vehicle *vehicle, int8 seatId = -1);
void ExitVehicle();
void ChangeSeat(int8 seatId, bool next = true);
+
// Transports
Transport * GetTransport() const { return m_transport; }
void SetTransport(Transport * t) { m_transport = t; }
+
void BuildMovementPacket(ByteBuffer *data) const;
+
bool isMoving() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_MOVING); }
bool isTurning() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_TURNING); }
bool canFly() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLY_MODE); }
bool IsFlying() const { return m_movementInfo.HasMovementFlag(MOVEMENTFLAG_FLYING); }
void SetFlying(bool apply);
+
void RewardRage( uint32 damage, uint32 weaponSpeedHitFactor, bool attacker );
+
virtual float GetFollowAngle() const { return M_PI/2; }
+
void OutDebugInfo() const;
virtual bool isBeingLoaded() const { return false;}
protected:
explicit Unit ();
+
UnitAI *i_AI, *i_disabledAI;
+
// Transports
Transport * m_transport;
+
void _UpdateSpells(uint32 time);
void _DeleteAuras();
+
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;
AuraMap::iterator m_AurasUpdateIterator;
uint32 m_removedAurasCount;
int32 m_procDeep;
+
typedef std::list<uint64> DynObjectGUIDs;
DynObjectGUIDs m_dynObjGUIDs;
+
typedef std::list<GameObject*> GameObjectList;
GameObjectList m_gameObj;
bool m_isSorted;
uint32 m_transform;
+
AuraEffectList m_modAuras[TOTAL_AURAS];
AuraList m_scAuras; // casted singlecast auras
AuraList m_interruptableAuras;
AuraList m_removedAuras;
AuraStateAurasMap m_auraStateAuras; // Used for improve performance of aura state checks on aura apply/remove
uint32 m_interruptMask;
+
float m_auraModifiersGroup[UNIT_MOD_END][MODIFIER_TYPE_END];
float m_weaponDamage[MAX_ATTACK][2];
bool m_canModifyStats;
VisibleAuraMap m_visibleAuras;
+
float m_speed_rate[MAX_MOVE_TYPE];
+
CharmInfo *m_charmInfo;
SharedVisionList m_sharedVision;
+
virtual SpellSchoolMask GetMeleeDamageSchoolMask() const;
+
MotionMaster i_motionMaster;
//uint32 m_unit_movement_flags;
+
uint32 m_reactiveTimer[MAX_REACTIVE];
uint32 m_regenTimer;
+
ThreatManager m_ThreatManager;
+
Vehicle *m_vehicle;
Vehicle *m_vehicleKit;
+
uint32 m_unitTypeMask;
+
private:
bool IsTriggeredAtSpellProcEvent(Unit *pVictim, Aura* aura, SpellEntry const * procSpell, uint32 procFlag, uint32 procExtra, WeaponAttackType attType, bool isVictim, bool active, SpellProcEventEntry const *& spellProcEvent );
bool HandleDummyAuraProc( Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 procFlag, uint32 procEx, uint32 cooldown);
@@ -1714,25 +1976,34 @@ class TRINITY_DLL_SPEC Unit : public WorldObject
bool HandleOverrideClassScriptAuraProc(Unit *pVictim, uint32 damage, AuraEffect* triggeredByAura, SpellEntry const *procSpell, uint32 cooldown);
bool HandleAuraRaidProcFromChargeWithValue(AuraEffect* triggeredByAura);
bool HandleAuraRaidProcFromCharge(AuraEffect* triggeredByAura);
+
void SetFeared(bool apply);
void SetConfused(bool apply);
void SetStunned(bool apply);
void SetRooted(bool apply);
+
uint32 m_state; // Even derived shouldn't modify
uint32 m_CombatTimer;
uint32 m_lastManaUse; // msecs
+
Spell* m_currentSpells[CURRENT_MAX_SPELL];
+
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;
+
uint32 m_reducedThreatPercent;
uint64 m_misdirectionTargetGUID;
};
+
namespace Trinity
{
template<class T>
@@ -1746,5 +2017,6 @@ namespace Trinity
}
}
}
+
#endif
diff --git a/src/game/UnitAI.cpp b/src/game/UnitAI.cpp
index a295d2d29b7..728e6f36916 100644
--- a/src/game/UnitAI.cpp
+++ b/src/game/UnitAI.cpp
@@ -17,26 +17,31 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "UnitAI.h"
#include "Player.h"
#include "Creature.h"
#include "SpellAuras.h"
#include "SpellMgr.h"
#include "CreatureAIImpl.h"
+
void UnitAI::AttackStart(Unit *victim)
{
if(victim && me->Attack(victim, true))
me->GetMotionMaster()->MoveChase(victim);
}
+
void UnitAI::AttackStartCaster(Unit *victim, float dist)
{
if(victim && me->Attack(victim, false))
me->GetMotionMaster()->MoveChase(victim, dist);
}
+
void UnitAI::DoMeleeAttackIfReady()
{
if(me->hasUnitState(UNIT_STAT_CASTING))
return;
+
//Make sure our attack is ready and we aren't currently casting before checking distance
if (me->isAttackReady())
{
@@ -57,10 +62,12 @@ void UnitAI::DoMeleeAttackIfReady()
}
}
}
+
bool UnitAI::DoSpellAttackIfReady(uint32 spell)
{
if(me->hasUnitState(UNIT_STAT_CASTING))
return true;
+
if(me->isAttackReady())
{
if(me->IsWithinCombatRange(me->getVictim(), GetSpellMaxRange(spell, false)))
@@ -73,12 +80,15 @@ bool UnitAI::DoSpellAttackIfReady(uint32 spell)
}
return true;
}
+
inline bool SelectTargetHelper(const Unit * me, const Unit * target, const bool &playerOnly, const float &dist, const int32 &aura)
{
if(playerOnly && (!target || target->GetTypeId() != TYPEID_PLAYER))
return false;
+
if(dist && (!me || !target || !me->IsWithinCombatRange(target, dist)))
return false;
+
if(aura)
{
if(aura > 0)
@@ -92,8 +102,10 @@ inline bool SelectTargetHelper(const Unit * me, const Unit * target, const bool
return false;
}
}
+
return true;
}
+
struct TargetDistanceOrder : public std::binary_function<const Unit *, const Unit *, bool>
{
const Unit * me;
@@ -104,6 +116,7 @@ struct TargetDistanceOrder : public std::binary_function<const Unit *, const Uni
return (me->GetExactDistSq(_Left) < me->GetExactDistSq(_Right));
}
};
+
Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float dist, bool playerOnly, int32 aura)
{
if(targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
@@ -111,13 +124,17 @@ Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float
std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList();
if(position >= m_threatlist.size())
return NULL;
+
std::list<Unit*> targetList;
for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr)
if(SelectTargetHelper(me, (*itr)->getTarget(), playerOnly, dist, aura))
targetList.push_back((*itr)->getTarget());
+
if(position >= targetList.size())
return NULL;
+
targetList.sort(TargetDistanceOrder(me));
+
if(targetType == SELECT_TARGET_NEAREST)
{
std::list<Unit*>::iterator i = targetList.begin();
@@ -159,14 +176,17 @@ Unit* UnitAI::SelectTarget(SelectAggroTarget targetType, uint32 position, float
}
}
}
+
if(SelectTargetHelper(me, (*i)->getTarget(), playerOnly, dist, aura))
return (*i)->getTarget();
else
m_threatlist.erase(i);
}
}
+
return NULL;
}
+
void UnitAI::SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAggroTarget targetType, float dist, bool playerOnly, int32 aura)
{
if(targetType == SELECT_TARGET_NEAREST || targetType == SELECT_TARGET_FARTHEST)
@@ -174,9 +194,11 @@ void UnitAI::SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAg
std::list<HostilReference*> &m_threatlist = me->getThreatManager().getThreatList();
if(m_threatlist.empty())
return;
+
for(std::list<HostilReference*>::iterator itr = m_threatlist.begin(); itr!= m_threatlist.end(); ++itr)
if(SelectTargetHelper(me, (*itr)->getTarget(), playerOnly, dist, aura))
targetList.push_back((*itr)->getTarget());
+
targetList.sort(TargetDistanceOrder(me));
targetList.resize(num);
if(targetType == SELECT_TARGET_FARTHEST)
@@ -199,6 +221,7 @@ void UnitAI::SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAg
if(targetType == SELECT_TARGET_RANDOM)
advance(i, rand()%m_threatlist.size());
}
+
if(SelectTargetHelper(me, (*i)->getTarget(), playerOnly, dist, aura))
{
targetList.push_back((*i)->getTarget());
@@ -208,10 +231,12 @@ void UnitAI::SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAg
}
}
}
+
float UnitAI::DoGetSpellMaxRange(uint32 spellId, bool positive)
{
return GetSpellMaxRange(spellId, positive);
}
+
void UnitAI::DoCast(uint32 spellId)
{
Unit *target = NULL;
@@ -245,28 +270,36 @@ void UnitAI::DoCast(uint32 spellId)
break;
}
}
+
if(target)
me->CastSpell(target, spellId, false);
}
+
#define UPDATE_TARGET(a) {if(AIInfo->target<a) AIInfo->target=a;}
+
void UnitAI::FillAISpellInfo()
{
AISpellInfo = new AISpellInfoType[GetSpellStore()->GetNumRows()];
+
AISpellInfoType *AIInfo = AISpellInfo;
const SpellEntry * spellInfo;
+
for(uint32 i = 0; i < GetSpellStore()->GetNumRows(); ++i, ++AIInfo)
{
spellInfo = GetSpellStore()->LookupEntry(i);
if(!spellInfo)
continue;
+
if(spellInfo->Attributes & SPELL_ATTR_CASTABLE_WHILE_DEAD)
AIInfo->condition = AICOND_DIE;
else if(IsPassiveSpell(i) || GetSpellDuration(spellInfo) == -1)
AIInfo->condition = AICOND_AGGRO;
else
AIInfo->condition = AICOND_COMBAT;
+
if(AIInfo->cooldown < spellInfo->RecoveryTime)
AIInfo->cooldown = spellInfo->RecoveryTime;
+
if(!GetSpellMaxRange(spellInfo, false))
UPDATE_TARGET(AITARGET_SELF)
else
@@ -274,11 +307,13 @@ void UnitAI::FillAISpellInfo()
for(uint32 j = 0; j < 3; ++j)
{
uint32 targetType = spellInfo->EffectImplicitTargetA[j];
+
if(targetType == TARGET_UNIT_TARGET_ENEMY
|| targetType == TARGET_DST_TARGET_ENEMY)
UPDATE_TARGET(AITARGET_VICTIM)
else if(targetType == TARGET_UNIT_AREA_ENEMY_DST)
UPDATE_TARGET(AITARGET_ENEMY)
+
if(spellInfo->Effect[j] == SPELL_EFFECT_APPLY_AURA)
{
if(targetType == TARGET_UNIT_TARGET_ENEMY)
@@ -294,11 +329,14 @@ void UnitAI::FillAISpellInfo()
AIInfo->maxRange = srange->maxRangeHostile * 3 / 4;
}
}
+
//Enable PlayerAI when charmed
void PlayerAI::OnCharmed(bool apply) { me->IsAIEnabled = apply; }
+
void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/)
{
Creature *charmer = (Creature*)me->GetCharmer();
+
//kill self if charm aura has infinite duration
if(charmer->IsInEvadeMode())
{
@@ -310,8 +348,10 @@ void SimpleCharmedAI::UpdateAI(const uint32 /*diff*/)
return;
}
}
+
if(!charmer->isInCombat())
me->GetMotionMaster()->MoveFollow(charmer, PET_FOLLOW_DIST, me->GetFollowAngle());
+
Unit *target = me->getVictim();
if(!target || !charmer->canAttack(target))
AttackStart(charmer->SelectNearestTarget());
diff --git a/src/game/UnitAI.h b/src/game/UnitAI.h
index 93992724c08..c5e02b07d1b 100644
--- a/src/game/UnitAI.h
+++ b/src/game/UnitAI.h
@@ -17,14 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_UNITAI_H
#define TRINITY_UNITAI_H
+
#include "Platform/Define.h"
#include <list>
#include "Unit.h"
+
class Unit;
class Player;
struct AISpellInfoType;
+
//Selection method used by SelectTarget
enum SelectAggroTarget
{
@@ -34,6 +38,7 @@ enum SelectAggroTarget
SELECT_TARGET_NEAREST,
SELECT_TARGET_FARTHEST,
};
+
class TRINITY_DLL_SPEC UnitAI
{
protected:
@@ -43,40 +48,54 @@ class TRINITY_DLL_SPEC UnitAI
virtual bool CanAIAttack(const Unit *who) const { return true; }
virtual void AttackStart(Unit *);
virtual void UpdateAI(const uint32 diff) = 0;
+
virtual void InitializeAI() { if(!me->isDead()) Reset(); }
+
virtual void Reset() {};
+
// Called when unit is charmed
virtual void OnCharmed(bool apply) = 0;
+
// Pass parameters between AI
virtual void DoAction(const int32 param = 0) {}
virtual uint32 GetData(uint32 id = 0) { return 0; }
virtual void SetData(uint32 id, uint32 value) {}
virtual void SetGUID(const uint64 &guid, int32 id = 0) {}
virtual uint64 GetGUID(int32 id = 0) { return 0; }
+
Unit* SelectTarget(SelectAggroTarget target, uint32 position = 0, float dist = 0, bool playerOnly = false, int32 aura = 0);
void SelectTargetList(std::list<Unit*> &targetList, uint32 num, SelectAggroTarget target, float dist = 0, bool playerOnly = false, int32 aura = 0);
+
void AttackStartCaster(Unit *victim, float dist);
+
void DoCast(uint32 spellId);
void DoCast(Unit* victim, uint32 spellId, bool triggered = false);
void DoCastVictim(uint32 spellId, bool triggered = false);
void DoCastAOE(uint32 spellId, bool triggered = false);
+
float DoGetSpellMaxRange(uint32 spellId, bool positive = false);
+
void DoMeleeAttackIfReady();
bool DoSpellAttackIfReady(uint32 spell);
+
static AISpellInfoType *AISpellInfo;
static void FillAISpellInfo();
};
+
class TRINITY_DLL_SPEC PlayerAI : public UnitAI
{
protected:
Player* const me;
public:
explicit PlayerAI(Player *p) : UnitAI((Unit*)p), me(p) {}
+
void OnCharmed(bool apply);
};
+
class TRINITY_DLL_SPEC SimpleCharmedAI : public PlayerAI
{
public:
void UpdateAI(const uint32 diff);
};
+
#endif
diff --git a/src/game/UnitEvents.h b/src/game/UnitEvents.h
index a4046ea46b8..67f8d01f0d6 100644
--- a/src/game/UnitEvents.h
+++ b/src/game/UnitEvents.h
@@ -17,41 +17,58 @@
* 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 TRINITY_DLL_SPEC UnitBaseEvent
{
private:
@@ -60,9 +77,13 @@ class TRINITY_DLL_SPEC UnitBaseEvent
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 TRINITY_DLL_SPEC ThreatRefStatusChangeEvent : public UnitBaseEvent
{
private:
@@ -76,18 +97,30 @@ class TRINITY_DLL_SPEC ThreatRefStatusChangeEvent : public UnitBaseEvent
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 TRINITY_DLL_SPEC ThreatManagerEvent : public ThreatRefStatusChangeEvent
{
private:
@@ -95,9 +128,12 @@ class TRINITY_DLL_SPEC ThreatManagerEvent : public ThreatRefStatusChangeEvent
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
index 7b2c86cd49a..e461d63e248 100644
--- a/src/game/UpdateData.cpp
+++ b/src/game/UpdateData.cpp
@@ -17,6 +17,7 @@
* 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"
@@ -25,28 +26,35 @@
#include "Opcodes.h"
#include "World.h"
#include <zlib/zlib.h>
+
UpdateData::UpdateData() : m_blockCount(0)
{
}
+
void UpdateData::AddOutOfRangeGUID(std::set<uint64>& 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)
@@ -55,10 +63,12 @@ void UpdateData::Compress(void* dst, uint32 *dst_size, void* src, int src_size)
*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)
{
@@ -66,12 +76,14 @@ void UpdateData::Compress(void* dst, uint32 *dst_size, void* src, int src_size)
*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)
{
@@ -79,6 +91,7 @@ void UpdateData::Compress(void* dst, uint32 *dst_size, void* src, int src_size)
*dst_size = 0;
return;
}
+
z_res = deflateEnd(&c_stream);
if (z_res != Z_OK)
{
@@ -86,32 +99,43 @@ void UpdateData::Compress(void* dst, uint32 *dst_size, void* src, int src_size)
*dst_size = 0;
return;
}
+
*dst_size = c_stream.total_out;
}
+
bool UpdateData::BuildPacket(WorldPacket *packet)
{
ASSERT(packet->empty()); // shouldn't happen
+
ByteBuffer buf(4 + (m_outOfRangeGUIDs.empty() ? 0 : 1 + 4 + 9 * m_outOfRangeGUIDs.size()) + m_data.wpos());
+
buf << (uint32) (!m_outOfRangeGUIDs.empty() ? m_blockCount + 1 : m_blockCount);
+
if(!m_outOfRangeGUIDs.empty())
{
buf << (uint8) UPDATETYPE_OUT_OF_RANGE_OBJECTS;
buf << (uint32) m_outOfRangeGUIDs.size();
+
for(std::set<uint64>::const_iterator i = m_outOfRangeGUIDs.begin(); i != m_outOfRangeGUIDs.end(); ++i)
{
buf.appendPackGUID(*i);
}
}
+
buf.append(m_data);
+
size_t pSize = buf.wpos(); // use real used data size
+
if (pSize > 100 ) // compress large packets
{
uint32 destsize = compressBound(pSize);
packet->resize( destsize + sizeof(uint32) );
+
packet->put<uint32>(0, pSize);
Compress(const_cast<uint8*>(packet->contents()) + sizeof(uint32), &destsize, (void*)buf.contents(), pSize);
if (destsize == 0)
return false;
+
packet->resize( destsize + sizeof(uint32) );
packet->SetOpcode( SMSG_COMPRESSED_UPDATE_OBJECT );
}
@@ -120,8 +144,10 @@ bool UpdateData::BuildPacket(WorldPacket *packet)
packet->append( buf );
packet->SetOpcode( SMSG_UPDATE_OBJECT );
}
+
return true;
}
+
void UpdateData::Clear()
{
m_data.clear();
diff --git a/src/game/UpdateData.h b/src/game/UpdateData.h
index d5052d50315..282ea566c7a 100644
--- a/src/game/UpdateData.h
+++ b/src/game/UpdateData.h
@@ -17,10 +17,13 @@
* 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
+
#include "ByteBuffer.h"
class WorldPacket;
+
enum OBJECT_UPDATE_TYPE
{
UPDATETYPE_VALUES = 0,
@@ -30,6 +33,7 @@ enum OBJECT_UPDATE_TYPE
UPDATETYPE_OUT_OF_RANGE_OBJECTS = 4,
UPDATETYPE_NEAR_OBJECTS = 5
};
+
enum OBJECT_UPDATE_FLAGS
{
UPDATEFLAG_NONE = 0x0000,
@@ -44,21 +48,26 @@ enum OBJECT_UPDATE_FLAGS
UPDATEFLAG_POSITION = 0x0100,
UPDATEFLAG_ROTATION = 0x0200
};
+
class UpdateData
{
public:
UpdateData();
+
void AddOutOfRangeGUID(std::set<uint64>& guids);
void AddOutOfRangeGUID(const uint64 &guid);
void AddUpdateBlock(const ByteBuffer &block);
bool BuildPacket(WorldPacket *packet);
bool HasData() { return m_blockCount > 0 || !m_outOfRangeGUIDs.empty(); }
void Clear();
+
std::set<uint64> const& GetOutOfRangeGUIDs() const { return m_outOfRangeGUIDs; }
+
protected:
uint32 m_blockCount;
std::set<uint64> 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
index 7d245c9a41f..8fa05a4cb86 100644
--- a/src/game/UpdateFields.h
+++ b/src/game/UpdateFields.h
@@ -17,9 +17,12 @@
* 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 3, 1, 3, 9947
+
enum EObjectFields
{
OBJECT_FIELD_GUID = 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
@@ -29,6 +32,7 @@ enum EObjectFields
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
@@ -71,6 +75,7 @@ enum EItemFields
ITEM_FIELD_PAD = OBJECT_END + 0x0039, // Size: 1, Type: INT, Flags: NONE
ITEM_END = OBJECT_END + 0x003A,
};
+
enum EContainerFields
{
CONTAINER_FIELD_NUM_SLOTS = ITEM_END + 0x0000, // Size: 1, Type: INT, Flags: PUBLIC
@@ -78,6 +83,7 @@ enum EContainerFields
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
@@ -170,6 +176,7 @@ enum EUnitFields
UNIT_FIELD_HOVERHEIGHT = OBJECT_END + 0x008C, // Size: 1, Type: FLOAT, Flags: PUBLIC
UNIT_FIELD_PADDING = OBJECT_END + 0x008D, // Size: 1, Type: INT, Flags: NONE
UNIT_END = OBJECT_END + 0x008E,
+
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
@@ -383,6 +390,7 @@ enum EUnitFields
PLAYER_GLYPHS_ENABLED = UNIT_END + 0x0479, // Size: 1, Type: INT, Flags: PRIVATE
PLAYER_END = UNIT_END + 0x047A,
};
+
enum EGameObjectFields
{
OBJECT_FIELD_CREATED_BY = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
@@ -395,6 +403,7 @@ enum EGameObjectFields
GAMEOBJECT_BYTES_1 = OBJECT_END + 0x000B, // Size: 1, Type: BYTES, Flags: PUBLIC
GAMEOBJECT_END = OBJECT_END + 0x000C,
};
+
enum EDynamicObjectFields
{
DYNAMICOBJECT_CASTER = OBJECT_END + 0x0000, // Size: 2, Type: LONG, Flags: PUBLIC
@@ -408,6 +417,7 @@ enum EDynamicObjectFields
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
diff --git a/src/game/UpdateMask.h b/src/game/UpdateMask.h
index 895b27dd348..66a6947fc37 100644
--- a/src/game/UpdateMask.h
+++ b/src/game/UpdateMask.h
@@ -17,84 +17,107 @@
* 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;
}
+
void SetBit (uint32 index)
{
( (uint8 *)mUpdateMask )[ index >> 3 ] |= 1 << ( index & 0x7 );
}
+
void UnsetBit (uint32 index)
{
( (uint8 *)mUpdateMask )[ index >> 3 ] &= (0xff ^ (1 << ( index & 0x7 ) ) );
}
+
bool GetBit (uint32 index) const
{
return ( ( (uint8 *)mUpdateMask)[ index >> 3 ] & ( 1 << ( index & 0x7 ) )) != 0;
}
+
uint32 GetBlockCount() const { return mBlocks; }
uint32 GetLength() const { return mBlocks << 2; }
uint32 GetCount() const { return mCount; }
uint8* GetMask() { return (uint8*)mUpdateMask; }
+
void SetCount (uint32 valuesCount)
{
if(mUpdateMask)
delete [] mUpdateMask;
+
mCount = valuesCount;
mBlocks = (valuesCount + 31) / 32;
+
mUpdateMask = new uint32[mBlocks];
memset(mUpdateMask, 0, mBlocks << 2);
}
+
void Clear()
{
if (mUpdateMask)
memset(mUpdateMask, 0, mBlocks << 2);
}
+
UpdateMask& operator = ( const UpdateMask& mask )
{
SetCount(mask.mCount);
memcpy(mUpdateMask, mask.mUpdateMask, mBlocks << 2);
+
return *this;
}
+
void operator &= ( const UpdateMask& mask )
{
ASSERT(mask.mCount <= mCount);
for (uint32 i = 0; i < mBlocks; ++i)
mUpdateMask[i] &= mask.mUpdateMask[i];
}
+
void operator |= ( const UpdateMask& mask )
{
ASSERT(mask.mCount <= mCount);
for (uint32 i = 0; i < mBlocks; ++i)
mUpdateMask[i] |= mask.mUpdateMask[i];
}
+
UpdateMask operator & ( const UpdateMask& mask ) const
{
ASSERT(mask.mCount <= mCount);
+
UpdateMask newmask;
newmask = *this;
newmask &= mask;
+
return newmask;
}
+
UpdateMask operator | ( const UpdateMask& mask ) const
{
ASSERT(mask.mCount <= mCount);
+
UpdateMask newmask;
newmask = *this;
newmask |= mask;
+
return newmask;
}
+
private:
uint32 mCount;
uint32 mBlocks;
diff --git a/src/game/Vehicle.cpp b/src/game/Vehicle.cpp
index a8c8d5fa785..50236430bbe 100644
--- a/src/game/Vehicle.cpp
+++ b/src/game/Vehicle.cpp
@@ -15,6 +15,7 @@
* 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 "ObjectMgr.h"
@@ -22,8 +23,10 @@
#include "Unit.h"
#include "Util.h"
#include "WorldPacket.h"
+
#include "CreatureAI.h"
#include "ZoneScript.h"
+
Vehicle::Vehicle(Unit *unit, VehicleEntry const *vehInfo) : me(unit), m_vehicleInfo(vehInfo), m_usableSeatNum(0)
{
for(uint32 i = 0; i < 8; ++i)
@@ -34,15 +37,17 @@ Vehicle::Vehicle(Unit *unit, VehicleEntry const *vehInfo) : me(unit), m_vehicleI
m_Seats.insert(std::make_pair(i, VehicleSeat(veSeat)));
if(veSeat->IsUsable())
++m_usableSeatNum;
- }
+ }
}
assert(!m_Seats.empty());
}
+
Vehicle::~Vehicle()
{
for(SeatMap::const_iterator itr = m_Seats.begin(); itr != m_Seats.end(); ++itr)
assert(!itr->second.passenger);
}
+
void Vehicle::Install()
{
if(Creature *cre = dynamic_cast<Creature*>(me))
@@ -63,11 +68,14 @@ void Vehicle::Install()
{
if(!cre->m_spells[i])
continue;
+
SpellEntry const *spellInfo = sSpellStore.LookupEntry(cre->m_spells[i]);
if(!spellInfo)
continue;
+
if(spellInfo->powerType == POWER_MANA)
break;
+
if(spellInfo->powerType == POWER_ENERGY)
{
me->setPowerType(POWER_ENERGY);
@@ -77,8 +85,10 @@ void Vehicle::Install()
}
}
}
+
Reset();
}
+
void Vehicle::InstallAllAccessories()
{
switch(me->GetEntry())
@@ -107,6 +117,7 @@ void Vehicle::InstallAllAccessories()
case 33214:InstallAccessory(33218,1,false);break; // Mechanolift 304-A
}
}
+
void Vehicle::Uninstall()
{
sLog.outDebug("Vehicle::Uninstall %u", me->GetEntry());
@@ -116,6 +127,7 @@ void Vehicle::Uninstall()
((TempSummon*)passenger)->UnSummon();
RemoveAllPassengers();
}
+
void Vehicle::Die()
{
sLog.outDebug("Vehicle::Die %u", me->GetEntry());
@@ -125,6 +137,7 @@ void Vehicle::Die()
passenger->setDeathState(JUST_DIED);
RemoveAllPassengers();
}
+
void Vehicle::Reset()
{
sLog.outDebug("Vehicle::Reset");
@@ -132,6 +145,7 @@ void Vehicle::Reset()
if(m_usableSeatNum)
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
}
+
void Vehicle::RemoveAllPassengers()
{
sLog.outDebug("Vehicle::RemoveAllPassengers");
@@ -151,18 +165,21 @@ void Vehicle::RemoveAllPassengers()
}
}
}
+
bool Vehicle::HasEmptySeat(int8 seatId) const
{
SeatMap::const_iterator seat = m_Seats.find(seatId);
if(seat == m_Seats.end()) return false;
return !seat->second.passenger;
}
+
Unit *Vehicle::GetPassenger(int8 seatId) const
{
SeatMap::const_iterator seat = m_Seats.find(seatId);
if(seat == m_Seats.end()) return NULL;
return seat->second.passenger;
}
+
int8 Vehicle::GetNextEmptySeat(int8 seatId, bool next) const
{
SeatMap::const_iterator seat = m_Seats.find(seatId);
@@ -186,6 +203,7 @@ int8 Vehicle::GetNextEmptySeat(int8 seatId, bool next) const
}
return seat->first;
}
+
void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion)
{
if(Unit *passenger = GetPassenger(seatId))
@@ -200,6 +218,7 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion)
}
passenger->ExitVehicle(); // this should not happen
}
+
//TODO: accessory should be minion
if(Creature *accessory = me->SummonCreature(entry, *me, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 30000))
{
@@ -210,16 +229,19 @@ void Vehicle::InstallAccessory(uint32 entry, int8 seatId, bool minion)
accessory->SendMovementFlagUpdate();
}
}
+
bool Vehicle::AddPassenger(Unit *unit, int8 seatId)
{
if(unit->GetVehicle() != this)
return false;
+
SeatMap::iterator seat;
if(seatId < 0) // no specific seat requirement
{
for(seat = m_Seats.begin(); seat != m_Seats.end(); ++seat)
if(!seat->second.passenger && seat->second.seatInfo->IsUsable())
break;
+
if(seat == m_Seats.end()) // no available seat
return false;
}
@@ -228,11 +250,15 @@ bool Vehicle::AddPassenger(Unit *unit, int8 seatId)
seat = m_Seats.find(seatId);
if(seat == m_Seats.end())
return false;
+
if(seat->second.passenger)
seat->second.passenger->ExitVehicle();
+
assert(!seat->second.passenger);
}
+
sLog.outDebug("Unit %s enter vehicle entry %u id %u dbguid %u seat %d", unit->GetName(), me->GetEntry(), m_vehicleInfo->m_ID, me->GetGUIDLow(), (int32)seat->first);
+
seat->second.passenger = unit;
if(seat->second.seatInfo->IsUsable())
{
@@ -241,9 +267,12 @@ bool Vehicle::AddPassenger(Unit *unit, int8 seatId)
if(!m_usableSeatNum)
me->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
}
+
if(seat->second.seatInfo->m_flags && !(seat->second.seatInfo->m_flags & 0x400))
unit->addUnitState(UNIT_STAT_ONVEHICLE);
+
//SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_PLAYER_CONTROLLED);
+
unit->AddUnitMovementFlag(MOVEMENTFLAG_ONTRANSPORT);
VehicleSeatEntry const *veSeat = seat->second.seatInfo;
unit->m_movementInfo.t_x = veSeat->m_attachmentOffsetX;
@@ -252,9 +281,11 @@ bool Vehicle::AddPassenger(Unit *unit, int8 seatId)
unit->m_movementInfo.t_o = 0;
unit->m_movementInfo.t_time = 0; // 1 for player
unit->m_movementInfo.t_seat = seat->first;
+
if(unit->GetTypeId() == TYPEID_PLAYER && seat->first == 0 && seat->second.seatInfo->m_flags & 0x800) // not right
if (!me->SetCharmedBy(unit, CHARM_TYPE_VEHICLE))
assert(false);
+
if(me->GetTypeId() == TYPEID_UNIT)
{
if(me->IsInWorld())
@@ -263,24 +294,32 @@ bool Vehicle::AddPassenger(Unit *unit, int8 seatId)
// move self = move all passengers
me->GetMap()->CreatureRelocation((Creature*)me, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation());
}
+
if(((Creature*)me)->IsAIEnabled)
((Creature*)me)->AI()->PassengerBoarded(unit, seat->first, true);
}
+
//if(unit->GetTypeId() == TYPEID_PLAYER)
// ((Player*)unit)->SendTeleportAckMsg();
//unit->SendMovementFlagUpdate();
+
return true;
}
+
void Vehicle::RemovePassenger(Unit *unit)
{
if(unit->GetVehicle() != this)
return;
+
SeatMap::iterator seat;
for(seat = m_Seats.begin(); seat != m_Seats.end(); ++seat)
if(seat->second.passenger == unit)
break;
+
assert(seat != m_Seats.end());
+
sLog.outDebug("Unit %s exit vehicle entry %u id %u dbguid %u seat %d", unit->GetName(), me->GetEntry(), m_vehicleInfo->m_ID, me->GetGUIDLow(), (int32)seat->first);
+
seat->second.passenger = NULL;
if(seat->second.seatInfo->IsUsable())
{
@@ -288,15 +327,21 @@ void Vehicle::RemovePassenger(Unit *unit)
me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
++m_usableSeatNum;
}
+
unit->clearUnitState(UNIT_STAT_ONVEHICLE);
+
//SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_SPELLCLICK);
+
if(unit->GetTypeId() == TYPEID_PLAYER && seat->first == 0 && seat->second.seatInfo->m_flags & 0x800)
me->RemoveCharmedBy(unit);
+
if(me->GetTypeId() == TYPEID_UNIT && ((Creature*)me)->IsAIEnabled)
((Creature*)me)->AI()->PassengerBoarded(unit, seat->first, false);
+
// only for flyable vehicles?
//CastSpell(this, 45472, true); // Parachute
}
+
void Vehicle::Dismiss()
{
sLog.outDebug("Vehicle::Dismiss %u", me->GetEntry());
diff --git a/src/game/Vehicle.h b/src/game/Vehicle.h
index 2a85912b7b6..620f194a15b 100644
--- a/src/game/Vehicle.h
+++ b/src/game/Vehicle.h
@@ -15,36 +15,46 @@
* 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_VEHICLE_H
#define MANGOSSERVER_VEHICLE_H
+
#include "ObjectDefines.h"
+
struct VehicleEntry;
struct VehicleSeatEntry;
class Unit;
+
enum PowerType
{
POWER_STEAM = 61,
POWER_PYRITE = 41,
};
+
struct VehicleSeat
{
explicit VehicleSeat(VehicleSeatEntry const *_seatInfo) : seatInfo(_seatInfo), passenger(NULL) {}
VehicleSeatEntry const *seatInfo;
Unit* passenger;
};
+
typedef std::map<int8, VehicleSeat> SeatMap;
+
class TRINITY_DLL_SPEC Vehicle
{
public:
explicit Vehicle(Unit *unit, VehicleEntry const *vehInfo);
virtual ~Vehicle();
+
void Install();
void Uninstall();
void Reset();
void Die();
void InstallAllAccessories();
+
Unit *GetBase() const { return me; }
VehicleEntry const *GetVehicleInfo() { return m_vehicleInfo; }
+
bool HasEmptySeat(int8 seatId) const;
Unit *GetPassenger(int8 seatId) const;
int8 GetNextEmptySeat(int8 seatId, bool next) const;
@@ -52,11 +62,14 @@ class TRINITY_DLL_SPEC Vehicle
void RemovePassenger(Unit *passenger);
void RemoveAllPassengers();
void Dismiss();
+
SeatMap m_Seats;
+
protected:
Unit *me;
VehicleEntry const *m_vehicleInfo;
uint32 m_usableSeatNum;
+
void InstallAccessory(uint32 entry, int8 seatId, bool minion = true);
};
#endif
diff --git a/src/game/VoiceChatHandler.cpp b/src/game/VoiceChatHandler.cpp
index 2c9c42b459f..6023233c444 100644
--- a/src/game/VoiceChatHandler.cpp
+++ b/src/game/VoiceChatHandler.cpp
@@ -17,11 +17,13 @@
* 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"
+
void WorldSession::HandleVoiceSessionEnableOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_VOICE_SESSION_ENABLE");
@@ -30,12 +32,14 @@ void WorldSession::HandleVoiceSessionEnableOpcode( WorldPacket & recv_data )
recv_data.read_skip<uint8>();
recv_data.hexlike();
}
+
void WorldSession::HandleChannelVoiceOnOpcode( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_CHANNEL_VOICE_ON");
// Enable Voice button in channel context menu
recv_data.hexlike();
}
+
void WorldSession::HandleSetActiveVoiceChannel( WorldPacket & recv_data )
{
sLog.outDebug("WORLD: CMSG_SET_ACTIVE_VOICE_CHANNEL");
diff --git a/src/game/WaypointManager.cpp b/src/game/WaypointManager.cpp
index add7cd8998d..b9fb027d9c5 100644
--- a/src/game/WaypointManager.cpp
+++ b/src/game/WaypointManager.cpp
@@ -17,17 +17,21 @@
* 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 "WaypointManager.h"
#include "ProgressBar.h"
#include "MapManager.h"
+
UNORDERED_MAP<uint32, WaypointPath*> waypoint_map;
WaypointStore WaypointMgr;
+
void WaypointStore::Free()
{
waypoint_map.clear();
}
+
void WaypointStore::Load()
{
QueryResult *result = WorldDatabase.PQuery("SELECT COUNT(id) FROM waypoint_data");
@@ -36,33 +40,42 @@ void WaypointStore::Load()
sLog.outError("an error occured while loading the table `waypoint_data` (maybe it doesn't exist ?)");
exit(1); // Stop server at loading non exited table or not accessable table
}
+
records = (*result)[0].GetUInt32();
delete result;
+
result = WorldDatabase.PQuery("SELECT id,point,position_x,position_y,position_z,move_flag,delay,action,action_chance FROM waypoint_data ORDER BY id, point");
if(!result)
{
sLog.outErrorDb("The table `waypoint_data` is empty or corrupted");
return;
}
+
WaypointPath* path_data;
uint32 total_records = result->GetRowCount();
+
barGoLink bar( total_records);
Field *fields;
uint32 last_id = 0;
+
do
{
fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
bar.step();
WaypointData *wp = new WaypointData;
+
if(last_id != id)
path_data = new WaypointPath;
+
float x,y,z;
x = fields[2].GetFloat();
y = fields[3].GetFloat();
z = fields[4].GetFloat();
+
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
+
wp->id = fields[1].GetUInt32();
wp->x = x;
wp->y = y;
@@ -71,35 +84,50 @@ void WaypointStore::Load()
wp->delay = fields[6].GetUInt32();
wp->event_id = fields[7].GetUInt32();
wp->event_chance = fields[8].GetUInt8();
+
path_data->push_back(wp);
+
if(id != last_id)
waypoint_map[id] = path_data;
+
last_id = id;
+
} while(result->NextRow()) ;
+
delete result;
}
+
void WaypointStore::UpdatePath(uint32 id)
{
if(waypoint_map.find(id)!= waypoint_map.end())
waypoint_map[id]->clear();
+
QueryResult *result;
+
result = WorldDatabase.PQuery("SELECT id,point,position_x,position_y,position_z,move_flag,delay,action,action_chance FROM waypoint_data WHERE id = %u ORDER BY point", id);
+
if(!result)
return;
+
WaypointPath* path_data;
path_data = new WaypointPath;
Field *fields;
+
do
{
fields = result->Fetch();
uint32 id = fields[0].GetUInt32();
+
WaypointData *wp = new WaypointData;
+
float x,y,z;
x = fields[2].GetFloat();
y = fields[3].GetFloat();
z = fields[4].GetFloat();
+
Trinity::NormalizeMapCoord(x);
Trinity::NormalizeMapCoord(y);
+
wp->id = fields[1].GetUInt32();
wp->x = x;
wp->y = y;
@@ -108,10 +136,14 @@ void WaypointStore::UpdatePath(uint32 id)
wp->delay = fields[6].GetUInt32();
wp->event_id = fields[7].GetUInt32();
wp->event_chance = fields[8].GetUInt8();
+
path_data->push_back(wp);
+
}
while (result->NextRow());
+
waypoint_map[id] = path_data;
+
delete result;
}
diff --git a/src/game/WaypointManager.h b/src/game/WaypointManager.h
index 0423aeb90c6..85f8b765d45 100644
--- a/src/game/WaypointManager.h
+++ b/src/game/WaypointManager.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_WAYPOINTMANAGER_H
#define TRINITY_WAYPOINTMANAGER_H
+
#include <vector>
+
struct WaypointData
{
uint32 id;
@@ -29,24 +32,31 @@ struct WaypointData
uint32 event_id;
uint8 event_chance;
};
+
typedef std::vector<WaypointData*> WaypointPath;
extern UNORDERED_MAP<uint32, WaypointPath*> waypoint_map;
+
class WaypointStore
{
private :
uint32 records;
+
public:
void UpdatePath(uint32 id);
void Load();
void Free();
+
WaypointPath* GetPath(uint32 id)
{
if(waypoint_map.find(id) != waypoint_map.end())
return waypoint_map[id];
else return 0;
}
+
inline uint32 GetRecordsCount() { return records; }
};
+
extern WaypointStore WaypointMgr;
+
#endif
diff --git a/src/game/WaypointMovementGenerator.cpp b/src/game/WaypointMovementGenerator.cpp
index 990bfd46084..cba005a78d9 100644
--- a/src/game/WaypointMovementGenerator.cpp
+++ b/src/game/WaypointMovementGenerator.cpp
@@ -28,53 +28,67 @@
#include "CreatureGroups.h"
//Player-specific
#include "Player.h"
+
template<class T>
void
WaypointMovementGenerator<T>::Initialize(T &u){}
+
template<>
void
WaypointMovementGenerator<Creature>::Finalize(Creature &u){}
+
template<>
void
WaypointMovementGenerator<Player>::Finalize(Player &u){}
+
template<class T>
void
WaypointMovementGenerator<T>::MovementInform(T &unit){}
+
template<>
void WaypointMovementGenerator<Creature>::MovementInform(Creature &unit)
{
unit.AI()->MovementInform(WAYPOINT_MOTION_TYPE, i_currentNode);
}
+
template<>
bool WaypointMovementGenerator<Creature>::GetDestination(float &x, float &y, float &z) const
{
if(i_destinationHolder.HasArrived())
return false;
+
i_destinationHolder.GetDestination(x, y, z);
return true;
}
+
template<>
bool WaypointMovementGenerator<Player>::GetDestination(float &x, float &y, float &z) const
{
return false;
}
+
template<>
void WaypointMovementGenerator<Creature>::Reset(Creature &unit)
{
StopedByPlayer = true;
i_nextMoveTime.Reset(0);
}
+
template<>
void WaypointMovementGenerator<Player>::Reset(Player &unit){}
+
template<>
void WaypointMovementGenerator<Creature>::InitTraveller(Creature &unit, const WaypointData &node)
{
node.run ? unit.RemoveUnitMovementFlag(MOVEMENTFLAG_WALK_MODE):
unit.AddUnitMovementFlag(MOVEMENTFLAG_WALK_MODE);
+
unit.SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
unit.SetUInt32Value(UNIT_FIELD_BYTES_1, 0);
+
unit.addUnitState(UNIT_STAT_ROAMING);
}
+
template<>
void
WaypointMovementGenerator<Creature>::Initialize(Creature &u)
@@ -94,6 +108,7 @@ WaypointMovementGenerator<Creature>::Initialize(Creature &u)
InitTraveller(u, *node);
i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z);
i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+
//Call for creature group update
if(u.GetFormation() && u.GetFormation()->getLeader() == &u)
u.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
@@ -101,32 +116,41 @@ WaypointMovementGenerator<Creature>::Initialize(Creature &u)
else
node = NULL;
}
+
template<>
void WaypointMovementGenerator<Player>::InitTraveller(Player &unit, const WaypointData &node){}
+
template<class T>
bool
WaypointMovementGenerator<T>::Update(T &unit, const uint32 &diff)
{
return false;
}
+
template<>
bool
WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff)
{
if(!&unit)
return true;
+
if(!path_id)
return false;
+
// Waypoint movement can be switched on/off
// This is quite handy for escort quests and other stuff
if(unit.hasUnitState(UNIT_STAT_ROOT | UNIT_STAT_STUNNED | UNIT_STAT_DISTRACTED))
return true;
+
// Clear the generator if the path doesn't exist
if(!waypoints || !waypoints->size())
return false;
+
Traveller<Creature> traveller(unit);
+
i_nextMoveTime.Update(diff);
i_destinationHolder.UpdateTraveller(traveller, diff, true);
+
if(i_nextMoveTime.Passed())
{
if(unit.IsStopped())
@@ -140,6 +164,7 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff)
StopedByPlayer = false;
return true;
}
+
if(i_currentNode == waypoints->size() - 1) //If that's our last waypoint
{
if(repeating) //If the movement is repeating
@@ -153,10 +178,12 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff)
}
else
++i_currentNode;
+
node = waypoints->at(i_currentNode);
InitTraveller(unit, *node);
i_destinationHolder.SetDestination(traveller, node->x, node->y, node->z);
i_nextMoveTime.Reset(i_destinationHolder.GetTotalTravelTime());
+
//Call for creature group update
if(unit.GetFormation() && unit.GetFormation()->getLeader() == &unit)
unit.GetFormation()->LeaderMoveTo(node->x, node->y, node->z);
@@ -166,9 +193,11 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff)
//Determine waittime
if(node->delay)
i_nextMoveTime.Reset(node->delay);
+
//note: disable "start" for mtmap
if(node->event_id && urand(0,99) < node->event_chance)
unit.GetMap()->ScriptsStart(sWaypointScripts, node->event_id, &unit, NULL/*, false*/);
+
MovementInform(unit);
unit.UpdateWaypointID(i_currentNode);
unit.clearUnitState(UNIT_STAT_ROAMING);
@@ -189,26 +218,32 @@ WaypointMovementGenerator<Creature>::Update(Creature &unit, const uint32 &diff)
}
return true;
}
+
template void WaypointMovementGenerator<Player>::Initialize(Player &);
template bool WaypointMovementGenerator<Player>::Update(Player &, const uint32 &);
template void WaypointMovementGenerator<Player>::MovementInform(Player &);
+
//----------------------------------------------------//
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);
@@ -218,23 +253,31 @@ void FlightPathMovementGenerator::Initialize(Player &player)
Traveller<Player> traveller(player);
// do not send movement, it was sent already
i_destinationHolder.SetDestination(traveller, i_path[i_currentNode].x, i_path[i_currentNode].y, i_path[i_currentNode].z, false);
+
player.SendMonsterMoveByPath(GetPath(),GetCurrentNode(),GetPathAtMapEnd());
+
// Storage to preload flightmaster grid at end of flight. For multi-stop flights, this will
// be reinitialized for each flightmaster at the end of each spline (or stop) in the flight.
+
uint32 nodeCount = i_mapIds.size(); // Get the number of nodes in the path. i_path and i_mapIds are the
// same size when loaded in ObjectMgr::GetTaxiPathNodes, called from LoadPath()
+
m_endMapId = i_mapIds[nodeCount -1]; // Get the map ID from the last node
m_preloadTargetNode = nodeCount / 2; // Split the number of nodes in half to preload the flightmaster half-way through the flight
m_endGridX = i_path[nodeCount -1].x; // Get the X position from the last node
m_endGridY = i_path[nodeCount -1].y; // Get tye Y position from the last node
+
}
+
void FlightPathMovementGenerator::Finalize(Player & player)
{
player.clearUnitState(UNIT_STAT_IN_FLIGHT);
+
float x, y, z;
i_destinationHolder.GetLocationNow(player.GetBaseMap(), x, y, z);
player.SetPosition(x, y, z, player.GetOrientation());
}
+
bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
{
if( MovementInProgress() )
@@ -255,6 +298,7 @@ bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
// 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);
}
+
// check if it's time to preload the flightmaster grid
// at path end
if (i_currentNode == m_preloadTargetNode)
@@ -271,13 +315,16 @@ bool FlightPathMovementGenerator::Update(Player &player, const uint32 &diff)
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(size_t i = 1; i < i_mapIds.size(); ++i)
{
@@ -288,10 +335,12 @@ void FlightPathMovementGenerator::SetCurrentNodeAfterTeleport()
}
}
}
+
void FlightPathMovementGenerator::PreloadEndGrid()
{
// used to preload the final grid where the flightmaster is
Map *endMap = MapManager::Instance().FindMap(m_endMapId);
+
// Load the grid
if (endMap)
{
@@ -302,12 +351,17 @@ void FlightPathMovementGenerator::PreloadEndGrid()
{
sLog.outDetail("Unable to determine map to preload flightmaster grid");
}
+
}
+
//
// 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];
@@ -316,19 +370,24 @@ int ShortenASTARRoute(short int *pathlist, int number)
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))
@@ -341,21 +400,26 @@ int ShortenASTARRoute(short int *pathlist, int number)
}
}
}
+
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
@@ -376,6 +440,7 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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;
@@ -383,19 +448,23 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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
@@ -404,8 +473,10 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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)
{
@@ -425,6 +496,7 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
v = 2*u;
}
}
+
if (u != v) //if they're out of order, swap this item with its parent
{
temp = openlist[u];
@@ -434,9 +506,11 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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;
@@ -448,17 +522,22 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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
@@ -480,16 +559,19 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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)
@@ -517,23 +599,28 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
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__
@@ -542,6 +629,7 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
{
//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__
@@ -550,8 +638,10 @@ int CreatePathAStar(gentity_t *bot, int from, int to, short int *pathlist)
return count;
}
}
+
return count; //return the number of nodes in the path, -1 if not found
}
+
/*
===========================================================================
GetFCost
@@ -569,6 +659,7 @@ 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)
@@ -581,8 +672,10 @@ int GetFCost(int to, int num, int parentNum, float *gcost)
}
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
index 1f0f0b9e2e0..4e64229b524 100644
--- a/src/game/WaypointMovementGenerator.h
+++ b/src/game/WaypointMovementGenerator.h
@@ -17,45 +17,58 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_WAYPOINTMOVEMENTGENERATOR_H
#define TRINITY_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 <vector>
#include <set>
+
#define FLIGHT_TRAVEL_UPDATE 100
#define STOP_TIME_FOR_PLAYER 3 * MINUTE * IN_MILISECONDS // 3 Minutes
+
template<class T, class P = Path>
class TRINITY_DLL_SPEC PathMovementBase
{
public:
PathMovementBase() : i_currentNode(0) {}
virtual ~PathMovementBase() {};
+
bool MovementInProgress(void) const { return i_currentNode < i_path.Size(); }
+
void LoadPath(T &);
void ReloadPath(T &);
uint32 GetCurrentNode() const { return i_currentNode; }
+
protected:
uint32 i_currentNode;
DestinationHolder< Traveller<T> > i_destinationHolder;
P i_path;
};
+
template<class T>
+
class TRINITY_DLL_SPEC WaypointMovementGenerator
: public MovementGeneratorMedium< T, WaypointMovementGenerator<T> >, public PathMovementBase<T>
{
public:
WaypointMovementGenerator(uint32 _path_id = 0, bool _repeating = true) :
i_nextMoveTime(0), path_id(_path_id), repeating(_repeating), StopedByPlayer(false), node(NULL) {}
+
void Initialize(T &);
void Finalize(T &);
void MovementInform(T &);
@@ -65,6 +78,7 @@ class TRINITY_DLL_SPEC WaypointMovementGenerator
bool Update(T &, const uint32 &);
bool GetDestination(float &x, float &y, float &z) const;
MovementGeneratorType GetMovementGeneratorType() { return WAYPOINT_MOTION_TYPE; }
+
private:
WaypointData *node;
uint32 path_id;
@@ -72,6 +86,7 @@ class TRINITY_DLL_SPEC WaypointMovementGenerator
WaypointPath *waypoints;
bool repeating, StopedByPlayer;
};
+
/** FlightPathMovementGenerator generates movement of the player for the paths
* and hence generates ground and activities for the player.
*/
@@ -88,14 +103,17 @@ public PathMovementBase<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;
bool HasArrived() const { return (i_currentNode >= i_path.Size()); }
void SetCurrentNodeAfterTeleport();
void SkipCurrentNode() { ++i_currentNode; }
bool GetDestination(float& x, float& y, float& z) const { i_destinationHolder.GetDestination(x,y,z); return true; }
+
private:
// storage for preloading the flightmaster grid at end
// before reaching final waypoint
diff --git a/src/game/Weather.cpp b/src/game/Weather.cpp
index 9eb8e523997..5d8d1fbd9ba 100644
--- a/src/game/Weather.cpp
+++ b/src/game/Weather.cpp
@@ -17,9 +17,11 @@
* 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 "Player.h"
@@ -27,20 +29,24 @@
#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() / (MINUTE*IN_MILISECONDS)) );
}
+
/// Launch a weather update
bool Weather::Update(uint32 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())
{
@@ -55,6 +61,7 @@ bool Weather::Update(uint32 diff)
}
return true;
}
+
/// Calculate the new weather
bool Weather::ReGenerate()
{
@@ -64,45 +71,56 @@ bool Weather::ReGenerate()
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(&gtime);
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
@@ -124,10 +142,12 @@ bool Weather::ReGenerate()
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;
@@ -137,11 +157,13 @@ bool Weather::ReGenerate()
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;
@@ -159,36 +181,46 @@ bool Weather::ReGenerate()
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)
@@ -232,22 +264,27 @@ bool Weather::UpdateWeather()
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:
diff --git a/src/game/Weather.h b/src/game/Weather.h
index 2e4e49a52db..e312f004060 100644
--- a/src/game/Weather.h
+++ b/src/game/Weather.h
@@ -17,15 +17,20 @@
* 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,
@@ -41,7 +46,9 @@ enum WeatherState
WEATHER_STATE_THUNDERS = 86,
WEATHER_STATE_BLACKRAIN = 90
};
+
struct WeatherZoneChances;
+
/// Weather for one zone
class Weather
{
diff --git a/src/game/Wintergrasp.cpp b/src/game/Wintergrasp.cpp
index f05ac4deada..ea336ec870e 100644
--- a/src/game/Wintergrasp.cpp
+++ b/src/game/Wintergrasp.cpp
@@ -15,27 +15,34 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "Wintergrasp.h"
#include "SpellAuras.h"
#include "Vehicle.h"
#include "ObjectMgr.h"
#include "World.h"
+
typedef uint32 TeamPair[2];
+
enum WintergraspQuest
{
SLAY_THEM_ALL_1 = 13180, //Horde Quest
SLAY_THEM_ALL_2 = 13178, //Horde Quest
+
NO_MERCY_MERCILESS = 13177, //Alliance Quest
NO_MERCY_MERCILESS_1 = 13179, //Alliance Quest
+
A_VICTORY_IN_WG = 13181,
H_VICTORY_IN_WG = 13183
};
+
enum CreatureEntry
{
CRE_ENG_A = 30499,
CRE_ENG_H = 30400,
CRE_PVP_KILL = 31086 //Quest Objective
};
+
const TeamPair CreatureEntryPair[] =
{
{32307, 32308}, // Guards
@@ -51,12 +58,14 @@ const TeamPair CreatureEntryPair[] =
{32615, 32626}, // Warbringer & Brigadier General
{0,0}
};
+
const TeamPair GODisplayPair[] =
{
{5651, 5652},
{8256, 8257},
{0,0}
};
+
void LoadTeamPair(TeamPairMap &pairMap, const TeamPair *pair)
{
while((*pair)[0])
@@ -66,21 +75,27 @@ void LoadTeamPair(TeamPairMap &pairMap, const TeamPair *pair)
++pair;
}
}
+
#define REMOVE_WARTIME_AURAS(p) (p)->RemoveAura(SPELL_RECRUIT);\
(p)->RemoveAura(SPELL_CORPORAL);(p)->RemoveAura(SPELL_LIEUTENANT)
#define REMOVE_TENACITY_AURA(p) CastTenacity(p, 0)
+
// Visual defines, easier to understand code
#define getDefenderTeam() m_defender
#define getAttackerTeam() OTHER_TEAM(m_defender)
+
typedef std::list<const AreaPOIEntry *> AreaPOIList;
+
bool OPvPWintergrasp::SetupOutdoorPvP()
{
if(!sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_ENABLED))
return false;
+
m_defender = TeamId(rand()%2);
m_changeDefender = false;
m_workshopCount[TEAM_ALLIANCE] = 0;
m_workshopCount[TEAM_HORDE] = 0;
+
// Select POI
AreaPOIList areaPOIs;
float minX = 9999, minY = 9999, maxX = -9999, maxY = -9999;
@@ -97,6 +112,7 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
}
}
minX -= 20; minY -= 20; maxX += 20; maxY += 20;
+
// Coords: 4290.330078, 1790.359985 - 5558.379883, 4048.889893
QueryResult *result = WorldDatabase.PQuery("SELECT guid FROM gameobject,gameobject_template"
" WHERE gameobject.map=571"
@@ -107,13 +123,16 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
minX, minY, maxX, maxY);
if (!result)
return false;
+
do
{
Field *fields = result->Fetch();
+
uint32 guid = fields[0].GetUInt32();
GameObjectData const * goData = objmgr.GetGOData(guid);
if (!goData) // this should not happen
continue;
+
float x = goData->posX, y = goData->posY;
float minDist = 100;
AreaPOIList::iterator poi = areaPOIs.end();
@@ -121,6 +140,7 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
{
if (!(*itr)->icon[1]) // note: may for other use
continue;
+
float dist = (abs((*itr)->x - x) + abs((*itr)->y - y));
if (minDist > dist)
{
@@ -128,16 +148,20 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
poi = itr;
}
}
+
if (poi == areaPOIs.end())
continue;
+
// add building to the list
TeamId teamId = x > POS_X_CENTER ? getDefenderTeam() : getAttackerTeam();
m_buildingStates[guid] = new BuildingState((*poi)->worldState, teamId, getDefenderTeam() != TEAM_ALLIANCE);
if ((*poi)->id == 2246)
m_gate = m_buildingStates[guid];
areaPOIs.erase(poi);
+
// add capture point
uint32 capturePointEntry = 0;
+
switch(goData->id)
{
case 192028: // NW
@@ -155,14 +179,17 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
if (capturePointEntry)
{
uint32 engGuid = 0;
+
QueryResult *result = WorldDatabase.PQuery("SELECT guid FROM creature"
" WHERE creature.map=571"
" AND creature.id IN (%u, %u);", CRE_ENG_A, CRE_ENG_H);
+
if (!result)
{
sLog.outError("Cannot find siege workshop master in creature!");
continue;
}
+
float minDist = 100;
do
{
@@ -171,6 +198,7 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
const CreatureData *creData = objmgr.GetCreatureData(guid);
if (!creData)
continue;
+
float dist = (abs(creData->posX - x) + abs(creData->posY - y));
if (minDist > dist)
{
@@ -179,11 +207,13 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
}
}while(result->NextRow());
delete result;
+
if (!engGuid)
{
sLog.outError("Cannot find nearby siege workshop master!");
continue;
}
+
SiegeWorkshop *workshop = new SiegeWorkshop(this, m_buildingStates[guid]);
if (!workshop->SetCapturePointData(capturePointEntry, goData->mapid, goData->posX + 40 * cos(goData->orientation + M_PI / 2), goData->posY + 40 * sin(goData->orientation + M_PI / 2), goData->posZ))
{
@@ -194,6 +224,7 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
const CreatureData *creData = objmgr.GetCreatureData(engGuid);
if (!creData)
continue;
+
workshop->m_engEntry = const_cast<uint32*>(&creData->id);
const_cast<CreatureData*>(creData)->displayid = 0;
workshop->m_workshopGuid = guid;
@@ -207,11 +238,13 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
}
}while(result->NextRow());
delete result;
+
if (!m_gate)
{
sLog.outError("Cannot find wintergrasp fortress gate!");
return false;
}
+
// Load Graveyard
GraveYardMap::const_iterator graveLow = objmgr.mGraveYardMap.lower_bound(ZONE_WINTERGRASP);
GraveYardMap::const_iterator graveUp = objmgr.mGraveYardMap.upper_bound(ZONE_WINTERGRASP);
@@ -226,6 +259,7 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
++itr;
continue;
}
+
GraveYardMap::const_iterator graveItr;
for (graveItr = graveLow; graveItr != graveUp; ++graveItr)
if (graveItr->second.safeLocId == loc->ID)
@@ -237,6 +271,7 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
graveData.team = 0;
graveItr = objmgr.mGraveYardMap.insert(std::make_pair(ZONE_WINTERGRASP, graveData));
}
+
for (BuildingStateMap::iterator stateItr = m_buildingStates.begin(); stateItr != m_buildingStates.end(); ++stateItr)
{
if (stateItr->second->worldState == (*itr)->worldState)
@@ -250,21 +285,28 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
else
++itr;
}
+
//for (AreaPOIList::iterator itr = areaPOIs.begin(); itr != areaPOIs.end(); ++itr)
// sLog.outError("not assigned %u %f %f", (*itr)->id, (*itr)->x, (*itr)->y);
+
//gameeventmgr.StartInternalEvent(GameEventWintergraspDefender[getDefenderTeam()]);
+
//Titan Relic eventid = 19982
objmgr.AddGOData(192829, 571, 5440, 2840.8, 420.43 + 10, 0);
+
LoadTeamPair(m_goDisplayPair, GODisplayPair);
LoadTeamPair(m_creEntryPair, CreatureEntryPair);
+
m_wartime = false;
m_timer = sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_START_TIME) * MINUTE * IN_MILISECONDS;
+
m_towerCount[getDefenderTeam()][DAMAGE_INTACT] = 4;
m_towerCount[getAttackerTeam()][DAMAGE_INTACT] = 3;
m_towerCount[TEAM_ALLIANCE][DAMAGE_DAMAGED] = 0;
m_towerCount[TEAM_HORDE][DAMAGE_DAMAGED] = 0;
m_towerCount[TEAM_ALLIANCE][DAMAGE_DESTROYED] = 0;
m_towerCount[TEAM_HORDE][DAMAGE_DESTROYED] = 0;
+
// Load custom rewards
if (sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_CUSTOM_HONOR))
{
@@ -275,10 +317,13 @@ bool OPvPWintergrasp::SetupOutdoorPvP()
m_customHonorReward[DAMAGED_BUILDING] = sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_DAMAGED_BUILDING);
m_customHonorReward[INTACT_BUILDING] = sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_INTACT_BUILDING);
}
+
RemoveOfflinePlayerWGAuras();
+
RegisterZone(ZONE_WINTERGRASP);
return true;
}
+
void OPvPWintergrasp::ProcessEvent(GameObject *obj, uint32 eventId)
{
if (eventId == 19982)
@@ -298,12 +343,14 @@ void OPvPWintergrasp::ProcessEvent(GameObject *obj, uint32 eventId)
if (eventId == obj->GetGOInfo()->building.damagedEvent)
{
state->damageState = DAMAGE_DAMAGED;
+
if (state->type == BUILDING_TOWER)
++m_towerCount[state->GetTeam()][DAMAGE_DAMAGED];
}
else if (eventId == obj->GetGOInfo()->building.destroyedEvent)
{
state->damageState = DAMAGE_DESTROYED;
+
if (state->type == BUILDING_WORKSHOP)
ModifyWorkshopCount(state->GetTeam(), false);
else if (state->type == BUILDING_TOWER)
@@ -316,24 +363,29 @@ void OPvPWintergrasp::ProcessEvent(GameObject *obj, uint32 eventId)
}
}
}
+
void OPvPWintergrasp::RemoveOfflinePlayerWGAuras()
{
// if server crashed while in battle there could be players with rank or tenacity
CharacterDatabase.PExecute("DELETE FROM character_aura WHERE spell IN (%u,%u,%u, %u)",
SPELL_RECRUIT, SPELL_CORPORAL, SPELL_LIEUTENANT, SPELL_TENACITY);
}
+
void OPvPWintergrasp::ModifyWorkshopCount(TeamId team, bool add)
{
if (team == TEAM_NEUTRAL)
return;
+
if (add)
++m_workshopCount[team];
else if (m_workshopCount[team])
--m_workshopCount[team];
else
sLog.outError("OPvPWintergrasp::ModifyWorkshopCount: negative workshop count!");
+
SendUpdateWorldState(MaxVehNumWorldState[team], m_workshopCount[team] * MAX_VEHICLE_PER_WORKSHOP);
}
+
uint32 OPvPWintergrasp::GetCreatureEntry(uint32 guidlow, const CreatureData *data)
{
if (getDefenderTeam() == TEAM_ALLIANCE)
@@ -347,6 +399,7 @@ uint32 OPvPWintergrasp::GetCreatureEntry(uint32 guidlow, const CreatureData *dat
}
return data->id;
}
+
WintergraspCreType OPvPWintergrasp::GetCreatureType(uint32 entry) const
{
// VEHICLES, GUARDS and TURRETS gives kill credit
@@ -385,6 +438,7 @@ WintergraspCreType OPvPWintergrasp::GetCreatureType(uint32 entry) const
return CREATURE_OTHER; // Revenants, Elementals, etc
}
}
+
void OPvPWintergrasp::OnCreatureCreate(Creature *creature, bool add)
{
uint32 entry = creature->GetEntry();
@@ -394,6 +448,7 @@ void OPvPWintergrasp::OnCreatureCreate(Creature *creature, bool add)
{
if (!creature->isSummon())
return;
+
TeamId team;
if (add)
{
@@ -403,6 +458,7 @@ void OPvPWintergrasp::OnCreatureCreate(Creature *creature, bool add)
team = TEAM_HORDE;
else
return;
+
if (uint32 engLowguid = GUID_LOPART(((TempSummon*)creature)->GetSummonerGUID()))
{
if (SiegeWorkshop *workshop = GetWorkshopByEngGuid(engLowguid))
@@ -420,6 +476,7 @@ void OPvPWintergrasp::OnCreatureCreate(Creature *creature, bool add)
}
}
}
+
if (m_tenacityStack > 0)
{
if (team == TEAM_ALLIANCE)
@@ -464,9 +521,11 @@ void OPvPWintergrasp::OnCreatureCreate(Creature *creature, bool add)
break;
}
}
+
void OPvPWintergrasp::OnGameObjectCreate(GameObject *go, bool add)
{
OutdoorPvP::OnGameObjectCreate(go, add);
+
if (UpdateGameObjectInfo(go))
{
if (add) m_gobjects.insert(go);
@@ -494,6 +553,7 @@ void OPvPWintergrasp::OnGameObjectCreate(GameObject *go, bool add)
}
}
}
+
void OPvPWintergrasp::UpdateAllWorldObject()
{
// update cre and go factions
@@ -501,13 +561,16 @@ void OPvPWintergrasp::UpdateAllWorldObject()
UpdateGameObjectInfo(*itr);
for (CreatureSet::iterator itr = m_creatures.begin(); itr != m_creatures.end(); ++itr)
UpdateCreatureInfo(*itr);
+
// rebuild and update building states
RebuildAllBuildings();
+
// update capture points
for (OPvPCapturePointMap::iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
if (SiegeWorkshop *workshop = dynamic_cast<SiegeWorkshop*>(itr->second))
workshop->SetTeamByBuildingState();
}
+
void OPvPWintergrasp::RebuildAllBuildings()
{
for (BuildingStateMap::const_iterator itr = m_buildingStates.begin(); itr != m_buildingStates.end(); ++itr)
@@ -520,14 +583,17 @@ void OPvPWintergrasp::RebuildAllBuildings()
}
else
itr->second->health = 0;
+
if (itr->second->damageState == DAMAGE_DESTROYED)
{
if (itr->second->type == BUILDING_WORKSHOP)
ModifyWorkshopCount(itr->second->GetTeam(), true);
}
+
itr->second->damageState = DAMAGE_INTACT;
itr->second->SetTeam(getDefenderTeam() == TEAM_ALLIANCE ? OTHER_TEAM(itr->second->defaultTeam) : itr->second->defaultTeam);
}
+
m_towerCount[getDefenderTeam()][DAMAGE_INTACT] = 4;
m_towerCount[getAttackerTeam()][DAMAGE_INTACT] = 3;
m_towerCount[TEAM_ALLIANCE][DAMAGE_DAMAGED] = 0;
@@ -535,6 +601,7 @@ void OPvPWintergrasp::RebuildAllBuildings()
m_towerCount[TEAM_ALLIANCE][DAMAGE_DESTROYED] = 0;
m_towerCount[TEAM_HORDE][DAMAGE_DESTROYED] = 0;
}
+
void OPvPWintergrasp::SendInitWorldStatesTo(Player *player) const
{
WorldPacket data(SMSG_INIT_WORLD_STATES, (4+4+4+2+(m_buildingStates.size()*8)));
@@ -542,23 +609,29 @@ void OPvPWintergrasp::SendInitWorldStatesTo(Player *player) const
data << uint32(ZONE_WINTERGRASP);
data << uint32(0);
data << uint16(4+5+4+m_buildingStates.size());
+
data << uint32(3803) << uint32(getDefenderTeam() == TEAM_ALLIANCE ? 1 : 0);
data << uint32(3802) << uint32(getDefenderTeam() != TEAM_ALLIANCE ? 1 : 0);
data << uint32(3801) << uint32(isWarTime() ? 0 : 1);
data << uint32(3710) << uint32(isWarTime() ? 1 : 0);
+
for (uint32 i = 0; i < 5; ++i)
data << ClockWorldState[i] << m_clock[i];
+
data << uint32(3490) << uint32(m_vehicles[TEAM_HORDE].size());
data << uint32(3491) << m_workshopCount[TEAM_HORDE] * MAX_VEHICLE_PER_WORKSHOP;
data << uint32(3680) << uint32(m_vehicles[TEAM_ALLIANCE].size());
data << uint32(3681) << m_workshopCount[TEAM_ALLIANCE] * MAX_VEHICLE_PER_WORKSHOP;
+
for (BuildingStateMap::const_iterator itr = m_buildingStates.begin(); itr != m_buildingStates.end(); ++itr)
itr->second->FillData(data);
+
if (player)
player->GetSession()->SendPacket(&data);
else
BroadcastPacket(data);
}
+
void OPvPWintergrasp::BroadcastStateChange(BuildingState *state) const
{
if (m_sendUpdate)
@@ -566,6 +639,7 @@ void OPvPWintergrasp::BroadcastStateChange(BuildingState *state) const
for (PlayerSet::const_iterator p_itr = m_players[team].begin(); p_itr != m_players[team].end(); ++p_itr)
state->SendUpdate(*p_itr);
}
+
// Called at Start and Battle End
bool OPvPWintergrasp::UpdateCreatureInfo(Creature *creature) const
{
@@ -606,6 +680,7 @@ bool OPvPWintergrasp::UpdateCreatureInfo(Creature *creature) const
creature->DisappearAndDie();
return false;
}
+
TeamPairMap::const_iterator itr = m_creEntryPair.find(creature->GetCreatureData()->id);
if (itr != m_creEntryPair.end())
{
@@ -616,17 +691,20 @@ bool OPvPWintergrasp::UpdateCreatureInfo(Creature *creature) const
}
return false;
}
+
// Return false = Need to rebuild at battle End/Start
// true = no need to rebuild (ie: Banners or teleporters)
bool OPvPWintergrasp::UpdateGameObjectInfo(GameObject *go) const
{
uint32 attFaction = 35;
uint32 defFaction = 35;
+
if (isWarTime())
{
attFaction = WintergraspFaction[getAttackerTeam()];
defFaction = WintergraspFaction[getDefenderTeam()];
}
+
switch(go->GetGOInfo()->displayId)
{
case 8244: // Defender's Portal - Vehicle Teleporter
@@ -635,6 +713,7 @@ bool OPvPWintergrasp::UpdateGameObjectInfo(GameObject *go) const
case 7967: // Titan relic
go->SetUInt32Value(GAMEOBJECT_FACTION, attFaction);
return true;
+
case 8165: // Wintergrasp Keep Door
case 7877: // Wintergrasp Fortress Wall
case 7878: // Wintergrasp Keep Tower
@@ -651,6 +730,7 @@ bool OPvPWintergrasp::UpdateGameObjectInfo(GameObject *go) const
go->SetUInt32Value(GAMEOBJECT_FACTION, WintergraspFaction[workshop->m_buildingState->GetTeam()]);
return false;
}
+
// Note: this is only for test, still need db support
TeamPairMap::const_iterator itr = m_goDisplayPair.find(go->GetGOInfo()->displayId);
if (itr != m_goDisplayPair.end())
@@ -661,15 +741,18 @@ bool OPvPWintergrasp::UpdateGameObjectInfo(GameObject *go) const
}
return false;
}
+
void OPvPWintergrasp::HandlePlayerEnterZone(Player * plr, uint32 zone)
{
if (isWarTime() && !plr->HasAura(SPELL_RECRUIT) && !plr->HasAura(SPELL_CORPORAL)
&& !plr->HasAura(SPELL_LIEUTENANT))
plr->CastSpell(plr, SPELL_RECRUIT, true);
+
SendInitWorldStatesTo(plr);
OutdoorPvP::HandlePlayerEnterZone(plr, zone);
UpdateTenacityStack();
}
+
void OPvPWintergrasp::HandlePlayerLeaveZone(Player * plr, uint32 zone)
{
if (!plr->GetSession()->PlayerLogout())
@@ -682,6 +765,7 @@ void OPvPWintergrasp::HandlePlayerLeaveZone(Player * plr, uint32 zone)
OutdoorPvP::HandlePlayerLeaveZone(plr, zone);
UpdateTenacityStack();
}
+
void OPvPWintergrasp::PromotePlayer(Player *killer) const
{
Aura *aur;
@@ -708,6 +792,7 @@ void OPvPWintergrasp::PromotePlayer(Player *killer) const
else if (killer->HasAura(SPELL_LIEUTENANT))
killer->CastSpell(killer, SPELL_LIEUTENANT, true);
}
+
void OPvPWintergrasp::HandleKill(Player *killer, Unit *victim)
{
bool ok = false;
@@ -715,6 +800,7 @@ void OPvPWintergrasp::HandleKill(Player *killer, Unit *victim)
{
if (victim->getLevel() >= 70)
ok = true;
+
if(killer->GetQuestStatus(SLAY_THEM_ALL_1) == QUEST_STATUS_INCOMPLETE || killer->GetQuestStatus(SLAY_THEM_ALL_2) == QUEST_STATUS_INCOMPLETE || killer->GetQuestStatus(NO_MERCY_MERCILESS_1) == QUEST_STATUS_INCOMPLETE || killer->GetQuestStatus(NO_MERCY_MERCILESS) == QUEST_STATUS_INCOMPLETE)
killer->KilledMonsterCredit(CRE_PVP_KILL,0);
}
@@ -729,6 +815,7 @@ void OPvPWintergrasp::HandleKill(Player *killer, Unit *victim)
break;
}
}
+
if (ok)
{
if (Group *pGroup = killer->GetGroup())
@@ -741,12 +828,15 @@ void OPvPWintergrasp::HandleKill(Player *killer, Unit *victim)
PromotePlayer(killer);
}
}
+
// Cast or removes Tenacity. MaxHP Modified, HP keeps it's % ratio
void OPvPWintergrasp::CastTenacity(Unit *unit, int32 newStack)
{
if (!unit)
return;
+
uint32 spellId = unit->GetTypeId() == TYPEID_PLAYER ? SPELL_TENACITY : SPELL_TENACITY_VEHICLE;
+
if (newStack)
unit->SetAuraStack(spellId, unit, newStack);
else
@@ -754,15 +844,18 @@ void OPvPWintergrasp::CastTenacity(Unit *unit, int32 newStack)
if (unit->GetTypeId() != TYPEID_PLAYER || unit->isAlive())
unit->SetHealth(uint32(unit->GetMaxHealth()*((float)unit->GetHealth()) / unit->GetMaxHealth()));
}
+
// Recalculates Tenacity and applies it to Players / Vehicles
void OPvPWintergrasp::UpdateTenacityStack()
{
if (!isWarTime())
return;
+
TeamId team = TEAM_NEUTRAL;
uint32 allianceNum = m_players[TEAM_ALLIANCE].size();;
uint32 hordeNum = m_players[TEAM_HORDE].size();;
int32 newStack = 0;
+
if (allianceNum && hordeNum)
{
if (allianceNum < hordeNum)
@@ -770,35 +863,44 @@ void OPvPWintergrasp::UpdateTenacityStack()
else if (allianceNum > hordeNum)
newStack = (1 - int32(allianceNum / hordeNum))*4; // negative, should cast on horde
}
+
if (newStack == m_tenacityStack)
return;
+
if (m_tenacityStack > 0 && newStack <= 0) // old buff was on alliance
team = TEAM_ALLIANCE;
else if (m_tenacityStack < 0 && newStack >= 0) // old buff was on horde
team = TEAM_HORDE;
+
m_tenacityStack = newStack;
+
// Remove old buff
if (team != TEAM_NEUTRAL)
{
for (PlayerSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if ((*itr)->getLevel() > 69)
REMOVE_TENACITY_AURA((*itr));
+
for (CreatureSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr)
REMOVE_TENACITY_AURA((*itr));
}
+
// Apply new buff
if (newStack)
{
team = newStack > 0 ? TEAM_ALLIANCE : TEAM_HORDE;
if (newStack < 0) newStack = -newStack;
int32 auraStack = newStack > 20 ? 20 : newStack; // Dont let it be higher than 20
+
for (PlayerSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if ((*itr)->getLevel() > 69)
CastTenacity((*itr), auraStack);
+
for (CreatureSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr)
CastTenacity((*itr), auraStack);
}
}
+
void OPvPWintergrasp::VehicleCastSpell(TeamId team, int32 spellId) const
{
if (spellId > 0)
@@ -808,12 +910,14 @@ void OPvPWintergrasp::VehicleCastSpell(TeamId team, int32 spellId) const
for (CreatureSet::const_iterator itr = m_vehicles[team].begin(); itr != m_vehicles[team].end(); ++itr)
(*itr)->RemoveAura((uint32)-spellId); // by stack?
}
+
void OPvPWintergrasp::LieutenantCastSpell(TeamId team, int32 spellId) const
{
for (PlayerSet::const_iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if ((*itr)->HasAura(SPELL_LIEUTENANT) && ((*itr)->getLevel() > 69))
(*itr)->CastSpell(*itr, (uint32)spellId, true);
}
+
void OPvPWintergrasp::UpdateClockDigit(uint32 &timer, uint32 digit, uint32 mod)
{
uint32 value = timer%mod;
@@ -824,6 +928,7 @@ void OPvPWintergrasp::UpdateClockDigit(uint32 &timer, uint32 digit, uint32 mod)
SendUpdateWorldState(ClockWorldState[digit], value);
}
}
+
void OPvPWintergrasp::UpdateClock()
{
uint32 timer = m_timer / 1000;
@@ -834,20 +939,25 @@ void OPvPWintergrasp::UpdateClock()
if (!isWarTime())
UpdateClockDigit(timer, 4, 10);
}
+
bool OPvPWintergrasp::Update(uint32 diff)
{
if(!sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_ENABLED))
return false;
+
if (m_timer > diff)
{
m_timer -= diff;
+
if (isWarTime())
OutdoorPvP::Update(diff); // update capture points
+
UpdateClock();
}
else
{
m_sendUpdate = false;
+
if (isWarTime())
{
if (m_changeDefender)
@@ -866,6 +976,7 @@ bool OPvPWintergrasp::Update(uint32 diff)
else
sWorld.SendZoneText(ZONE_WINTERGRASP, "Horde has successfully defended the fortress!");
}
+
EndBattle();
}
else
@@ -873,26 +984,33 @@ bool OPvPWintergrasp::Update(uint32 diff)
sWorld.SendZoneText(ZONE_WINTERGRASP, "Battle begins!");
StartBattle();
}
+
UpdateAllWorldObject();
UpdateClock();
+
SendInitWorldStatesTo();
m_sendUpdate = true;
}
+
return false;
}
+
void OPvPWintergrasp::StartBattle()
{
m_wartime = true;
m_timer = sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_BATTLE_TIME) * MINUTE * IN_MILISECONDS;
+
// Add recruit Aura, Add Tenacity
TeamCastSpell(getDefenderTeam(), SPELL_RECRUIT);
TeamCastSpell(getAttackerTeam(), SPELL_RECRUIT);
UpdateTenacityStack();
}
+
void OPvPWintergrasp::EndBattle()
{
m_wartime = false;
m_timer = sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_INTERVAL) * MINUTE * IN_MILISECONDS;
+
for (uint32 team = 0; team < 2; ++team)
{
// destroyed all vehicles
@@ -902,8 +1020,10 @@ void OPvPWintergrasp::EndBattle()
m_vehicles[team].erase(m_vehicles[team].begin());
veh->setDeathState(JUST_DIED);
}
+
if (m_players[team].empty())
continue;
+
// calculate rewards
uint32 intactNum = 0;
uint32 damagedNum = 0;
@@ -914,22 +1034,26 @@ void OPvPWintergrasp::EndBattle()
++damagedNum;
else if (workshop->m_buildingState->damageState == DAMAGE_INTACT)
++intactNum;
+
uint32 spellRewardId = team == getDefenderTeam() ? SPELL_VICTORY_REWARD : SPELL_DEFEAT_REWARD;
uint32 baseHonor = 0;
uint32 marks = 0;
uint32 playersWithRankNum = 0;
+
if (sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_CUSTOM_HONOR))
{
// Calculate Level 70+ with Corporal or Lieutenant rank
for (PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
if ((*itr)->getLevel() > 69 && ((*itr)->HasAura(SPELL_LIEUTENANT) || (*itr)->HasAura(SPELL_CORPORAL)))
++playersWithRankNum;
+
baseHonor = m_customHonorReward[(team == getDefenderTeam()) ? WIN_BATTLE : LOSE_BATTLE];
baseHonor += (m_customHonorReward[DAMAGED_TOWER] * m_towerCount[OTHER_TEAM(team)][DAMAGED_TOWER]);
baseHonor += (m_customHonorReward[DESTROYED_TOWER] * m_towerCount[OTHER_TEAM(team)][DAMAGED_TOWER]);
baseHonor += (m_customHonorReward[INTACT_BUILDING] * intactNum);
baseHonor += (m_customHonorReward[DAMAGED_BUILDING] * damagedNum);
}
+
// Revive players, remove auras and give rewards
for (PlayerSet::iterator itr = m_players[team].begin(); itr != m_players[team].end(); ++itr)
{
@@ -940,8 +1064,10 @@ void OPvPWintergrasp::EndBattle()
(*itr)->ResurrectPlayer(1.0f);
ObjectAccessor::Instance().ConvertCorpseForPlayer((*itr)->GetGUID());
}
+
if ((*itr)->getLevel() < 70)
continue; // No rewards for level <70
+
// give rewards
if (sWorld.getConfig(CONFIG_OUTDOORPVP_WINTERGRASP_CUSTOM_HONOR))
{
@@ -1006,37 +1132,45 @@ void OPvPWintergrasp::EndBattle()
if((*itr)->HasAura(SPELL_LIEUTENANT) || (*itr)->HasAura(SPELL_CORPORAL))
(*itr)->AreaExploredOrEventHappens(A_VICTORY_IN_WG);
(*itr)->AreaExploredOrEventHappens(H_VICTORY_IN_WG);
+
}
REMOVE_WARTIME_AURAS(*itr);
REMOVE_TENACITY_AURA(*itr);
(*itr)->CombatStop(true);
}
}
+
//3.2.0: TeamCastSpell(getAttackerTeam(), SPELL_TELEPORT_DALARAN);
RemoveOfflinePlayerWGAuras();
}
+
void OPvPWintergrasp::SetData(uint32 id, uint32 value)
{
//if (id == DATA_ENGINEER_DIE)
// if (SiegeWorkshop *workshop = GetWorkshopByEngGuid(value))
// workshop->DespawnAllVehicles();
}
+
bool OPvPWintergrasp::CanBuildVehicle(SiegeWorkshop *workshop) const
{
TeamId team = workshop->m_buildingState->GetTeam();
if (team == TEAM_NEUTRAL)
return false;
+
return isWarTime()
&& workshop->m_buildingState->damageState != DAMAGE_DESTROYED
&& m_vehicles[team].size() < m_workshopCount[team] * MAX_VEHICLE_PER_WORKSHOP;
}
+
uint32 OPvPWintergrasp::GetData(uint32 id)
{
// if can build more vehicles
if (SiegeWorkshop *workshop = GetWorkshopByEngGuid(id))
return CanBuildVehicle(workshop) ? 1 : 0;
+
return 0;
}
+
void OPvPWintergrasp::RewardMarkOfHonor(Player *plr, uint32 count)
{
// 'Inactive' this aura prevents the player from gaining honor points and battleground tokens
@@ -1044,26 +1178,32 @@ void OPvPWintergrasp::RewardMarkOfHonor(Player *plr, uint32 count)
return;
if (count == 0)
return;
+
ItemPosCountVec dest;
uint32 no_space_count = 0;
uint8 msg = plr->CanStoreNewItem(NULL_BAG, NULL_SLOT, dest, WG_MARK_OF_HONOR, count, &no_space_count);
+
if (msg == EQUIP_ERR_ITEM_NOT_FOUND)
{
sLog.outErrorDb("Wintergrasp reward item (Entry %u) not exist in `item_template`.", WG_MARK_OF_HONOR);
return;
}
+
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, WG_MARK_OF_HONOR, true, 0))
plr->SendNewItem(item, count, true, false);
}
+
SiegeWorkshop *OPvPWintergrasp::GetWorkshop(uint32 lowguid) const
{
if (OPvPCapturePoint *cp = GetCapturePoint(lowguid))
return dynamic_cast<SiegeWorkshop*>(cp);
return NULL;
}
+
SiegeWorkshop *OPvPWintergrasp::GetWorkshopByEngGuid(uint32 lowguid) const
{
for (OutdoorPvP::OPvPCapturePointMap::const_iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
@@ -1072,6 +1212,7 @@ SiegeWorkshop *OPvPWintergrasp::GetWorkshopByEngGuid(uint32 lowguid) const
return workshop;
return NULL;
}
+
SiegeWorkshop *OPvPWintergrasp::GetWorkshopByGOGuid(uint32 lowguid) const
{
for (OutdoorPvP::OPvPCapturePointMap::const_iterator itr = m_capturePoints.begin(); itr != m_capturePoints.end(); ++itr)
@@ -1080,14 +1221,17 @@ SiegeWorkshop *OPvPWintergrasp::GetWorkshopByGOGuid(uint32 lowguid) const
return workshop;
return NULL;
}
+
/*######
##SiegeWorkshop
######*/
+
SiegeWorkshop::SiegeWorkshop(OPvPWintergrasp *opvp, BuildingState *state)
: OPvPCapturePoint(opvp), m_buildingState(state), m_wintergrasp(opvp)
, m_engineer(NULL), m_engGuid(0)
{
}
+
void SiegeWorkshop::SetTeamByBuildingState()
{
if (m_buildingState->GetTeam() == TEAM_ALLIANCE)
@@ -1105,14 +1249,17 @@ void SiegeWorkshop::SetTeamByBuildingState()
m_value = 0;
m_State = OBJECTIVESTATE_NEUTRAL;
}
+
if (m_team != m_buildingState->GetTeam())
{
TeamId oldTeam = m_team;
m_team = m_buildingState->GetTeam();
ChangeTeam(oldTeam);
}
+
SendChangePhase();
}
+
void SiegeWorkshop::ChangeTeam(TeamId oldTeam)
{
uint32 entry = 0;
@@ -1123,15 +1270,20 @@ void SiegeWorkshop::ChangeTeam(TeamId oldTeam)
entry = m_team == TEAM_ALLIANCE ? CRE_ENG_A : CRE_ENG_H;
m_wintergrasp->ModifyWorkshopCount(m_team, true);
}
+
GameObject::SetGoArtKit(CapturePointArtKit[m_team], m_capturePoint, m_capturePointGUID);
+
m_buildingState->SetTeam(m_team);
// TODO: this may be sent twice
m_wintergrasp->BroadcastStateChange(m_buildingState);
+
if (m_buildingState->building)
m_buildingState->building->SetUInt32Value(GAMEOBJECT_FACTION, WintergraspFaction[m_team]);
+
if (entry)
{
*m_engEntry = entry;
+
if (m_engineer)
{
m_engineer->SetOriginalEntry(entry);
@@ -1145,5 +1297,6 @@ void SiegeWorkshop::ChangeTeam(TeamId oldTeam)
}
else if (m_engineer)
m_engineer->SetVisibility(VISIBILITY_OFF);
+
sLog.outDebug("Wintergrasp workshop now belongs to %u.", (uint32)m_buildingState->GetTeam());
}
diff --git a/src/game/Wintergrasp.h b/src/game/Wintergrasp.h
index 291a80a5e1d..92a7b2acd9b 100644
--- a/src/game/Wintergrasp.h
+++ b/src/game/Wintergrasp.h
@@ -15,25 +15,33 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_WINTERGRASP_H
#define TRINITY_WINTERGRASP_H
+
#include "OutdoorPvPImpl.h"
+
#define ZONE_WINTERGRASP 4197
#define POS_X_CENTER 4700
#define MAX_VEHICLE_PER_WORKSHOP 4
+
const uint32 WintergraspFaction[3] = {1732, 1735, 35};
const uint32 WG_MARK_OF_HONOR = 43589;
const uint32 VehNumWorldState[2] = {3680,3490};
const uint32 MaxVehNumWorldState[2] = {3681,3491};
const uint32 ClockWorldState[5] = {3785,3784,3782,3976,3975};
+
enum WintergraspSpell
{
SPELL_RECRUIT = 37795,
SPELL_CORPORAL = 33280,
SPELL_LIEUTENANT = 55629,
+
SPELL_TENACITY = 58549,
SPELL_TENACITY_VEHICLE = 59911,
+
SPELL_TELEPORT_DALARAN = 53360,
+
SPELL_TOWER_CONTROL = 62064,
SPELL_VICTORY_REWARD = 56902,
SPELL_DEFEAT_REWARD = 58494,
@@ -46,6 +54,7 @@ enum WintergraspSpell
// SPELL_RULERS_OF_WG = 52108,
// SPELL_ESSENCE_OF_WG = 58045,
};
+
enum WintergraspRewardEvent
{
WIN_BATTLE,
@@ -57,8 +66,11 @@ enum WintergraspRewardEvent
WG_REWARD_EVENT_MAX,
};
+
/* Not used / Not implemented
+
const uint16 GameEventWintergraspDefender[2] = {50, 51};
+
enum OutdoorPvP_WG_Sounds
{
OutdoorPvP_WG_SOUND_KEEP_CLAIMED = 8192,
@@ -68,10 +80,12 @@ enum OutdoorPvP_WG_Sounds
OutdoorPvP_WG_SOUND_KEEP_ASSAULTED_HORDE = 8174,
OutdoorPvP_WG_SOUND_NEAR_VICTORY = 8456
};
+
enum DataId
{
DATA_ENGINEER_DIE,
};
+
enum OutdoorPvP_WG_KeepStatus
{
OutdoorPvP_WG_KEEP_TYPE_NEUTRAL = 0,
@@ -83,6 +97,7 @@ enum OutdoorPvP_WG_KeepStatus
OutdoorPvP_WG_KEEP_STATUS_HORDE_OCCUPIED = 4
};
*/
+
enum WintergraspCreType
{
CREATURE_OTHER,
@@ -92,19 +107,23 @@ enum WintergraspCreType
CREATURE_GUARD,
CREATURE_SPECIAL,
};
+
enum BuildingType
{
BUILDING_WALL,
BUILDING_WORKSHOP,
BUILDING_TOWER,
};
+
enum DamageState
{
DAMAGE_INTACT,
DAMAGE_DAMAGED,
DAMAGE_DESTROYED,
};
+
const uint32 AreaPOIIconId[3][3] = {{7,8,9},{4,5,6},{1,2,3}};
+
struct BuildingState
{
explicit BuildingState(uint32 _worldState, TeamId _team, bool asDefault)
@@ -119,14 +138,17 @@ struct BuildingState
GameObject *building;
uint32 *graveTeam;
BuildingType type;
+
void SendUpdate(Player *player) const
{
player->SendUpdateWorldState(worldState, AreaPOIIconId[team][damageState]);
}
+
void FillData(WorldPacket &data) const
{
data << worldState << AreaPOIIconId[team][damageState];
}
+
TeamId GetTeam() const { return team; }
void SetTeam(TeamId t)
{
@@ -134,11 +156,15 @@ struct BuildingState
if(graveTeam)
*graveTeam = TeamId2Team[t];
}
+
private:
TeamId team;
};
+
typedef std::map<uint32, uint32> TeamPairMap;
+
class SiegeWorkshop;
+
class OPvPWintergrasp : public OutdoorPvP
{
protected:
@@ -148,18 +174,26 @@ class OPvPWintergrasp : public OutdoorPvP
public:
explicit OPvPWintergrasp() : m_tenacityStack(0), m_gate(NULL) {}
bool SetupOutdoorPvP();
+
uint32 GetCreatureEntry(uint32 guidlow, const CreatureData *data);
//uint32 GetGameObjectEntry(uint32 guidlow, uint32 entry);
+
void OnCreatureCreate(Creature *creature, bool add);
void OnGameObjectCreate(GameObject *go, bool add);
+
void ProcessEvent(GameObject *obj, uint32 eventId);
+
void HandlePlayerEnterZone(Player *plr, uint32 zone);
void HandlePlayerLeaveZone(Player *plr, uint32 zone);
void HandleKill(Player *killer, Unit *victim);
+
bool Update(uint32 diff);
+
void BroadcastStateChange(BuildingState *state) const;
+
uint32 GetData(uint32 id);
void SetData(uint32 id, uint32 value);
+
void ModifyWorkshopCount(TeamId team, bool add);
uint32 GetTimer() const { return m_timer / 1000; };
TeamId GetTeam() const { return m_defender; };
@@ -167,24 +201,32 @@ class OPvPWintergrasp : public OutdoorPvP
protected:
TeamId m_defender;
int32 m_tenacityStack;
+
BuildingStateMap m_buildingStates;
BuildingState *m_gate;
+
CreatureSet m_creatures;
CreatureSet m_vehicles[2];
GameObjectSet m_gobjects;
+
TeamPairMap m_creEntryPair, m_goDisplayPair;
+
bool m_wartime;
bool m_changeDefender;
uint32 m_timer;
uint32 m_clock[5];
uint32 m_workshopCount[2];
uint32 m_towerCount[2][2];
+
uint32 m_customHonorReward[WG_REWARD_EVENT_MAX];
+
SiegeWorkshop *GetWorkshop(uint32 lowguid) const;
SiegeWorkshop *GetWorkshopByEngGuid(uint32 lowguid) const;
SiegeWorkshop *GetWorkshopByGOGuid(uint32 lowguid) const;
+
void StartBattle();
void EndBattle();
+
void UpdateClock();
void UpdateClockDigit(uint32 &timer, uint32 digit, uint32 mod);
void PromotePlayer(Player *player) const;
@@ -193,15 +235,20 @@ class OPvPWintergrasp : public OutdoorPvP
void UpdateAllWorldObject();
bool UpdateCreatureInfo(Creature *creature) const;
bool UpdateGameObjectInfo(GameObject *go) const;
+
bool CanBuildVehicle(SiegeWorkshop *workshop) const;
WintergraspCreType GetCreatureType(uint32 entry) const;
+
void RebuildAllBuildings();
+
void LieutenantCastSpell(TeamId team, int32 spellId) const;
void VehicleCastSpell(TeamId team, int32 spellId) const;
+
void SendInitWorldStatesTo(Player *player = NULL) const;
void RemoveOfflinePlayerWGAuras();
void RewardMarkOfHonor(Player *player, uint32 count);
};
+
class SiegeWorkshop : public OPvPCapturePoint
{
public:
@@ -210,7 +257,9 @@ class SiegeWorkshop : public OPvPCapturePoint
void ChangeState() {}
void ChangeTeam(TeamId oldteam);
//void DespawnAllVehicles();
+
//bool CanBuildVehicle() const { return m_vehicles.size() < MAX_VEHICLE_PER_WORKSHOP && m_buildingState->damageState != DAMAGE_DESTROYED; }
+
uint32 *m_engEntry;
uint32 m_engGuid;
Creature *m_engineer;
@@ -220,4 +269,5 @@ class SiegeWorkshop : public OPvPCapturePoint
protected:
OPvPWintergrasp *m_wintergrasp;
};
+
#endif
diff --git a/src/game/World.cpp b/src/game/World.cpp
index f73eacc4720..17b2529002f 100644
--- a/src/game/World.cpp
+++ b/src/game/World.cpp
@@ -17,9 +17,11 @@
* 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"
@@ -66,17 +68,22 @@
#include "CreatureGroups.h"
#include "Transports.h"
#include "ProgressBar.h"
+
INSTANTIATE_SINGLETON_1(World);
+
volatile bool World::m_stopEvent = false;
uint8 World::m_ExitCode = SHUTDOWN_EXIT_CODE;
volatile uint32 World::m_worldLoopCounter = 0;
+
float World::m_MaxVisibleDistanceOnContinents = DEFAULT_VISIBILITY_DISTANCE;
float World::m_MaxVisibleDistanceInInstances = DEFAULT_VISIBILITY_INSTANCE;
float World::m_MaxVisibleDistanceInBGArenas = DEFAULT_VISIBILITY_BGARENAS;
float World::m_MaxVisibleDistanceForObject = DEFAULT_VISIBILITY_DISTANCE;
+
float World::m_MaxVisibleDistanceInFlight = DEFAULT_VISIBILITY_DISTANCE;
float World::m_VisibleUnitGreyDistance = 0;
float World::m_VisibleObjectGreyDistance = 0;
+
/// World constructor
World::World()
{
@@ -94,12 +101,16 @@ World::World()
m_resultQueue = NULL;
m_NextDailyQuestReset = 0;
m_scheduledScripts = 0;
+
m_defaultDbcLocale = LOCALE_enUS;
m_availableDbcLocaleMask = 0;
+
m_updateTimeSum = 0;
m_updateTimeCount = 0;
+
m_isClosed = false;
}
+
/// World destructor
World::~World()
{
@@ -110,17 +121,24 @@ World::~World()
delete m_sessions.begin()->second;
m_sessions.erase(m_sessions.begin());
}
+
///- Empty the WeatherMap
for (WeatherMap::const_iterator itr = m_weathers.begin(); itr != m_weathers.end(); ++itr)
delete itr->second;
+
m_weathers.clear();
+
CliCommandHolder* command;
while (cliCmdQueue.next(command))
delete command;
+
VMAP::VMapFactory::clear();
+
if (m_resultQueue) delete m_resultQueue;
+
//TODO free addSessQueue
}
+
/// Find a player in a specified zone
Player* World::FindPlayerInZone(uint32 zone)
{
@@ -141,37 +159,46 @@ Player* World::FindPlayerInZone(uint32 zone)
}
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::const_iterator itr = m_sessions.find(id);
+
if (itr != m_sessions.end() && itr->second)
{
if (itr->second->PlayerLoading())
return false;
itr->second->KickPlayer();
}
+
return true;
}
+
void World::AddSession(WorldSession* s)
{
addSessQueue.add(s);
}
+
void
World::AddSession_ (WorldSession* s)
{
ASSERT (s);
+
//NOTE - Still there is race condition in WorldSession* being used in the Sockets
+
///- kick already loaded player with same account (if any) and remove session
///- if player is in loading and want to load again, return
if (!RemoveSession (s->GetAccountId ()))
@@ -180,12 +207,15 @@ World::AddSession_ (WorldSession* s)
delete s; // session not added yet in session list, so not listed in queue
return;
}
+
// decrease session counts only at not reconnection case
bool decrease_session = true;
+
// if session already exist, prepare to it deleting at next world update
// NOTE - KickPlayer() should be called on "old" in RemoveSession()
{
SessionMap::const_iterator old = m_sessions.find(s->GetAccountId ());
+
if (old != m_sessions.end())
{
// prevent decrease sessions count if session queued
@@ -195,14 +225,18 @@ World::AddSession_ (WorldSession* s)
delete old->second;
}
}
+
m_sessions[s->GetAccountId ()] = s;
+
uint32 Sessions = GetActiveAndQueuedSessionCount ();
uint32 pLimit = GetPlayerAmountLimit ();
uint32 QueueSize = GetQueueSize (); //number of players in the queue
+
//so we don't count the user trying to
//login as a session and queue the socket that we are using
if (decrease_session)
--Sessions;
+
if (pLimit > 0 && Sessions >= pLimit && s->GetSecurity () == SEC_PLAYER && !HasRecentlyDisconnected(s))
{
AddQueuedPlayer (s);
@@ -210,6 +244,7 @@ World::AddSession_ (WorldSession* s)
sLog.outDetail ("PlayerQueue: Account id %u is in Queue Position (%u).", s->GetAccountId (), ++QueueSize);
return;
}
+
WorldPacket packet(SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
packet << uint8 (AUTH_OK);
packet << uint32 (0); // BillingTimeRemaining
@@ -217,12 +252,17 @@ World::AddSession_ (WorldSession* s)
packet << uint32 (0); // BillingTimeRested
packet << uint8 (s->Expansion()); // 0 - normal, 1 - TBC, must be set in database manually for each account
s->SendPacket (&packet);
+
s->SendAddonsInfo();
+
WorldPacket pkt(SMSG_CLIENTCACHE_VERSION, 4);
pkt << uint32(sWorld.getConfig(CONFIG_CLIENTCACHE_VERSION));
s->SendPacket(&pkt);
+
s->SendTutorialsData();
+
UpdateMaxSessionCounters ();
+
// Updates the population
if (pLimit > 0)
{
@@ -233,9 +273,11 @@ World::AddSession_ (WorldSession* s)
sLog.outDetail ("Server Population (%f).", popu);
}
}
+
bool World::HasRecentlyDisconnected(WorldSession* session)
{
if (!session) return false;
+
if (uint32 tolerance = getConfig(CONFIG_INTERVAL_DISCONNECT_TOLERANCE))
{
for (DisconnectMap::iterator i = m_disconnects.begin(); i != m_disconnects.end();)
@@ -252,18 +294,23 @@ bool World::HasRecentlyDisconnected(WorldSession* session)
}
return false;
}
+
int32 World::GetQueuePos(WorldSession* sess)
{
uint32 position = 1;
+
for (Queue::const_iterator iter = m_QueuedPlayer.begin(); iter != m_QueuedPlayer.end(); ++iter, ++position)
if ((*iter) == sess)
return position;
+
return 0;
}
+
void World::AddQueuedPlayer(WorldSession* sess)
{
sess->SetInQueue(true);
m_QueuedPlayer.push_back (sess);
+
// The 1st SMSG_AUTH_RESPONSE needs to contain other info too.
WorldPacket packet (SMSG_AUTH_RESPONSE, 1 + 4 + 1 + 4 + 1);
packet << uint8 (AUTH_WAIT_QUEUE);
@@ -273,16 +320,21 @@ void World::AddQueuedPlayer(WorldSession* sess)
packet << uint8 (sess->Expansion () ? 1 : 0); // 0 - normal, 1 - TBC, must be set in database manually for each account
packet << uint32(GetQueuePos (sess));
sess->SendPacket (&packet);
+
//sess->SendAuthWaitQue (GetQueuePos (sess));
}
+
bool World::RemoveQueuedPlayer(WorldSession* sess)
{
// sessions count including queued to remove (if removed_session set)
uint32 sessions = GetActiveSessionCount();
+
uint32 position = 1;
Queue::iterator iter = m_QueuedPlayer.begin();
+
// search to remove and count skipped positions
bool found = false;
+
for (;iter != m_QueuedPlayer.end(); ++iter, ++position)
{
if (*iter==sess)
@@ -293,11 +345,14 @@ bool World::RemoveQueuedPlayer(WorldSession* sess)
break;
}
}
+
// iter point to next socked after removed or end()
// position store position of removed socket and then new position next socket after removed
+
// if session not queued then we need decrease sessions count
if (!found && sessions)
--sessions;
+
// accept first in queue
if ((!m_playerLimit || sessions < m_playerLimit) && !m_QueuedPlayer.empty())
{
@@ -305,49 +360,60 @@ bool World::RemoveQueuedPlayer(WorldSession* sess)
pop_sess->SetInQueue(false);
pop_sess->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);
+
return found;
}
+
/// 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)
{
@@ -360,13 +426,17 @@ void World::LoadConfigSettings(bool reload)
}
//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 a Trinity Core Server."));
+
///- Get string for new logins (newly created characters)
SetNewCharString(sConfig.GetStringDefault("PlayerStart.String", ""));
+
///- Send server info on login?
m_configs[CONFIG_ENABLE_SINFO_LOGIN] = sConfig.GetIntDefault("Server.LoginInfo", 0);
+
///- Read all rates from the config file
rate_values[RATE_HEALTH] = sConfig.GetFloatDefault("Rate.Health", 1);
if (rate_values[RATE_HEALTH] < 0)
@@ -458,6 +528,7 @@ void World::LoadConfigSettings(bool reload)
}
for (uint8 i = 0; i < MAX_MOVE_TYPE; ++i) playerBaseMoveSpeed[i] = baseMoveSpeed[i] * rate_values[RATE_MOVESPEED];
rate_values[RATE_CORPSE_DECAY_LOOTED] = sConfig.GetFloatDefault("Rate.Corpse.Decay.Looted",0.5f);
+
rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = sConfig.GetFloatDefault("TargetPosRecalculateRange",1.5f);
if (rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] < CONTACT_DISTANCE)
{
@@ -470,6 +541,7 @@ void World::LoadConfigSettings(bool reload)
rate_values[RATE_TARGET_POS_RECALCULATION_RANGE],NOMINAL_MELEE_RANGE,NOMINAL_MELEE_RANGE);
rate_values[RATE_TARGET_POS_RECALCULATION_RANGE] = NOMINAL_MELEE_RANGE;
}
+
rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = sConfig.GetFloatDefault("DurabilityLoss.OnDeath", 10.0f);
if (rate_values[RATE_DURABILITY_LOSS_ON_DEATH] < 0.0f)
{
@@ -482,6 +554,7 @@ void World::LoadConfigSettings(bool reload)
rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = 0.0f;
}
rate_values[RATE_DURABILITY_LOSS_ON_DEATH] = rate_values[RATE_DURABILITY_LOSS_ON_DEATH] / 100.0f;
+
rate_values[RATE_DURABILITY_LOSS_DAMAGE] = sConfig.GetFloatDefault("DurabilityLossChance.Damage",0.5f);
if (rate_values[RATE_DURABILITY_LOSS_DAMAGE] < 0.0f)
{
@@ -506,8 +579,11 @@ void World::LoadConfigSettings(bool reload)
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_DURABILITY_LOSS_IN_PVP] = sConfig.GetBoolDefault("DurabilityLoss.InPvP", false);
+
m_configs[CONFIG_COMPRESSION] = sConfig.GetIntDefault("Compression", 1);
if (m_configs[CONFIG_COMPRESSION] < 1 || m_configs[CONFIG_COMPRESSION] > 9)
{
@@ -518,6 +594,7 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_GRID_UNLOAD] = sConfig.GetBoolDefault("GridUnload", true);
m_configs[CONFIG_INTERVAL_SAVE] = sConfig.GetIntDefault("PlayerSaveInterval", 15 * MINUTE * IN_MILISECONDS);
m_configs[CONFIG_INTERVAL_DISCONNECT_TOLERANCE] = sConfig.GetIntDefault("DisconnectToleranceInterval", 0);
+
m_configs[CONFIG_INTERVAL_GRIDCLEAN] = sConfig.GetIntDefault("GridCleanUpDelay", 5 * MINUTE * IN_MILISECONDS);
if (m_configs[CONFIG_INTERVAL_GRIDCLEAN] < MIN_GRID_DELAY)
{
@@ -526,6 +603,7 @@ void World::LoadConfigSettings(bool reload)
}
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)
{
@@ -534,7 +612,9 @@ void World::LoadConfigSettings(bool reload)
}
if (reload)
MapManager::Instance().SetMapUpdateInterval(m_configs[CONFIG_INTERVAL_MAPUPDATE]);
+
m_configs[CONFIG_INTERVAL_CHANGEWEATHER] = sConfig.GetIntDefault("ChangeWeatherInterval", 10 * MINUTE * IN_MILISECONDS);
+
if (reload)
{
uint32 val = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
@@ -543,6 +623,7 @@ void World::LoadConfigSettings(bool reload)
}
else
m_configs[CONFIG_PORT_WORLD] = sConfig.GetIntDefault("WorldServerPort", DEFAULT_WORLDSERVER_PORT);
+
if (reload)
{
uint32 val = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
@@ -551,10 +632,12 @@ void World::LoadConfigSettings(bool reload)
}
else
m_configs[CONFIG_SOCKET_SELECTTIME] = sConfig.GetIntDefault("SocketSelectTime", DEFAULT_SOCKET_SELECT_TIME);
+
m_configs[CONFIG_GROUP_XP_DISTANCE] = sConfig.GetIntDefault("MaxGroupXPDistance", 74);
/// \todo Add MonsterSight and GuarderSight (with meaning) in Trinityd.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);
@@ -563,6 +646,7 @@ void World::LoadConfigSettings(bool reload)
}
else
m_configs[CONFIG_GAME_TYPE] = sConfig.GetIntDefault("GameType", 0);
+
if (reload)
{
uint32 val = sConfig.GetIntDefault("RealmZone", REALM_ZONE_DEVELOPMENT);
@@ -571,6 +655,7 @@ void World::LoadConfigSettings(bool reload)
}
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);
@@ -584,31 +669,37 @@ void World::LoadConfigSettings(bool reload)
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_MIN_PLAYER_NAME] = sConfig.GetIntDefault ("MinPlayerName", 2);
if (m_configs[CONFIG_MIN_PLAYER_NAME] < 1 || m_configs[CONFIG_MIN_PLAYER_NAME] > MAX_PLAYER_NAME)
{
sLog.outError("MinPlayerName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_PLAYER_NAME],MAX_PLAYER_NAME);
m_configs[CONFIG_MIN_PLAYER_NAME] = 2;
}
+
m_configs[CONFIG_MIN_CHARTER_NAME] = sConfig.GetIntDefault ("MinCharterName", 2);
if (m_configs[CONFIG_MIN_CHARTER_NAME] < 1 || m_configs[CONFIG_MIN_CHARTER_NAME] > MAX_CHARTER_NAME)
{
sLog.outError("MinCharterName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_CHARTER_NAME],MAX_CHARTER_NAME);
m_configs[CONFIG_MIN_CHARTER_NAME] = 2;
}
+
m_configs[CONFIG_MIN_PET_NAME] = sConfig.GetIntDefault ("MinPetName", 2);
if (m_configs[CONFIG_MIN_PET_NAME] < 1 || m_configs[CONFIG_MIN_PET_NAME] > MAX_PET_NAME)
{
sLog.outError("MinPetName (%i) must be in range 1..%u. Set to 2.",m_configs[CONFIG_MIN_PET_NAME],MAX_PET_NAME);
m_configs[CONFIG_MIN_PET_NAME] = 2;
}
+
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])
@@ -616,19 +707,23 @@ void World::LoadConfigSettings(bool reload)
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_HEROIC_CHARACTERS_PER_REALM] = sConfig.GetIntDefault("HeroicCharactersPerRealm", 1);
if (int32(m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM]) < 0 || m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] > 10)
{
sLog.outError("HeroicCharactersPerRealm (%i) must be in range 0..10. Set to 1.",m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM]);
m_configs[CONFIG_HEROIC_CHARACTERS_PER_REALM] = 1;
}
+
m_configs[CONFIG_MIN_LEVEL_FOR_HEROIC_CHARACTER_CREATING] = sConfig.GetIntDefault("MinLevelForHeroicCharacterCreating", 55);
+
m_configs[CONFIG_SKIP_CINEMATICS] = sConfig.GetIntDefault("SkipCinematics", 0);
if (int32(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", DEFAULT_MAX_LEVEL);
@@ -637,12 +732,15 @@ void World::LoadConfigSettings(bool reload)
}
else
m_configs[CONFIG_MAX_PLAYER_LEVEL] = sConfig.GetIntDefault("MaxPlayerLevel", DEFAULT_MAX_LEVEL);
+
if (m_configs[CONFIG_MAX_PLAYER_LEVEL] > MAX_LEVEL)
{
sLog.outError("MaxPlayerLevel (%i) must be in range 1..%u. Set to %u.",m_configs[CONFIG_MAX_PLAYER_LEVEL],MAX_LEVEL,MAX_LEVEL);
m_configs[CONFIG_MAX_PLAYER_LEVEL] = MAX_LEVEL;
}
+
m_configs[CONFIG_MIN_DUALSPEC_LEVEL] = sConfig.GetIntDefault("MinDualSpecLevel", 40);
+
m_configs[CONFIG_START_PLAYER_LEVEL] = sConfig.GetIntDefault("StartPlayerLevel", 1);
if (m_configs[CONFIG_START_PLAYER_LEVEL] < 1)
{
@@ -654,6 +752,7 @@ void World::LoadConfigSettings(bool reload)
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_START_HEROIC_PLAYER_LEVEL] = sConfig.GetIntDefault("StartHeroicPlayerLevel", 55);
if (m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] < 1)
{
@@ -667,6 +766,7 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL],m_configs[CONFIG_MAX_PLAYER_LEVEL]);
m_configs[CONFIG_START_HEROIC_PLAYER_LEVEL] = m_configs[CONFIG_MAX_PLAYER_LEVEL];
}
+
m_configs[CONFIG_START_PLAYER_MONEY] = sConfig.GetIntDefault("StartPlayerMoney", 0);
if (int32(m_configs[CONFIG_START_PLAYER_MONEY]) < 0)
{
@@ -679,12 +779,14 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_START_PLAYER_MONEY],MAX_MONEY_AMOUNT,MAX_MONEY_AMOUNT);
m_configs[CONFIG_START_PLAYER_MONEY] = MAX_MONEY_AMOUNT;
}
+
m_configs[CONFIG_MAX_HONOR_POINTS] = sConfig.GetIntDefault("MaxHonorPoints", 75000);
if (int32(m_configs[CONFIG_MAX_HONOR_POINTS]) < 0)
{
sLog.outError("MaxHonorPoints (%i) can't be negative. Set to 0.",m_configs[CONFIG_MAX_HONOR_POINTS]);
m_configs[CONFIG_MAX_HONOR_POINTS] = 0;
}
+
m_configs[CONFIG_START_HONOR_POINTS] = sConfig.GetIntDefault("StartHonorPoints", 0);
if (int32(m_configs[CONFIG_START_HONOR_POINTS]) < 0)
{
@@ -698,12 +800,14 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_START_HONOR_POINTS],m_configs[CONFIG_MAX_HONOR_POINTS],m_configs[CONFIG_MAX_HONOR_POINTS]);
m_configs[CONFIG_START_HONOR_POINTS] = m_configs[CONFIG_MAX_HONOR_POINTS];
}
+
m_configs[CONFIG_MAX_ARENA_POINTS] = sConfig.GetIntDefault("MaxArenaPoints", 5000);
if (int32(m_configs[CONFIG_MAX_ARENA_POINTS]) < 0)
{
sLog.outError("MaxArenaPoints (%i) can't be negative. Set to 0.",m_configs[CONFIG_MAX_ARENA_POINTS]);
m_configs[CONFIG_MAX_ARENA_POINTS] = 0;
}
+
m_configs[CONFIG_START_ARENA_POINTS] = sConfig.GetIntDefault("StartArenaPoints", 0);
if (int32(m_configs[CONFIG_START_ARENA_POINTS]) < 0)
{
@@ -717,13 +821,17 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_START_ARENA_POINTS],m_configs[CONFIG_MAX_ARENA_POINTS],m_configs[CONFIG_MAX_ARENA_POINTS]);
m_configs[CONFIG_START_ARENA_POINTS] = m_configs[CONFIG_MAX_ARENA_POINTS];
}
+
m_configs[CONFIG_ALL_TAXI_PATHS] = sConfig.GetBoolDefault("AllFlightPaths", false);
m_configs[CONFIG_INSTANT_TAXI] = sConfig.GetBoolDefault("InstantFlightPaths", false);
+
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_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", 30 * MINUTE * IN_MILISECONDS);
+
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)
@@ -731,11 +839,13 @@ void World::LoadConfigSettings(bool reload)
sLog.outError("MinPetitionSigns (%i) must be in range 0..9. Set to 9.", m_configs[CONFIG_MIN_PETITION_SIGNS]);
m_configs[CONFIG_MIN_PETITION_SIGNS] = 9;
}
+
m_configs[CONFIG_GM_LOGIN_STATE] = sConfig.GetIntDefault("GM.LoginState", 2);
m_configs[CONFIG_GM_VISIBLE_STATE] = sConfig.GetIntDefault("GM.Visible", 2);
//m_configs[CONFIG_GM_ACCEPT_TICKETS] = sConfig.GetIntDefault("GM.AcceptTickets", 2);
m_configs[CONFIG_GM_CHAT] = sConfig.GetIntDefault("GM.Chat", 2);
m_configs[CONFIG_GM_WISPERING_TO] = sConfig.GetIntDefault("GM.WhisperingTo", 2);
+
m_configs[CONFIG_GM_LEVEL_IN_GM_LIST] = sConfig.GetIntDefault("GM.InGMList.Level", SEC_ADMINISTRATOR);
m_configs[CONFIG_GM_LEVEL_IN_WHO_LIST] = sConfig.GetIntDefault("GM.InWhoList.Level", SEC_ADMINISTRATOR);
m_configs[CONFIG_GM_LOG_TRADE] = sConfig.GetBoolDefault("GM.LogTrade", false);
@@ -755,8 +865,11 @@ void World::LoadConfigSettings(bool reload)
}
m_configs[CONFIG_GM_LOWER_SECURITY] = sConfig.GetBoolDefault("GM.LowerSecurity", false);
m_configs[CONFIG_GM_ALLOW_ACHIEVEMENT_GAINS] = sConfig.GetBoolDefault("GM.AllowAchievementGain", true);
+
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 (int32(m_configs[CONFIG_UPTIME_UPDATE])<=0)
{
@@ -768,6 +881,7 @@ void World::LoadConfigSettings(bool reload)
m_timers[WUPDATE_UPTIME].SetInterval(m_configs[CONFIG_UPTIME_UPDATE]*MINUTE*IN_MILISECONDS);
m_timers[WUPDATE_UPTIME].Reset();
}
+
// log db cleanup interval
m_configs[CONFIG_LOGDB_CLEARINTERVAL] = sConfig.GetIntDefault("LogDB.Opt.ClearInterval", 10);
if (int32(m_configs[CONFIG_LOGDB_CLEARINTERVAL]) <= 0)
@@ -783,48 +897,60 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_LOGDB_CLEARTIME] = sConfig.GetIntDefault("LogDB.Opt.ClearTime", 1209600); // 14 days default
sLog.outString("Will clear `logs` table of entries older than %i seconds every %u minutes.",
m_configs[CONFIG_LOGDB_CLEARTIME], m_configs[CONFIG_LOGDB_CLEARINTERVAL]);
+
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_MILLING] = sConfig.GetBoolDefault("SkillChance.Milling",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_IMMEDIATELY] = sConfig.GetBoolDefault("SaveRespawnTimeImmediately",true);
m_configs[CONFIG_WEATHER] = sConfig.GetBoolDefault("ActivateWeather",true);
+
m_configs[CONFIG_DISABLE_BREATHING] = sConfig.GetIntDefault("DisableWaterBreath", SEC_CONSOLE);
+
m_configs[CONFIG_ALWAYS_MAX_SKILL_FOR_LEVEL] = sConfig.GetBoolDefault("AlwaysMaxSkillForLevel", false);
+
if (reload)
{
uint32 val = sConfig.GetIntDefault("Expansion",1);
@@ -833,15 +959,20 @@ void World::LoadConfigSettings(bool reload)
}
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_FLEE_ASSISTANCE_RADIUS] = sConfig.GetIntDefault("CreatureFamilyFleeAssistanceRadius",30);
m_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_RADIUS] = sConfig.GetIntDefault("CreatureFamilyAssistanceRadius",10);
m_configs[CONFIG_CREATURE_FAMILY_ASSISTANCE_DELAY] = sConfig.GetIntDefault("CreatureFamilyAssistanceDelay",1500);
m_configs[CONFIG_CREATURE_FAMILY_FLEE_DELAY] = sConfig.GetIntDefault("CreatureFamilyFleeDelay",7000);
+
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 MAX_LEVEL(100)
m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.LowLevelHideDiff", 4);
if (m_configs[CONFIG_QUEST_LOW_LEVEL_HIDE_DIFF] > MAX_LEVEL)
@@ -849,31 +980,41 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = sConfig.GetIntDefault("Quests.HighLevelHideDiff", 7);
if (m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] > MAX_LEVEL)
m_configs[CONFIG_QUEST_HIGH_LEVEL_HIDE_DIFF] = MAX_LEVEL;
+
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_CHAT_STRICT_LINK_CHECKING_SEVERITY] = sConfig.GetIntDefault("ChatStrictLinkChecking.Severity", 0);
m_configs[CONFIG_CHAT_STRICT_LINK_CHECKING_KICK] = sConfig.GetIntDefault("ChatStrictLinkChecking.Kick", 0);
+
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_DEATH_BONES_WORLD] = sConfig.GetBoolDefault("Death.Bones.World", true);
m_configs[CONFIG_DEATH_BONES_BG_OR_ARENA] = sConfig.GetBoolDefault("Death.Bones.BattlegroundOrArena", true);
+
m_configs[CONFIG_DIE_COMMAND_MODE] = sConfig.GetBoolDefault("Die.Command.Mode", true);
+
m_configs[CONFIG_THREAT_RADIUS] = sConfig.GetIntDefault("ThreatRadius", 60);
+
// 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_configs[CONFIG_BATTLEGROUND_CAST_DESERTER] = sConfig.GetBoolDefault("Battleground.CastDeserter", true);
m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_ENABLE] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.Enable", false);
m_configs[CONFIG_BATTLEGROUND_QUEUE_ANNOUNCER_PLAYERONLY] = sConfig.GetBoolDefault("Battleground.QueueAnnouncer.PlayerOnly", false);
@@ -888,7 +1029,9 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_ARENA_QUEUE_ANNOUNCER_PLAYERONLY] = sConfig.GetBoolDefault("Arena.QueueAnnouncer.PlayerOnly", false);
m_configs[CONFIG_ARENA_SEASON_ID] = sConfig.GetIntDefault ("Arena.ArenaSeason.ID", 1);
m_configs[CONFIG_ARENA_SEASON_IN_PROGRESS] = sConfig.GetBoolDefault("Arena.ArenaSeason.InProgress", true);
+
m_configs[CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN] = sConfig.GetBoolDefault("OffhandCheckAtSpellUnlearn", false);
+
if (int clientCacheId = sConfig.GetIntDefault("ClientCacheVersion", 0))
{
// overwrite DB/old value
@@ -900,13 +1043,16 @@ void World::LoadConfigSettings(bool reload)
else
sLog.outError("ClientCacheVersion can't be negative %d, ignored.", clientCacheId);
}
+
m_configs[CONFIG_INSTANT_LOGOUT] = sConfig.GetIntDefault("InstantLogout", SEC_MODERATOR);
+
m_configs[CONFIG_GUILD_EVENT_LOG_COUNT] = sConfig.GetIntDefault("Guild.EventLogRecordsCount", GUILD_EVENTLOG_MAX_RECORDS);
if (m_configs[CONFIG_GUILD_EVENT_LOG_COUNT] < GUILD_EVENTLOG_MAX_RECORDS)
m_configs[CONFIG_GUILD_EVENT_LOG_COUNT] = GUILD_EVENTLOG_MAX_RECORDS;
m_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = sConfig.GetIntDefault("Guild.BankEventLogRecordsCount", GUILD_BANK_MAX_LOGS);
if (m_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] < GUILD_BANK_MAX_LOGS)
m_configs[CONFIG_GUILD_BANK_EVENT_LOG_COUNT] = GUILD_BANK_MAX_LOGS;
+
m_VisibleUnitGreyDistance = sConfig.GetFloatDefault("Visibility.Distance.Grey.Unit", 1);
if (m_VisibleUnitGreyDistance > MAX_VISIBILITY_DISTANCE)
{
@@ -919,6 +1065,7 @@ void World::LoadConfigSettings(bool reload)
sLog.outError("Visibility.Distance.Grey.Object can't be greater %f",MAX_VISIBILITY_DISTANCE);
m_VisibleObjectGreyDistance = MAX_VISIBILITY_DISTANCE;
}
+
//visibility on continents
m_MaxVisibleDistanceOnContinents = sConfig.GetFloatDefault("Visibility.Distance.Continents", DEFAULT_VISIBILITY_DISTANCE);
if(m_MaxVisibleDistanceOnContinents < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
@@ -931,6 +1078,7 @@ void World::LoadConfigSettings(bool reload)
sLog.outError("Visibility.Distance.Continents can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
m_MaxVisibleDistanceOnContinents = MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance;
}
+
//visibility in instances
m_MaxVisibleDistanceInInstances = sConfig.GetFloatDefault("Visibility.Distance.Instances", DEFAULT_VISIBILITY_INSTANCE);
if(m_MaxVisibleDistanceInInstances < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
@@ -943,6 +1091,7 @@ void World::LoadConfigSettings(bool reload)
sLog.outError("Visibility.Distance.Instances can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
m_MaxVisibleDistanceInInstances = MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance;
}
+
//visibility in BG/Arenas
m_MaxVisibleDistanceInBGArenas = sConfig.GetFloatDefault("Visibility.Distance.BGArenas", DEFAULT_VISIBILITY_BGARENAS);
if(m_MaxVisibleDistanceInBGArenas < 45*sWorld.getRate(RATE_CREATURE_AGGRO))
@@ -955,6 +1104,7 @@ void World::LoadConfigSettings(bool reload)
sLog.outError("Visibility.Distance.BGArenas can't be greater %f",MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance);
m_MaxVisibleDistanceInBGArenas = MAX_VISIBILITY_DISTANCE - m_VisibleUnitGreyDistance;
}
+
m_MaxVisibleDistanceForObject = sConfig.GetFloatDefault("Visibility.Distance.Object", DEFAULT_VISIBILITY_DISTANCE);
if(m_MaxVisibleDistanceForObject < INTERACTION_DISTANCE)
{
@@ -972,10 +1122,12 @@ void World::LoadConfigSettings(bool reload)
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)
@@ -986,6 +1138,7 @@ void World::LoadConfigSettings(bool reload)
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", "");
@@ -998,6 +1151,7 @@ void World::LoadConfigSettings(bool reload)
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");
+
m_configs[CONFIG_MAX_WHO] = sConfig.GetIntDefault("MaxWhoListReturns", 49);
m_configs[CONFIG_PET_LOS] = sConfig.GetBoolDefault("vmap.petLOS", false);
m_configs[CONFIG_BG_START_MUSIC] = sConfig.GetBoolDefault("MusicInBattleground", false);
@@ -1014,9 +1168,10 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_PVP_TOKEN_COUNT] = sConfig.GetIntDefault("PvPToken.ItemCount", 1);
if (m_configs[CONFIG_PVP_TOKEN_COUNT] < 1)
m_configs[CONFIG_PVP_TOKEN_COUNT] = 1;
+
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_ENABLED] = sConfig.GetBoolDefault("OutdoorPvP.Wintergrasp.Enabled", true);
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_START_TIME] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.StartTime", 30);
- m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_BATTLE_TIME] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.BattleTime", 30);
+ m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_BATTLE_TIME] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.BattleTime", 30);
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_INTERVAL] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.Interval", 150);
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_CUSTOM_HONOR] = sConfig.GetBoolDefault("OutdoorPvP.Wintergrasp.CustomHonorRewards", false);
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_WIN_BATTLE] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.CustomHonorBattleWin", 3000);
@@ -1025,12 +1180,14 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_DESTROYED_TOWER] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.CustomHonorDestroyedTower", 750);
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_DAMAGED_BUILDING] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.CustomHonorDamagedBuilding", 750);
m_configs[CONFIG_OUTDOORPVP_WINTERGRASP_INTACT_BUILDING] = sConfig.GetIntDefault("OutdoorPvP.Wintergrasp.CustomHonorIntactBuilding", 1500);
+
m_configs[CONFIG_NO_RESET_TALENT_COST] = sConfig.GetBoolDefault("NoResetTalentsCost", false);
m_configs[CONFIG_SHOW_KICK_IN_WORLD] = sConfig.GetBoolDefault("ShowKickInWorld", false);
m_configs[CONFIG_INTERVAL_LOG_UPDATE] = sConfig.GetIntDefault("RecordUpdateTimeDiffInterval", 60000);
m_configs[CONFIG_MIN_LOG_UPDATE] = sConfig.GetIntDefault("MinRecordUpdateTimeDiff", 10);
m_configs[CONFIG_CHECK_DB] = sConfig.GetBoolDefault("CheckDB", true);
m_configs[CONFIG_NUMTHREADS] = sConfig.GetIntDefault("MapUpdate.Threads",1);
+
std::string forbiddenmaps = sConfig.GetStringDefault("ForbiddenMaps", "");
char * forbiddenMaps = new char[forbiddenmaps.length() + 1];
forbiddenMaps[forbiddenmaps.length()] = 0;
@@ -1044,6 +1201,7 @@ void World::LoadConfigSettings(bool reload)
token = strtok(NULL,delim);
}
delete[] forbiddenMaps;
+
// chat logging
m_configs[CONFIG_CHATLOG_CHANNEL] = sConfig.GetBoolDefault("ChatLogs.Channel", false);
m_configs[CONFIG_CHATLOG_WHISPER] = sConfig.GetBoolDefault("ChatLogs.Whisper", false);
@@ -1055,15 +1213,19 @@ void World::LoadConfigSettings(bool reload)
m_configs[CONFIG_CHATLOG_ADDON] = sConfig.GetBoolDefault("ChatLogs.Addon", false);
m_configs[CONFIG_CHATLOG_BGROUND] = sConfig.GetBoolDefault("ChatLogs.BattleGround", false);
}
+
/// 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)
@@ -1078,34 +1240,45 @@ void World::SetInitialWorldSettings()
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 Trinityd.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 Trinity strings...");
if (!objmgr.LoadTrinityStrings())
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 Script Names...");
objmgr.LoadScriptNames();
+
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();
sLog.outString("Loading Localization strings...");
objmgr.LoadCreatureLocales();
@@ -1119,141 +1292,202 @@ void World::SetInitialWorldSettings()
objmgr.SetDBCLocaleIndex(GetDefaultDbcLocale()); // Get once for all the locale index of DBC language (console/broadcasts)
sLog.outString(">>> Localization strings loaded");
sLog.outString();
+
sLog.outString("Loading Page Texts...");
objmgr.LoadPageTexts();
+
sLog.outString("Loading Player info in cache...");
objmgr.LoadPlayerInfoInCache();
+
sLog.outString("Loading Game Object Templates..."); // must be after LoadPageTexts
objmgr.LoadGameobjectInfo();
+
sLog.outString("Loading Spell Chain Data...");
spellmgr.LoadSpellChains();
+
sLog.outString("Loading Spell Required Data...");
spellmgr.LoadSpellRequired();
+
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 Spell Bonus Data...");
spellmgr.LoadSpellBonusess();
+
sLog.outString("Loading Aggro Spells Definitions...");
spellmgr.LoadSpellThreats();
+
sLog.outString("Loading NPC Texts...");
objmgr.LoadGossipText();
+
sLog.outString("Loading Enchant Spells Proc datas...");
spellmgr.LoadSpellEnchantProcData();
+
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 ItemRequiredTarget...");
objmgr.LoadItemRequiredTarget();
+
sLog.outString("Loading Creature Reputation OnKill Data...");
objmgr.LoadReputationOnKill();
+
sLog.outString("Loading Points Of Interest Data...");
objmgr.LoadPointsOfInterest();
+
sLog.outString("Loading Creature Data...");
objmgr.LoadCreatures();
+
sLog.outString("Loading Creature Linked Respawn...");
objmgr.LoadCreatureLinkedRespawn(); // must be after LoadCreatures()
+
sLog.outString("Loading pet levelup spells...");
spellmgr.LoadPetLevelupSpellMap();
+
sLog.outString("Loading pet default spell additional to levelup spells...");
spellmgr.LoadPetDefaultSpells();
+
sLog.outString("Loading Creature Addon Data...");
sLog.outString();
objmgr.LoadCreatureAddons(); // must be after LoadCreatureTemplates() and LoadCreatures()
sLog.outString(">>> Creature Addon Data loaded");
sLog.outString();
+
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 Objects Pooling Data...");
poolhandler.LoadFromDB();
+
sLog.outString("Loading Game Event Data...");
sLog.outString();
gameeventmgr.LoadFromDB();
sLog.outString(">>> Game Event Data loaded");
sLog.outString();
+
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...");
sLog.outString();
objmgr.LoadQuestRelations(); // must be after quest load
sLog.outString(">>> Quests Relations loaded");
sLog.outString();
+
sLog.outString("Loading UNIT_NPC_FLAG_SPELLCLICK Data...");
objmgr.LoadNPCSpellClickSpells();
+
sLog.outString("Loading SpellArea Data..."); // must be after quest load
spellmgr.LoadSpellAreas();
+
sLog.outString("Loading AreaTrigger definitions...");
objmgr.LoadAreaTriggerTeleports();
+
sLog.outString("Loading Access Requirements...");
objmgr.LoadAccessRequirements(); // 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 spell pet auras...");
spellmgr.LoadSpellPetAuras();
+
sLog.outString("Loading spell extra attributes...(TODO)");
spellmgr.LoadSpellCustomAttr();
+
sLog.outString("Loading enchant custom attributes...");
spellmgr.LoadEnchantCustomAttr();
+
sLog.outString("Loading linked spells...");
spellmgr.LoadSpellLinked();
+
sLog.outString("Loading Player Create Info & Level Stats...");
sLog.outString();
objmgr.LoadPlayerInfo();
sLog.outString(">>> Player Create Info & Level Stats loaded");
sLog.outString();
+
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 Disabled Spells...");
objmgr.LoadSpellDisabledEntrys();
+
sLog.outString("Loading Loot Tables...");
sLog.outString();
LoadLootTables();
sLog.outString(">>> Loot Tables loaded");
sLog.outString();
+
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();
+
sLog.outString("Loading Achievements...");
sLog.outString();
achievementmgr.LoadAchievementReferenceList();
@@ -1264,6 +1498,7 @@ void World::SetInitialWorldSettings()
achievementmgr.LoadCompletedAchievements();
sLog.outString(">>> Achievements loaded");
sLog.outString();
+
///- Load dynamic data tables from the database
sLog.outString("Loading Auctions...");
sLog.outString();
@@ -1271,40 +1506,57 @@ void World::SetInitialWorldSettings()
auctionmgr.LoadAuctions();
sLog.outString(">>> Auctions loaded");
sLog.outString();
+
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 GameObjects for quests...");
objmgr.LoadGameObjectForQuests();
+
sLog.outString("Loading BattleMasters...");
sBattleGroundMgr.LoadBattleMastersEntry();
+
sLog.outString("Loading GameTeleports...");
objmgr.LoadGameTele();
+
sLog.outString("Loading Npc Text Id...");
objmgr.LoadNpcTextId(); // must be after load Creature and NpcText
+
sLog.outString("Loading Npc Options...");
objmgr.LoadNpcOptions();
+
sLog.outString("Loading Vendors...");
objmgr.LoadVendors(); // must be after load CreatureTemplate and ItemTemplate
+
sLog.outString("Loading Trainers...");
objmgr.LoadTrainerSpell(); // must be after load CreatureTemplate
+
sLog.outString("Loading Waypoints...");
sLog.outString();
WaypointMgr.Load();
+
sLog.outString("Loading Creature Formations...");
formation_mgr.LoadCreatureFormations();
+
sLog.outString("Loading GM tickets...");
objmgr.LoadGMTickets();
+
///- Handle outdated emails (delete/return)
sLog.outString("Returning old mails...");
objmgr.ReturnOrDeleteOldMails(false);
+
sLog.outString("Loading Autobroadcasts...");
LoadAutobroadcasts();
+
///- Load and initialize scripts
sLog.outString("Loading Scripts...");
sLog.outString();
@@ -1316,17 +1568,23 @@ void World::SetInitialWorldSettings()
objmgr.LoadWaypointScripts();
sLog.outString(">>> Scripts loaded");
sLog.outString();
+
sLog.outString("Loading Scripts text locales..."); // must be after Load*Scripts calls
objmgr.LoadDbScriptStrings();
+
sLog.outString("Loading CreatureEventAI Texts...");
CreatureEAI_Mgr.LoadCreatureEventAI_Texts();
+
sLog.outString("Loading CreatureEventAI Summons...");
CreatureEAI_Mgr.LoadCreatureEventAI_Summons();
+
sLog.outString("Loading CreatureEventAI Scripts...");
CreatureEAI_Mgr.LoadCreatureEventAI_Scripts();
+
sLog.outString("Initializing Scripts...");
if (!LoadScriptingModule())
exit(1);
+
/// Check db
if (m_configs[CONFIG_CHECK_DB])
{
@@ -1339,10 +1597,12 @@ void World::SetInitialWorldSettings()
}
else
sLog.outError("You have disabled DB check. We strongly recommend you to enable it to prevent unpredictable bugs and crashes.");
+
///- Initialize game time and timers
sLog.outDebug("DEBUG:: Initialize game time and timers");
m_gameTime = time(NULL);
m_startTime=m_gameTime;
+
tm local;
time_t curr;
time(&curr);
@@ -1350,8 +1610,10 @@ void World::SetInitialWorldSettings()
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);
+
loginDatabase.PExecute("INSERT INTO uptime (realmid, starttime, startstring, uptime, revision) VALUES('%u', " UI64FMTD ", '%s', 0, '%s')",
realmID, uint64(m_startTime), isoDate, _FULLVERSION);
+
static uint32 abtimer = 0;
abtimer = sConfig.GetIntDefault("AutoBroadcast.Timer", 60000);
m_timers[WUPDATE_OBJECTS].SetInterval(IN_MILISECONDS/2);
@@ -1365,6 +1627,7 @@ void World::SetInitialWorldSettings()
m_timers[WUPDATE_CLEANDB].SetInterval(m_configs[CONFIG_LOGDB_CLEARINTERVAL]*MINUTE*IN_MILISECONDS);
// clean logs table every 14 days by default
m_timers[WUPDATE_AUTOBROADCAST].SetInterval(abtimer);
+
//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)
@@ -1372,35 +1635,47 @@ void World::SetInitialWorldSettings()
//1440
mail_timer_expires = ((DAY * IN_MILISECONDS) / (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();
Player::InitVisibleBits();
+
///- Initialize MapManager
sLog.outString("Starting Map System");
MapManager::Instance().Initialize();
+
sLog.outString("Starting Game Event system...");
uint32 nextGameEvent = gameeventmgr.Initialize();
m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent); //depend on next event
+
///- Initialize Battlegrounds
sLog.outString("Starting BattleGround System");
sBattleGroundMgr.CreateInitialBattleGrounds();
sBattleGroundMgr.InitAutomaticArenaPointDistribution();
+
///- Initialize outdoor pvp
sLog.outString("Starting Outdoor PvP System");
sOutdoorPvPMgr.InitOutdoorPvP();
+
//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("Loading Transports Events...");
objmgr.LoadTransportEvents();
+
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 objects Pooling system...");
poolhandler.Initialize();
+
sLog.outString("Initialize AuctionHouseBot...");
auctionbot.Initialize();
+
// possibly enable db logging; avoid massive startup spam by doing it here.
if (sLog.GetLogDBLater())
{
@@ -1413,19 +1688,26 @@ void World::SetInitialWorldSettings()
sLog.SetLogDB(false);
sLog.SetLogDBLater(false);
}
+
Script->OnServerStartup();
+
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)
{
@@ -1437,20 +1719,25 @@ void World::DetectDBCLang()
availableLocalsStr += " ";
}
}
+
if (default_locale != m_lang_confid && m_lang_confid < MAX_LOCALE &&
(m_availableDbcLocaleMask & (1 << m_lang_confid)))
{
default_locale = m_lang_confid;
}
+
if (default_locale >= MAX_LOCALE)
{
sLog.outError("Unable to determine your DBC Locale! (corrupt DBC?)");
exit(1);
}
+
m_defaultDbcLocale = LocaleConstant(default_locale);
+
sLog.outString("Using %s DBC Locale as default. All available DBC locales: %s",localeNames[m_defaultDbcLocale],availableLocalsStr.empty() ? "<none>" : availableLocalsStr.c_str());
sLog.outString();
}
+
void World::RecordTimeDiff(const char *text, ...)
{
if (m_updateTimeCount != 1)
@@ -1460,8 +1747,10 @@ void World::RecordTimeDiff(const char *text, ...)
m_currentTime = getMSTime();
return;
}
+
uint32 thisTime = getMSTime();
uint32 diff = getMSTimeDiff(m_currentTime, thisTime);
+
if (diff > m_configs[CONFIG_MIN_LOG_UPDATE])
{
va_list ap;
@@ -1471,34 +1760,49 @@ void World::RecordTimeDiff(const char *text, ...)
va_end(ap);
sLog.outDetail("Difftime %s: %u.", str, diff);
}
+
m_currentTime = thisTime;
}
+
void World::LoadAutobroadcasts()
{
m_Autobroadcasts.clear();
+
QueryResult *result = WorldDatabase.Query("SELECT text FROM autobroadcast");
+
if (!result)
{
barGoLink bar(1);
bar.step();
+
sLog.outString();
sLog.outString(">> Loaded 0 autobroadcasts definitions");
return;
}
+
barGoLink bar(result->GetRowCount());
+
uint32 count = 0;
+
do
{
bar.step();
+
Field *fields = result->Fetch();
+
std::string message = fields[0].GetCppString();
+
m_Autobroadcasts.push_back(message);
+
count++;
} while(result->NextRow());
+
delete result;
+
sLog.outString();
sLog.outString(">> Loaded %u autobroadcasts definitions", count);
}
+
/// Update the World !
void World::Update(uint32 diff)
{
@@ -1517,24 +1821,29 @@ void World::Update(uint32 diff)
++m_updateTimeCount;
}
}
+
///- Update the different timers
for (int i = 0; i < WUPDATE_COUNT; ++i)
if (m_timers[i].GetCurrent()>=0)
m_timers[i].Update(diff);
else m_timers[i].SetCurrent(0);
+
///- Update the game time and check for shutdown time
_UpdateGameTime();
+
/// Handle daily quests reset time
if (m_gameTime > m_NextDailyQuestReset)
{
ResetDailyQuests();
m_NextDailyQuestReset += DAY;
}
+
/// <ul><li> Handle auctions when the timer has passed
if (m_timers[WUPDATE_AUCTIONS].Passed())
{
auctionbot.Update();
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)
@@ -1542,23 +1851,28 @@ void World::Update(uint32 diff)
mail_timer = 0;
objmgr.ReturnOrDeleteOldMails(true);
}
+
///- Handle expired auctions
auctionmgr.Update();
}
+
/// <li> Handle session updates when the timer has passed
RecordTimeDiff(NULL);
UpdateSessions(diff);
RecordTimeDiff("UpdateSessions");
+
/// <li> Handle weather updates when the timer has passed
if (m_timers[WUPDATE_WEATHERS].Passed())
{
m_timers[WUPDATE_WEATHERS].Reset();
+
///- Send an update signal to Weather objects
WeatherMap::iterator itr, next;
for (itr = m_weathers.begin(); itr != m_weathers.end(); itr = next)
{
next = itr;
++next;
+
///- and remove Weather objects for zones with no player
//As interval > WorldTick
if (!itr->second->Update(m_timers[WUPDATE_WEATHERS].GetInterval()))
@@ -1573,9 +1887,11 @@ void World::Update(uint32 diff)
{
uint32 tmpDiff = (m_gameTime - m_startTime);
uint32 maxClientsNum = GetMaxActiveSessionCount();
+
m_timers[WUPDATE_UPTIME].Reset();
loginDatabase.PExecute("UPDATE uptime SET uptime = %u, maxplayers = %u WHERE realmid = %u AND starttime = " UI64FMTD, tmpDiff, maxClientsNum, realmID, uint64(m_startTime));
}
+
/// <li> Clean logs table
if (sWorld.getConfig(CONFIG_LOGDB_CLEARTIME) > 0) // if not enabled, ignore the timer
{
@@ -1583,19 +1899,23 @@ void World::Update(uint32 diff)
{
uint32 tmpDiff = (m_gameTime - m_startTime);
uint32 maxClientsNum = sWorld.GetMaxActiveSessionCount();
+
m_timers[WUPDATE_CLEANDB].Reset();
loginDatabase.PExecute("DELETE FROM logs WHERE (time + %u) < "UI64FMTD";",
sWorld.getConfig(CONFIG_LOGDB_CLEARTIME), uint64(time(0)));
}
}
+
/// <li> Handle all other objects
///- Update objects when the timer has passed (maps, transport, creatures,...)
MapManager::Instance().Update(diff); // As interval = 0
+
/*if (m_timers[WUPDATE_OBJECTS].Passed())
{
m_timers[WUPDATE_OBJECTS].Reset();
MapManager::Instance().DoDelayedMovesAndRemoves();
}*/
+
static uint32 autobroadcaston = 0;
autobroadcaston = sConfig.GetIntDefault("AutoBroadcast.On", 0);
if (autobroadcaston == 1)
@@ -1606,21 +1926,27 @@ void World::Update(uint32 diff)
SendRNDBroadcast();
}
}
+
sBattleGroundMgr.Update(diff);
RecordTimeDiff("UpdateBattleGroundMgr");
+
sOutdoorPvPMgr.Update(diff);
RecordTimeDiff("UpdateOutdoorPvPMgr");
+
// execute callbacks from sql queries that were queued recently
UpdateResultQueue();
RecordTimeDiff("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())
{
@@ -1629,11 +1955,14 @@ void World::Update(uint32 diff)
m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
m_timers[WUPDATE_EVENTS].Reset();
}
+
// update the instance reset times
sInstanceSaveManager.Update();
+
// And last, but not least handle the issued cli commands
ProcessCliCommands();
}
+
void World::ForceGameEventUpdate()
{
m_timers[WUPDATE_EVENTS].Reset(); // to give time for Update() to be processed
@@ -1641,6 +1970,7 @@ void World::ForceGameEventUpdate()
m_timers[WUPDATE_EVENTS].SetInterval(nextGameEvent);
m_timers[WUPDATE_EVENTS].Reset();
}
+
/// Send a packet to all players (except self if mentioned)
void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 team)
{
@@ -1657,6 +1987,7 @@ void World::SendGlobalMessage(WorldPacket *packet, WorldSession *self, uint32 te
}
}
}
+
/// Send a packet to all GMs (except self if mentioned)
void World::SendGlobalGMMessage(WorldPacket *packet, WorldSession *self, uint32 team)
{
@@ -1674,6 +2005,7 @@ void World::SendGlobalGMMessage(WorldPacket *packet, WorldSession *self, uint32
}
}
}
+
namespace MaNGOS
{
class WorldWorldTextBuilder
@@ -1684,14 +2016,17 @@ namespace MaNGOS
void operator()(WorldPacketList& data_list, int32 loc_idx)
{
char const* text = objmgr.GetMangosString(i_textId,loc_idx);
+
if (i_args)
{
// we need copy va_list before use or original va_list will corrupted
va_list ap;
va_copy(ap,*i_args);
+
char str [2048];
vsnprintf(str,2048,text, ap);
va_end(ap);
+
do_helper(data_list,&str[0]);
}
else
@@ -1702,10 +2037,13 @@ namespace MaNGOS
void do_helper(WorldPacketList& data_list, char* text)
{
char* pos = text;
+
while(char* line = lineFromMessage(pos))
{
WorldPacket* data = new WorldPacket();
+
uint32 lineLength = (line ? strlen(line) : 0) + 1;
+
data->Initialize(SMSG_MESSAGECHAT, 100); // guess size
*data << uint8(CHAT_MSG_SYSTEM);
*data << uint32(LANG_UNIVERSAL);
@@ -1715,59 +2053,75 @@ namespace MaNGOS
*data << uint32(lineLength);
*data << line;
*data << uint8(0);
+
data_list.push_back(data);
}
}
+
int32 i_textId;
va_list* i_args;
};
} // namespace MaNGOS
+
/// Send a System Message to all players (except self if mentioned)
void World::SendWorldText(int32 string_id, ...)
{
va_list ap;
va_start(ap, string_id);
+
MaNGOS::WorldWorldTextBuilder wt_builder(string_id, &ap);
MaNGOS::LocalizedPacketListDo<MaNGOS::WorldWorldTextBuilder> wt_do(wt_builder);
for (SessionMap::const_iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
{
if (!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld())
continue;
+
wt_do(itr->second->GetPlayer());
}
+
va_end(ap);
}
+
/// Send a System Message to all GMs (except self if mentioned)
void World::SendGMText(int32 string_id, ...)
{
va_list ap;
va_start(ap, string_id);
+
MaNGOS::WorldWorldTextBuilder wt_builder(string_id, &ap);
MaNGOS::LocalizedPacketListDo<MaNGOS::WorldWorldTextBuilder> wt_do(wt_builder);
for (SessionMap::iterator itr = m_sessions.begin(); itr != m_sessions.end(); ++itr)
{
if (!itr->second || !itr->second->GetPlayer() || !itr->second->GetPlayer()->IsInWorld())
continue;
+
if (itr->second->GetSecurity() < SEC_MODERATOR)
continue;
+
wt_do(itr->second->GetPlayer());
}
+
va_end(ap);
}
+
/// DEPRICATED, only for debug purpose. Send a System Message to all players (except self if mentioned)
void World::SendGlobalText(const char* text, WorldSession *self)
{
WorldPacket data;
+
// need copy to prevent corruption by strtok call in LineFromMessage original string
char* buf = strdup(text);
char* pos = buf;
+
while(char* line = ChatHandler::LineFromMessage(pos))
{
ChatHandler::FillMessageData(&data, NULL, CHAT_MSG_SYSTEM, LANG_UNIVERSAL, NULL, 0, line, NULL);
SendGlobalMessage(&data, self);
}
+
free(buf);
}
+
/// 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)
{
@@ -1785,6 +2139,7 @@ void World::SendZoneMessage(uint32 zone, WorldPacket *packet, WorldSession *self
}
}
}
+
/// 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)
{
@@ -1792,14 +2147,17 @@ void World::SendZoneText(uint32 zone, const char* text, WorldSession *self, uint
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()
{
m_QueuedPlayer.clear(); // prevent send queue update packet and login queued sessions
+
// session not removed at kick and will removed in next update tick
for (SessionMap::const_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)
{
@@ -1808,6 +2166,7 @@ void World::KickAllLess(AccountTypes sec)
if (itr->second->GetSecurity() < sec)
itr->second->KickPlayer();
}
+
/// Ban an account or ban an IP address, duration will be parsed using TimeStringToSecs if it is positive, otherwise permban
BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string duration, std::string reason, std::string author)
{
@@ -1815,8 +2174,10 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura
loginDatabase.escape_string(reason);
std::string safe_author=author;
loginDatabase.escape_string(safe_author);
+
uint32 duration_secs = TimeStringToSecs(duration);
QueryResult *resultAccounts = NULL; //used for kicking
+
///- Update the database with ban information
switch(mode)
{
@@ -1836,6 +2197,7 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura
default:
return BAN_SYNTAX_ERROR;
}
+
if (!resultAccounts)
{
if (mode==BAN_IP)
@@ -1843,25 +2205,30 @@ BanReturn World::BanAccount(BanMode mode, std::string nameOrIP, std::string dura
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 (mode!=BAN_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());
}
+
if (WorldSession* sess = FindSession(account))
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(BanMode mode, std::string nameOrIP)
{
@@ -1877,13 +2244,16 @@ bool World::RemoveBanAccount(BanMode mode, std::string nameOrIP)
account = accmgr.GetId (nameOrIP);
else if (mode == BAN_CHARACTER)
account = objmgr.GetPlayerAccountIdByPlayerName (nameOrIP);
+
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()
{
@@ -1891,6 +2261,7 @@ void World::_UpdateGameTime()
time_t thisTime = time(NULL);
uint32 elapsed = uint32(thisTime - m_gameTime);
m_gameTime = thisTime;
+
///- if there is a shutdown timer
if (!m_stopEvent && m_ShutdownTimer > 0 && elapsed > 0)
{
@@ -1906,18 +2277,22 @@ void World::_UpdateGameTime()
else
{
m_ShutdownTimer -= elapsed;
+
ShutdownMsg();
}
}
}
+
/// Shutdown the server
void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode)
{
// ignore if server shutdown at next tick
if (m_stopEvent)
return;
+
m_ShutdownMask = options;
m_ExitCode = exitcode;
+
///- If the shutdown time is 0, set m_stopEvent (except if shutdown is 'idle' with remaining sessions)
if (time==0)
{
@@ -1932,14 +2307,17 @@ void World::ShutdownServ(uint32 time, uint32 options, uint8 exitcode)
m_ShutdownTimer = time;
ShutdownMsg(true);
}
+
Script->OnServerShutdown();
}
+
/// 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) ||
@@ -1955,24 +2333,31 @@ void World::ShutdownMsg(bool show, Player* player)
(m_ShutdownTimer>12*HOUR && (m_ShutdownTimer % (12*HOUR))==0))
{
std::string str = secsToTimeString(m_ShutdownTimer);
+
ServerMessageType 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()
{
// nothing cancel or too later
if (!m_ShutdownTimer || m_stopEvent)
return;
+
ServerMessageType msgid = (m_ShutdownMask & SHUTDOWN_MASK_RESTART) ? SERVER_MSG_RESTART_CANCELLED : SERVER_MSG_SHUTDOWN_CANCELLED;
+
m_ShutdownMask = 0;
m_ShutdownTimer = 0;
m_ExitCode = SHUTDOWN_EXIT_CODE; // to default value
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(ServerMessageType type, const char *text, Player* player)
{
@@ -1980,24 +2365,29 @@ void World::SendServerMessage(ServerMessageType type, const char *text, Player*
data << uint32(type);
if (type <= SERVER_MSG_STRING)
data << text;
+
if (player)
player->GetSession()->SendPacket(&data);
else
SendGlobalMessage(&data);
}
+
void World::UpdateSessions(uint32 diff)
{
///- Add new sessions
WorldSession* sess;
while(addSessQueue.next(sess))
AddSession_ (sess);
+
///- 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
{
@@ -2008,10 +2398,12 @@ void World::UpdateSessions(uint32 diff)
}
}
}
+
// This handles the issued and queued CLI commands
void World::ProcessCliCommands()
{
CliCommandHolder::Print* zprint = NULL;
+
CliCommandHolder* command;
while (cliCmdQueue.next(command))
{
@@ -2020,23 +2412,29 @@ void World::ProcessCliCommands()
CliHandler(zprint).ParseCommands(command->m_command);
delete command;
}
+
// print the console message here so it looks right
if (zprint)
zprint("TC> ");
}
+
void World::SendRNDBroadcast()
{
if (m_Autobroadcasts.empty())
return;
+
std::string msg;
+
std::list<std::string>::const_iterator itr = m_Autobroadcasts.begin();
std::advance(itr, rand() % m_Autobroadcasts.size());
msg = *itr;
+
static uint32 abcenter = 0;
abcenter = sConfig.GetIntDefault("AutoBroadcast.Center", 0);
if (abcenter == 0)
{
sWorld.SendWorldText(LANG_AUTO_BROADCAST, msg.c_str());
+
sLog.outString("AutoBroadcast: '%s'",msg.c_str());
}
if (abcenter == 1)
@@ -2044,31 +2442,38 @@ void World::SendRNDBroadcast()
WorldPacket data(SMSG_NOTIFICATION, (msg.size()+1));
data << msg;
sWorld.SendGlobalMessage(&data);
+
sLog.outString("AutoBroadcast: '%s'",msg.c_str());
}
if (abcenter == 2)
{
sWorld.SendWorldText(LANG_AUTO_BROADCAST, msg.c_str());
+
WorldPacket data(SMSG_NOTIFICATION, (msg.size()+1));
data << msg;
sWorld.SendGlobalMessage(&data);
+
sLog.outString("AutoBroadcast: '%s'",msg.c_str());
}
}
+
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)
@@ -2080,18 +2485,22 @@ void World::_UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId
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);
@@ -2099,10 +2508,13 @@ void World::InitDailyQuestResetTime()
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;
@@ -2112,6 +2524,7 @@ void World::InitDailyQuestResetTime()
m_NextDailyQuestReset = (curTime >= curDayResetTime) ? curDayResetTime + DAY : curDayResetTime;
}
}
+
void World::ResetDailyQuests()
{
sLog.outDetail("Daily quests reset for all characters.");
@@ -2120,6 +2533,7 @@ void World::ResetDailyQuests()
if (itr->second->GetPlayer())
itr->second->GetPlayer()->ResetDailyQuestStatus();
}
+
void World::UpdateAllowedSecurity()
{
QueryResult *result = loginDatabase.PQuery("SELECT allowedSecurityLevel from realmlist WHERE id = '%d'", realmID);
@@ -2130,15 +2544,18 @@ void World::UpdateAllowedSecurity()
delete result;
}
}
+
void World::SetPlayerLimit(int32 limit, bool needUpdate)
{
m_playerLimit = limit;
}
+
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()));
}
+
void World::LoadDBVersion()
{
QueryResult* result = WorldDatabase.Query("SELECT db_version, script_version, cache_id FROM version LIMIT 1");
@@ -2146,14 +2563,18 @@ void World::LoadDBVersion()
if (result)
{
Field* fields = result->Fetch();
+
m_DBVersion = fields[0].GetCppString();
m_CreatureEventAIVersion = fields[1].GetCppString();
+
// will be overwrite by config values if different and non-0
m_configs[CONFIG_CLIENTCACHE_VERSION] = fields[2].GetUInt32();
delete result;
}
+
if (m_DBVersion.empty())
m_DBVersion = "Unknown world database.";
+
if (m_CreatureEventAIVersion.empty())
m_CreatureEventAIVersion = "Unknown creature EventAI.";
}
diff --git a/src/game/World.h b/src/game/World.h
index ef38a893e16..5e64e74661e 100644
--- a/src/game/World.h
+++ b/src/game/World.h
@@ -17,19 +17,24 @@
* 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 "SharedDefines.h"
#include "ace/Atomic_Op.h"
+
#include <map>
#include <set>
#include <list>
+
class Object;
class WorldPacket;
class WorldSession;
@@ -40,6 +45,7 @@ struct ScriptInfo;
class SqlResultQueue;
class QueryResult;
class WorldSocket;
+
// ServerMessages.dbc
enum ServerMessageType
{
@@ -49,17 +55,20 @@ enum ServerMessageType
SERVER_MSG_SHUTDOWN_CANCELLED = 4,
SERVER_MSG_RESTART_CANCELLED = 5
};
+
enum ShutdownMask
{
SHUTDOWN_MASK_RESTART = 1,
SHUTDOWN_MASK_IDLE = 2,
};
+
enum ShutdownExitCode
{
SHUTDOWN_EXIT_CODE = 0,
ERROR_EXIT_CODE = 1,
RESTART_EXIT_CODE = 2,
};
+
/// Timers for different object refresh rates
enum WorldTimers
{
@@ -74,6 +83,7 @@ enum WorldTimers
WUPDATE_AUTOBROADCAST = 8,
WUPDATE_COUNT = 9
};
+
/// Configuration elements
enum WorldConfigs
{
@@ -244,7 +254,7 @@ enum WorldConfigs
CONFIG_MIN_LOG_UPDATE,
CONFIG_CHECK_DB,
CONFIG_ENABLE_SINFO_LOGIN,
- CONFIG_PET_LOS,
+ CONFIG_PET_LOS,
CONFIG_NUMTHREADS,
CONFIG_OFFHAND_CHECK_AT_SPELL_UNLEARN,
CONFIG_CHATLOG_CHANNEL,
@@ -263,6 +273,7 @@ enum WorldConfigs
CONFIG_GUILD_BANK_EVENT_LOG_COUNT,
CONFIG_VALUE_COUNT
};
+
/// Server rates
enum Rates
{
@@ -328,6 +339,7 @@ enum Rates
RATE_MOVESPEED,
MAX_RATES
};
+
/// Type of server
enum RealmType
{
@@ -339,6 +351,7 @@ enum RealmType
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
@@ -372,6 +385,7 @@ enum RealmZone
REALM_ZONE_QA_SERVER = 28, // any language
REALM_ZONE_CN9 = 29 // basic-Latin at create, any at login
};
+
// DB scripting commands
#define SCRIPT_COMMAND_TALK 0 // source = unit, target=any, datalong (0=say, 1=whisper, 2=yell, 3=emote text, 4=boss emote text)
#define SCRIPT_COMMAND_EMOTE 1 // source = unit, datalong = anim_id
@@ -392,12 +406,15 @@ enum RealmZone
#define SCRIPT_COMMAND_LOAD_PATH 20 // source = unit, path = datalong, repeatable datalong2
#define SCRIPT_COMMAND_CALLSCRIPT_TO_UNIT 21 // datalong scriptid, lowguid datalong2, dataint table
#define SCRIPT_COMMAND_KILL 22 // datalong removecorpse
+
/// Storage class for commands issued for delayed execution
struct CliCommandHolder
{
typedef void Print(const char*);
+
char *m_command;
Print* m_print;
+
CliCommandHolder(const char *command, Print* zprint)
: m_print(zprint)
{
@@ -405,15 +422,19 @@ struct CliCommandHolder
m_command = new char[len];
memcpy(m_command, command, len);
}
+
~CliCommandHolder() { delete[] m_command; }
};
+
/// The World
class World
{
public:
static volatile uint32 m_worldLoopCounter;
+
World();
~World();
+
WorldSession* FindSession(uint32 id) const;
void AddSession(WorldSession *s);
void SendRNDBroadcast();
@@ -436,19 +457,25 @@ class World
m_MaxPlayerCount = std::max(m_MaxPlayerCount, m_PlayerCount);
}
inline void DecreasePlayerCount() { m_PlayerCount--; }
+
Player* FindPlayerInZone(uint32 zone);
Weather* FindWeather(uint32 id) const;
Weather* AddWeather(uint32 zone_id);
void RemoveWeather(uint32 zone_id);
+
/// Deny clients?
bool IsClosed() { return m_isClosed; }
+
/// Close world
void SetClosed(bool val) { m_isClosed = val; }
+
/// 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_allowedSecurityLevel < 0 ? SEC_PLAYER : m_allowedSecurityLevel; }
+
/// Set the active session server limit (or security level limitation)
void SetPlayerLimit(int32 limit, bool needUpdate = false);
+
//player Queue
typedef std::list<WorldSession*> Queue;
void AddQueuedPlayer(WorldSession*);
@@ -456,22 +483,28 @@ class World
int32 GetQueuePos(WorldSession*);
bool HasRecentlyDisconnected(WorldSession*);
uint32 GetQueueSize() const { return m_QueuedPlayer.size(); }
+
/// \todo Actions on m_allowMovement still to be implemented
/// Is movement allowed?
bool getAllowMovement() const { return m_allowMovement; }
/// Allow/Disallow object movements
void SetAllowMovement(bool allow) { m_allowMovement = allow; }
+
/// Set a new Message of the Day
void SetMotd(const std::string& motd) { m_motd = motd; }
/// Get the current Message of the Day
const char* GetMotd() const { return m_motd.c_str(); }
+
/// Set the string for new characters (first login)
void SetNewCharString(std::string str) { m_newCharString = str; }
/// Get the string for new characters (first login)
const std::string& GetNewCharString() const { return m_newCharString; }
+
LocaleConstant 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?
@@ -481,14 +514,17 @@ class World
/// Update time
uint32 GetUpdateTime() const { return m_updateTime; }
void SetRecordDiffInterval(int32 t) { if(t >= 0) m_configs[CONFIG_INTERVAL_LOG_UPDATE] = (uint32)t; }
+
/// 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 SendGlobalText(const char* text, WorldSession *self);
void SendGMText(int32 string_id, ...);
@@ -497,6 +533,7 @@ class World
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(ServerMessageType type, const char *text = "", Player* player = NULL);
+
/// Are we in the middle of a shutdown?
bool IsShutdowning() const { return m_ShutdownTimer > 0; }
void ShutdownServ(uint32 time, uint32 options, uint8 exitcode);
@@ -505,18 +542,22 @@ class World
static uint8 GetExitCode() { return m_ExitCode; }
static void StopNow(uint8 exitcode) { m_stopEvent = true; m_ExitCode = exitcode; }
static bool IsStopped() { return m_stopEvent; }
+
void Update(uint32 diff);
+
void UpdateSessions( uint32 diff );
/// Set a server rate (see #Rates)
void setRate(Rates rate,float value) { rate_values[rate]=value; }
/// Get a server rate (see #Rates)
float getRate(Rates rate) const { return rate_values[rate]; }
+
/// Set a server configuration element (see #WorldConfigs)
void setConfig(uint32 index,uint32 value)
{
if(index<CONFIG_VALUE_COUNT)
m_configs[index]=value;
}
+
/// Get a server configuration element (see #WorldConfigs)
uint32 getConfig(uint32 index) const
{
@@ -525,47 +566,64 @@ class World
else
return 0;
}
+
/// Are we on a "Player versus Player" server?
bool IsPvPRealm() { return (getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_PVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_RPPVP || getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP); }
bool IsFFAPvPRealm() { return getConfig(CONFIG_GAME_TYPE) == REALM_TYPE_FFA_PVP; }
+
void KickAll();
void KickAllLess(AccountTypes sec);
BanReturn BanAccount(BanMode mode, std::string nameOrIP, std::string duration, std::string reason, std::string author);
bool RemoveBanAccount(BanMode mode, std::string nameOrIP);
+
uint32 IncreaseScheduledScriptsCount() { return (uint32)++m_scheduledScripts; }
uint32 DecreaseScheduledScriptCount() { return (uint32)--m_scheduledScripts; }
uint32 DecreaseScheduledScriptCount(size_t count) { return (uint32)(m_scheduledScripts -= count); }
bool IsScriptScheduled() const { return m_scheduledScripts > 0; }
+
bool IsAllowedMap(uint32 mapid) { return m_forbiddenMapIds.count(mapid) == 0 ;}
+
// for max speed access
static float GetMaxVisibleDistanceOnContinents() { return m_MaxVisibleDistanceOnContinents; }
static float GetMaxVisibleDistanceInInstances() { return m_MaxVisibleDistanceInInstances; }
static float GetMaxVisibleDistanceInBGArenas() { return m_MaxVisibleDistanceInBGArenas; }
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::Print* zprintf, char const* input ) { cliCmdQueue.add(new CliCommandHolder(input, zprintf)); }
+
void UpdateResultQueue();
void InitResultQueue();
+
void ForceGameEventUpdate();
+
void UpdateRealmCharCount(uint32 accid);
+
void UpdateAllowedSecurity();
+
LocaleConstant GetAvailableDbcLocale(LocaleConstant locale) const { if(m_availableDbcLocaleMask & (1 << locale)) return locale; else return m_defaultDbcLocale; }
+
//used World DB version
void LoadDBVersion();
char const* GetDBVersion() { return m_DBVersion.c_str(); }
char const* GetCreatureEventAIVersion() { return m_CreatureEventAIVersion.c_str(); }
+
//used Script version
void SetScriptsVersion(char const* version) { m_ScriptsVersion = version ? version : "unknown scripting library"; }
char const* GetScriptsVersion() { return m_ScriptsVersion.c_str(); }
+
void RecordTimeDiff(const char * text, ...);
+
void LoadAutobroadcasts();
protected:
void _UpdateGameTime();
// callback for UpdateRealmCharacters
void _UpdateRealmCharCount(QueryResult *resultCharCount, uint32 accountId);
+
void InitDailyQuestResetTime();
void ResetDailyQuests();
private:
@@ -573,9 +631,12 @@ class World
static uint8 m_ExitCode;
uint32 m_ShutdownTimer;
uint32 m_ShutdownMask;
+
bool m_isClosed;
+
//atomic op counter for active scripts amount
ACE_Atomic_Op<ACE_Thread_Mutex, long> m_scheduledScripts;
+
time_t m_startTime;
time_t m_gameTime;
IntervalTimer m_timers[WUPDATE_COUNT];
@@ -584,6 +645,7 @@ class World
uint32 m_updateTime, m_updateTimeSum;
uint32 m_updateTimeCount;
uint32 m_currentTime;
+
typedef UNORDERED_MAP<uint32, Weather*> WeatherMap;
WeatherMap m_weathers;
typedef UNORDERED_MAP<uint32, WorldSession*> SessionMap;
@@ -594,7 +656,9 @@ class World
uint32 m_maxQueuedSessionCount;
uint32 m_PlayerCount;
uint32 m_MaxPlayerCount;
+
std::string m_newCharString;
+
float rate_values[MAX_RATES];
uint32 m_configs[CONFIG_VALUE_COUNT];
int32 m_playerLimit;
@@ -606,31 +670,41 @@ class World
std::string m_motd;
std::string m_dataPath;
std::set<uint32> m_forbiddenMapIds;
+
// for max speed access
static float m_MaxVisibleDistanceOnContinents;
static float m_MaxVisibleDistanceInInstances;
static float m_MaxVisibleDistanceInBGArenas;
static float m_MaxVisibleDistanceForObject;
+
static float m_MaxVisibleDistanceInFlight;
static float m_VisibleUnitGreyDistance;
static float m_VisibleObjectGreyDistance;
+
// CLI command holder to be thread safe
ACE_Based::LockedQueue<CliCommandHolder*,ACE_Thread_Mutex> cliCmdQueue;
SqlResultQueue *m_resultQueue;
+
// next daily quests reset time
time_t m_NextDailyQuestReset;
+
//Player Queue
Queue m_QueuedPlayer;
+
//sessions that are added async
void AddSession_(WorldSession* s);
ACE_Based::LockedQueue<WorldSession*, ACE_Thread_Mutex> addSessQueue;
+
//used versions
std::string m_DBVersion;
std::string m_CreatureEventAIVersion;
std::string m_ScriptsVersion;
+
std::list<std::string> m_Autobroadcasts;
};
+
extern uint32 realmID;
+
#define sWorld Trinity::Singleton<World>::Instance()
#endif
/// @}
diff --git a/src/game/WorldLog.cpp b/src/game/WorldLog.cpp
index 377b8758d9f..435e3279948 100644
--- a/src/game/WorldLog.cpp
+++ b/src/game/WorldLog.cpp
@@ -17,56 +17,69 @@
* 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"
#include "Log.h"
+
#define CLASS_LOCK MaNGOS::ClassLevelLockable<WorldLog, ACE_Thread_Mutex>
INSTANTIATE_SINGLETON_2(WorldLog, CLASS_LOCK);
INSTANTIATE_CLASS_MUTEX(WorldLog, ACE_Thread_Mutex);
+
WorldLog::WorldLog() : i_file(NULL)
{
Initialize();
}
+
WorldLog::~WorldLog()
{
if( i_file != NULL )
fclose(i_file);
i_file = NULL;
}
+
/// 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");
}
+
m_dbWorld = sConfig.GetBoolDefault("LogDB.World", false); // can be VERY heavy if enabled
}
+
void WorldLog::outTimestampLog(char const *fmt, ...)
{
if( LogWorld() )
{
Guard guard(*this);
ASSERT(i_file);
+
Log::outTimestamp(i_file);
va_list args;
va_start(args, fmt);
vfprintf(i_file, fmt, args);
//fprintf(i_file, "\n" );
va_end(args);
+
fflush(i_file);
}
+
if (sLog.GetLogDB() && m_dbWorld)
{
va_list ap2;
@@ -77,19 +90,23 @@ void WorldLog::outTimestampLog(char const *fmt, ...)
va_end(ap2);
}
}
+
void WorldLog::outLog(char const *fmt, ...)
{
if( LogWorld() )
{
Guard guard(*this);
ASSERT(i_file);
+
va_list args;
va_start(args, fmt);
vfprintf(i_file, fmt, args);
//fprintf(i_file, "\n" );
va_end(args);
+
fflush(i_file);
}
+
if (sLog.GetLogDB() && m_dbWorld)
{
va_list ap2;
@@ -100,5 +117,6 @@ void WorldLog::outLog(char const *fmt, ...)
va_end(ap2);
}
}
+
#define sWorldLog WorldLog::Instance()
diff --git a/src/game/WorldLog.h b/src/game/WorldLog.h
index 29d07c77df1..e6b72e654f7 100644
--- a/src/game/WorldLog.h
+++ b/src/game/WorldLog.h
@@ -17,15 +17,20 @@
* 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 TRINITY_WORLDLOG_H
#define TRINITY_WORLDLOG_H
+
#include "Common.h"
#include "Policies/Singleton.h"
#include "Errors.h"
+
#include <stdarg.h>
+
/// %Log packets to a file
class MANGOS_DLL_DECL WorldLog : public MaNGOS::Singleton<WorldLog, MaNGOS::ClassLevelLockable<WorldLog, ACE_Thread_Mutex> >
{
@@ -34,8 +39,10 @@ class MANGOS_DLL_DECL WorldLog : public MaNGOS::Singleton<WorldLog, MaNGOS::Clas
WorldLog(const WorldLog &);
WorldLog& operator=(const WorldLog &);
typedef MaNGOS::ClassLevelLockable<WorldLog, ACE_Thread_Mutex>::Lock Guard;
+
/// Close the file in destructor
~WorldLog();
+
public:
void Initialize();
/// Is the world logger active?
@@ -43,10 +50,13 @@ class MANGOS_DLL_DECL WorldLog : public MaNGOS::Singleton<WorldLog, MaNGOS::Clas
/// %Log to the file
void outLog(char const *fmt, ...);
void outTimestampLog(char const *fmt, ...);
+
private:
FILE *i_file;
+
bool m_dbWorld;
};
+
#define sWorldLog WorldLog::Instance()
#endif
/// @}
diff --git a/src/game/WorldSession.cpp b/src/game/WorldSession.cpp
index 41164032a3a..f5515bf78c1 100644
--- a/src/game/WorldSession.cpp
+++ b/src/game/WorldSession.cpp
@@ -17,9 +17,11 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/** \file
\ingroup u2w
*/
+
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include "Common.h"
#include "Database/DatabaseEnv.h"
@@ -39,6 +41,7 @@
#include "SocialMgr.h"
#include "zlib/zlib.h"
#include "ScriptCalls.h"
+
/// WorldSession constructor
WorldSession::WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, time_t mute_time, LocaleConstant locale) :
LookingForGroup_auto_join(false), LookingForGroup_auto_add(false), m_muteTime(mute_time),
@@ -54,12 +57,14 @@ m_latency(0), m_TutorialsChanged(false)
loginDatabase.PExecute("UPDATE account SET online = 1 WHERE id = %u;", GetAccountId());
}
}
+
/// WorldSession destructor
WorldSession::~WorldSession()
{
///- unload player if not unloaded
if (_player)
LogoutPlayer (true);
+
/// - If have unclosed socket, close it
if (m_Socket)
{
@@ -67,41 +72,53 @@ WorldSession::~WorldSession()
m_Socket->RemoveReference ();
m_Socket = NULL;
}
+
///- empty incoming packet queue
WorldPacket* packet;
while(_recvQueue.next(packet))
delete packet;
+
loginDatabase.PExecute("UPDATE account SET online = 0 WHERE id = %u;", GetAccountId());
CharacterDatabase.PExecute("UPDATE characters SET online = 0 WHERE account = %u;", GetAccountId());
}
+
void WorldSession::SizeError(WorldPacket const& packet, uint32 size) const
{
sLog.outError("Client (account %u) send packet %s (%u) with size " SIZEFMTD " but expected %u (attempt crash server?), skipped",
GetAccountId(),LookupOpcodeName(packet.GetOpcode()),packet.GetOpcode(),packet.size(),size);
}
+
/// Get the player name
char const* WorldSession::GetPlayerName() const
{
return GetPlayer() ? GetPlayer()->GetName() : "<none>";
}
+
/// Send a packet to the client
void WorldSession::SendPacket(WorldPacket const* packet)
{
if (!m_Socket)
return;
+
#ifdef TRINITY_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();
}
@@ -111,19 +128,24 @@ void WorldSession::SendPacket(WorldPacket const* packet)
uint64 fullTime = uint64(lastTime - firstTime);
sLog.outDetail("Send all time packets count: " UI64FMTD " bytes: " UI64FMTD " 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: " UI64FMTD " bytes: " UI64FMTD " 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 // !TRINITY_DEBUG
+
if (m_Socket->SendPacket (*packet) == -1)
m_Socket->CloseSocket ();
}
+
/// Add an incoming packet to the queue
void WorldSession::QueuePacket(WorldPacket* new_packet)
{
_recvQueue.add(new_packet);
}
+
/// Logging helper for unexpected opcodes
void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, const char *reason)
{
@@ -132,6 +154,7 @@ void WorldSession::LogUnexpectedOpcode(WorldPacket* packet, const char *reason)
packet->GetOpcode(),
reason);
}
+
/// Logging helper for unexpected opcodes
void WorldSession::LogUnprocessedTail(WorldPacket *packet)
{
@@ -139,8 +162,10 @@ void WorldSession::LogUnprocessedTail(WorldPacket *packet)
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode(),
packet->rpos(),packet->wpos());
+
packet->print_storage();
}
+
/// Update the WorldSession (triggered by World update)
bool WorldSession::Update(uint32 /*diff*/)
{
@@ -154,6 +179,7 @@ bool WorldSession::Update(uint32 /*diff*/)
LookupOpcodeName(packet->GetOpcode()),
packet->GetOpcode());
#endif*/
+
if(packet->GetOpcode() >= NUM_MSG_TYPES)
{
sLog.outError( "SESSION: received non-existed opcode %s (0x%.4X)",
@@ -214,10 +240,12 @@ bool WorldSession::Update(uint32 /*diff*/)
LogUnexpectedOpcode(packet, "the player not pass queue yet");
break;
}
+
// single from authed time opcodes send in to after logout time
// and before other STATUS_LOGGEDIN_OR_RECENTLY_LOGGOUT opcodes.
if (packet->GetOpcode() != CMSG_SET_ACTIVE_VOICE_CHANNEL)
m_playerRecentlyLogout = false;
+
(this->*opHandle.handler)(*packet);
if (sLog.IsOutDebug() && packet->rpos() < packet->wpos())
LogUnprocessedTail(packet);
@@ -241,33 +269,42 @@ bool WorldSession::Update(uint32 /*diff*/)
}
}
}
+
delete packet;
}
+
///- Cleanup socket pointer if need
if (m_Socket && m_Socket->IsClosed ())
{
m_Socket->RemoveReference ();
m_Socket = NULL;
}
+
///- If necessary, log the player out
time_t currTime = time(NULL);
if (!m_Socket || (ShouldLogOut(currTime) && !m_playerLoading))
LogoutPlayer(true);
+
if (!m_Socket)
return false; //Will remove this session from the world session map
+
return true;
}
+
/// %Log the player out
void WorldSession::LogoutPlayer(bool Save)
{
// finish pending transfers before starting the logout
while(_player && _player->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
+
m_playerLogout = true;
+
if (_player)
{
if (uint64 lguid = GetPlayer()->GetLootGUID())
DoLootRelease(lguid);
+
///- If the player just died before logging out, make him appear as a ghost
//FIXME: logout must be delayed in case lost connection with client in time of combat
if (_player->GetDeathTimer())
@@ -281,6 +318,7 @@ void WorldSession::LogoutPlayer(bool Save)
_player->CombatStop();
_player->getHostilRefManager().setOnlineOfflineState(false);
_player->RemoveAllAurasOnDeath();
+
// build set of player who attack _player or who have pet attacking of _player
std::set<Player*> aset;
for(Unit::AttackerSet::const_iterator itr = _player->getAttackers().begin(); itr != _player->getAttackers().end(); ++itr)
@@ -295,13 +333,16 @@ void WorldSession::LogoutPlayer(bool Save)
if((*itr)->GetTypeId()==TYPEID_PLAYER)
aset.insert((Player*)(*itr));
}
+
_player->SetPvPDeath(!aset.empty());
_player->KillPlayer();
_player->BuildPlayerRepop();
_player->RepopAtGraveyard();
+
// give honor to all attackers from set like group case
for(std::set<Player*>::const_iterator itr = aset.begin(); itr != aset.end(); ++itr)
(*itr)->RewardHonor(_player,aset.size());
+
// give bg rewards and update counters like kill by first from attackers
// this can't be called for all attackers.
if(!aset.empty())
@@ -320,10 +361,13 @@ void WorldSession::LogoutPlayer(bool Save)
//drop a flag if player is carrying it
if(BattleGround *bg = _player->GetBattleGround())
bg->EventPlayerLoggedOut(_player);
+
///- Teleport to home if the player is in an invalid instance
if(!_player->m_InstanceValid && !_player->isGameMaster())
_player->TeleportTo(_player->m_homebindMapId, _player->m_homebindX, _player->m_homebindY, _player->m_homebindZ, _player->GetOrientation());
+
sOutdoorPvPMgr.HandlePlayerLeaveZone(_player,_player->GetZoneId());
+
for (int i=0; i < PLAYER_MAX_BATTLEGROUND_QUEUES; ++i)
{
if(BattleGroundQueueTypeId bgQueueTypeId = _player->GetBattleGroundQueueTypeId(i))
@@ -332,16 +376,19 @@ void WorldSession::LogoutPlayer(bool Save)
sBattleGroundMgr.m_BattleGroundQueues[ bgQueueTypeId ].RemovePlayer(_player->GetGUID(), true);
}
}
+
// Repop at GraveYard or other player far teleport will prevent saving player because of not present map
// Teleport player immediately for correct player save
while(_player->IsBeingTeleportedFar())
HandleMoveWorldportAckOpcode();
+
///- 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->SetMemberStats(_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;
@@ -349,8 +396,10 @@ void WorldSession::LogoutPlayer(bool Save)
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)
@@ -365,20 +414,26 @@ void WorldSession::LogoutPlayer(bool Save)
}
_player->SaveToDB();
}
+
///- Leave all channels before player delete...
_player->CleanupChannels();
+
///- If the player is in a group (or invited), remove him. If the group if then only 1 person, disband the group.
_player->UninviteFromGroup();
+
// remove player from the group if he is:
// a) in group; b) not in raid group; c) logging out normally (not being kicked or disconnected)
if(_player->GetGroup() && !_player->GetGroup()->isRaidGroup() && m_Socket)
_player->RemoveFromGroup();
+
///- 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);
sSocialMgr.RemovePlayerSocial (_player->GetGUIDLow ());
+
///- 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
@@ -387,42 +442,52 @@ void WorldSession::LogoutPlayer(bool Save)
Map* _map = _player->GetMap();
_map->Remove(_player, true);
SetPlayer(NULL); // deleted in Remove call
+
///- 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" );
}
+
//Hook for OnLogout Event
Script->OnLogout(_player);
+
m_playerLogout = false;
m_playerRecentlyLogout = true;
LogoutRequest(0);
}
+
/// Kick a player out of the World
void WorldSession::KickPlayer()
{
if (m_Socket)
m_Socket->CloseSocket ();
}
+
/// Cancel channeling handler
+
void WorldSession::SendAreaTriggerMessage(const char* Text, ...)
{
va_list ap;
char szStr [1024];
szStr[0] = '\0';
+
va_start(ap, Text);
vsnprintf( szStr, 1024, Text, ap );
va_end(ap);
+
uint32 length = strlen(szStr)+1;
WorldPacket data(SMSG_AREA_TRIGGER_MESSAGE, 4+length);
data << length;
data << szStr;
SendPacket(&data);
}
+
void WorldSession::SendNotification(const char *format,...)
{
if(format)
@@ -433,11 +498,13 @@ void WorldSession::SendNotification(const char *format,...)
va_start(ap, format);
vsnprintf( szStr, 1024, format, ap );
va_end(ap);
+
WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
data << szStr;
SendPacket(&data);
}
}
+
void WorldSession::SendNotification(int32 string_id,...)
{
char const* format = GetTrinityString(string_id);
@@ -449,45 +516,53 @@ void WorldSession::SendNotification(int32 string_id,...)
va_start(ap, string_id);
vsnprintf( szStr, 1024, format, ap );
va_end(ap);
+
WorldPacket data(SMSG_NOTIFICATION, (strlen(szStr)+1));
data << szStr;
SendPacket(&data);
}
}
+
void WorldSession::SendSetPhaseShift(uint32 PhaseShift)
{
WorldPacket data(SMSG_SET_PHASE_SHIFT, 4);
data << uint32(PhaseShift);
SendPacket(&data);
}
+
const char * WorldSession::GetTrinityString( int32 entry ) const
{
return objmgr.GetTrinityString(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 processed in WorldSocket::OnRead",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
+
void WorldSession::Handle_ServerSide( WorldPacket& recvPacket )
{
sLog.outError( "SESSION: received server-side opcode %s (0x%.4X)",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
+
void WorldSession::Handle_Deprecated( WorldPacket& recvPacket )
{
sLog.outError( "SESSION: received deprecated opcode %s (0x%.4X)",
LookupOpcodeName(recvPacket.GetOpcode()),
recvPacket.GetOpcode());
}
+
void WorldSession::SendAuthWaitQue(uint32 position)
{
if(position == 0)
@@ -504,6 +579,7 @@ void WorldSession::SendAuthWaitQue(uint32 position)
SendPacket(&packet);
}
}
+
void WorldSession::LoadGlobalAccountData()
{
LoadAccountData(
@@ -511,16 +587,20 @@ void WorldSession::LoadGlobalAccountData()
GLOBAL_CACHE_MASK
);
}
+
void WorldSession::LoadAccountData(QueryResult* result, uint32 mask)
{
for (uint32 i = 0; i < NUM_ACCOUNT_DATA_TYPES; ++i)
if (mask & (1 << i))
m_accountData[i] = AccountData();
+
if(!result)
return;
+
do
{
Field *fields = result->Fetch();
+
uint32 type = fields[0].GetUInt32();
if (type >= NUM_ACCOUNT_DATA_TYPES)
{
@@ -528,22 +608,28 @@ void WorldSession::LoadAccountData(QueryResult* result, uint32 mask)
mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
continue;
}
+
if ((mask & (1 << type))==0)
{
sLog.outError("Table `%s` have non appropriate for table account data type (%u), ignore.",
mask == GLOBAL_CACHE_MASK ? "account_data" : "character_account_data", type);
continue;
}
+
m_accountData[type].Time = fields[1].GetUInt32();
m_accountData[type].Data = fields[2].GetCppString();
+
} while (result->NextRow());
+
delete result;
}
+
void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::string data)
{
if ((1 << type) & GLOBAL_CACHE_MASK)
{
uint32 acc = GetAccountId();
+
CharacterDatabase.BeginTransaction ();
CharacterDatabase.PExecute("DELETE FROM account_data WHERE account='%u' AND type='%u'", acc, type);
CharacterDatabase.escape_string(data);
@@ -555,15 +641,18 @@ void WorldSession::SetAccountData(AccountDataType type, time_t time_, std::strin
// _player can be NULL and packet received after logout but m_GUID still store correct guid
if(!m_GUIDLow)
return;
+
CharacterDatabase.BeginTransaction ();
CharacterDatabase.PExecute("DELETE FROM character_account_data WHERE guid='%u' AND type='%u'", m_GUIDLow, type);
CharacterDatabase.escape_string(data);
CharacterDatabase.PExecute("INSERT INTO character_account_data VALUES ('%u','%u','%u','%s')", m_GUIDLow, type, (uint32)time_, data.c_str());
CharacterDatabase.CommitTransaction ();
}
+
m_accountData[type].Time = time_;
m_accountData[type].Data = data;
}
+
void WorldSession::SendAccountDataTimes()
{
WorldPacket data( SMSG_ACCOUNT_DATA_TIMES, 4+1+8*4 ); // changed in WotLK
@@ -573,24 +662,31 @@ void WorldSession::SendAccountDataTimes()
data << uint32(m_accountData[i].Time); // also unix time
SendPacket(&data);
}
+
void WorldSession::LoadTutorialsData()
{
for ( int aX = 0 ; aX < 8 ; ++aX )
m_Tutorials[ aX ] = 0;
+
QueryResult *result = CharacterDatabase.PQuery("SELECT tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7 FROM character_tutorial WHERE account = '%u'", GetAccountId());
+
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 WorldSession::SendTutorialsData()
{
WorldPacket data(SMSG_TUTORIAL_FLAGS, 4*8);
@@ -598,10 +694,12 @@ void WorldSession::SendTutorialsData()
data << m_Tutorials[i];
SendPacket(&data);
}
+
void WorldSession::SaveTutorialsData()
{
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'", GetAccountId());
@@ -610,6 +708,7 @@ void WorldSession::SaveTutorialsData()
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'",
@@ -619,8 +718,10 @@ void WorldSession::SaveTutorialsData()
{
CharacterDatabase.PExecute("INSERT INTO character_tutorial (account,tut0,tut1,tut2,tut3,tut4,tut5,tut6,tut7) VALUES ('%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u', '%u')", GetAccountId(), 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 WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
{
data >> mi->flags;
@@ -630,10 +731,12 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
data >> mi->y;
data >> mi->z;
data >> mi->o;
+
if(mi->flags & MOVEMENTFLAG_ONTRANSPORT)
{
if(!data.readPackGUID(mi->t_guid))
return;
+
data >> mi->t_x;
data >> mi->t_y;
data >> mi->t_z;
@@ -641,11 +744,14 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
data >> mi->t_time;
data >> mi->t_seat;
}
+
if((mi->flags & (MOVEMENTFLAG_SWIMMING | MOVEMENTFLAG_FLYING)) || (mi->unk1 & 0x20))
{
data >> mi->s_pitch;
}
+
data >> mi->fallTime;
+
if(mi->flags & MOVEMENTFLAG_JUMPING)
{
data >> mi->j_zspeed;
@@ -653,53 +759,70 @@ void WorldSession::ReadMovementInfo(WorldPacket &data, MovementInfo *mi)
data >> mi->j_cosAngle;
data >> mi->j_xyspeed;
}
+
if(mi->flags & MOVEMENTFLAG_SPLINE)
{
data >> mi->u_unk1;
}
}
+
void WorldSession::ReadAddonsInfo(WorldPacket &data)
{
if (data.rpos() + 4 > data.size())
return;
uint32 size;
data >> size;
+
if(!size)
return;
+
if(size > 0xFFFFF)
{
sLog.outError("WorldSession::ReadAddonsInfo addon info too big, size %u", size);
return;
}
+
uLongf uSize = size;
+
uint32 pos = data.rpos();
+
ByteBuffer addonInfo;
addonInfo.resize(size);
+
if (uncompress(const_cast<uint8*>(addonInfo.contents()), &uSize, const_cast<uint8*>(data.contents() + pos), data.size() - pos) == Z_OK)
{
uint32 addonsCount;
addonInfo >> addonsCount; // addons count
+
for(uint32 i = 0; i < addonsCount; ++i)
{
std::string addonName;
uint8 enabled;
uint32 crc, unk1;
+
// check next addon data format correctness
if(addonInfo.rpos()+1 > addonInfo.size())
return;
+
addonInfo >> addonName;
+
addonInfo >> enabled >> crc >> unk1;
+
sLog.outDebug("ADDON: Name: %s, Enabled: 0x%x, CRC: 0x%x, Unknown2: 0x%x", addonName.c_str(), enabled, crc, unk1);
+
m_addonsList.push_back(AddonInfo(addonName, enabled, crc));
}
+
uint32 unk2;
addonInfo >> unk2;
+
if(addonInfo.rpos() != addonInfo.size())
sLog.outDebug("packet under read!");
}
else
sLog.outError("Addon packet uncompress error!");
}
+
void WorldSession::SendAddonsInfo()
{
unsigned char tdata[256] =
@@ -721,11 +844,14 @@ void WorldSession::SendAddonsInfo()
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
};
+
WorldPacket data(SMSG_ADDON_INFO, 4);
+
for(AddonsList::iterator itr = m_addonsList.begin(); itr != m_addonsList.end(); ++itr)
{
uint8 state = 2; // 2 is sent here
data << uint8(state);
+
uint8 unk1 = 1; // 1 is sent here
data << uint8(unk1);
if (unk1)
@@ -734,8 +860,10 @@ void WorldSession::SendAddonsInfo()
data << uint8(unk2);
if (unk2) // if CRC is wrong, add public key (client need it)
data.append(tdata, sizeof(tdata));
+
data << uint32(0);
}
+
uint8 unk3 = 0; // 0 is sent here
data << uint8(unk3);
if (unk3)
@@ -744,7 +872,9 @@ void WorldSession::SendAddonsInfo()
data << uint8(0);
}
}
+
m_addonsList.clear();
+
uint32 count = 0;
data << uint32(count);
/*for(uint32 i = 0; i < count; ++i)
@@ -754,12 +884,15 @@ void WorldSession::SendAddonsInfo()
string (16 bytes)
uint32
}*/
+
SendPacket(&data);
}
+
void WorldSession::SetPlayer( Player *plr )
{
_player = plr;
+
// set m_GUID that can be used while player loggined and later until m_playerRecentlyLogout not reset
if(_player)
m_GUIDLow = _player->GetGUIDLow();
-}
+} \ No newline at end of file
diff --git a/src/game/WorldSession.h b/src/game/WorldSession.h
index 761f7ff4446..b26f5ba4933 100644
--- a/src/game/WorldSession.h
+++ b/src/game/WorldSession.h
@@ -17,18 +17,23 @@
* 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"
#include "SharedDefines.h"
+
class MailItemsInfo;
struct ItemPrototype;
struct AuctionEntry;
struct DeclinedName;
struct MovementInfo;
+
class Creature;
class Item;
class Object;
@@ -42,6 +47,7 @@ class QueryResult;
class LoginQueryHolder;
class CharacterHandler;
struct AreaTableEntry;
+
enum AccountDataType
{
GLOBAL_CONFIG_CACHE = 0, // 0x01 g
@@ -53,15 +59,20 @@ enum AccountDataType
PER_CHARACTER_LAYOUT_CACHE = 6, // 0x40 p
PER_CHARACTER_CHAT_CACHE = 7, // 0x80 p
};
+
#define NUM_ACCOUNT_DATA_TYPES 8
+
#define GLOBAL_CACHE_MASK 0x15
#define PER_CHARACTER_CACHE_MASK 0xEA
+
struct AccountData
{
AccountData() : Time(0), Data("") {}
+
time_t Time;
std::string Data;
};
+
struct AddonInfo
{
AddonInfo(const std::string& name, uint8 enabled, uint32 crc)
@@ -70,16 +81,20 @@ struct AddonInfo
Enabled = enabled;
CRC = crc;
}
+
std::string Name;
uint8 Enabled;
uint32 CRC;
};
+
typedef std::list<AddonInfo> AddonsList;
+
enum PartyOperation
{
PARTY_OP_INVITE = 0,
PARTY_OP_LEAVE = 2
};
+
enum PartyResult
{
PARTY_RESULT_OK = 0,
@@ -94,6 +109,7 @@ enum PartyResult
PARTY_RESULT_TARGET_IGNORE_YOU = 9,
PARTY_RESULT_INVITE_RESTRICTED = 13
};
+
/// Player session in the World
class TRINITY_DLL_SPEC WorldSession
{
@@ -101,12 +117,17 @@ class TRINITY_DLL_SPEC WorldSession
public:
WorldSession(uint32 id, WorldSocket *sock, AccountTypes sec, uint8 expansion, 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 ReadAddonsInfo(WorldPacket &data);
void SendAddonsInfo();
+
void ReadMovementInfo(WorldPacket &data, MovementInfo *mi);
+
void SendPacket(WorldPacket const* packet);
void SendNotification(const char *format,...) ATTR_PRINTF(2,3);
void SendNotification(int32 string_id,...);
@@ -116,6 +137,7 @@ class TRINITY_DLL_SPEC WorldSession
void SendPartyResult(PartyOperation operation, const std::string& member, PartyResult res);
void SendAreaTriggerMessage(const char* Text, ...) ATTR_PRINTF(2,3);
void SendSetPhaseShift(uint32 phaseShift);
+
AccountTypes GetSecurity() const { return _security; }
uint32 GetAccountId() const { return _accountId; }
Player* GetPlayer() const { return _player; }
@@ -124,30 +146,39 @@ class TRINITY_DLL_SPEC WorldSession
std::string const& GetRemoteAddress() { return m_Address; }
void SetPlayer(Player *plr);
uint8 Expansion() const { return m_expansion; }
+
/// Session in auth.queue currently
void SetInQueue(bool state) { m_inQueue = state; }
+
/// Is the user engaged in a log out process?
bool isLogingOut() const { return _logoutTime || m_playerLogout; }
+
/// Engage the logout process for the user
void LogoutRequest(time_t requestTime)
{
_logoutTime = requestTime;
}
+
/// Is logout cooldown expired?
bool ShouldLogOut(time_t currTime) const
{
return (_logoutTime > 0 && currTime >= _logoutTime + 20);
}
+
void LogoutPlayer(bool Save);
void KickPlayer();
+
void QueuePacket(WorldPacket* new_packet);
bool Update(uint32 diff);
+
/// Handle the authentication waiting queue (to be completed)
void SendAuthWaitQue(uint32 position);
+
//void SendTestCreatureQueryOpcode( uint32 entry, uint64 guid, uint32 testvalue );
void SendNameQueryOpcode(Player* p);
void SendNameQueryOpcodeFromDB(uint64 guid);
static void SendNameQueryOpcodeFromDBCallBack(QueryResult *result, uint32 accountId);
+
void SendTrainerList( uint64 guid );
void SendTrainerList( uint64 guid, const std::string& strTitle );
void SendListInventory( uint64 guid );
@@ -155,15 +186,21 @@ class TRINITY_DLL_SPEC WorldSession
void SendTabardVendorActivate( uint64 guid );
void SendSpiritResurrect();
void SendBindPoint(Creature* npc);
+
void SendAttackStop(Unit const* enemy);
+
void SendBattlegGroundList( uint64 guid, BattleGroundTypeId bgTypeId );
+
void SendTradeStatus(uint32 status);
void SendCancelTrade();
+
void SendStablePet(uint64 guid );
void SendPetitionQueryOpcode( uint64 petitionguid);
void SendUpdateTrade();
+
//pet
void SendPetNameQuery(uint64 guid, uint32 petnumber);
+
// Account Data
AccountData *GetAccountData(AccountDataType type) { return &m_accountData[type]; }
void SetAccountData(AccountDataType type, time_t time_, std::string data);
@@ -177,6 +214,7 @@ class TRINITY_DLL_SPEC WorldSession
{
return m_Tutorials[intId];
}
+
void SetTutorialInt(uint32 intId, uint32 value)
{
if(m_Tutorials[intId] != value)
@@ -185,11 +223,13 @@ class TRINITY_DLL_SPEC WorldSession
m_TutorialsChanged = true;
}
}
+
//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, const std::string& subject, uint32 itemTextId, MailItemsInfo *mi, uint32 money, 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);
@@ -197,14 +237,17 @@ class TRINITY_DLL_SPEC WorldSession
void SendAuctionOwnerNotification( 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( uint32 mountDisplayId, uint32 path, uint32 pathNode = 0 );
bool SendLearnNewTaxiNode( Creature* unit );
+
// Guild/Arena Team
void SendGuildCommandResult(uint32 typecmd, const std::string& str, uint32 cmdresult);
void SendArenaTeamCommandResult(uint32 team_action, const std::string& team, const std::string& player, uint32 error_id);
@@ -213,55 +256,76 @@ class TRINITY_DLL_SPEC WorldSession
void SendPetitionShowList( uint64 guid );
void SendSaveGuildEmblem( uint32 msg );
void SendBattleGroundOrArenaJoinError(uint8 err);
+
// 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() const { return m_sessionDbcLocale; }
int GetSessionDbLocaleIndex() const { return m_sessionDbLocaleIndex; }
const char *GetTrinityString(int32 entry) const;
+
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_Deprecated(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 HandleShowingHelmOpcode(WorldPacket& recv_data);
void HandleShowingCloakOpcode(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);
@@ -274,6 +338,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleLogoutRequestOpcode(WorldPacket& recvPacket);
void HandlePlayerLogoutOpcode(WorldPacket& recvPacket);
void HandleLogoutCancelOpcode(WorldPacket& recvPacket);
+
// GM Ticket opcodes
void HandleGMTicketCreateOpcode(WorldPacket& recvPacket);
void HandleGMTicketUpdateOpcode(WorldPacket& recvPacket);
@@ -281,8 +346,11 @@ class TRINITY_DLL_SPEC WorldSession
void HandleGMTicketGetTicketOpcode(WorldPacket& recvPacket);
void HandleGMTicketSystemStatusOpcode(WorldPacket& recvPacket);
void SendGMTicketGetTicket(uint32 status, char const* text);
+
//void HandleGMSurveySubmit(WorldPacket& recvPacket);
+
void HandleTogglePvP(WorldPacket& recvPacket);
+
void HandleZoneUpdateOpcode(WorldPacket& recvPacket);
void HandleSetTargetOpcode(WorldPacket& recvPacket);
void HandleSetSelectionOpcode(WorldPacket& recvPacket);
@@ -299,23 +367,33 @@ class TRINITY_DLL_SPEC WorldSession
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 HandleSetWatchedFactionOpcode(WorldPacket & recv_data);
void HandleSetFactionInactiveOpcode(WorldPacket & recv_data);
+
void HandleUpdateAccountData(WorldPacket& recvPacket);
void HandleRequestAccountData(WorldPacket& recvPacket);
void HandleSetActionButtonOpcode(WorldPacket& recvPacket);
+
void HandleGameObjectUseOpcode(WorldPacket& recPacket);
void HandleMeetingStoneInfo(WorldPacket& recPacket);
void HandleGameobjectReportUse(WorldPacket& recvPacket);
+
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 HandleMoveNotActiveMover(WorldPacket &recv_data);
@@ -323,9 +401,12 @@ class TRINITY_DLL_SPEC WorldSession
void HandleRequestVehicleExit(WorldPacket &recv_data);
void HandleChangeSeatsOnControlledVehicle(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);
@@ -345,6 +426,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleGroupChangeSubGroupOpcode( WorldPacket & recv_data );
void HandleGroupAssistantLeaderOpcode( WorldPacket & recv_data );
void HandlePartyAssignmentOpcode( WorldPacket & recv_data );
+
void HandlePetitionBuyOpcode(WorldPacket& recv_data);
void HandlePetitionShowSignOpcode(WorldPacket& recv_data);
void HandlePetitionQueryOpcode(WorldPacket& recv_data);
@@ -353,6 +435,7 @@ class TRINITY_DLL_SPEC WorldSession
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);
@@ -375,11 +458,13 @@ class TRINITY_DLL_SPEC WorldSession
void HandleGuildDelRankOpcode(WorldPacket& recvPacket);
void HandleGuildChangeInfoTextOpcode(WorldPacket& recvPacket);
void HandleSaveGuildEmblemOpcode(WorldPacket& recvPacket);
+
void HandleTaxiNodeStatusQueryOpcode(WorldPacket& recvPacket);
void HandleTaxiQueryAvailableNodes(WorldPacket& recvPacket);
void HandleActivateTaxiOpcode(WorldPacket& recvPacket);
void HandleActivateTaxiExpressOpcode(WorldPacket& recvPacket);
void HandleMoveSplineDoneOpcode(WorldPacket& recvPacket);
+
void HandleTabardVendorActivateOpcode(WorldPacket& recvPacket);
void HandleBankerActivateOpcode(WorldPacket& recvPacket);
void HandleBuyBankSlotOpcode(WorldPacket& recvPacket);
@@ -397,8 +482,10 @@ class TRINITY_DLL_SPEC WorldSession
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);
@@ -409,6 +496,7 @@ class TRINITY_DLL_SPEC WorldSession
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 );
@@ -417,6 +505,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleAuctionListOwnerItems( WorldPacket & recv_data );
void HandleAuctionPlaceBid( WorldPacket & recv_data );
void HandleAuctionListPendingSales( WorldPacket & recv_data );
+
void HandleGetMailList( WorldPacket & recv_data );
void HandleSendMail( WorldPacket & recv_data );
void HandleMailTakeMoney( WorldPacket & recv_data );
@@ -428,6 +517,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleMailCreateTextItem(WorldPacket & recv_data );
void HandleQueryNextMailTime(WorldPacket & recv_data );
void HandleCancelChanneling(WorldPacket & recv_data );
+
void SendItemPageInfo( ItemPrototype *itemProto );
void HandleSplitItemOpcode(WorldPacket& recvPacket);
void HandleSwapInvItemOpcode(WorldPacket& recvPacket);
@@ -446,9 +536,11 @@ class TRINITY_DLL_SPEC WorldSession
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);
@@ -456,10 +548,12 @@ class TRINITY_DLL_SPEC WorldSession
void HandleCancelAuraOpcode(WorldPacket& recvPacket);
void HandleCancelGrowthAuraOpcode(WorldPacket& recvPacket);
void HandleCancelAutoRepeatSpellOpcode(WorldPacket& recvPacket);
+
void HandleLearnTalentOpcode(WorldPacket& recvPacket);
void HandleLearnPreviewTalents(WorldPacket& recvPacket);
void HandleTalentWipeConfirmOpcode(WorldPacket& recvPacket);
void HandleUnlearnSkillOpcode(WorldPacket& recvPacket);
+
void HandleQuestgiverStatusQueryOpcode(WorldPacket& recvPacket);
void HandleQuestgiverStatusMultipleQuery(WorldPacket& recvPacket);
void HandleQuestgiverHelloOpcode(WorldPacket& recvPacket);
@@ -476,14 +570,17 @@ class TRINITY_DLL_SPEC WorldSession
void HandleQuestgiverQuestAutoLaunch(WorldPacket& recvPacket);
void HandlePushQuestToParty(WorldPacket& recvPacket);
void HandleQuestPushResult(WorldPacket& recvPacket);
+
bool processChatmessageFurtherAfterSecurityChecks(std::string&, uint32);
void HandleMessagechatOpcode(WorldPacket& recvPacket);
void HandleTextEmoteOpcode(WorldPacket& recvPacket);
void HandleChatIgnoredOpcode(WorldPacket& recvPacket);
+
void HandleReclaimCorpseOpcode( WorldPacket& recvPacket );
void HandleCorpseQueryOpcode( WorldPacket& recvPacket );
void HandleResurrectResponseOpcode(WorldPacket& recvPacket);
void HandleSummonResponseOpcode(WorldPacket& recv_data);
+
void HandleJoinChannel(WorldPacket& recvPacket);
void HandleLeaveChannel(WorldPacket& recvPacket);
void HandleChannelList(WorldPacket& recvPacket);
@@ -504,13 +601,17 @@ class TRINITY_DLL_SPEC WorldSession
void HandleChannelDisplayListQuery(WorldPacket& recvPacket);
void HandleGetChannelMemberCount(WorldPacket& recvPacket);
void HandleSetChannelWatch(WorldPacket& recvPacket);
+
void HandleCompleteCinematic(WorldPacket& recvPacket);
void HandleNextCinematicCamera(WorldPacket& recvPacket);
+
void HandlePageQuerySkippedOpcode(WorldPacket& recvPacket);
void HandlePageTextQueryOpcode(WorldPacket& recvPacket);
+
void HandleTutorialFlag ( WorldPacket & recv_data );
void HandleTutorialClear( WorldPacket & recv_data );
void HandleTutorialReset( WorldPacket & recv_data );
+
//Pet
void HandlePetAction( WorldPacket & recv_data );
void HandlePetActionHelper(Unit *pet, uint64 guid1, uint16 spellid, uint16 flag, uint64 guid2);
@@ -524,11 +625,15 @@ class TRINITY_DLL_SPEC WorldSession
void HandlePetCastSpellOpcode( WorldPacket& recvPacket );
void HandlePetLearnTalent( WorldPacket& recvPacket );
void HandleLearnPreviewTalentsPet( WorldPacket& recvPacket );
+
void HandleSetActionBarToggles(WorldPacket& recv_data);
+
void HandleCharRenameOpcode(WorldPacket& recv_data);
static void HandleChangePlayerNameOpcodeCallBack(QueryResult *result, uint32 accountId, std::string newname);
void HandleSetPlayerDeclinedNames(WorldPacket& recv_data);
+
void HandleTotemDestroyed(WorldPacket& recv_data);
+
//BattleGround
void HandleBattlemasterHelloOpcode(WorldPacket &recv_data);
void HandleBattlemasterJoinOpcode(WorldPacket &recv_data);
@@ -539,6 +644,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleLeaveBattlefieldOpcode( WorldPacket &recv_data );
void HandleBattlemasterJoinArena( WorldPacket &recv_data );
void HandleReportPvPAFK( WorldPacket &recv_data );
+
void HandleWardenDataOpcode(WorldPacket& recv_data);
void HandleWorldTeleportOpcode(WorldPacket& recv_data);
void HandleMinimapPingOpcode(WorldPacket& recv_data);
@@ -561,6 +667,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleTimeSyncResp(WorldPacket& recv_data);
void HandleWhoisOpcode(WorldPacket& recv_data);
void HandleResetInstancesOpcode(WorldPacket& recv_data);
+
// Arena Team
void HandleInspectArenaTeamsOpcode(WorldPacket& recv_data);
void HandleArenaTeamQueryOpcode(WorldPacket& recv_data);
@@ -572,19 +679,24 @@ class TRINITY_DLL_SPEC WorldSession
void HandleArenaTeamRemoveOpcode(WorldPacket& recv_data);
void HandleArenaTeamDisbandOpcode(WorldPacket& recv_data);
void HandleArenaTeamLeaderOpcode(WorldPacket& recv_data);
+
void HandleAreaSpiritHealerQueryOpcode(WorldPacket& recv_data);
void HandleAreaSpiritHealerQueueOpcode(WorldPacket& recv_data);
void HandleCancelMountAuraOpcode(WorldPacket& recv_data);
void HandleSelfResOpcode(WorldPacket& recv_data);
void HandleComplainOpcode(WorldPacket& recv_data);
void HandleRequestPetInfoOpcode(WorldPacket& recv_data);
+
// Socket gem
void HandleSocketOpcode(WorldPacket& recv_data);
+
void HandleCancelTempEnchantmentOpcode(WorldPacket& recv_data);
+
void HandleChannelVoiceOnOpcode(WorldPacket & recv_data);
void HandleVoiceSessionEnableOpcode(WorldPacket& recv_data);
void HandleSetActiveVoiceChannel(WorldPacket& recv_data);
void HandleSetTaxiBenchmarkOpcode(WorldPacket& recv_data);
+
// Guild Bank
void HandleGuildPermissions(WorldPacket& recv_data);
void HandleGuildBankMoneyWithdrawn(WorldPacket& recv_data);
@@ -594,10 +706,12 @@ class TRINITY_DLL_SPEC WorldSession
void HandleGuildBankDepositMoney(WorldPacket& recv_data);
void HandleGuildBankWithdrawMoney(WorldPacket& recv_data);
void HandleGuildBankSwapItems(WorldPacket& recv_data);
+
void HandleGuildBankUpdateTab(WorldPacket& recv_data);
void HandleGuildBankBuyTab(WorldPacket& recv_data);
void HandleQueryGuildBankTabText(WorldPacket& recv_data);
void HandleSetGuildBankTabText(WorldPacket& recv_data);
+
// Calendar
void HandleCalendarGetCalendar(WorldPacket& recv_data);
void HandleCalendarGetEvent(WorldPacket& recv_data);
@@ -614,6 +728,7 @@ class TRINITY_DLL_SPEC WorldSession
void HandleCalendarEventModeratorStatus(WorldPacket& recv_data);
void HandleCalendarComplain(WorldPacket& recv_data);
void HandleCalendarGetNumPending(WorldPacket& recv_data);
+
void HandleSpellClick(WorldPacket& recv_data);
void HandleMirrrorImageDataRequest( WorldPacket & recv_data );
void HandleAlterAppearance(WorldPacket& recv_data);
@@ -635,16 +750,20 @@ class TRINITY_DLL_SPEC WorldSession
private:
// private trade methods
void moveItems(Item* myItems[], Item* hisItems[]);
+
// logging helper
void LogUnexpectedOpcode(WorldPacket *packet, const char * reason);
void LogUnprocessedTail(WorldPacket *packet);
+
uint32 m_GUIDLow; // set loggined or recently logout player (while m_playerRecentlyLogout set)
Player *_player;
WorldSocket *m_Socket;
std::string m_Address;
+
AccountTypes _security;
uint32 _accountId;
uint8 m_expansion;
+
time_t _logoutTime;
bool m_inQueue; // session wait in auth.queue
bool m_playerLoading; // code processed in LoginPlayer
diff --git a/src/game/WorldSocket.cpp b/src/game/WorldSocket.cpp
index 16df744e8fa..1b63ec01421 100644
--- a/src/game/WorldSocket.cpp
+++ b/src/game/WorldSocket.cpp
@@ -17,6 +17,7 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <ace/Message_Block.h>
#include <ace/OS_NS_string.h>
#include <ace/OS_NS_unistd.h>
@@ -27,8 +28,10 @@
#include <ace/OS_NS_string.h>
#include <ace/Reactor.h>
#include <ace/Auto_Ptr.h>
+
#include "WorldSocket.h"
#include "Common.h"
+
#include "Util.h"
#include "World.h"
#include "WorldPacket.h"
@@ -42,11 +45,13 @@
#include "WorldSocketMgr.h"
#include "Log.h"
#include "WorldLog.h"
+
#if defined( __GNUC__ )
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
+
struct ServerPktHeader
{
/**
@@ -62,31 +67,38 @@ struct ServerPktHeader
}
header[headerIndex++] = 0xFF &(size>>8);
header[headerIndex++] = 0xFF &size;
+
header[headerIndex++] = 0xFF & cmd;
header[headerIndex++] = 0xFF & (cmd>>8);
}
+
uint8 getHeaderLength()
{
// cmd = 2 bytes, size= 2||3bytes
return 2+(isLargePacket()?3:2);
}
+
bool isLargePacket()
{
return size > 0x7FFF;
}
+
const uint32 size;
uint8 header[5];
};
+
struct ClientPktHeader
{
uint16 size;
uint32 cmd;
};
+
#if defined( __GNUC__ )
#pragma pack()
#else
#pragma pack(pop)
#endif
+
WorldSocket::WorldSocket (void) :
WorldHandler (),
m_Session (0),
@@ -102,45 +114,60 @@ m_LastPingTime (ACE_Time_Value::zero)
{
reference_counting_policy ().value (ACE_Event_Handler::Reference_Counting_Policy::ENABLED);
}
+
WorldSocket::~WorldSocket (void)
{
if (m_RecvWPct)
delete m_RecvWPct;
+
if (m_OutBuffer)
m_OutBuffer->release ();
+
closing_ = true;
+
peer ().close ();
+
WorldPacket* pct;
while (m_PacketQueue.dequeue_head (pct) == 0)
delete pct;
}
+
bool WorldSocket::IsClosed (void) const
{
return closing_;
}
+
void WorldSocket::CloseSocket (void)
{
{
ACE_GUARD (LockType, Guard, m_OutBufferLock);
+
if (closing_)
return;
+
closing_ = true;
peer ().close_writer ();
}
+
{
ACE_GUARD (LockType, Guard, m_SessionLock);
+
m_Session = NULL;
}
}
+
const std::string& WorldSocket::GetRemoteAddress (void) const
{
return m_Address;
}
+
int WorldSocket::SendPacket (const WorldPacket& pct)
{
ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
+
if (closing_)
return -1;
+
// Dump outgoing packet.
if (sWorldLog.LogWorld ())
{
@@ -149,19 +176,24 @@ int WorldSocket::SendPacket (const WorldPacket& pct)
pct.size (),
LookupOpcodeName (pct.GetOpcode ()),
pct.GetOpcode ());
+
uint32 p = 0;
while (p < pct.size ())
{
for (uint32 j = 0; j < 16 && p < pct.size (); j++)
sWorldLog.outLog ("%.2X ", const_cast<WorldPacket&>(pct)[p++]);
+
sWorldLog.outLog ("\n");
}
sWorldLog.outLog ("\n");
}
+
if (iSendPacket (pct) == -1)
{
WorldPacket* npct;
+
ACE_NEW_RETURN (npct, WorldPacket (pct), -1);
+
// NOTE maybe check of the size of the queue can be good ?
// to make it bounded instead of unbounded
if (m_PacketQueue.enqueue_tail (npct) == -1)
@@ -171,64 +203,86 @@ int WorldSocket::SendPacket (const WorldPacket& pct)
return -1;
}
}
+
return 0;
}
+
long WorldSocket::AddReference (void)
{
return static_cast<long> (add_reference ());
}
+
long WorldSocket::RemoveReference (void)
{
return static_cast<long> (remove_reference ());
}
+
int WorldSocket::open (void *a)
{
ACE_UNUSED_ARG (a);
+
// Prevent double call to this func.
if (m_OutBuffer)
return -1;
+
// This will also prevent the socket from being Updated
// while we are initializing it.
m_OutActive = true;
+
// Hook for the manager.
if (sWorldSocketMgr->OnSocketOpen (this) == -1)
return -1;
+
// Allocate the buffer.
ACE_NEW_RETURN (m_OutBuffer, ACE_Message_Block (m_OutBufferSize), -1);
+
// Store peer address.
ACE_INET_Addr remote_addr;
+
if (peer ().get_remote_addr (remote_addr) == -1)
{
sLog.outError ("WorldSocket::open: peer ().get_remote_addr errno = %s", ACE_OS::strerror (errno));
return -1;
}
+
m_Address = remote_addr.get_host_addr ();
+
// Send startup packet.
WorldPacket packet (SMSG_AUTH_CHALLENGE, 4);
packet << m_Seed;
+
if (SendPacket (packet) == -1)
return -1;
+
// Register with ACE Reactor
if (reactor ()->register_handler(this, ACE_Event_Handler::READ_MASK | ACE_Event_Handler::WRITE_MASK) == -1)
{
sLog.outError ("WorldSocket::open: unable to register client handler errno = %s", ACE_OS::strerror (errno));
return -1;
}
+
// reactor takes care of the socket from now on
remove_reference ();
+
return 0;
}
+
int WorldSocket::close (int)
{
shutdown ();
+
closing_ = true;
+
remove_reference ();
+
return 0;
}
+
int WorldSocket::handle_input (ACE_HANDLE)
{
if (closing_)
return -1;
+
switch (handle_input_missing_data ())
{
case -1 :
@@ -238,13 +292,16 @@ int WorldSocket::handle_input (ACE_HANDLE)
{
return Update (); // interesting line ,isn't it ?
}
+
DEBUG_LOG ("WorldSocket::handle_input: Peer error closing connection errno = %s", ACE_OS::strerror (errno));
+
errno = ECONNRESET;
return -1;
}
case 0:
{
DEBUG_LOG ("WorldSocket::handle_input: Peer has closed connection");
+
errno = ECONNRESET;
return -1;
}
@@ -253,87 +310,118 @@ int WorldSocket::handle_input (ACE_HANDLE)
default:
return Update (); // another interesting line ;)
}
+
ACE_NOTREACHED(return -1);
}
+
int WorldSocket::handle_output (ACE_HANDLE)
{
ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
+
if (closing_)
return -1;
+
const size_t send_len = m_OutBuffer->length ();
+
if (send_len == 0)
return cancel_wakeup_output (Guard);
+
#ifdef MSG_NOSIGNAL
ssize_t n = peer ().send (m_OutBuffer->rd_ptr (), send_len, MSG_NOSIGNAL);
#else
ssize_t n = peer ().send (m_OutBuffer->rd_ptr (), send_len);
#endif // MSG_NOSIGNAL
+
if (n == 0)
return -1;
else if (n == -1)
{
if (errno == EWOULDBLOCK || errno == EAGAIN)
return schedule_wakeup_output (Guard);
+
return -1;
}
else if (n < send_len) //now n > 0
{
m_OutBuffer->rd_ptr (static_cast<size_t> (n));
+
// move the data to the base of the buffer
m_OutBuffer->crunch ();
+
return schedule_wakeup_output (Guard);
}
else //now n == send_len
{
m_OutBuffer->reset ();
+
if (!iFlushPacketQueue ())
return cancel_wakeup_output (Guard);
else
return schedule_wakeup_output (Guard);
}
+
ACE_NOTREACHED (return 0);
}
+
int WorldSocket::handle_close (ACE_HANDLE h, ACE_Reactor_Mask)
{
// Critical section
{
ACE_GUARD_RETURN (LockType, Guard, m_OutBufferLock, -1);
+
closing_ = true;
+
if (h == ACE_INVALID_HANDLE)
peer ().close_writer ();
}
+
// Critical section
{
ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
+
m_Session = NULL;
}
+
return 0;
}
+
int WorldSocket::Update (void)
{
if (closing_)
return -1;
+
if (m_OutActive || m_OutBuffer->length () == 0)
return 0;
+
return handle_output (get_handle ());
}
+
int WorldSocket::handle_input_header (void)
{
ACE_ASSERT (m_RecvWPct == NULL);
+
ACE_ASSERT (m_Header.length () == sizeof (ClientPktHeader));
+
m_Crypt.DecryptRecv ((uint8*) m_Header.rd_ptr (), sizeof (ClientPktHeader));
+
ClientPktHeader& header = *((ClientPktHeader*) m_Header.rd_ptr ());
+
EndianConvertReverse(header.size);
EndianConvert(header.cmd);
+
if ((header.size < 4) || (header.size > 10240) || (header.cmd > 10240))
{
sLog.outError ("WorldSocket::handle_input_header: client sent malformed packet size = %d , cmd = %d",
header.size, header.cmd);
+
errno = EINVAL;
return -1;
}
+
header.size -= 4;
+
ACE_NEW_RETURN (m_RecvWPct, WorldPacket ((uint16) header.cmd, header.size), -1);
+
if(header.size > 0)
{
m_RecvWPct->resize (header.size);
@@ -343,27 +431,37 @@ int WorldSocket::handle_input_header (void)
{
ACE_ASSERT(m_RecvPct.space() == 0);
}
+
return 0;
}
+
int WorldSocket::handle_input_payload (void)
{
// set errno properly here on error !!!
// now have a header and payload
+
ACE_ASSERT (m_RecvPct.space () == 0);
ACE_ASSERT (m_Header.space () == 0);
ACE_ASSERT (m_RecvWPct != NULL);
+
const int ret = ProcessIncoming (m_RecvWPct);
+
m_RecvPct.base (NULL, 0);
m_RecvPct.reset ();
m_RecvWPct = NULL;
+
m_Header.reset ();
+
if (ret == -1)
errno = EINVAL;
+
return ret;
}
+
int WorldSocket::handle_input_missing_data (void)
{
char buf [4096];
+
ACE_Data_Block db ( sizeof (buf),
ACE_Message_Block::MB_DATA,
buf,
@@ -371,15 +469,21 @@ int WorldSocket::handle_input_missing_data (void)
0,
ACE_Message_Block::DONT_DELETE,
0);
+
ACE_Message_Block message_block(&db,
ACE_Message_Block::DONT_DELETE,
0);
+
const size_t recv_size = message_block.space ();
+
const ssize_t n = peer ().recv (message_block.wr_ptr (),
recv_size);
+
if (n <= 0)
return n;
+
message_block.wr_ptr (n);
+
while (message_block.length () > 0)
{
if (m_Header.space () > 0)
@@ -388,6 +492,7 @@ int WorldSocket::handle_input_missing_data (void)
const size_t to_header = (message_block.length () > m_Header.space () ? m_Header.space () : message_block.length ());
m_Header.copy (message_block.rd_ptr (), to_header);
message_block.rd_ptr (to_header);
+
if (m_Header.space () > 0)
{
// Couldn't receive the whole header this time.
@@ -395,6 +500,7 @@ int WorldSocket::handle_input_missing_data (void)
errno = EWOULDBLOCK;
return -1;
}
+
// We just received nice new header
if (handle_input_header () == -1)
{
@@ -402,6 +508,7 @@ int WorldSocket::handle_input_missing_data (void)
return -1;
}
}
+
// Its possible on some error situations that this happens
// for example on closing when epoll receives more chunked data and stuff
// hope this is not hack ,as proper m_RecvWPct is asserted around
@@ -411,6 +518,7 @@ int WorldSocket::handle_input_missing_data (void)
errno = EINVAL;
return -1;
}
+
// We have full read header, now check the data payload
if (m_RecvPct.space () > 0)
{
@@ -418,6 +526,7 @@ int WorldSocket::handle_input_missing_data (void)
const size_t to_data = (message_block.length () > m_RecvPct.space () ? m_RecvPct.space () : message_block.length ());
m_RecvPct.copy (message_block.rd_ptr (), to_data);
message_block.rd_ptr (to_data);
+
if (m_RecvPct.space () > 0)
{
// Couldn't receive the whole data this time.
@@ -426,6 +535,7 @@ int WorldSocket::handle_input_missing_data (void)
return -1;
}
}
+
//just received fresh new payload
if (handle_input_payload () == -1)
{
@@ -433,14 +543,19 @@ int WorldSocket::handle_input_missing_data (void)
return -1;
}
}
+
return n == recv_size ? 1 : 2;
}
+
int WorldSocket::cancel_wakeup_output (GuardType& g)
{
if (!m_OutActive)
return 0;
+
m_OutActive = false;
+
g.release ();
+
if (reactor ()->cancel_wakeup
(this, ACE_Event_Handler::WRITE_MASK) == -1)
{
@@ -448,30 +563,41 @@ int WorldSocket::cancel_wakeup_output (GuardType& g)
sLog.outError ("WorldSocket::cancel_wakeup_output");
return -1;
}
+
return 0;
}
+
int WorldSocket::schedule_wakeup_output (GuardType& g)
{
if (m_OutActive)
return 0;
+
m_OutActive = true;
+
g.release ();
+
if (reactor ()->schedule_wakeup
(this, ACE_Event_Handler::WRITE_MASK) == -1)
{
sLog.outError ("WorldSocket::schedule_wakeup_output");
return -1;
}
+
return 0;
}
+
int WorldSocket::ProcessIncoming (WorldPacket* new_pct)
{
ACE_ASSERT (new_pct);
+
// manage memory ;)
ACE_Auto_Ptr<WorldPacket> aptr (new_pct);
+
const ACE_UINT16 opcode = new_pct->GetOpcode ();
+
if (closing_)
return -1;
+
// Dump received packet.
if (sWorldLog.LogWorld ())
{
@@ -480,15 +606,18 @@ int WorldSocket::ProcessIncoming (WorldPacket* new_pct)
new_pct->size (),
LookupOpcodeName (new_pct->GetOpcode ()),
new_pct->GetOpcode ());
+
uint32 p = 0;
while (p < new_pct->size ())
{
for (uint32 j = 0; j < 16 && p < new_pct->size (); j++)
sWorldLog.outLog ("%.2X ", (*new_pct)[p++]);
+
sWorldLog.outLog ("\n");
}
sWorldLog.outLog ("\n");
}
+
try {
switch(opcode)
{
@@ -500,13 +629,16 @@ int WorldSocket::ProcessIncoming (WorldPacket* new_pct)
sLog.outError ("WorldSocket::ProcessIncoming: Player send CMSG_AUTH_SESSION again");
return -1;
}
+
return HandleAuthSession (*new_pct);
case CMSG_KEEP_ALIVE:
DEBUG_LOG ("CMSG_KEEP_ALIVE ,size: %d", new_pct->size ());
+
return 0;
default:
{
ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
+
if (m_Session != NULL)
{
// OK ,give the packet to WorldSession
@@ -533,10 +665,13 @@ int WorldSocket::ProcessIncoming (WorldPacket* new_pct)
sLog.outDebug("Dumping error causing packet:");
new_pct->hexlike();
}
+
return -1;
}
+
ACE_NOTREACHED (return 0);
}
+
int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
{
// NOTE: ATM the socket is singlethread, have this in mind ...
@@ -551,15 +686,19 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
Sha1Hash sha1;
BigNumber v, s, g, N;
WorldPacket packet, SendAddonPacked;
+
BigNumber K;
+
if(sWorld.IsClosed())
{
packet.Initialize(SMSG_AUTH_RESPONSE, 1);
packet << uint8(AUTH_REJECT);
SendPacket (packet);
+
sLog.outError ("WorldSocket::HandleAuthSession: World closed, denying client (%s).", m_Session->GetRemoteAddress().c_str());
return -1;
}
+
// Read the content of the packet
recvPacket >> BuiltNumberClient; // for now no use
recvPacket >> unk2;
@@ -567,16 +706,19 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
recvPacket >> unk3;
recvPacket >> clientSeed;
recvPacket.read (digest, 20);
+
DEBUG_LOG ("WorldSocket::HandleAuthSession: client %u, unk2 %u, account %s, unk3 %u, clientseed %u",
BuiltNumberClient,
unk2,
account.c_str (),
unk3,
clientSeed);
+
// 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.
+
QueryResult *result =
loginDatabase.PQuery ("SELECT "
"id, " //0
@@ -592,32 +734,43 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
"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.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (unknown account).");
return -1;
}
+
Field* fields = result->Fetch ();
+
uint8 expansion = fields[7].GetUInt8();
uint32 world_expansion = sWorld.getConfig(CONFIG_EXPANSION);
if(expansion > world_expansion)
expansion = world_expansion;
//expansion = ((sWorld.getConfig(CONFIG_EXPANSION) > fields[7].GetUInt8()) ? fields[7].GetUInt8() : sWorld.getConfig(CONFIG_EXPANSION));
+
N.SetHexStr ("894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7");
g.SetDword (7);
+
v.SetHexStr(fields[5].GetString());
s.SetHexStr (fields[6].GetString ());
+
const char* sStr = s.AsHexStr (); //Must be freed by OPENSSL_free()
const char* vStr = v.AsHexStr (); //Must be freed by OPENSSL_free()
+
DEBUG_LOG ("WorldSocket::HandleAuthSession: (s,v) check s: %s v: %s",
sStr,
vStr);
+
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
{
@@ -626,38 +779,49 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
packet.Initialize (SMSG_AUTH_RESPONSE, 1);
packet << uint8 (AUTH_FAILED);
SendPacket (packet);
+
delete result;
sLog.outBasic ("WorldSocket::HandleAuthSession: Sent Auth Response (Account IP differs).");
return -1;
}
}
+
id = fields[0].GetUInt32 ();
security = fields[1].GetUInt16 ();
/*
if(security > SEC_ADMINISTRATOR) // prevent invalid security settings in DB
security = SEC_ADMINISTRATOR;
*/
+
K.SetHexStr (fields[2].GetString ());
+
time_t mutetime = time_t (fields[8].GetUInt64 ());
+
locale = LocaleConstant (fields[9].GetUInt8 ());
if (locale >= MAX_LOCALE)
locale = LOCALE_enUS;
+
delete result;
+
// Re-check account ban (same check as in realmd)
QueryResult *banresult =
loginDatabase.PQuery ("SELECT 1 FROM account_banned WHERE id = %u AND active = 1 "
"UNION "
"SELECT 1 FROM ip_banned WHERE ip = '%s'",
id, GetRemoteAddress().c_str());
+
if (banresult) // if account banned
{
packet.Initialize (SMSG_AUTH_RESPONSE, 1);
packet << uint8 (AUTH_BANNED);
SendPacket (packet);
+
delete banresult;
+
sLog.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (Account banned).");
return -1;
}
+
// Check locked state for server
sWorld.UpdateAllowedSecurity();
AccountTypes allowedAccountType = sWorld.GetPlayerSecurityLimit ();
@@ -666,58 +830,79 @@ int WorldSocket::HandleAuthSession (WorldPacket& recvPacket)
{
WorldPacket Packet (SMSG_AUTH_RESPONSE, 1);
Packet << uint8 (AUTH_UNAVAILABLE);
+
SendPacket (packet);
+
sLog.outDetail ("WorldSocket::HandleAuthSession: User tries to login but his security level is not enough");
return -1;
}
+
// Check that Key and account name are the same on client and server
Sha1Hash sha;
+
uint32 t = 0;
uint32 seed = m_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.outError ("WorldSocket::HandleAuthSession: Sent Auth Response (authentification failed).");
return -1;
}
+
std::string address = GetRemoteAddress ();
+
DEBUG_LOG ("WorldSocket::HandleAuthSession: Client '%s' authenticated successfully from %s.",
account.c_str (),
address.c_str ());
+
// Update the last_ip in the database
// No SQL injection, username escaped.
loginDatabase.escape_string (address);
+
loginDatabase.PExecute ("UPDATE account "
"SET last_ip = '%s' "
"WHERE username = '%s'",
address.c_str (),
safe_account.c_str ());
+
// NOTE ATM the socket is single-threaded, have this in mind ...
ACE_NEW_RETURN (m_Session, WorldSession (id, this, AccountTypes(security), expansion, mutetime, locale), -1);
+
m_Crypt.Init(&K);
+
m_Session->LoadGlobalAccountData();
m_Session->LoadTutorialsData();
m_Session->ReadAddonsInfo(recvPacket);
+
// In case needed sometime the second arg is in microseconds 1 000 000 = 1 sec
ACE_OS::sleep (ACE_Time_Value (0, 10000));
+
sWorld.AddSession (m_Session);
+
return 0;
}
+
int WorldSocket::HandlePing (WorldPacket& recvPacket)
{
uint32 ping;
uint32 latency;
+
// Get the ping packet content
recvPacket >> ping;
recvPacket >> latency;
+
if (m_LastPingTime == ACE_Time_Value::zero)
m_LastPingTime = ACE_OS::gettimeofday (); // for 1st ping
else
@@ -726,18 +911,23 @@ int WorldSocket::HandlePing (WorldPacket& recvPacket)
ACE_Time_Value diff_time (cur_time);
diff_time -= m_LastPingTime;
m_LastPingTime = cur_time;
+
if (diff_time < ACE_Time_Value (27))
{
++m_OverSpeedPings;
+
uint32 max_count = sWorld.getConfig (CONFIG_MAX_OVERSPEED_PINGS);
+
if (max_count && m_OverSpeedPings > max_count)
{
ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
+
if (m_Session && m_Session->GetSecurity () == SEC_PLAYER)
{
sLog.outError ("WorldSocket::HandlePing: Player kicked for "
"over-speed pings address = %s",
GetRemoteAddress ().c_str ());
+
return -1;
}
}
@@ -745,9 +935,11 @@ int WorldSocket::HandlePing (WorldPacket& recvPacket)
else
m_OverSpeedPings = 0;
}
+
// critical section
{
ACE_GUARD_RETURN (LockType, Guard, m_SessionLock, -1);
+
if (m_Session)
m_Session->SetLatency (latency);
else
@@ -759,10 +951,12 @@ int WorldSocket::HandlePing (WorldPacket& recvPacket)
return -1;
}
}
+
WorldPacket packet (SMSG_PONG, 4);
packet << ping;
return SendPacket (packet);
}
+
int WorldSocket::iSendPacket (const WorldPacket& pct)
{
ServerPktHeader header(pct.size()+2, pct.GetOpcode());
@@ -771,19 +965,25 @@ int WorldSocket::iSendPacket (const WorldPacket& pct)
errno = ENOBUFS;
return -1;
}
-
+
+
m_Crypt.EncryptSend ( header.header, header.getHeaderLength());
+
if (m_OutBuffer->copy ((char*) header.header, header.getHeaderLength()) == -1)
ACE_ASSERT (false);
+
if (!pct.empty ())
if (m_OutBuffer->copy ((char*) pct.contents (), pct.size ()) == -1)
ACE_ASSERT (false);
+
return 0;
}
+
bool WorldSocket::iFlushPacketQueue ()
{
WorldPacket *pct;
bool haveone = false;
+
while (m_PacketQueue.dequeue_head (pct) == 0)
{
if (iSendPacket (*pct) == -1)
@@ -794,6 +994,7 @@ bool WorldSocket::iFlushPacketQueue ()
sLog.outError ("WorldSocket::iFlushPacketQueue m_PacketQueue->enqueue_head");
return false;
}
+
break;
}
else
@@ -802,6 +1003,7 @@ bool WorldSocket::iFlushPacketQueue ()
delete pct;
}
}
+
return haveone;
}
diff --git a/src/game/WorldSocket.h b/src/game/WorldSocket.h
index 6847185cc8f..94f57d8d636 100644
--- a/src/game/WorldSocket.h
+++ b/src/game/WorldSocket.h
@@ -17,13 +17,16 @@
* 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 WorldSocket.h
* \author Derex <derex101@gmail.com>
*/
+
#ifndef _WORLDSOCKET_H
#define _WORLDSOCKET_H
+
#include <ace/Basic_Types.h>
#include <ace/Synch_Traits.h>
#include <ace/Svc_Handler.h>
@@ -34,16 +37,21 @@
#include <ace/Guard_T.h>
#include <ace/Unbounded_Queue.h>
#include <ace/Message_Block.h>
+
#if !defined (ACE_LACKS_PRAGMA_ONCE)
#pragma once
#endif /* ACE_LACKS_PRAGMA_ONCE */
+
#include "Common.h"
#include "Auth/AuthCrypt.h"
+
class ACE_Message_Block;
class WorldPacket;
class WorldSession;
+
/// Handler that can communicate over stream sockets.
typedef ACE_Svc_Handler<ACE_SOCK_STREAM, ACE_NULL_SYNCH> WorldHandler;
+
/**
* WorldSocket.
*
@@ -87,102 +95,142 @@ class WorldSocket : protected WorldHandler
friend class ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR >;
friend class WorldSocketMgr;
friend class ReactorRunnable;
+
/// Declare the acceptor for this class
typedef ACE_Acceptor< WorldSocket, ACE_SOCK_ACCEPTOR > Acceptor;
+
/// Mutex type used for various synchronizations.
typedef ACE_Thread_Mutex LockType;
typedef ACE_Guard<LockType> GuardType;
+
/// Queue for storing packets for which there is no space.
typedef ACE_Unbounded_Queue< WorldPacket* > PacketQueueT;
+
/// Check if socket is closed.
bool IsClosed (void) const;
+
/// Close the socket.
void CloseSocket (void);
+
/// Get address of connected peer.
const std::string& GetRemoteAddress (void) const;
+
/// Send A packet on the socket, this function is reentrant.
/// @param pct packet to send
/// @return -1 of failure
int SendPacket (const WorldPacket& pct);
+
/// Add reference to this object.
long AddReference (void);
+
/// Remove reference to this object.
long RemoveReference (void);
+
protected:
/// things called by ACE framework.
WorldSocket (void);
virtual ~WorldSocket (void);
+
/// Called on open ,the void* is the acceptor.
virtual int open (void *);
+
/// Called on failures inside of the acceptor, don't call from your code.
virtual int close (int);
+
/// Called when we can read from the socket.
virtual int handle_input (ACE_HANDLE = ACE_INVALID_HANDLE);
+
/// Called when the socket can write.
virtual int handle_output (ACE_HANDLE = ACE_INVALID_HANDLE);
+
/// Called when connection is closed or error happens.
virtual int handle_close (ACE_HANDLE = ACE_INVALID_HANDLE,
ACE_Reactor_Mask = ACE_Event_Handler::ALL_EVENTS_MASK);
+
/// Called by WorldSocketMgr/ReactorRunnable.
int Update (void);
+
private:
/// Helper functions for processing incoming data.
int handle_input_header (void);
int handle_input_payload (void);
int handle_input_missing_data (void);
+
/// Help functions to mark/unmark the socket for output.
/// @param g the guard is for m_OutBufferLock, the function will release it
int cancel_wakeup_output (GuardType& g);
int schedule_wakeup_output (GuardType& g);
+
/// process one incoming packet.
/// @param new_pct received packet ,note that you need to delete it.
int ProcessIncoming (WorldPacket* new_pct);
+
/// Called by ProcessIncoming() on CMSG_AUTH_SESSION.
int HandleAuthSession (WorldPacket& recvPacket);
+
/// Called by ProcessIncoming() on CMSG_PING.
int HandlePing (WorldPacket& recvPacket);
+
/// Try to write WorldPacket to m_OutBuffer ,return -1 if no space
/// Need to be called with m_OutBufferLock lock held
int iSendPacket (const WorldPacket& pct);
+
/// Flush m_PacketQueue if there are packets in it
/// Need to be called with m_OutBufferLock lock held
/// @return true if it wrote to the buffer ( AKA you need
/// to mark the socket for output ).
bool iFlushPacketQueue ();
+
private:
/// Time in which the last ping was received
ACE_Time_Value m_LastPingTime;
+
/// Keep track of over-speed pings ,to prevent ping flood.
uint32 m_OverSpeedPings;
+
/// Address of the remote peer
std::string m_Address;
+
/// Class used for managing encryption of the headers
AuthCrypt m_Crypt;
+
/// Mutex lock to protect m_Session
LockType m_SessionLock;
+
/// Session to which received packets are routed
WorldSession* m_Session;
+
/// here are stored the fragments of the received data
WorldPacket* m_RecvWPct;
+
/// This block actually refers to m_RecvWPct contents,
/// which allows easy and safe writing to it.
/// It wont free memory when its deleted. m_RecvWPct takes care of freeing.
ACE_Message_Block m_RecvPct;
+
/// Fragment of the received header.
ACE_Message_Block m_Header;
+
/// Mutex for protecting output related data.
LockType m_OutBufferLock;
+
/// Buffer used for writing output.
ACE_Message_Block *m_OutBuffer;
+
/// Size of the m_OutBuffer.
size_t m_OutBufferSize;
+
/// Here are stored packets for which there was no space on m_OutBuffer,
/// this allows not-to kick player if its buffer is overflowed.
PacketQueueT m_PacketQueue;
+
/// True if the socket is registered with the reactor for output
bool m_OutActive;
+
uint32 m_Seed;
};
+
#endif /* _WORLDSOCKET_H */
+
/// @}
diff --git a/src/game/WorldSocketMgr.cpp b/src/game/WorldSocketMgr.cpp
index 9b7f50bfc57..cc08962efa5 100644
--- a/src/game/WorldSocketMgr.cpp
+++ b/src/game/WorldSocketMgr.cpp
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/** \file WorldSocketMgr.cpp
* \ingroup u2w
* \author Derex <derex101@gmail.com>
*/
+
#include "WorldSocketMgr.h"
+
#include <ace/ACE.h>
#include <ace/Log_Msg.h>
#include <ace/Reactor.h>
@@ -34,12 +37,15 @@
#include <ace/os_include/netinet/os_tcp.h>
#include <ace/os_include/sys/os_types.h>
#include <ace/os_include/sys/os_socket.h>
+
#include <set>
+
#include "Log.h"
#include "Common.h"
#include "Config/ConfigEnv.h"
#include "Database/DatabaseEnv.h"
#include "WorldSocket.h"
+
/**
* This is a helper class to WorldSocketMgr ,that manages
* network threads, and assigning connections from acceptor thread
@@ -48,66 +54,90 @@
class ReactorRunnable : protected ACE_Task_Base
{
public:
+
ReactorRunnable () :
m_ThreadId (-1),
m_Connections (0),
m_Reactor (0)
{
ACE_Reactor_Impl* imp = 0;
+
#if defined (ACE_HAS_EVENT_POLL) || defined (ACE_HAS_DEV_POLL)
+
imp = new ACE_Dev_Poll_Reactor ();
+
imp->max_notify_iterations (128);
imp->restart (1);
+
#else
+
imp = new ACE_TP_Reactor ();
imp->max_notify_iterations (128);
+
#endif
+
m_Reactor = new ACE_Reactor (imp, 1);
}
+
virtual ~ReactorRunnable ()
{
Stop ();
Wait ();
+
if (m_Reactor)
delete m_Reactor;
}
+
void Stop ()
{
m_Reactor->end_reactor_event_loop ();
}
+
int Start ()
{
if (m_ThreadId != -1)
return -1;
+
return (m_ThreadId = activate ());
}
+
void Wait () { ACE_Task_Base::wait (); }
+
long Connections ()
{
return static_cast<long> (m_Connections.value ());
}
+
int AddSocket (WorldSocket* sock)
{
ACE_GUARD_RETURN (ACE_Thread_Mutex, Guard, m_NewSockets_Lock, -1);
+
++m_Connections;
sock->AddReference();
sock->reactor (m_Reactor);
m_NewSockets.insert (sock);
+
return 0;
}
+
ACE_Reactor* GetReactor ()
{
return m_Reactor;
}
+
protected:
+
void AddNewSockets ()
{
ACE_GUARD (ACE_Thread_Mutex, Guard, m_NewSockets_Lock);
+
if (m_NewSockets.empty ())
return;
+
for (SocketSet::const_iterator i = m_NewSockets.begin (); i != m_NewSockets.end (); ++i)
{
WorldSocket* sock = (*i);
+
if (sock->IsClosed ())
{
sock->RemoveReference ();
@@ -116,22 +146,31 @@ class ReactorRunnable : protected ACE_Task_Base
else
m_Sockets.insert (sock);
}
+
m_NewSockets.clear ();
}
+
virtual int svc ()
{
DEBUG_LOG ("Network Thread Starting");
+
WorldDatabase.ThreadStart ();
+
ACE_ASSERT (m_Reactor);
+
SocketSet::iterator i, t;
+
while (!m_Reactor->reactor_event_loop_done ())
{
// dont be too smart to move this outside the loop
// the run_reactor_event_loop will modify interval
ACE_Time_Value interval (0, 10000);
+
if (m_Reactor->run_reactor_event_loop (interval) == -1)
break;
+
AddNewSockets ();
+
for (i = m_Sockets.begin (); i != m_Sockets.end ();)
{
if ((*i)->Update () == -1)
@@ -147,20 +186,28 @@ class ReactorRunnable : protected ACE_Task_Base
++i;
}
}
+
WorldDatabase.ThreadEnd ();
+
DEBUG_LOG ("Network Thread Exitting");
+
return 0;
}
+
private:
typedef ACE_Atomic_Op<ACE_SYNCH_MUTEX, long> AtomicInt;
typedef std::set<WorldSocket*> SocketSet;
+
ACE_Reactor* m_Reactor;
AtomicInt m_Connections;
int m_ThreadId;
+
SocketSet m_Sockets;
+
SocketSet m_NewSockets;
ACE_Thread_Mutex m_NewSockets_Lock;
};
+
WorldSocketMgr::WorldSocketMgr () :
m_NetThreadsCount (0),
m_NetThreads (0),
@@ -170,71 +217,95 @@ WorldSocketMgr::WorldSocketMgr () :
m_Acceptor (0)
{
}
+
WorldSocketMgr::~WorldSocketMgr ()
{
if (m_NetThreads)
delete [] m_NetThreads;
+
if(m_Acceptor)
delete m_Acceptor;
}
+
int
WorldSocketMgr::StartReactiveIO (ACE_UINT16 port, const char* address)
{
m_UseNoDelay = sConfig.GetBoolDefault ("Network.TcpNodelay", true);
+
int num_threads = sConfig.GetIntDefault ("Network.Threads", 1);
+
if (num_threads <= 0)
{
sLog.outError ("Network.Threads is wrong in your config file");
return -1;
}
+
m_NetThreadsCount = static_cast<size_t> (num_threads + 1);
+
m_NetThreads = new ReactorRunnable[m_NetThreadsCount];
+
sLog.outBasic ("Max allowed socket connections %d",ACE::max_handles ());
+
// -1 means use default
m_SockOutKBuff = sConfig.GetIntDefault ("Network.OutKBuff", -1);
+
m_SockOutUBuff = sConfig.GetIntDefault ("Network.OutUBuff", 65536);
+
if ( m_SockOutUBuff <= 0 )
{
sLog.outError ("Network.OutUBuff is wrong in your config file");
return -1;
}
+
WorldSocket::Acceptor *acc = new WorldSocket::Acceptor;
m_Acceptor = acc;
+
ACE_INET_Addr listen_addr (port, address);
+
if (acc->open (listen_addr, m_NetThreads[0].GetReactor (), ACE_NONBLOCK) == -1)
{
sLog.outError ("Failed to open acceptor ,check if the port is free");
return -1;
}
+
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Start ();
+
return 0;
}
+
int
WorldSocketMgr::StartNetwork (ACE_UINT16 port, const char* address)
{
if (!sLog.IsOutDebug ())
ACE_Log_Msg::instance ()->priority_mask (LM_ERROR, ACE_Log_Msg::PROCESS);
+
if (StartReactiveIO (port, address) == -1)
return -1;
+
return 0;
}
+
void
WorldSocketMgr::StopNetwork ()
{
if (m_Acceptor)
{
WorldSocket::Acceptor* acc = dynamic_cast<WorldSocket::Acceptor*> (m_Acceptor);
+
if (acc)
acc->close ();
}
+
if (m_NetThreadsCount != 0)
{
for (size_t i = 0; i < m_NetThreadsCount; ++i)
m_NetThreads[i].Stop ();
}
+
Wait ();
}
+
void
WorldSocketMgr::Wait ()
{
@@ -244,6 +315,7 @@ WorldSocketMgr::Wait ()
m_NetThreads[i].Wait ();
}
}
+
int
WorldSocketMgr::OnSocketOpen (WorldSocket* sock)
{
@@ -259,7 +331,9 @@ WorldSocketMgr::OnSocketOpen (WorldSocket* sock)
return -1;
}
}
+
static const int ndoption = 1;
+
// Set TCP_NODELAY.
if (m_UseNoDelay)
{
@@ -272,15 +346,21 @@ WorldSocketMgr::OnSocketOpen (WorldSocket* sock)
return -1;
}
}
+
sock->m_OutBufferSize = static_cast<size_t> (m_SockOutUBuff);
+
// we skip the Acceptor Thread
size_t min = 1;
+
ACE_ASSERT (m_NetThreadsCount >= 1);
+
for (size_t i = 1; i < m_NetThreadsCount; ++i)
if (m_NetThreads[i].Connections () < m_NetThreads[min].Connections ())
min = i;
+
return m_NetThreads[min].AddSocket (sock);
}
+
WorldSocketMgr*
WorldSocketMgr::Instance ()
{
diff --git a/src/game/WorldSocketMgr.h b/src/game/WorldSocketMgr.h
index 0ad8574a2fb..94cfa78324c 100644
--- a/src/game/WorldSocketMgr.h
+++ b/src/game/WorldSocketMgr.h
@@ -17,47 +17,64 @@
* 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 WorldSocketMgr.h
* \author Derex <derex101@gmail.com>
*/
+
#ifndef __WORLDSOCKETMGR_H
#define __WORLDSOCKETMGR_H
+
#include <ace/Basic_Types.h>
#include <ace/Singleton.h>
#include <ace/Thread_Mutex.h>
+
class WorldSocket;
class ReactorRunnable;
class ACE_Event_Handler;
+
/// Manages all sockets connected to peers and network threads
class WorldSocketMgr
{
public:
friend class WorldSocket;
friend class ACE_Singleton<WorldSocketMgr,ACE_Thread_Mutex>;
+
/// Start network, listen at address:port .
int StartNetwork (ACE_UINT16 port, const char* address);
+
/// Stops all network threads, It will wait for all running threads .
void StopNetwork ();
+
/// Wait untill all network threads have "joined" .
void Wait ();
+
/// Make this class singleton .
static WorldSocketMgr* Instance ();
+
private:
int OnSocketOpen(WorldSocket* sock);
+
int StartReactiveIO(ACE_UINT16 port, const char* address);
+
private:
WorldSocketMgr ();
virtual ~WorldSocketMgr ();
+
ReactorRunnable* m_NetThreads;
size_t m_NetThreadsCount;
+
int m_SockOutKBuff;
int m_SockOutUBuff;
bool m_UseNoDelay;
+
ACE_Event_Handler* m_Acceptor;
};
+
#define sWorldSocketMgr WorldSocketMgr::Instance ()
+
#endif
/// @}
diff --git a/src/game/ZoneScript.h b/src/game/ZoneScript.h
index 9e81ae341b9..6ace3c36474 100644
--- a/src/game/ZoneScript.h
+++ b/src/game/ZoneScript.h
@@ -15,27 +15,37 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef ZONE_SCRIPT_H_
#define ZONE_SCRIPT_H_
+
#include "Common.h"
#include "Creature.h"
+
//struct CreatureData;
class Creature;
class GameObject;
+
class TRINITY_DLL_SPEC ZoneScript
{
public:
explicit ZoneScript() {}
+
virtual uint32 GetCreatureEntry(uint32 guidlow, const CreatureData *data) { return data->id; }
virtual uint32 GetGameObjectEntry(uint32 guidlow, uint32 entry) { return entry; }
+
virtual void OnCreatureCreate(Creature *, bool add) {}
virtual void OnGameObjectCreate(GameObject *go, bool add) {}
+
//All-purpose data storage 64 bit
virtual uint64 GetData64(uint32 /*DataId*/) { return 0; }
virtual void SetData64(uint32 /*DataId*/, uint64 /*Value*/) {}
+
//All-purpose data storage 32 bit
virtual uint32 GetData(uint32 /*DataId*/) { return 0; }
virtual void SetData(uint32 /*DataId*/, uint32 /*Value*/) {}
+
virtual void ProcessEvent(GameObject *obj, uint32 eventId) {}
};
-#endif
+
+#endif \ No newline at end of file
diff --git a/src/game/pchdef.h b/src/game/pchdef.h
index a5d445675e0..7252e980e7d 100644
--- a/src/game/pchdef.h
+++ b/src/game/pchdef.h
@@ -1,6 +1,7 @@
//add here most rarely modified headers to speed up debug build compilation
#include "WorldSocket.h" // must be first to make ACE happy with ACE includes in it
#include "Common.h"
+
#include "MapManager.h"
#include "Log.h"
#include "ObjectAccessor.h"
diff --git a/src/shared/Auth/AuthCrypt.cpp b/src/shared/Auth/AuthCrypt.cpp
index 56d70d4c14e..e8126ad9f73 100644
--- a/src/shared/Auth/AuthCrypt.cpp
+++ b/src/shared/Auth/AuthCrypt.cpp
@@ -17,48 +17,65 @@
* 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"
#include "Log.h"
#include "BigNumber.h"
+
AuthCrypt::AuthCrypt()
{
_initialized = false;
}
+
AuthCrypt::~AuthCrypt()
{
+
}
+
void AuthCrypt::Init(BigNumber *K)
{
uint8 ServerEncryptionKey[SEED_KEY_SIZE] = { 0x22, 0xBE, 0xE5, 0xCF, 0xBB, 0x07, 0x64, 0xD9, 0x00, 0x45, 0x1B, 0xD0, 0x24, 0xB8, 0xD5, 0x45 };
HmacHash serverEncryptHmac(SEED_KEY_SIZE, (uint8*)ServerEncryptionKey);
uint8 *encryptHash = serverEncryptHmac.ComputeHash(K);
+
uint8 ServerDecryptionKey[SEED_KEY_SIZE] = { 0xF4, 0x66, 0x31, 0x59, 0xFC, 0x83, 0x6E, 0x31, 0x31, 0x02, 0x51, 0xD5, 0x44, 0x31, 0x67, 0x98 };
HmacHash clientDecryptHmac(SEED_KEY_SIZE, (uint8*)ServerDecryptionKey);
uint8 *decryptHash = clientDecryptHmac.ComputeHash(K);
+
//SARC4 _serverDecrypt(encryptHash);
_clientDecrypt.Init(decryptHash);
_serverEncrypt.Init(encryptHash);
//SARC4 _clientEncrypt(decryptHash);
+
uint8 syncBuf[1024];
+
memset(syncBuf, 0, 1024);
+
_serverEncrypt.UpdateData(1024, syncBuf);
//_clientEncrypt.UpdateData(1024, syncBuf);
+
memset(syncBuf, 0, 1024);
+
//_serverDecrypt.UpdateData(1024, syncBuf);
_clientDecrypt.UpdateData(1024, syncBuf);
+
_initialized = true;
}
+
void AuthCrypt::DecryptRecv(uint8 *data, size_t len)
{
if (!_initialized)
return;
+
_clientDecrypt.UpdateData(len, data);
}
+
void AuthCrypt::EncryptSend(uint8 *data, size_t len)
{
if (!_initialized)
return;
+
_serverEncrypt.UpdateData(len, data);
}
diff --git a/src/shared/Auth/AuthCrypt.h b/src/shared/Auth/AuthCrypt.h
index 7f885e563ab..226fde018ae 100644
--- a/src/shared/Auth/AuthCrypt.h
+++ b/src/shared/Auth/AuthCrypt.h
@@ -17,20 +17,27 @@
* 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 <Common.h>
#include "SARC4.h"
+
class BigNumber;
+
class AuthCrypt
{
public:
AuthCrypt();
~AuthCrypt();
+
void Init(BigNumber *K);
void DecryptRecv(uint8 *, size_t);
void EncryptSend(uint8 *, size_t);
+
bool IsInitialized() { return _initialized; }
+
private:
SARC4 _clientDecrypt;
SARC4 _serverEncrypt;
diff --git a/src/shared/Auth/BigNumber.cpp b/src/shared/Auth/BigNumber.cpp
index 837b75804df..303687c266c 100644
--- a/src/shared/Auth/BigNumber.cpp
+++ b/src/shared/Auth/BigNumber.cpp
@@ -17,143 +17,182 @@
* 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 <openssl/bn.h>
#include <algorithm>
+
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);
}
+
bool BigNumber::isZero() const
{
return BN_is_zero(_bn)!=0;
}
+
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;
}
+
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
index 898f53c2a05..f1b3a0beda2 100644
--- a/src/shared/Auth/BigNumber.h
+++ b/src/shared/Auth/BigNumber.h
@@ -17,10 +17,14 @@
* 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"
+
struct bignum_st;
+
class BigNumber
{
public:
@@ -28,12 +32,16 @@ class 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)
{
@@ -64,15 +72,22 @@ class BigNumber
BigNumber t(*this);
return t %= bn;
}
+
bool isZero() const;
+
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);
+
const char *AsHexStr();
const char *AsDecStr();
+
private:
struct bignum_st *_bn;
uint8 *_array;
diff --git a/src/shared/Auth/Hmac.cpp b/src/shared/Auth/Hmac.cpp
index ed68ce8c5df..985b4fb9a56 100644
--- a/src/shared/Auth/Hmac.cpp
+++ b/src/shared/Auth/Hmac.cpp
@@ -17,32 +17,40 @@
* 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(uint32 len, uint8 *seed)
{
ASSERT(len == SEED_KEY_SIZE);
+
HMAC_CTX_init(&m_ctx);
HMAC_Init_ex(&m_ctx, seed, SEED_KEY_SIZE, EVP_sha1(), NULL);
}
+
HmacHash::~HmacHash()
{
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::Finalize()
{
uint32 length = 0;
HMAC_Final(&m_ctx, (uint8*)m_digest, &length);
ASSERT(length == SHA_DIGEST_LENGTH)
}
+
uint8 *HmacHash::ComputeHash(BigNumber *bn)
{
HMAC_Update(&m_ctx, bn->AsByteArray(), bn->GetNumBytes());
diff --git a/src/shared/Auth/Hmac.h b/src/shared/Auth/Hmac.h
index a039617f801..76a302d68de 100644
--- a/src/shared/Auth/Hmac.h
+++ b/src/shared/Auth/Hmac.h
@@ -17,13 +17,18 @@
* 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 <openssl/hmac.h>
#include <openssl/sha.h>
+
class BigNumber;
+
#define SEED_KEY_SIZE 16
+
class HmacHash
{
public:
diff --git a/src/shared/Auth/SARC4.cpp b/src/shared/Auth/SARC4.cpp
index 5687deece59..f59bb7f0c53 100644
--- a/src/shared/Auth/SARC4.cpp
+++ b/src/shared/Auth/SARC4.cpp
@@ -15,14 +15,17 @@
* 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/SARC4.h"
#include <openssl/sha.h>
+
SARC4::SARC4()
{
EVP_CIPHER_CTX_init(&m_ctx);
EVP_EncryptInit_ex(&m_ctx, EVP_rc4(), NULL, NULL, NULL);
EVP_CIPHER_CTX_set_key_length(&m_ctx, SHA_DIGEST_LENGTH);
}
+
SARC4::SARC4(uint8 *seed)
{
EVP_CIPHER_CTX_init(&m_ctx);
@@ -30,14 +33,17 @@ SARC4::SARC4(uint8 *seed)
EVP_CIPHER_CTX_set_key_length(&m_ctx, SHA_DIGEST_LENGTH);
EVP_EncryptInit_ex(&m_ctx, NULL, NULL, seed, NULL);
}
+
SARC4::~SARC4()
{
EVP_CIPHER_CTX_cleanup(&m_ctx);
}
+
void SARC4::Init(uint8 *seed)
{
EVP_EncryptInit_ex(&m_ctx, NULL, NULL, seed, NULL);
}
+
void SARC4::UpdateData(int len, uint8 *data)
{
int outlen = 0;
diff --git a/src/shared/Auth/SARC4.h b/src/shared/Auth/SARC4.h
index 350b4ad51e6..3f15328d6cb 100644
--- a/src/shared/Auth/SARC4.h
+++ b/src/shared/Auth/SARC4.h
@@ -15,10 +15,13 @@
* 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_SARC4_H
#define _AUTH_SARC4_H
+
#include "Common.h"
#include <openssl/evp.h>
+
class SARC4
{
public:
diff --git a/src/shared/Auth/Sha1.cpp b/src/shared/Auth/Sha1.cpp
index 1cb1ea5fb40..802f1bbcdff 100644
--- a/src/shared/Auth/Sha1.cpp
+++ b/src/shared/Auth/Sha1.cpp
@@ -17,29 +17,36 @@
* 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 "Auth/BigNumber.h"
#include <stdarg.h>
+
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)
@@ -49,10 +56,12 @@ void Sha1Hash::UpdateBigNumbers(BigNumber *bn0, ...)
}
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
index 9b6dad84234..bd2b1afa876 100644
--- a/src/shared/Auth/Sha1.h
+++ b/src/shared/Auth/Sha1.h
@@ -17,25 +17,34 @@
* 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 <openssl/sha.h>
#include <openssl/crypto.h>
+
class BigNumber;
+
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; };
+
private:
SHA_CTX mC;
uint8 mDigest[SHA_DIGEST_LENGTH];
diff --git a/src/shared/ByteBuffer.h b/src/shared/ByteBuffer.h
index 9f454425327..b22dbdee729 100644
--- a/src/shared/ByteBuffer.h
+++ b/src/shared/ByteBuffer.h
@@ -17,12 +17,15 @@
* 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 ByteBufferException
{
public:
@@ -31,6 +34,7 @@ class ByteBufferException
{
PrintPosError();
}
+
void PrintPosError() const
{
sLog.outError("ERROR: Attempted to %s in ByteBuffer (pos: " SIZEFMTD " size: "SIZEFMTD") value with size: " SIZEFMTD,
@@ -42,157 +46,188 @@ class ByteBufferException
size_t esize;
size_t size;
};
+
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 <typename T> void append(T value)
{
EndianConvert(value);
append((uint8 *)&value, sizeof(value));
}
+
template <typename T> void put(size_t pos,T value)
{
EndianConvert(value);
put(pos,(uint8 *)&value,sizeof(value));
}
+
ByteBuffer &operator<<(uint8 value)
{
append<uint8>(value);
return *this;
}
+
ByteBuffer &operator<<(uint16 value)
{
append<uint16>(value);
return *this;
}
+
ByteBuffer &operator<<(uint32 value)
{
append<uint32>(value);
return *this;
}
+
ByteBuffer &operator<<(uint64 value)
{
append<uint64>(value);
return *this;
}
+
// signed as in 2e complement
ByteBuffer &operator<<(int8 value)
{
append<int8>(value);
return *this;
}
+
ByteBuffer &operator<<(int16 value)
{
append<int16>(value);
return *this;
}
+
ByteBuffer &operator<<(int32 value)
{
append<int32>(value);
return *this;
}
+
ByteBuffer &operator<<(int64 value)
{
append<int64>(value);
return *this;
}
+
// floating points
ByteBuffer &operator<<(float value)
{
append<float>(value);
return *this;
}
+
ByteBuffer &operator<<(double value)
{
append<double>(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<char>() > 0 ? true : false;
return *this;
}
+
ByteBuffer &operator>>(uint8 &value)
{
value = read<uint8>();
return *this;
}
+
ByteBuffer &operator>>(uint16 &value)
{
value = read<uint16>();
return *this;
}
+
ByteBuffer &operator>>(uint32 &value)
{
value = read<uint32>();
return *this;
}
+
ByteBuffer &operator>>(uint64 &value)
{
value = read<uint64>();
return *this;
}
+
//signed as in 2e complement
ByteBuffer &operator>>(int8 &value)
{
value = read<int8>();
return *this;
}
+
ByteBuffer &operator>>(int16 &value)
{
value = read<int16>();
return *this;
}
+
ByteBuffer &operator>>(int32 &value)
{
value = read<int32>();
return *this;
}
+
ByteBuffer &operator>>(int64 &value)
{
value = read<int64>();
return *this;
}
+
ByteBuffer &operator>>(float &value)
{
value = read<float>();
return *this;
}
+
ByteBuffer &operator>>(double &value)
{
value = read<double>();
return *this;
}
+
ByteBuffer &operator>>(std::string& value)
{
value.clear();
@@ -205,36 +240,45 @@ class ByteBuffer
}
return *this;
}
+
uint8 operator[](size_t pos) const
{
return read<uint8>(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<typename T>
void read_skip() { read_skip(sizeof(T)); }
+
void read_skip(size_t skip)
{
if(_rpos + skip > size())
throw ByteBufferException(false, _rpos, skip, size());
_rpos += skip;
}
+
template <typename T> T read()
{
T r = read<T>(_rpos);
_rpos += sizeof(T);
return r;
}
+
template <typename T> T read(size_t pos) const
{
if(pos + sizeof(T) > size())
@@ -243,6 +287,7 @@ class ByteBuffer
EndianConvert(val);
return val;
}
+
void read(uint8 *dest, size_t len)
{
if(_rpos + len > size())
@@ -250,67 +295,85 @@ class ByteBuffer
memcpy(dest, &_storage[_rpos], len);
_rpos += len;
}
+
bool readPackGUID(uint64& guid)
{
if(rpos() + 1 > size())
return false;
+
guid = 0;
+
uint8 guidmark = 0;
(*this) >> guidmark;
+
for(int i = 0; i < 8; ++i)
{
if(guidmark & (uint8(1) << i))
{
if(rpos() + 1 > size())
return false;
+
uint8 bit;
(*this) >> bit;
guid |= (uint64(bit) << (i * 8));
}
}
+
return true;
}
+
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<class T> 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.wpos())
append(buffer.contents(), buffer.wpos());
}
+
// can be used in SMSG_MONSTER_MOVE opcode
void appendPackXYZ(float x, float y, float z)
{
@@ -320,10 +383,12 @@ class ByteBuffer
packed |= ((int)(z / 0.25f) & 0x3FF) << 22;
*this << packed;
}
+
void appendPackGUID(uint64 guid)
{
if (_storage.size() < _wpos + sizeof(guid) + 1)
_storage.resize(_wpos + sizeof(guid) + 1);
+
size_t mask_position = wpos();
*this << uint8(0);
for(uint8 i = 0; i < 8; ++i)
@@ -333,39 +398,48 @@ class ByteBuffer
_storage[mask_position] |= uint8(1 << i);
*this << uint8(guid & 0xFF);
}
+
guid >>= 8;
}
}
+
void put(size_t pos, const uint8 *src, size_t cnt)
{
if(pos + cnt > size())
throw ByteBufferException(true, pos, cnt, size());
memcpy(&_storage[pos], src, cnt);
}
+
void print_storage() const
{
if(!sLog.IsOutDebug()) // optimize disabled debug output
return;
+
sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() );
for(uint32 i = 0; i < size(); ++i)
sLog.outDebugInLine("%u - ", read<uint8>(i) );
sLog.outDebug(" ");
}
+
void textlike() const
{
if(!sLog.IsOutDebug()) // optimize disabled debug output
return;
+
sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() );
for(uint32 i = 0; i < size(); ++i)
sLog.outDebugInLine("%c", read<uint8>(i) );
sLog.outDebug(" ");
}
+
void hexlike() const
{
if(!sLog.IsOutDebug()) // optimize disabled debug output
return;
+
uint32 j = 1, k = 1;
sLog.outDebug("STORAGE_SIZE: %lu", (unsigned long)size() );
+
for(uint32 i = 0; i < size(); ++i)
{
if ((i == (j * 8)) && ((i != (k * 16))))
@@ -385,13 +459,16 @@ class ByteBuffer
if (read<uint8>(i) < 0x10)
{
sLog.outDebugInLine("\n");
+
sLog.outDebugInLine("0%X ", read<uint8>(i) );
}
else
{
sLog.outDebugInLine("\n");
+
sLog.outDebugInLine("%X ", read<uint8>(i) );
}
+
++k;
++j;
}
@@ -409,10 +486,12 @@ class ByteBuffer
}
sLog.outDebugInLine("\n");
}
+
protected:
size_t _rpos, _wpos;
std::vector<uint8> _storage;
};
+
template <typename T>
inline ByteBuffer &operator<<(ByteBuffer &b, std::vector<T> v)
{
@@ -423,6 +502,7 @@ inline ByteBuffer &operator<<(ByteBuffer &b, std::vector<T> v)
}
return b;
}
+
template <typename T>
inline ByteBuffer &operator>>(ByteBuffer &b, std::vector<T> &v)
{
@@ -437,6 +517,7 @@ inline ByteBuffer &operator>>(ByteBuffer &b, std::vector<T> &v)
}
return b;
}
+
template <typename T>
inline ByteBuffer &operator<<(ByteBuffer &b, std::list<T> v)
{
@@ -447,6 +528,7 @@ inline ByteBuffer &operator<<(ByteBuffer &b, std::list<T> v)
}
return b;
}
+
template <typename T>
inline ByteBuffer &operator>>(ByteBuffer &b, std::list<T> &v)
{
@@ -461,6 +543,7 @@ inline ByteBuffer &operator>>(ByteBuffer &b, std::list<T> &v)
}
return b;
}
+
template <typename K, typename V>
inline ByteBuffer &operator<<(ByteBuffer &b, std::map<K, V> &m)
{
@@ -471,6 +554,7 @@ inline ByteBuffer &operator<<(ByteBuffer &b, std::map<K, V> &m)
}
return b;
}
+
template <typename K, typename V>
inline ByteBuffer &operator>>(ByteBuffer &b, std::map<K, V> &m)
{
@@ -486,6 +570,7 @@ inline ByteBuffer &operator>>(ByteBuffer &b, std::map<K, V> &m)
}
return b;
}
+
// TODO: Make a ByteBuffer.cpp and move all this inlining to it.
template<> inline std::string ByteBuffer::read<std::string>()
{
@@ -493,17 +578,20 @@ template<> inline std::string ByteBuffer::read<std::string>()
*this >> tmp;
return tmp;
}
+
template<>
inline void ByteBuffer::read_skip<char*>()
{
std::string temp;
*this >> temp;
}
+
template<>
inline void ByteBuffer::read_skip<char const*>()
{
read_skip<char*>();
}
+
template<>
inline void ByteBuffer::read_skip<std::string>()
{
diff --git a/src/shared/Common.cpp b/src/shared/Common.cpp
index 17bd0a90174..27ae9184d69 100644
--- a/src/shared/Common.cpp
+++ b/src/shared/Common.cpp
@@ -17,7 +17,9 @@
* 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",
@@ -29,11 +31,13 @@ char const* localeNames[MAX_LOCALE] = {
"esMX",
"ruRU"
};
+
LocaleConstant GetLocaleByName(const 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
index bfe243b34ab..8c948fb1d6d 100644
--- a/src/shared/Common.h
+++ b/src/shared/Common.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_COMMON_H
#define TRINITYCORE_COMMON_H
+
// config.h needs to be included 1st
// TODO this thingy looks like hack ,but its not, need to
// make separate header however, because It makes mess here.
@@ -56,7 +58,9 @@
#undef PACKAGE_VERSION
#undef VERSION
#endif //HAVE_CONFIG_H
+
#include "Platform/Define.h"
+
#if COMPILER == COMPILER_MICROSOFT
# pragma warning(disable:4996) // 'function': was declared deprecated
#ifndef __SHOW_STUPID_WARNINGS__
@@ -71,6 +75,7 @@
# pragma warning(disable:4522) //warning when class has 2 constructors
#endif // __SHOW_STUPID_WARNINGS__
#endif // __GNUC__
+
#include "Utilities/UnorderedMap.h"
#include <stdio.h>
#include <stdlib.h>
@@ -80,11 +85,13 @@
#include <errno.h>
#include <signal.h>
#include <assert.h>
+
#if PLATFORM == PLATFORM_WINDOWS
#define STRCASECMP stricmp
#else
#define STRCASECMP strcasecmp
#endif
+
#include <set>
#include <list>
#include <string>
@@ -92,13 +99,16 @@
#include <queue>
#include <sstream>
#include <algorithm>
+
#include "LockedQueue.h"
#include "Threading.h"
+
#include <ace/Basic_Types.h>
#include <ace/Guard_T.h>
#include <ace/RW_Thread_Mutex.h>
#include <ace/Thread_Mutex.h>
+
#if PLATFORM == PLATFORM_WINDOWS
# define FD_SETSIZE 4096
# include <ace/config-all.h>
@@ -115,8 +125,11 @@
# include <unistd.h>
# include <netdb.h>
#endif
+
#if COMPILER == COMPILER_MICROSOFT
+
#include <float.h>
+
#define I32FMT "%08I32X"
#define I64FMT "%016I64X"
#define snprintf _snprintf
@@ -124,20 +137,30 @@
#define vsnprintf _vsnprintf
#define strdup _strdup
#define finite(X) _finite(X)
+
#else
+
#define stricmp strcasecmp
#define strnicmp strncasecmp
#define I32FMT "%08X"
#define I64FMT "%016llX"
+
#endif
+
#define UI64FMTD ACE_UINT64_FORMAT_SPECIFIER
#define UI64LIT(N) ACE_UINT64_LITERAL(N)
+
#define SI64FMTD ACE_INT64_FORMAT_SPECIFIER
#define SI64LIT(N) ACE_INT64_LITERAL(N)
+
#define SIZEFMTD ACE_SIZE_T_FORMAT_SPECIFIER
+
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,
@@ -147,6 +170,7 @@ enum TimeConstants
YEAR = MONTH*12,
IN_MILISECONDS = 1000
};
+
enum AccountTypes
{
SEC_PLAYER = 0,
@@ -155,6 +179,7 @@ enum AccountTypes
SEC_ADMINISTRATOR = 3,
SEC_CONSOLE = 4 // must be always last in list, accounts must have less security level always also
};
+
enum LocaleConstant
{
LOCALE_enUS = 0,
@@ -167,18 +192,25 @@ enum LocaleConstant
LOCALE_esMX = 7,
LOCALE_ruRU = 8
};
+
#define MAX_LOCALE 9
+
extern char const* localeNames[MAX_LOCALE];
+
LocaleConstant GetLocaleByName(const std::string& name);
+
// we always use stdlibc++ std::max/std::min, undefine some not C++ standard defines (Win API and some other 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
index 5636e17fd6d..b56b804b50a 100644
--- a/src/shared/Config/Config.cpp
+++ b/src/shared/Config/Config.cpp
@@ -17,38 +17,49 @@
* 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;
}
+
std::string Config::GetStringDefault(const char * name, std::string def)
{
if(!mConf)
@@ -58,6 +69,7 @@ std::string Config::GetStringDefault(const char * name, std::string def)
return std::string(def);
return std::string(node->getValue());
};
+
bool Config::GetBoolDefault(const char * name, const bool def)
{
if(!mConf)
@@ -73,6 +85,7 @@ bool Config::GetBoolDefault(const char * name, const bool def)
else
return false;
};
+
int32 Config::GetIntDefault(const char * name, const int32 def)
{
if(!mConf)
@@ -82,6 +95,7 @@ int32 Config::GetIntDefault(const char * name, const int32 def)
return def;
return atoi(node->getValue());
};
+
float Config::GetFloatDefault(const char * name, const float def)
{
if(!mConf)
diff --git a/src/shared/Config/Config.h b/src/shared/Config/Config.h
index 582c5266a8c..7070e6180c0 100644
--- a/src/shared/Config/Config.h
+++ b/src/shared/Config/Config.h
@@ -17,28 +17,37 @@
* 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 <Policies/Singleton.h>
#include "Platform/Define.h"
+
class DOTCONFDocument;
+
class TRINITY_DLL_SPEC Config
{
public:
Config();
~Config();
+
bool SetSource(const char *file, bool ignorecase = true);
bool Reload();
+
std::string GetStringDefault(const char * name, std::string def);
bool GetBoolDefault(const char * name, const bool def);
int32 GetIntDefault(const char * name, const int32 def);
float GetFloatDefault(const char * name, const float def);
+
std::string GetFilename() const { return mFilename; }
private:
std::string mFilename;
bool mIgnoreCase;
DOTCONFDocument *mConf;
};
+
#define sConfig Trinity::Singleton<Config>::Instance()
+
#endif
diff --git a/src/shared/Config/ConfigEnv.h b/src/shared/Config/ConfigEnv.h
index c98fe6b38b0..75209a7fc6b 100644
--- a/src/shared/Config/ConfigEnv.h
+++ b/src/shared/Config/ConfigEnv.h
@@ -17,10 +17,14 @@
* 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"
+
#endif
diff --git a/src/shared/Config/dotconfpp/dotconfpp.cpp b/src/shared/Config/dotconfpp/dotconfpp.cpp
index 0492c69f805..e779637256e 100644
--- a/src/shared/Config/dotconfpp/dotconfpp.cpp
+++ b/src/shared/Config/dotconfpp/dotconfpp.cpp
@@ -1,6 +1,8 @@
#include "Common.h"
+
#include "dotconfpp.h"
+
#ifdef WIN32
#define PATH_MAX _MAX_PATH
#define strcasecmp stricmp
@@ -12,14 +14,17 @@
#include <stdint.h>
#include <strings.h>
#endif
+
#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);
@@ -30,12 +35,14 @@ DOTCONFDocumentNode::~DOTCONFDocumentNode()
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){
@@ -43,6 +50,7 @@ const char* DOTCONFDocumentNode::getValue(int index) const
}
return values[index];
}
+
DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity):
mempool(NULL),
curParent(NULL), curPrev(NULL), curLine(0), file(NULL), fileName(NULL)
@@ -52,9 +60,11 @@ DOTCONFDocument::DOTCONFDocument(DOTCONFDocument::CaseSensitive caseSensitivity)
} else {
cmp_func = strcasecmp;
}
+
mempool = new AsyncDNSMemPool(1024);
mempool->initialize();
}
+
DOTCONFDocument::~DOTCONFDocument()
{
for(std::list<DOTCONFDocumentNode*>::iterator i = nodeTree.begin(); i != nodeTree.end(); ++i){
@@ -69,6 +79,7 @@ DOTCONFDocument::~DOTCONFDocument()
free(fileName);
delete mempool;
}
+
int DOTCONFDocument::cleanupLine(char * line)
{
char * start = line;
@@ -76,12 +87,15 @@ int DOTCONFDocument::cleanupLine(char * 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());
@@ -98,6 +112,7 @@ int DOTCONFDocument::cleanupLine(char * line)
if(*line == '=' && !quoted){
*line = ' ';continue;
}
+
// Allowing \" in there causes problems with directory paths
// like "C:\TrinIty\"
//if(*line == '\\' && (*(line+1) == '"' || *(line+1) == '\'')){
@@ -116,6 +131,7 @@ int DOTCONFDocument::cleanupLine(char * line)
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());
@@ -137,6 +153,7 @@ int DOTCONFDocument::cleanupLine(char * line)
if(isspace((unsigned char)*line) && !quoted){
*bg++ = 0;
if(strlen(start)){
+
if(concat){
word = (char*)mempool->alloc(strlen(words.back())+strlen(start)+1);
strcpy(word, words.back());
@@ -150,16 +167,20 @@ int DOTCONFDocument::cleanupLine(char * line)
}
start = bg;
while(isspace((unsigned char)*++line)) {}
+
continue;
}
*bg++ = *line++;
}
+
if(quoted && !multiline){
error(curLine, fileName, "unterminated quote");
return -1;
}
+
return multiline?1:0;
}
+
int DOTCONFDocument::parseLine()
{
char * word = NULL;
@@ -167,21 +188,26 @@ int DOTCONFDocument::parseLine()
char * nodeValue = NULL;
DOTCONFDocumentNode * tagNode = NULL;
bool newNode = false;
+
for(std::list<char*>::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;
@@ -216,9 +242,11 @@ int DOTCONFDocument::parseLine()
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;
@@ -232,6 +260,7 @@ int DOTCONFDocument::parseLine()
tagNode->pushValue(nodeValue);
}
}
+
return 0;
}
int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent)
@@ -240,8 +269,10 @@ int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent)
int ret = 0;
curLine = 0;
curParent = _parent;
+
quoted = false;
size_t slen = 0;
+
while(fgets(str, 511, file)){
++curLine;
slen = strlen(str);
@@ -266,11 +297,14 @@ int DOTCONFDocument::parseFile(DOTCONFDocumentNode * _parent)
}
}
}
+
return ret;
}
+
int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator & from)
{
int ret = 0;
+
DOTCONFDocumentNode * tagNode = NULL;
int vi = 0;
for(std::list<DOTCONFDocumentNode*>::iterator i = from; i != nodeTree.end(); ++i){
@@ -282,6 +316,7 @@ int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator
}
vi = 0;
while( vi < tagNode->valuesCount ){
+
if(strstr(tagNode->values[vi], "${") && strchr(tagNode->values[vi], '}') ){
ret = macroSubstitute(tagNode, vi );
mempool->free();
@@ -295,18 +330,24 @@ int DOTCONFDocument::checkConfig(const std::list<DOTCONFDocumentNode*>::iterator
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;
@@ -316,12 +357,17 @@ int DOTCONFDocument::setContent(const char * _fileName)
fgets((char*)&utf8header, 4, file); // Try read header
if (utf8header!=0x00BFBBEF) // If not exist
fseek(file, 0, SEEK_SET); // Reset read position
+
ret = parseFile();
+
(void) fclose(file);
+
if(!ret){
+
if( (ret = checkConfig(nodeTree.begin())) == -1){
return -1;
}
+
std::list<DOTCONFDocumentNode*>::iterator from;
DOTCONFDocumentNode * tagNode = NULL;
int vi = 0;
@@ -338,6 +384,7 @@ int DOTCONFDocument::setContent(const char * _fileName)
error(tagNode->lineNum, tagNode->fileName, "realpath(%s) failed: %s", tagNode->values[vi], strerror(errno));
return -1;
}
+
bool processed = false;
for(std::list<char*>::const_iterator itInode = processedFiles.begin(); itInode != processedFiles.end(); ++itInode){
if(!strcmp(*itInode, realpathBuf)){
@@ -348,14 +395,18 @@ int DOTCONFDocument::setContent(const char * _fileName)
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)
@@ -368,11 +419,14 @@ int DOTCONFDocument::setContent(const char * _fileName)
}
}
+
if(!requiredOptions.empty())
ret = checkRequiredOptions();
}
+
return ret;
}
+
int DOTCONFDocument::checkRequiredOptions()
{
for(std::list<char*>::const_iterator ci = requiredOptions.begin(); ci != requiredOptions.end(); ++ci){
@@ -390,30 +444,40 @@ int DOTCONFDocument::checkRequiredOptions()
}
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 != '-'){
@@ -428,6 +492,7 @@ char * DOTCONFDocument::getSubstitution(char * macro, int lineNum)
} else {
defaultValue = NULL;
}
+
char * subs = getenv(variable);
if( subs ){
buf = mempool->strdup(subs);
@@ -454,6 +519,7 @@ char * DOTCONFDocument::getSubstitution(char * macro, int lineNum)
}
return buf;
}
+
int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueIndex)
{
int ret = 0;
@@ -462,6 +528,7 @@ int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueInd
char * value = (char*)mempool->alloc(valueLen);
char * v = value;
char * subs = NULL;
+
while(*macro){
if(*macro == '$' && *(macro+1) == '{'){
char * m = strchr(macro, '}');
@@ -481,10 +548,12 @@ int DOTCONFDocument::macroSubstitute(DOTCONFDocumentNode * tagNode, int valueInd
*v++ = *macro++;
}
*v = 0;
+
free(tagNode->values[valueIndex]);
tagNode->values[valueIndex] = strdup(value);
return ret;
}
+
const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const
{
if ( !nodeTree.empty() ) {
@@ -493,19 +562,25 @@ const DOTCONFDocumentNode * DOTCONFDocument::getFirstNode() const
return NULL;
}
}
+
const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, const DOTCONFDocumentNode * parentNode, const DOTCONFDocumentNode * startNode) const
{
+
std::list<DOTCONFDocumentNode*>::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;
}
@@ -515,6 +590,7 @@ const DOTCONFDocumentNode * DOTCONFDocument::findNode(const char * nodeName, con
}
return NULL;
}
+
void DOTCONFDocument::setRequiredOptionNames(const char ** requiredOptionNames)
{
while(*requiredOptionNames){
diff --git a/src/shared/Config/dotconfpp/dotconfpp.h b/src/shared/Config/dotconfpp/dotconfpp.h
index 687753eb807..51455854ee7 100644
--- a/src/shared/Config/dotconfpp/dotconfpp.h
+++ b/src/shared/Config/dotconfpp/dotconfpp.h
@@ -1,17 +1,24 @@
+
#ifndef DOTCONFPP_H
#define DOTCONFPP_H
+
#include <list>
+
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <ctype.h>
#include <errno.h>
+
#include <sys/types.h>
#include <sys/stat.h>
+
#include "mempool.h"
+
class DOTCONFDocument;
+
class DOTCONFDocumentNode
{
friend class DOTCONFDocument;
@@ -27,12 +34,16 @@ private:
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; }
@@ -41,6 +52,7 @@ public:
const char * getName() const { return name; }
const DOTCONFDocument * getDocument() const { return document; }
};
+
class DOTCONFDocument
{
public:
@@ -59,6 +71,7 @@ private:
char * fileName;
std::list<char*> words;
int (*cmp_func)(const char *, const char *);
+
int checkRequiredOptions();
int parseLine();
int parseFile(DOTCONFDocumentNode * _parent = NULL);
@@ -66,15 +79,20 @@ private:
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
index 019cfe3cb86..cec8e8d119f 100644
--- a/src/shared/Config/dotconfpp/mempool.cpp
+++ b/src/shared/Config/dotconfpp/mempool.cpp
@@ -1,20 +1,25 @@
+
#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; i<chunksCount; ++i){
@@ -22,15 +27,19 @@ AsyncDNSMemPool::~AsyncDNSMemPool()
}
::free(chunks);
}
+
int AsyncDNSMemPool::initialize()
{
chunksCount = 1;
chunks = (PoolChunk**)::malloc(sizeof(PoolChunk*));
if(chunks == NULL)
return -1;
+
chunks[chunksCount-1] = new PoolChunk(defaultSize);
+
return 0;
}
+
void AsyncDNSMemPool::addNewChunk(size_t size)
{
++chunksCount;
@@ -40,6 +49,7 @@ void AsyncDNSMemPool::addNewChunk(size_t size)
else
chunks[chunksCount-1] = new PoolChunk(size);
}
+
void * AsyncDNSMemPool::alloc(size_t size)
{
PoolChunk * chunk = NULL;
@@ -54,17 +64,20 @@ void * AsyncDNSMemPool::alloc(size_t 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; i<chunksCount; ++i){
pu += chunks[i]->pos;
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){
@@ -75,10 +88,12 @@ void AsyncDNSMemPool::free()
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
index 6bf71eb6b54..81c01d15a58 100644
--- a/src/shared/Config/dotconfpp/mempool.h
+++ b/src/shared/Config/dotconfpp/mempool.h
@@ -1,13 +1,17 @@
+
#ifndef ASYNC_DNS_MEMPOOL_H
#define ASYNC_DNS_MEMPOOL_H
+
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
+
#undef free
#undef calloc
#undef strdup
+
class AsyncDNSMemPool
{
private:
@@ -15,23 +19,29 @@ private:
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/DBCFileLoader.cpp b/src/shared/Database/DBCFileLoader.cpp
index adf796fbce6..e7ebeedecfb 100644
--- a/src/shared/Database/DBCFileLoader.cpp
+++ b/src/shared/Database/DBCFileLoader.cpp
@@ -17,42 +17,59 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
+
#include "DBCFileLoader.h"
+
DBCFileLoader::DBCFileLoader()
{
data = NULL;
fieldsOffset = NULL;
}
+
bool DBCFileLoader::Load(const char *filename, const char *fmt)
{
+
uint32 header;
if(data)
{
delete [] data;
data=NULL;
}
+
FILE * f=fopen(filename,"rb");
if(!f)return false;
+
if(fread(&header,4,1,f)!=1) // Number of records
return false;
+
EndianConvert(header);
if(header!=0x43424457)
return false; //'WDBC'
+
if(fread(&recordCount,4,1,f)!=1) // Number of records
return false;
+
EndianConvert(recordCount);
+
if(fread(&fieldCount,4,1,f)!=1) // Number of fields
return false;
+
EndianConvert(fieldCount);
+
if(fread(&recordSize,4,1,f)!=1) // Size of a record
return false;
+
EndianConvert(recordSize);
+
if(fread(&stringSize,4,1,f)!=1) // String size
return false;
+
EndianConvert(stringSize);
+
fieldsOffset = new uint32[fieldCount];
fieldsOffset[0] = 0;
for(uint32 i = 1; i < fieldCount; i++)
@@ -63,13 +80,17 @@ bool DBCFileLoader::Load(const char *filename, const char *fmt)
else // 4 byte fields (int32/float/strings)
fieldsOffset[i] += 4;
}
+
data = new unsigned char[recordSize*recordCount+stringSize];
stringTable = data + recordSize*recordCount;
+
if(fread(data,recordSize*recordCount+stringSize,1,f)!=1)
return false;
+
fclose(f);
return true;
}
+
DBCFileLoader::~DBCFileLoader()
{
if(data)
@@ -77,11 +98,13 @@ DBCFileLoader::~DBCFileLoader()
if(fieldsOffset)
delete [] fieldsOffset;
}
+
DBCFileLoader::Record DBCFileLoader::getRecord(size_t id)
{
assert(data);
return Record(*this, data + id*recordSize);
}
+
uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos)
{
uint32 recordsize = 0;
@@ -107,10 +130,13 @@ uint32 DBCFileLoader::GetFormatRecordSize(const char * format,int32* index_pos)
recordsize += 1;
break;
}
+
if(index_pos)
*index_pos = i;
+
return recordsize;
}
+
char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**& indexTable, uint32 sqlRecordCount, uint32 sqlHighestIndex, char *& sqlDataTable)
{
/*
@@ -120,14 +146,18 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**
float field1,
int field2
}entry;
+
this func will generate entry[rows] data;
*/
+
typedef char * ptr;
if(strlen(format)!=fieldCount)
return NULL;
+
//get struct size and index pos
int32 i;
uint32 recordsize=GetFormatRecordSize(format,&i);
+
if(i>=0)
{
uint32 maxi=0;
@@ -137,9 +167,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**
uint32 ind=getRecord(y).getUInt (i);
if(ind>maxi)maxi=ind;
}
+
// If higher index avalible from sql - use it instead of dbcs
if (sqlHighestIndex > maxi)
maxi = sqlHighestIndex;
+
++maxi;
records=maxi;
indexTable=new ptr[maxi];
@@ -150,8 +182,11 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**
records = recordCount + sqlRecordCount;
indexTable = new ptr[recordCount+ sqlRecordCount];
}
+
char* dataTable= new char[(recordCount + sqlRecordCount)*recordsize];
+
uint32 offset=0;
+
for(uint32 y =0;y<recordCount;++y)
{
if(i>=0)
@@ -160,6 +195,7 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**
}
else
indexTable[y]=&dataTable[offset];
+
for(uint32 x=0;x<fieldCount;x++)
{
switch(format[x])
@@ -185,15 +221,20 @@ char* DBCFileLoader::AutoProduceData(const char* format, uint32& records, char**
}
}
sqlDataTable = dataTable + offset;
+
return dataTable;
}
+
char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable)
{
if(strlen(format)!=fieldCount)
return NULL;
+
char* stringPool= new char[stringSize];
memcpy(stringPool,stringTable,stringSize);
+
uint32 offset=0;
+
for(uint32 y =0;y<recordCount;y++)
{
for(uint32 x=0;x<fieldCount;x++)
@@ -219,6 +260,7 @@ char* DBCFileLoader::AutoProduceStrings(const char* format, char* dataTable)
break;
}
}
+
return stringPool;
}
diff --git a/src/shared/Database/DBCFileLoader.h b/src/shared/Database/DBCFileLoader.h
index fd1f5539ee3..ef29af84bc1 100644
--- a/src/shared/Database/DBCFileLoader.h
+++ b/src/shared/Database/DBCFileLoader.h
@@ -15,11 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DBC_FILE_LOADER_H
#define DBC_FILE_LOADER_H
#include "Platform/Define.h"
#include "Utilities/ByteConverter.h"
#include <cassert>
+
enum
{
FT_NA='x', //not used or unknown, 4 byte size
@@ -34,12 +36,15 @@ enum
FT_SQL_PRESENT='p', //Used in sql format to mark column present in sql dbc
FT_SQL_ABSENT='a' //Used in sql format to mark column absent in sql dbc
};
+
class DBCFileLoader
{
public:
DBCFileLoader();
~DBCFileLoader();
+
bool Load(const char *filename, const char *fmt);
+
class Record
{
public:
@@ -62,6 +67,7 @@ class DBCFileLoader
assert(field < file.fieldCount);
return *reinterpret_cast<uint8*>(offset+file.GetOffset(field));
}
+
const char *getString(size_t field) const
{
assert(field < file.fieldCount);
@@ -69,15 +75,20 @@ class DBCFileLoader
assert(stringOffset < file.stringSize);
return reinterpret_cast<char*>(file.stringTable + stringOffset);
}
+
private:
Record(DBCFileLoader &file_, unsigned char *offset_): offset(offset_), file(file_) {}
unsigned char *offset;
DBCFileLoader &file;
+
friend class DBCFileLoader;
+
};
+
// Get record by id
Record getRecord(size_t id);
/// Get begin iterator over records
+
uint32 GetNumRows() const { return recordCount;}
uint32 GetRowSize() const { return recordSize;}
uint32 GetCols() const { return fieldCount; }
@@ -87,6 +98,7 @@ class DBCFileLoader
char* AutoProduceStrings(const char* fmt, char* dataTable);
static uint32 GetFormatRecordSize(const char * format, int32 * index_pos = NULL);
private:
+
uint32 recordSize;
uint32 recordCount;
uint32 fieldCount;
diff --git a/src/shared/Database/DBCStore.h b/src/shared/Database/DBCStore.h
index 60e533a88e1..e02265ec523 100644
--- a/src/shared/Database/DBCStore.h
+++ b/src/shared/Database/DBCStore.h
@@ -15,10 +15,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DBCSTORE_H
#define DBCSTORE_H
+
#include "DBCFileLoader.h"
#include "Log.h"
+
struct SqlDbc
{
const std::string * formatString;
@@ -38,6 +41,7 @@ struct SqlDbc
else if (sqlTableName[i] == '.')
sqlTableName[i] = '_';
}
+
// Get sql index position
DBCFileLoader::GetFormatRecordSize(fmt, &indexPos);
if (indexPos>=0)
@@ -55,6 +59,7 @@ struct SqlDbc
}
}
};
+
template<class T>
class DBCStorage
{
@@ -62,16 +67,19 @@ class DBCStorage
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, SqlDbc * sql)
{
DBCFileLoader dbc;
// Check if load was sucessful, only then continue
if(!dbc.Load(fn, fmt))
return false;
+
uint32 sqlRecordCount = 0;
uint32 sqlHighestIndex = 0;
Field *fields = NULL;
@@ -83,6 +91,7 @@ class DBCStorage
if (sql->indexPos >= 0)
query +=" ORDER BY + " + *sql->indexName + " DESC";
query += ";";
+
result = WorldDatabase.Query(query.c_str());
if (result)
{
@@ -103,7 +112,9 @@ class DBCStorage
char * sqlDataTable;
fieldCount = dbc.GetCols();
m_dataTable = (T*)dbc.AutoProduceData(fmt,nCount,(char**&)indexTable, sqlRecordCount, sqlHighestIndex, sqlDataTable);
+
m_stringPoolList.push_back(dbc.AutoProduceStrings(fmt,(char*)m_dataTable));
+
// Insert sql data into arrays
if (result)
{
@@ -115,6 +126,7 @@ class DBCStorage
{
if (!fields)
fields = result->Fetch();
+
if(sql->indexPos >= 0)
{
uint32 id = fields[sql->sqlIndexPos].GetUInt32();
@@ -129,6 +141,7 @@ class DBCStorage
indexTable[rowIndex]=(T*)&sqlDataTable[offset];
uint32 columnNumber = 0;
uint32 sqlColumnNumber = 0;
+
for(;columnNumber < sql->formatString->size();++columnNumber)
{
if ((*sql->formatString)[columnNumber] == FT_SQL_ABSENT)
@@ -198,35 +211,44 @@ class DBCStorage
delete result;
return false;
}
+
fields = NULL;
++rowIndex;
}while (result->NextRow());
}
delete result;
}
+
// 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;
+
DBCFileLoader 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();
@@ -234,6 +256,7 @@ class DBCStorage
}
nCount = 0;
}
+
private:
char const* fmt;
uint32 nCount;
@@ -242,4 +265,5 @@ class DBCStorage
T* m_dataTable;
StringPoolList m_stringPoolList;
};
+
#endif
diff --git a/src/shared/Database/Database.cpp b/src/shared/Database/Database.cpp
index 9bfae3479bf..572d3db6f1d 100644
--- a/src/shared/Database/Database.cpp
+++ b/src/shared/Database/Database.cpp
@@ -17,17 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "DatabaseEnv.h"
#include "Config/ConfigEnv.h"
+
#include "Common.h"
#include "../../game/UpdateFields.h"
+
#include <ctime>
#include <iostream>
#include <fstream>
+
Database::~Database()
{
/*Delete objects*/
}
+
bool Database::Initialize(const char *)
{
// Enable logging of SQL commands (usally only GM commands)
@@ -39,37 +44,46 @@ bool Database::Initialize(const char *)
if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\'))
m_logsDir.append("/");
}
+
return true;
}
+
void Database::ThreadStart()
{
}
+
void Database::ThreadEnd()
{
}
+
void Database::escape_string(std::string& str)
{
if(str.empty())
return;
+
char* buf = new char[str.size()*2+1];
escape_string(buf,str.c_str(),str.size());
str = buf;
delete[] buf;
}
+
bool Database::PExecuteLog(const char * format,...)
{
if (!format)
return false;
+
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap );
va_end(ap);
+
if(res==-1)
{
sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
return false;
}
+
if( m_logSQL )
{
time_t curr;
@@ -78,6 +92,7 @@ bool Database::PExecuteLog(const char * format,...)
local=*(localtime(&curr)); // dereference and assign
char fName[128];
sprintf( fName, "%04d-%02d-%02d_logSQL.sql", local.tm_year+1900, local.tm_mon+1, local.tm_mday );
+
FILE* log_file;
std::string logsDir_fname = m_logsDir+fName;
log_file = fopen(logsDir_fname.c_str(), "a");
@@ -92,58 +107,74 @@ bool Database::PExecuteLog(const char * format,...)
sLog.outError("SQL-Logging is disabled - Log file for the SQL commands could not be openend: %s",fName);
}
}
+
return Execute(szQuery);
}
+
void Database::SetResultQueue(SqlResultQueue * queue)
{
m_queryQueues[ACE_Based::Thread::current()] = queue;
+
}
+
QueryResult* Database::PQuery(const char *format,...)
{
if(!format) return NULL;
+
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap );
va_end(ap);
+
if(res==-1)
{
sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
return false;
}
+
return Query(szQuery);
}
+
QueryNamedResult* Database::PQueryNamed(const char *format,...)
{
if(!format) return NULL;
+
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap );
va_end(ap);
+
if(res==-1)
{
sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
return false;
}
+
return QueryNamed(szQuery);
}
+
bool Database::PExecute(const char * format,...)
{
if (!format)
return false;
+
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap );
va_end(ap);
+
if(res==-1)
{
sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
return false;
}
+
return Execute(szQuery);
}
+
bool Database::_UpdateDataBlobValue(const uint32 guid, const uint32 field, const int32 value)
{
return PExecute(
@@ -153,6 +184,7 @@ bool Database::_UpdateDataBlobValue(const uint32 guid, const uint32 field, const
"' ',SUBSTRING_INDEX(`data`,' ',%i)) WHERE guid=%u",
field, field+1, value, -int32(PLAYER_END-field), guid);
}
+
bool Database::_SetDataBlobValue(const uint32 guid, const uint32 field, const uint32 value)
{
return PExecute(
@@ -161,22 +193,27 @@ bool Database::_SetDataBlobValue(const uint32 guid, const uint32 field, const ui
"%u,' ',SUBSTRING_INDEX(`data`,' ',%i)) WHERE guid=%u",
field, value, -int32(PLAYER_END-field), guid);
}
+
bool Database::DirectPExecute(const char * format,...)
{
if (!format)
return false;
+
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap );
va_end(ap);
+
if(res==-1)
{
sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
return false;
}
+
return DirectExecute(szQuery);
}
+
bool Database::CheckRequiredField( char const* table_name, char const* required_name )
{
// check required field
@@ -186,7 +223,9 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_
delete result;
return true;
}
+
// check fail, prepare readabale error message
+
// search current required_* field in DB
QueryNamedResult* result2 = PQueryNamed("SELECT * FROM %s LIMIT 1",table_name);
if(result2)
@@ -201,7 +240,9 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_
break;
}
}
+
delete result2;
+
if(!reqName.empty())
sLog.outErrorDb("Table `%s` have field `%s` but expected `%s`! Not all sql updates applied?",table_name,reqName.c_str(),required_name);
else
@@ -209,5 +250,6 @@ bool Database::CheckRequiredField( char const* table_name, char const* required_
}
else
sLog.outErrorDb("Table `%s` fields list query fail but expected have `%s`! No records in `%s`?",table_name,required_name,table_name);
+
return false;
}
diff --git a/src/shared/Database/Database.h b/src/shared/Database/Database.h
index 34438d994dc..6172a61c5f9 100644
--- a/src/shared/Database/Database.h
+++ b/src/shared/Database/Database.h
@@ -17,35 +17,48 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DATABASE_H
#define DATABASE_H
+
#include "Threading.h"
#include "Utilities/UnorderedMap.h"
#include "Database/SqlDelayThread.h"
+
class SqlTransaction;
class SqlResultQueue;
class SqlQueryHolder;
+
typedef UNORDERED_MAP<ACE_Based::Thread* , SqlTransaction*> TransactionQueues;
typedef UNORDERED_MAP<ACE_Based::Thread* , SqlResultQueue*> QueryQueues;
+
#define MAX_QUERY_LEN 32*1024
+
class TRINITY_DLL_SPEC Database
{
protected:
Database() : m_threadBody(NULL), m_delayThread(NULL) {};
+
TransactionQueues m_tranQueues; ///< Transaction queues from diff. threads
QueryQueues m_queryQueues; ///< Query queues from diff threads
SqlDelayThread* m_threadBody; ///< Pointer to delay sql executer (owned by m_delayThread)
ACE_Based::Thread* m_delayThread; ///< Pointer to executer thread
+
public:
+
virtual ~Database();
+
virtual bool Initialize(const char *infoString);
virtual void InitDelayThread() = 0;
virtual void HaltDelayThread() = 0;
+
virtual QueryResult* Query(const char *sql) = 0;
QueryResult* PQuery(const char *format,...) ATTR_PRINTF(2,3);
virtual QueryNamedResult* QueryNamed(const char *sql) = 0;
QueryNamedResult* PQueryNamed(const char *format,...) ATTR_PRINTF(2,3);
+
/// Async queries and query holders, implemented in DatabaseImpl.h
+
// Query / member
template<class Class>
bool AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql);
@@ -83,14 +96,18 @@ class TRINITY_DLL_SPEC Database
bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder);
template<class Class, typename ParamType1>
bool DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1);
+
virtual bool Execute(const char *sql) = 0;
bool PExecute(const char *format,...) ATTR_PRINTF(2,3);
virtual bool DirectExecute(const char* sql) = 0;
bool DirectPExecute(const char *format,...) ATTR_PRINTF(2,3);
+
bool _UpdateDataBlobValue(const uint32 guid, const uint32 field, const int32 value);
bool _SetDataBlobValue(const uint32 guid, const uint32 field, const uint32 value);
+
// Writes SQL commands to a LOG file (see Trinityd.conf "LogSQL")
bool PExecuteLog(const char *format,...) ATTR_PRINTF(2,3);
+
virtual bool BeginTransaction() // nothing do if DB not support transactions
{
return true;
@@ -103,15 +120,20 @@ class TRINITY_DLL_SPEC Database
{
return false;
}
+
virtual operator bool () const = 0;
+
virtual unsigned long escape_string(char *to, const char *from, unsigned long length) { strncpy(to,from,length); return length; }
void escape_string(std::string& str);
+
// must be called before first query in thread (one time for thread using one from existed Database objects)
virtual void ThreadStart();
// must be called before finish thread run (one time for thread using one from existed Database objects)
virtual void ThreadEnd();
+
// sets the result queue of the current thread, be careful what thread you call this from
void SetResultQueue(SqlResultQueue * queue);
+
bool CheckRequiredField(char const* table_name, char const* required_name);
private:
bool m_logSQL;
diff --git a/src/shared/Database/DatabaseEnv.h b/src/shared/Database/DatabaseEnv.h
index 2e89d74e97c..d5d6867e82f 100644
--- a/src/shared/Database/DatabaseEnv.h
+++ b/src/shared/Database/DatabaseEnv.h
@@ -17,13 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#if !defined(DATABASEENV_H)
#define DATABASEENV_H
+
#include "Common.h"
#include "Log.h"
#include "Errors.h"
+
#include "Database/Field.h"
#include "Database/QueryResult.h"
+
#ifdef DO_POSTGRESQL
#include "Database/QueryResultPostgre.h"
#include "Database/Database.h"
@@ -43,8 +47,10 @@ typedef DatabaseMysql DatabaseType;
#define _CONCAT3_(A,B,C) "CONCAT( " A " , " B " , " C " )"
#define _OFFSET_ "LIMIT %d,1"
#endif
+
extern DatabaseType WorldDatabase;
extern DatabaseType CharacterDatabase;
extern DatabaseType loginDatabase;
+
#endif
diff --git a/src/shared/Database/DatabaseImpl.h b/src/shared/Database/DatabaseImpl.h
index ac856664290..7cbd0ed8ba5 100644
--- a/src/shared/Database/DatabaseImpl.h
+++ b/src/shared/Database/DatabaseImpl.h
@@ -17,9 +17,12 @@
* 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/Database.h"
#include "Database/SqlOperations.h"
+
/// Function body definitions for the template function members of the Database class
+
#define ASYNC_QUERY_BODY(sql, queue_itr) \
if (!sql) return false; \
\
@@ -30,6 +33,7 @@
queue_itr = m_queryQueues.find(queryThread); \
if (queue_itr == m_queryQueues.end()) return false; \
}
+
#define ASYNC_PQUERY_BODY(format, szQuery) \
if(!format) return false; \
\
@@ -48,6 +52,7 @@
return false; \
} \
}
+
#define ASYNC_DELAYHOLDER_BODY(holder, queue_itr) \
if (!holder) return false; \
\
@@ -58,7 +63,9 @@
queue_itr = m_queryQueues.find(queryThread); \
if (queue_itr == m_queryQueues.end()) return false; \
}
+
// -- Query / member --
+
template<class Class>
bool
Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const char *sql)
@@ -66,6 +73,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*), const c
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class>(object, method), itr->second));
}
+
template<class Class, typename ParamType1>
bool
Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql)
@@ -73,6 +81,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1>(object, method, (QueryResult*)NULL, param1), itr->second));
}
+
template<class Class, typename ParamType1, typename ParamType2>
bool
Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql)
@@ -80,6 +89,7 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2>(object, method, (QueryResult*)NULL, param1, param2), itr->second));
}
+
template<class Class, typename ParamType1, typename ParamType2, typename ParamType3>
bool
Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql)
@@ -87,7 +97,9 @@ Database::AsyncQuery(Class *object, void (Class::*method)(QueryResult*, ParamTyp
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::QueryCallback<Class, ParamType1, ParamType2, ParamType3>(object, method, (QueryResult*)NULL, param1, param2, param3), itr->second));
}
+
// -- Query / static --
+
template<typename ParamType1>
bool
Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *sql)
@@ -95,6 +107,7 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1>(method, (QueryResult*)NULL, param1), itr->second));
}
+
template<typename ParamType1, typename ParamType2>
bool
Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *sql)
@@ -102,6 +115,7 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2), Param
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2>(method, (QueryResult*)NULL, param1, param2), itr->second));
}
+
template<typename ParamType1, typename ParamType2, typename ParamType3>
bool
Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *sql)
@@ -109,7 +123,9 @@ Database::AsyncQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamT
ASYNC_QUERY_BODY(sql, itr)
return m_threadBody->Delay(new SqlQuery(sql, new Trinity::SQueryCallback<ParamType1, ParamType2, ParamType3>(method, (QueryResult*)NULL, param1, param2, param3), itr->second));
}
+
// -- PQuery / member --
+
template<class Class>
bool
Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const char *format,...)
@@ -117,6 +133,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*), const
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(object, method, szQuery);
}
+
template<class Class, typename ParamType1>
bool
Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...)
@@ -124,6 +141,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(object, method, param1, szQuery);
}
+
template<class Class, typename ParamType1, typename ParamType2>
bool
Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...)
@@ -131,6 +149,7 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(object, method, param1, param2, szQuery);
}
+
template<class Class, typename ParamType1, typename ParamType2, typename ParamType3>
bool
Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...)
@@ -138,7 +157,9 @@ Database::AsyncPQuery(Class *object, void (Class::*method)(QueryResult*, ParamTy
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(object, method, param1, param2, param3, szQuery);
}
+
// -- PQuery / static --
+
template<typename ParamType1>
bool
Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param1, const char *format,...)
@@ -146,6 +167,7 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1), ParamType1 param
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(method, param1, szQuery);
}
+
template<typename ParamType1, typename ParamType2>
bool
Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2), ParamType1 param1, ParamType2 param2, const char *format,...)
@@ -153,6 +175,7 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2), Para
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(method, param1, param2, szQuery);
}
+
template<typename ParamType1, typename ParamType2, typename ParamType3>
bool
Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2, ParamType3), ParamType1 param1, ParamType2 param2, ParamType3 param3, const char *format,...)
@@ -160,7 +183,9 @@ Database::AsyncPQuery(void (*method)(QueryResult*, ParamType1, ParamType2, Param
ASYNC_PQUERY_BODY(format, szQuery)
return AsyncQuery(method, param1, param2, param3, szQuery);
}
+
// -- QueryHolder --
+
template<class Class>
bool
Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*), SqlQueryHolder *holder)
@@ -168,6 +193,7 @@ Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, Sq
ASYNC_DELAYHOLDER_BODY(holder, itr)
return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*>(object, method, (QueryResult*)NULL, holder), m_threadBody, itr->second);
}
+
template<class Class, typename ParamType1>
bool
Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, SqlQueryHolder*, ParamType1), SqlQueryHolder *holder, ParamType1 param1)
@@ -175,6 +201,7 @@ Database::DelayQueryHolder(Class *object, void (Class::*method)(QueryResult*, Sq
ASYNC_DELAYHOLDER_BODY(holder, itr)
return holder->Execute(new Trinity::QueryCallback<Class, SqlQueryHolder*, ParamType1>(object, method, (QueryResult*)NULL, holder, param1), m_threadBody, itr->second);
}
+
#undef ASYNC_QUERY_BODY
#undef ASYNC_PQUERY_BODY
#undef ASYNC_DELAYHOLDER_BODY
diff --git a/src/shared/Database/DatabaseMysql.cpp b/src/shared/Database/DatabaseMysql.cpp
index 235fb96f127..f08ea67cbbe 100644
--- a/src/shared/Database/DatabaseMysql.cpp
+++ b/src/shared/Database/DatabaseMysql.cpp
@@ -17,7 +17,9 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DO_POSTGRESQL
+
#include "Util.h"
#include "Policies/SingletonImp.h"
#include "Platform/Define.h"
@@ -26,15 +28,19 @@
#include "Database/MySQLDelayThread.h"
#include "Database/SqlOperations.h"
#include "Timer.h"
+
void DatabaseMysql::ThreadStart()
{
mysql_thread_init();
}
+
void DatabaseMysql::ThreadEnd()
{
mysql_thread_end();
}
+
size_t DatabaseMysql::db_count = 0;
+
DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
{
// before first connection
@@ -42,6 +48,7 @@ DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
{
// Mysql Library Init
mysql_library_init(-1, NULL, NULL);
+
if (!mysql_thread_safe())
{
sLog.outError("FATAL ERROR: Used MySQL library isn't thread-safe.");
@@ -49,20 +56,26 @@ DatabaseMysql::DatabaseMysql() : Database(), mMysql(0)
}
}
}
+
DatabaseMysql::~DatabaseMysql()
{
if (m_delayThread)
HaltDelayThread();
+
if (mMysql)
mysql_close(mMysql);
+
//Free Mysql library pointers for last ~DB
if(--db_count == 0)
mysql_library_end();
}
+
bool DatabaseMysql::Initialize(const char *infoString)
{
+
if(!Database::Initialize(infoString))
return false;
+
tranThread = NULL;
MYSQL *mysqlInit;
mysqlInit = mysql_init(NULL);
@@ -71,13 +84,19 @@ bool DatabaseMysql::Initialize(const char *infoString)
sLog.outError( "Could not initialize Mysql connection" );
return false;
}
+
InitDelayThread();
+
Tokens tokens = StrSplit(infoString, ";");
+
Tokens::iterator iter;
+
std::string host, port_or_socket, user, password, database;
int port;
char const* unix_socket;
+
iter = tokens.begin();
+
if(iter != tokens.end())
host = *iter++;
if(iter != tokens.end())
@@ -88,6 +107,7 @@ bool DatabaseMysql::Initialize(const char *infoString)
password = *iter++;
if(iter != tokens.end())
database = *iter++;
+
mysql_options(mysqlInit,MYSQL_SET_CHARSET_NAME,"utf8");
#ifdef WIN32
if(host==".") // named pipe use option (Windows)
@@ -117,14 +137,17 @@ bool DatabaseMysql::Initialize(const char *infoString)
unix_socket = 0;
}
#endif
+
mMysql = mysql_real_connect(mysqlInit, host.c_str(), user.c_str(),
password.c_str(), database.c_str(), port, unix_socket, 0);
+
if (mMysql)
{
sLog.outDetail( "Connected to MySQL database at %s",
host.c_str());
sLog.outString( "MySQL client library: %s", mysql_get_client_info());
sLog.outString( "MySQL server ver: %s ", mysql_get_server_info( mMysql));
+
/*----------SET AUTOCOMMIT ON---------*/
// It seems mysql 5.0.x have enabled this feature
// by default. In crash case you can lose data!!!
@@ -138,10 +161,12 @@ bool DatabaseMysql::Initialize(const char *infoString)
else
sLog.outDetail("AUTOCOMMIT NOT SET TO 1");
/*-------------------------------------*/
+
// set connection properties to UTF8 to properly handle locales for different
// server configs - core sends data in UTF8, so MySQL must expect UTF8 too
PExecute("SET NAMES `utf8`");
PExecute("SET CHARACTER SET `utf8`");
+
#if MYSQL_VERSION_ID >= 50003
my_bool my_true = (my_bool)1;
if (mysql_options(mMysql, MYSQL_OPT_RECONNECT, &my_true))
@@ -155,6 +180,7 @@ bool DatabaseMysql::Initialize(const char *infoString)
#else
#warning "Your mySQL client lib version does not support reconnecting after a timeout.\nIf this causes you any trouble we advice you to upgrade your mySQL client libs to at least mySQL 5.0.13 to resolve this problem."
#endif
+
return true;
}
else
@@ -165,10 +191,12 @@ bool DatabaseMysql::Initialize(const char *infoString)
return false;
}
}
+
bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount)
{
if (!mMysql)
return 0;
+
{
// guarded block for thread-safe mySQL request
ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
@@ -187,54 +215,72 @@ bool DatabaseMysql::_Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **p
sLog.outDebug("[%u ms] SQL: %s", getMSTimeDiff(_s,getMSTime()), sql );
#endif
}
+
*pResult = mysql_store_result(mMysql);
*pRowCount = mysql_affected_rows(mMysql);
*pFieldCount = mysql_field_count(mMysql);
// end guarded block
}
+
if (!*pResult )
return false;
+
if (!*pRowCount)
{
mysql_free_result(*pResult);
return false;
}
+
*pFields = mysql_fetch_fields(*pResult);
return true;
}
+
QueryResult* DatabaseMysql::Query(const char *sql)
{
MYSQL_RES *result = NULL;
MYSQL_FIELD *fields = NULL;
uint64 rowCount = 0;
uint32 fieldCount = 0;
+
if(!_Query(sql,&result,&fields,&rowCount,&fieldCount))
return NULL;
+
QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount);
+
queryResult->NextRow();
+
return queryResult;
}
+
QueryNamedResult* DatabaseMysql::QueryNamed(const char *sql)
{
MYSQL_RES *result = NULL;
MYSQL_FIELD *fields = NULL;
uint64 rowCount = 0;
uint32 fieldCount = 0;
+
if(!_Query(sql,&result,&fields,&rowCount,&fieldCount))
return NULL;
+
QueryFieldNames names(fieldCount);
for (uint32 i = 0; i < fieldCount; i++)
names[i] = fields[i].name;
+
QueryResultMysql *queryResult = new QueryResultMysql(result, fields, rowCount, fieldCount);
+
queryResult->NextRow();
+
return new QueryNamedResult(queryResult,names);
}
+
bool DatabaseMysql::Execute(const char *sql)
{
if (!mMysql)
return false;
+
// don't use queued execution if it has not been initialized
if (!m_threadBody) return DirectExecute(sql);
+
tranThread = ACE_Based::Thread::current(); // owner of this transaction
TransactionQueues::iterator i = m_tranQueues.find(tranThread);
if (i != m_tranQueues.end() && i->second != NULL)
@@ -246,15 +292,19 @@ bool DatabaseMysql::Execute(const char *sql)
// Simple sql statement
m_threadBody->Delay(new SqlStatement(sql));
}
+
return true;
}
+
bool DatabaseMysql::DirectExecute(const char* sql)
{
if (!mMysql)
return false;
+
{
// guarded block for thread-safe mySQL request
ACE_Guard<ACE_Thread_Mutex> query_connection_guard(mMutex);
+
#ifdef MANGOS_DEBUG
uint32 _s = getMSTime();
#endif
@@ -272,8 +322,10 @@ bool DatabaseMysql::DirectExecute(const char* sql)
}
// end guarded block
}
+
return true;
}
+
bool DatabaseMysql::_TransactionCmd(const char *sql)
{
if (mysql_query(mMysql, sql))
@@ -288,15 +340,18 @@ bool DatabaseMysql::_TransactionCmd(const char *sql)
}
return true;
}
+
bool DatabaseMysql::BeginTransaction()
{
if (!mMysql)
return false;
+
// don't use queued execution if it has not been initialized
if (!m_threadBody)
{
if (tranThread == ACE_Based::Thread::current())
return false; // huh? this thread already started transaction
+
mMutex.acquire();
if (!_TransactionCmd("START TRANSACTION"))
{
@@ -305,19 +360,24 @@ bool DatabaseMysql::BeginTransaction()
}
return true; // transaction started
}
+
tranThread = ACE_Based::Thread::current(); // owner of this transaction
TransactionQueues::iterator i = m_tranQueues.find(tranThread);
if (i != m_tranQueues.end() && i->second != NULL)
// If for thread exists queue and also contains transaction
// delete that transaction (not allow trans in trans)
delete i->second;
+
m_tranQueues[tranThread] = new SqlTransaction();
+
return true;
}
+
bool DatabaseMysql::CommitTransaction()
{
if (!mMysql)
return false;
+
// don't use queued execution if it has not been initialized
if (!m_threadBody)
{
@@ -328,6 +388,7 @@ bool DatabaseMysql::CommitTransaction()
mMutex.release();
return _res;
}
+
tranThread = ACE_Based::Thread::current();
TransactionQueues::iterator i = m_tranQueues.find(tranThread);
if (i != m_tranQueues.end() && i->second != NULL)
@@ -339,10 +400,12 @@ bool DatabaseMysql::CommitTransaction()
else
return false;
}
+
bool DatabaseMysql::RollbackTransaction()
{
if (!mMysql)
return false;
+
// don't use queued execution if it has not been initialized
if (!m_threadBody)
{
@@ -353,6 +416,7 @@ bool DatabaseMysql::RollbackTransaction()
mMutex.release();
return _res;
}
+
tranThread = ACE_Based::Thread::current();
TransactionQueues::iterator i = m_tranQueues.find(tranThread);
if (i != m_tranQueues.end() && i->second != NULL)
@@ -362,22 +426,28 @@ bool DatabaseMysql::RollbackTransaction()
}
return true;
}
+
unsigned long DatabaseMysql::escape_string(char *to, const char *from, unsigned long length)
{
if (!mMysql || !to || !from || !length)
return 0;
+
return(mysql_real_escape_string(mMysql, to, from, length));
}
+
void DatabaseMysql::InitDelayThread()
{
assert(!m_delayThread);
+
//New delay thread for delay execute
m_threadBody = new MySQLDelayThread(this); // will deleted at m_delayThread delete
m_delayThread = new ACE_Based::Thread(m_threadBody);
}
+
void DatabaseMysql::HaltDelayThread()
{
if (!m_threadBody || !m_delayThread) return;
+
m_threadBody->Stop(); //Stop event
m_delayThread->wait(); //Wait for flush to DB
delete m_delayThread; //This also deletes m_threadBody
diff --git a/src/shared/Database/DatabaseMysql.h b/src/shared/Database/DatabaseMysql.h
index 3a7fa4f5def..4612ebfc462 100644
--- a/src/shared/Database/DatabaseMysql.h
+++ b/src/shared/Database/DatabaseMysql.h
@@ -17,13 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DO_POSTGRESQL
+
#ifndef _DATABASEMYSQL_H
#define _DATABASEMYSQL_H
+
#include "Database.h"
#include "Policies/Singleton.h"
#include "ace/Thread_Mutex.h"
#include "ace/Guard_T.h"
+
#ifdef WIN32
#define FD_SETSIZE 1024
#include <winsock2.h>
@@ -31,12 +35,15 @@
#else
#include <mysql.h>
#endif
+
class TRINITY_DLL_SPEC DatabaseMysql : public Database
{
friend class Trinity::OperatorNew<DatabaseMysql>;
+
public:
DatabaseMysql();
~DatabaseMysql();
+
//! Initializes Mysql and connects to a server.
/*! infoString should be formated like hostname;username;password;database. */
bool Initialize(const char *infoString);
@@ -49,18 +56,25 @@ class TRINITY_DLL_SPEC DatabaseMysql : public Database
bool BeginTransaction();
bool CommitTransaction();
bool RollbackTransaction();
+
operator bool () const { return mMysql != NULL; }
+
unsigned long escape_string(char *to, const char *from, unsigned long length);
using Database::escape_string;
+
// must be call before first query in thread
void ThreadStart();
// must be call before finish thread run
void ThreadEnd();
private:
ACE_Thread_Mutex mMutex;
+
ACE_Based::Thread * tranThread;
+
MYSQL *mMysql;
+
static size_t db_count;
+
bool _TransactionCmd(const char *sql);
bool _Query(const char *sql, MYSQL_RES **pResult, MYSQL_FIELD **pFields, uint64* pRowCount, uint32* pFieldCount);
};
diff --git a/src/shared/Database/Field.cpp b/src/shared/Database/Field.cpp
index 2467eabd448..9a1fbfa5178 100644
--- a/src/shared/Database/Field.cpp
+++ b/src/shared/Database/Field.cpp
@@ -17,21 +17,28 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "DatabaseEnv.h"
+
Field::Field() :
mValue(NULL), mType(DB_TYPE_UNKNOWN)
{
}
+
Field::Field(Field &f)
{
const char *value;
+
value = f.GetString();
+
if (value && (mValue = new char[strlen(value) + 1]))
strcpy(mValue, value);
else
mValue = NULL;
+
mType = f.GetType();
}
+
Field::Field(const char *value, enum Field::DataTypes type) :
mType(type)
{
@@ -40,13 +47,16 @@ mType(type)
else
mValue = NULL;
}
+
Field::~Field()
{
if(mValue) delete [] mValue;
}
+
void Field::SetValue(const char *value)
{
if(mValue) delete [] mValue;
+
if (value)
{
mValue = new char[strlen(value) + 1];
diff --git a/src/shared/Database/Field.h b/src/shared/Database/Field.h
index fd259423aef..d1238f838a5 100644
--- a/src/shared/Database/Field.h
+++ b/src/shared/Database/Field.h
@@ -17,11 +17,14 @@
* 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(FIELD_H)
#define FIELD_H
+
class Field
{
public:
+
enum DataTypes
{
DB_TYPE_UNKNOWN = 0x00,
@@ -30,11 +33,15 @@ class Field
DB_TYPE_FLOAT = 0x03,
DB_TYPE_BOOL = 0x04
};
+
Field();
Field(Field &f);
Field(const char *value, enum DataTypes type);
+
~Field();
+
enum DataTypes GetType() const { return mType; }
+
const char *GetString() const { return mValue; }
std::string GetCppString() const
{
@@ -69,8 +76,11 @@ class Field
else
return 0;
}
+
void SetType(enum DataTypes type) { mType = type; }
+
void SetValue(const char *value);
+
private:
char *mValue;
enum DataTypes mType;
diff --git a/src/shared/Database/MySQLDelayThread.h b/src/shared/Database/MySQLDelayThread.h
index f8dba08bddc..fcebe3fbd35 100644
--- a/src/shared/Database/MySQLDelayThread.h
+++ b/src/shared/Database/MySQLDelayThread.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef __MYSQLDELAYTHREAD_H
#define __MYSQLDELAYTHREAD_H
+
#include "Database/SqlDelayThread.h"
+
class MySQLDelayThread : public SqlDelayThread
{
public:
diff --git a/src/shared/Database/QueryResult.h b/src/shared/Database/QueryResult.h
index 9d5bb57e4e9..f9f1a009833 100644
--- a/src/shared/Database/QueryResult.h
+++ b/src/shared/Database/QueryResult.h
@@ -17,39 +17,52 @@
* 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(QUERYRESULT_H)
#define QUERYRESULT_H
+
class TRINITY_DLL_SPEC QueryResult
{
public:
QueryResult(uint64 rowCount, uint32 fieldCount)
: mFieldCount(fieldCount), mRowCount(rowCount) {}
+
virtual ~QueryResult() {}
+
virtual bool NextRow() = 0;
+
Field *Fetch() const { return mCurrentRow; }
+
const Field & operator [] (int index) const { return mCurrentRow[index]; }
+
uint32 GetFieldCount() const { return mFieldCount; }
uint64 GetRowCount() const { return mRowCount; }
+
protected:
Field *mCurrentRow;
uint32 mFieldCount;
uint64 mRowCount;
};
+
typedef std::vector<std::string> QueryFieldNames;
+
class MANGOS_DLL_SPEC QueryNamedResult
{
public:
explicit QueryNamedResult(QueryResult* query, QueryFieldNames const& names) : mQuery(query), mFieldNames(names) {}
~QueryNamedResult() { delete mQuery; }
+
// compatible interface with QueryResult
bool NextRow() { return mQuery->NextRow(); }
Field *Fetch() const { return mQuery->Fetch(); }
uint32 GetFieldCount() const { return mQuery->GetFieldCount(); }
uint64 GetRowCount() const { return mQuery->GetRowCount(); }
Field const& operator[] (int index) const { return (*mQuery)[index]; }
+
// named access
Field const& operator[] (const std::string &name) const { return mQuery->Fetch()[GetField_idx(name)]; }
QueryFieldNames const& GetFieldNames() const { return mFieldNames; }
+
uint32 GetField_idx(const std::string &name) const
{
for(size_t idx = 0; idx < mFieldNames.size(); ++idx)
@@ -60,9 +73,11 @@ class MANGOS_DLL_SPEC QueryNamedResult
ASSERT(false && "unknown field name");
return uint32(-1);
}
+
protected:
QueryResult *mQuery;
QueryFieldNames mFieldNames;
};
+
#endif
diff --git a/src/shared/Database/QueryResultMysql.cpp b/src/shared/Database/QueryResultMysql.cpp
index ef8a77ec002..2e4738469c9 100644
--- a/src/shared/Database/QueryResultMysql.cpp
+++ b/src/shared/Database/QueryResultMysql.cpp
@@ -17,35 +17,47 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DO_POSTGRESQL
+
#include "DatabaseEnv.h"
+
QueryResultMysql::QueryResultMysql(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount) :
QueryResult(rowCount, fieldCount), mResult(result)
{
+
mCurrentRow = new Field[mFieldCount];
ASSERT(mCurrentRow);
+
for (uint32 i = 0; i < mFieldCount; i++)
mCurrentRow[i].SetType(ConvertNativeType(fields[i].type));
}
+
QueryResultMysql::~QueryResultMysql()
{
EndQuery();
}
+
bool QueryResultMysql::NextRow()
{
MYSQL_ROW row;
+
if (!mResult)
return false;
+
row = mysql_fetch_row(mResult);
if (!row)
{
EndQuery();
return false;
}
+
for (uint32 i = 0; i < mFieldCount; i++)
mCurrentRow[i].SetValue(row[i]);
+
return true;
}
+
void QueryResultMysql::EndQuery()
{
if (mCurrentRow)
@@ -53,12 +65,14 @@ void QueryResultMysql::EndQuery()
delete [] mCurrentRow;
mCurrentRow = 0;
}
+
if (mResult)
{
mysql_free_result(mResult);
mResult = 0;
}
}
+
enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysqlType) const
{
switch (mysqlType)
@@ -75,6 +89,7 @@ enum Field::DataTypes QueryResultMysql::ConvertNativeType(enum_field_types mysql
case FIELD_TYPE_NULL:
return Field::DB_TYPE_STRING;
case FIELD_TYPE_TINY:
+
case FIELD_TYPE_SHORT:
case FIELD_TYPE_LONG:
case FIELD_TYPE_INT24:
diff --git a/src/shared/Database/QueryResultMysql.h b/src/shared/Database/QueryResultMysql.h
index fb9d422ebf2..89aceb12b13 100644
--- a/src/shared/Database/QueryResultMysql.h
+++ b/src/shared/Database/QueryResultMysql.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef DO_POSTGRESQL
+
#if !defined(QUERYRESULTMYSQL_H)
#define QUERYRESULTMYSQL_H
+
#ifdef WIN32
#define FD_SETSIZE 1024
#include <winsock2.h>
@@ -27,15 +30,20 @@
#else
#include <mysql.h>
#endif
+
class QueryResultMysql : public QueryResult
{
public:
QueryResultMysql(MYSQL_RES *result, MYSQL_FIELD *fields, uint64 rowCount, uint32 fieldCount);
+
~QueryResultMysql();
+
bool NextRow();
+
private:
enum Field::DataTypes ConvertNativeType(enum_field_types mysqlType) const;
void EndQuery();
+
MYSQL_RES *mResult;
};
#endif
diff --git a/src/shared/Database/SQLStorage.cpp b/src/shared/Database/SQLStorage.cpp
index e4de05c7c77..372baafe278 100644
--- a/src/shared/Database/SQLStorage.cpp
+++ b/src/shared/Database/SQLStorage.cpp
@@ -17,13 +17,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "SQLStorage.h"
#include "SQLStorageImpl.h"
+
#ifdef DO_POSTGRESQL
extern DatabasePostgre WorldDatabase;
#else
extern DatabaseMysql WorldDatabase;
#endif
+
const char CreatureInfosrcfmt[]="iiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiliiis";
const char CreatureInfodstfmt[]="iiiiiiiisssiiiiiiiiiiffiffiifiiiiiiiiiiffiiiiiiiiiiiiiiiiiiiiiiiisiiffliiiiiliiii";
const char CreatureDataAddonInfofmt[]="iiiiiiis";
@@ -37,6 +40,7 @@ const char ItemPrototypedstfmt[]="iiiisiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiiii
const char PageTextfmt[]="isi";
const char InstanceTemplatesrcfmt[]="iiiiiiffffs";
const char InstanceTemplatedstfmt[]="iiiiiiffffi";
+
SQLStorage sCreatureStorage(CreatureInfosrcfmt, CreatureInfodstfmt, "entry","creature_template");
SQLStorage sCreatureDataAddonStorage(CreatureDataAddonInfofmt,"guid","creature_addon");
SQLStorage sCreatureModelStorage(CreatureModelfmt,"modelid","creature_model_info");
@@ -46,6 +50,7 @@ SQLStorage sGOStorage(GameObjectInfosrcfmt, GameObjectInfodstfmt, "entry","gameo
SQLStorage sItemStorage(ItemPrototypesrcfmt, ItemPrototypedstfmt, "entry","item_template");
SQLStorage sPageTextStore(PageTextfmt,"entry","page_text");
SQLStorage sInstanceTemplate(InstanceTemplatesrcfmt, InstanceTemplatedstfmt, "map","instance_template");
+
void SQLStorage::Free ()
{
uint32 offset=0;
@@ -55,6 +60,7 @@ void SQLStorage::Free ()
for(uint32 y=0;y<MaxEntry;y++)
if(pIndex[y])
delete [] *(char**)((char*)(pIndex[y])+offset);
+
offset += sizeof(char*);
}
else if (dst_format[x]==FT_LOGIC)
@@ -63,9 +69,11 @@ void SQLStorage::Free ()
offset += sizeof(char);
else
offset += 4;
+
delete [] pIndex;
delete [] data;
}
+
void SQLStorage::Load()
{
SQLStorageLoader loader;
diff --git a/src/shared/Database/SQLStorage.h b/src/shared/Database/SQLStorage.h
index 96f817c64e7..cc165af532e 100644
--- a/src/shared/Database/SQLStorage.h
+++ b/src/shared/Database/SQLStorage.h
@@ -17,21 +17,27 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef SQLSTORAGE_H
#define SQLSTORAGE_H
+
#include "Common.h"
#include "Database/DatabaseEnv.h"
+
class SQLStorage
{
template<class T>
friend struct SQLStorageLoaderBase;
+
public:
+
SQLStorage(const char* fmt, const char * _entry_field, const char * sqlname)
{
src_format = fmt;
dst_format = fmt;
init(_entry_field, sqlname);
}
+
SQLStorage(const char* src_fmt, const char* dst_fmt, const char * _entry_field, const char * sqlname)
{
src_format = src_fmt;
@@ -39,10 +45,12 @@ class SQLStorage
init(_entry_field, sqlname);
}
+
~SQLStorage()
{
Free();
}
+
template<class T>
T const* LookupEntry(uint32 id) const
{
@@ -52,12 +60,16 @@ class SQLStorage
return NULL;
return reinterpret_cast<T const*>(pIndex[id]);
}
+
uint32 RecordCount;
uint32 MaxEntry;
uint32 iNumFields;
+
char const* GetTableName() const { return table; }
+
void Load();
void Free();
+
private:
void init(const char * _entry_field, const char * sqlname)
{
@@ -68,7 +80,9 @@ class SQLStorage
iNumFields = strlen(src_format);
MaxEntry = 0;
}
+
char** pIndex;
+
char *data;
const char *src_format;
const char *dst_format;
@@ -76,11 +90,13 @@ class SQLStorage
const char *entry_field;
//bool HasString;
};
+
template <class T>
struct SQLStorageLoaderBase
{
public:
void Load(SQLStorage &storage);
+
template<class S, class D>
void convert(uint32 field_pos, S src, D &dst);
template<class S>
@@ -88,13 +104,16 @@ struct SQLStorageLoaderBase
template<class D>
void convert_from_str(uint32 field_pos, char * src, D& dst);
void convert_str_to_str(uint32 field_pos, char *src, char *&dst);
+
private:
template<class V>
void storeValue(V value, SQLStorage &store, char *p, int x, uint32 &offset);
void storeValue(char * value, SQLStorage &store, char *p, int x, uint32 &offset);
};
+
struct SQLStorageLoader : public SQLStorageLoaderBase<SQLStorageLoader>
{
};
+
#endif
diff --git a/src/shared/Database/SQLStorageImpl.h b/src/shared/Database/SQLStorageImpl.h
index c229327a0ec..b511bdad68c 100644
--- a/src/shared/Database/SQLStorageImpl.h
+++ b/src/shared/Database/SQLStorageImpl.h
@@ -15,15 +15,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "ProgressBar.h"
#include "Log.h"
#include "DBCFileLoader.h"
+
template<class T>
template<class S, class D>
void SQLStorageLoaderBase<T>::convert(uint32 /*field_pos*/, S src, D &dst)
{
dst = D(src);
}
+
template<class T>
void SQLStorageLoaderBase<T>::convert_str_to_str(uint32 /*field_pos*/, char *src, char *&dst)
{
@@ -39,6 +42,7 @@ void SQLStorageLoaderBase<T>::convert_str_to_str(uint32 /*field_pos*/, char *src
memcpy(dst, src, l);
}
}
+
template<class T>
template<class S>
void SQLStorageLoaderBase<T>::convert_to_str(uint32 /*field_pos*/, S /*src*/, char * & dst)
@@ -46,12 +50,14 @@ void SQLStorageLoaderBase<T>::convert_to_str(uint32 /*field_pos*/, S /*src*/, ch
dst = new char[1];
*dst = 0;
}
+
template<class T>
template<class D>
void SQLStorageLoaderBase<T>::convert_from_str(uint32 /*field_pos*/, char * /*src*/, D& dst)
{
dst = 0;
}
+
template<class T>
template<class V>
void SQLStorageLoaderBase<T>::storeValue(V value, SQLStorage &store, char *p, int x, uint32 &offset)
@@ -81,6 +87,7 @@ void SQLStorageLoaderBase<T>::storeValue(V value, SQLStorage &store, char *p, in
break;
}
}
+
template<class T>
void SQLStorageLoaderBase<T>::storeValue(char * value, SQLStorage &store, char *p, int x, uint32 &offset)
{
@@ -109,6 +116,7 @@ void SQLStorageLoaderBase<T>::storeValue(char * value, SQLStorage &store, char *
break;
}
}
+
template<class T>
void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
{
@@ -120,8 +128,10 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
sLog.outError("Error loading %s table (not exist?)\n", store.table);
exit(1); // Stop server at loading non exited table or not accessable table
}
+
maxi = (*result)[0].GetUInt32()+1;
delete result;
+
result = WorldDatabase.PQuery("SELECT COUNT(*) FROM %s", store.table);
if(result)
{
@@ -131,15 +141,19 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
}
else
store.RecordCount = 0;
+
result = WorldDatabase.PQuery("SELECT * FROM %s", store.table);
+
if(!result)
{
sLog.outError("%s table is empty!\n", store.table);
store.RecordCount = 0;
return;
}
+
uint32 recordsize = 0;
uint32 offset = 0;
+
if(store.iNumFields != result->GetFieldCount())
{
store.RecordCount = 0;
@@ -147,6 +161,7 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
delete result;
exit(1); // Stop server at loading broken or non-compatible table.
}
+
//get struct size
uint32 sc=0;
uint32 bo=0;
@@ -159,8 +174,10 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
else if (store.dst_format[x]==FT_BYTE)
++bb;
recordsize=(store.iNumFields-sc-bo-bb)*4+sc*sizeof(char*)+bo*sizeof(bool)+bb*sizeof(char);
+
char** newIndex=new char*[maxi];
memset(newIndex,0,maxi*sizeof(char*));
+
char * _data= new char[store.RecordCount *recordsize];
uint32 count=0;
barGoLink bar( store.RecordCount );
@@ -170,6 +187,7 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
bar.step();
char *p=(char*)&_data[recordsize*count];
newIndex[fields[0].GetUInt32()]=p;
+
offset=0;
for(uint32 x = 0; x < store.iNumFields; x++)
switch(store.src_format[x])
@@ -187,7 +205,9 @@ void SQLStorageLoaderBase<T>::Load(SQLStorage &store)
}
++count;
}while( result->NextRow() );
+
delete result;
+
store.pIndex = newIndex;
store.MaxEntry = maxi;
store.data = _data;
diff --git a/src/shared/Database/SqlDelayThread.cpp b/src/shared/Database/SqlDelayThread.cpp
index bca8dcd8cd6..88b6b85df70 100644
--- a/src/shared/Database/SqlDelayThread.cpp
+++ b/src/shared/Database/SqlDelayThread.cpp
@@ -17,17 +17,21 @@
* 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/SqlDelayThread.h"
#include "Database/SqlOperations.h"
#include "DatabaseEnv.h"
+
SqlDelayThread::SqlDelayThread(Database* db) : m_dbEngine(db), m_running(true)
{
}
+
void SqlDelayThread::run()
{
#ifndef DO_POSTGRESQL
mysql_thread_init();
#endif
+
while (m_running)
{
// if the running state gets turned off while sleeping
@@ -40,10 +44,12 @@ void SqlDelayThread::run()
delete s;
}
}
+
#ifndef DO_POSTGRESQL
mysql_thread_end();
#endif
}
+
void SqlDelayThread::Stop()
{
m_running = false;
diff --git a/src/shared/Database/SqlDelayThread.h b/src/shared/Database/SqlDelayThread.h
index 422b01ac650..3c24d3525b7 100644
--- a/src/shared/Database/SqlDelayThread.h
+++ b/src/shared/Database/SqlDelayThread.h
@@ -17,26 +17,34 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef __SQLDELAYTHREAD_H
#define __SQLDELAYTHREAD_H
+
#include "ace/Thread_Mutex.h"
#include "LockedQueue.h"
#include "Threading.h"
+
class Database;
class SqlOperation;
+
class SqlDelayThread : public ACE_Based::Runnable
{
typedef ACE_Based::LockedQueue<SqlOperation*, ACE_Thread_Mutex> SqlQueue;
+
private:
SqlQueue m_sqlQueue; ///< Queue of SQL statements
Database* m_dbEngine; ///< Pointer to used Database engine
volatile bool m_running;
+
SqlDelayThread();
public:
SqlDelayThread(Database* db);
+
///< Put sql statement to delay queue
bool Delay(SqlOperation* sql) { m_sqlQueue.add(sql); return true; }
+
virtual void Stop(); ///< Stop event
virtual void run(); ///< Main Thread loop
};
diff --git a/src/shared/Database/SqlOperations.cpp b/src/shared/Database/SqlOperations.cpp
index 9ca698d733e..396f2e36bc2 100644
--- a/src/shared/Database/SqlOperations.cpp
+++ b/src/shared/Database/SqlOperations.cpp
@@ -17,16 +17,20 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "SqlOperations.h"
#include "SqlDelayThread.h"
#include "DatabaseEnv.h"
#include "DatabaseImpl.h"
+
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
+
void SqlStatement::Execute(Database *db)
{
/// just do it
db->DirectExecute(m_sql);
}
+
void SqlTransaction::Execute(Database *db)
{
if(m_queue.empty())
@@ -36,6 +40,7 @@ void SqlTransaction::Execute(Database *db)
{
char const *sql = m_queue.front();
m_queue.pop();
+
if(!db->DirectExecute(sql))
{
free((void*)const_cast<char*>(sql));
@@ -47,11 +52,14 @@ void SqlTransaction::Execute(Database *db)
}
return;
}
+
free((void*)const_cast<char*>(sql));
}
db->DirectExecute("COMMIT");
}
+
/// ---- ASYNC QUERIES ----
+
void SqlQuery::Execute(Database *db)
{
if(!m_callback || !m_queue)
@@ -61,6 +69,7 @@ void SqlQuery::Execute(Database *db)
/// add the callback to the sql result queue of the thread it originated from
m_queue->add(m_callback);
}
+
void SqlResultQueue::Update()
{
/// execute the callbacks waiting in the synchronization queue
@@ -71,16 +80,19 @@ void SqlResultQueue::Update()
delete callback;
}
}
+
bool SqlQueryHolder::Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue)
{
if(!callback || !thread || !queue)
return false;
+
/// delay the execution of the queries, sync them with the delay thread
/// which will in turn resync on execution (via the queue) and call back
SqlQueryHolderEx *holderEx = new SqlQueryHolderEx(this, callback, queue);
thread->Delay(holderEx);
return true;
}
+
bool SqlQueryHolder::SetQuery(size_t index, const char *sql)
{
if(m_queries.size() <= index)
@@ -88,16 +100,19 @@ bool SqlQueryHolder::SetQuery(size_t index, const char *sql)
sLog.outError("Query index (%u) out of range (size: %u) for query: %s",index,(uint32)m_queries.size(),sql);
return false;
}
+
if(m_queries[index].first != NULL)
{
sLog.outError("Attempt assign query to holder index (%u) where other query stored (Old: [%s] New: [%s])",
index,m_queries[index].first,sql);
return false;
}
+
/// not executed yet, just stored (it's not called a holder for nothing)
m_queries[index] = SqlResultPair(strdup(sql), NULL);
return true;
}
+
bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...)
{
if(!format)
@@ -105,18 +120,22 @@ bool SqlQueryHolder::SetPQuery(size_t index, const char *format, ...)
sLog.outError("Query (index: %u) is empty.",index);
return false;
}
+
va_list ap;
char szQuery [MAX_QUERY_LEN];
va_start(ap, format);
int res = vsnprintf( szQuery, MAX_QUERY_LEN, format, ap );
va_end(ap);
+
if(res==-1)
{
sLog.outError("SQL Query truncated (and not execute) for format: %s",format);
return false;
}
+
return SetQuery(index,szQuery);
}
+
QueryResult* SqlQueryHolder::GetResult(size_t index)
{
if(index < m_queries.size())
@@ -133,12 +152,14 @@ QueryResult* SqlQueryHolder::GetResult(size_t index)
else
return NULL;
}
+
void SqlQueryHolder::SetResult(size_t index, QueryResult *result)
{
/// store the result in the holder
if(index < m_queries.size())
m_queries[index].second = result;
}
+
SqlQueryHolder::~SqlQueryHolder()
{
for(size_t i = 0; i < m_queries.size(); i++)
@@ -153,23 +174,28 @@ SqlQueryHolder::~SqlQueryHolder()
}
}
}
+
void SqlQueryHolder::SetSize(size_t size)
{
/// to optimize push_back, reserve the number of queries about to be executed
m_queries.resize(size);
}
+
void SqlQueryHolderEx::Execute(Database *db)
{
if(!m_holder || !m_callback || !m_queue)
return;
+
/// we can do this, we are friends
std::vector<SqlQueryHolder::SqlResultPair> &queries = m_holder->m_queries;
+
for(size_t i = 0; i < queries.size(); i++)
{
/// execute all queries in the holder and pass the results
char const *sql = queries[i].first;
if(sql) m_holder->SetResult(i, db->Query(sql));
}
+
/// sync with the caller thread
m_queue->add(m_callback);
}
diff --git a/src/shared/Database/SqlOperations.h b/src/shared/Database/SqlOperations.h
index 164c7258ec3..e91d83b6611 100644
--- a/src/shared/Database/SqlOperations.h
+++ b/src/shared/Database/SqlOperations.h
@@ -17,16 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef __SQLOPERATIONS_H
#define __SQLOPERATIONS_H
+
#include "Common.h"
+
#include "ace/Thread_Mutex.h"
#include "LockedQueue.h"
#include <queue>
#include "Utilities/Callback.h"
+
/// ---- BASE ---
+
class Database;
class SqlDelayThread;
+
class SqlOperation
{
public:
@@ -34,7 +40,9 @@ class SqlOperation
virtual void Execute(Database *db) = 0;
virtual ~SqlOperation() {}
};
+
/// ---- ASYNC STATEMENTS / TRANSACTIONS ----
+
class SqlStatement : public SqlOperation
{
private:
@@ -44,6 +52,7 @@ class SqlStatement : public SqlOperation
~SqlStatement() { void* tofree = const_cast<char*>(m_sql); free(tofree); }
void Execute(Database *db);
};
+
class SqlTransaction : public SqlOperation
{
private:
@@ -53,18 +62,22 @@ class SqlTransaction : public SqlOperation
void DelayExecute(const char *sql) { m_queue.push(strdup(sql)); }
void Execute(Database *db);
};
+
/// ---- ASYNC QUERIES ----
+
class SqlQuery; /// contains a single async query
class QueryResult; /// the result of one
class SqlResultQueue; /// queue for thread sync
class SqlQueryHolder; /// groups several async quries
class SqlQueryHolderEx; /// points to a holder, added to the delay thread
+
class SqlResultQueue : public ACE_Based::LockedQueue<MaNGOS::IQueryCallback* , ACE_Thread_Mutex>
{
public:
SqlResultQueue() {}
void Update();
};
+
class SqlQuery : public SqlOperation
{
private:
@@ -77,6 +90,7 @@ class SqlQuery : public SqlOperation
~SqlQuery() { void* tofree = const_cast<char*>(m_sql); free(tofree); }
void Execute(Database *db);
};
+
class SqlQueryHolder
{
friend class SqlQueryHolderEx;
@@ -93,6 +107,7 @@ class SqlQueryHolder
void SetResult(size_t index, QueryResult *result);
bool Execute(Trinity::IQueryCallback * callback, SqlDelayThread *thread, SqlResultQueue *queue);
};
+
class SqlQueryHolderEx : public SqlOperation
{
private:
diff --git a/src/shared/Errors.h b/src/shared/Errors.h
index a7784fb7c14..bb17b94cbd0 100644
--- a/src/shared/Errors.h
+++ b/src/shared/Errors.h
@@ -17,17 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_ERRORS_H
#define TRINITYCORE_ERRORS_H
+
#include "Common.h"
+
#if PLATFORM != PLATFORM_WINDOWS
#ifndef HAVE_CONFIG_H
#include <config.h>
#endif
#endif
+
#ifdef HAVE_ACE_STACK_TRACE_H
#include "ace/Stack_Trace.h"
#endif
+
#ifdef HAVE_ACE_STACK_TRACE_H // old versions ACE not have Stack_Trace.h but used at some oS for better compatibility
#define WPAssert( assertion ) { if (!(assertion)) { ACE_Stack_Trace st; fprintf( stderr, "\n%s:%i in %s ASSERTION FAILED:\n %s\n%s\n", __FILE__, __LINE__,__FUNCTION__, #assertion, st.c_str()); assert( #assertion &&0 ); } }
#else
@@ -35,7 +40,9 @@
#endif
#define WPError( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "%\n%s:%i in %s ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( false ); }
#define WPWarning( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "\n%s:%i in %s WARNING:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); }
+
#define WPFatal( assertion, errmsg ) if( ! (assertion) ) { sLog.outError( "\n%s:%i in %s FATAL ERROR:\n %s\n", __FILE__, __LINE__, __FUNCTION__, (char *)errmsg ); assert( #assertion &&0 ); abort(); }
+
#define ASSERT WPAssert
#endif
diff --git a/src/shared/LockedQueue.h b/src/shared/LockedQueue.h
index 7585989fdd0..6543487da81 100644
--- a/src/shared/LockedQueue.h
+++ b/src/shared/LockedQueue.h
@@ -15,13 +15,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef LOCKEDQUEUE_H
#define LOCKEDQUEUE_H
+
#include <ace/Guard_T.h>
#include <ace/Thread_Mutex.h>
#include <deque>
#include <assert.h>
#include "Errors.h"
+
namespace ACE_Based
{
template <class T, class LockType, typename StorageType=std::deque<T> >
@@ -29,41 +32,56 @@ namespace ACE_Based
{
//! Lock access to the queue.
LockType _lock;
+
//! Storage backing the queue.
StorageType _queue;
+
//! Cancellation flag.
volatile bool _canceled;
+
public:
+
//! Create a LockedQueue.
LockedQueue() : _canceled(false) {}
+
//! Destroy a LockedQueue.
virtual ~LockedQueue() { }
+
//! Adds an item to the queue.
void add(const T& item)
{
ACE_Guard<LockType> g(this->_lock);
+
//ASSERT(!this->_canceled);
// throw Cancellation_Exception();
+
_queue.push_back(item);
}
+
//! Gets the next result in the queue, if any.
bool next(T& result)
{
ACE_Guard<LockType> g(this->_lock);
+
if (_queue.empty())
return false;
+
//ASSERT (!_queue.empty() || !this->_canceled);
// throw Cancellation_Exception();
result = _queue.front();
_queue.pop_front();
+
return true;
}
+
//! Cancels the queue.
void cancel()
{
ACE_Guard<LockType> g(this->_lock);
+
_canceled = true;
}
+
//! Checks if the queue is cancelled.
bool cancelled()
{
diff --git a/src/shared/Log.cpp b/src/shared/Log.cpp
index e842949d08e..89544a82426 100644
--- a/src/shared/Log.cpp
+++ b/src/shared/Log.cpp
@@ -17,14 +17,18 @@
* 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 "Policies/SingletonImp.h"
#include "Config/ConfigEnv.h"
#include "Util.h"
+
#include <stdarg.h>
#include <stdio.h>
+
INSTANTIATE_SINGLETON_1( Log );
+
Log::Log() :
raLogfile(NULL), logfile(NULL), gmLogfile(NULL), charLogfile(NULL),
dberLogfile(NULL), chatLogfile(NULL), m_gmlog_per_account(false), m_colored(false)
@@ -32,54 +36,68 @@ Log::Log() :
{
Initialize();
}
+
Log::~Log()
{
if( logfile != NULL )
fclose(logfile);
logfile = NULL;
+
if( gmLogfile != NULL )
fclose(gmLogfile);
gmLogfile = NULL;
+
if (charLogfile != NULL)
fclose(charLogfile);
charLogfile = NULL;
+
if( dberLogfile != NULL )
fclose(dberLogfile);
dberLogfile = NULL;
+
if (raLogfile != NULL)
fclose(raLogfile);
raLogfile = NULL;
+
if (chatLogfile != NULL)
fclose(chatLogfile);
chatLogfile = NULL;
+
if (arenaLogFile != NULL)
fclose(arenaLogFile);
arenaLogFile = NULL;
}
+
void Log::SetLogLevel(char *Level)
{
int32 NewLevel =atoi((char*)Level);
if ( NewLevel <0 )
NewLevel = 0;
m_logLevel = NewLevel;
+
outString( "LogLevel is %u",m_logLevel );
}
+
void Log::SetLogFileLevel(char *Level)
{
int32 NewLevel =atoi((char*)Level);
if ( NewLevel <0 )
NewLevel = 0;
m_logFileLevel = NewLevel;
+
outString( "LogFileLevel is %u",m_logFileLevel );
}
+
void Log::SetDBLogLevel(char *Level)
{
int32 NewLevel = atoi((char*)Level);
if ( NewLevel < 0 )
NewLevel = 0;
m_dbLogLevel = NewLevel;
+
outString( "DBLogLevel is %u",m_dbLogLevel );
}
+
void Log::Initialize()
{
/// Check whether we'll log GM commands/RA events/character outputs/chat stuffs
@@ -87,8 +105,10 @@ void Log::Initialize()
m_dbRA = sConfig.GetBoolDefault("LogDB.RA", false);
m_dbGM = sConfig.GetBoolDefault("LogDB.GM", false);
m_dbChat = sConfig.GetBoolDefault("LogDB.Chat", false);
+
/// Realm must be 0 by default
SetRealmID(0);
+
/// Common log files data
m_logsDir = sConfig.GetStringDefault("LogsDir","");
if(!m_logsDir.empty())
@@ -96,10 +116,13 @@ void Log::Initialize()
if((m_logsDir.at(m_logsDir.length()-1)!='/') && (m_logsDir.at(m_logsDir.length()-1)!='\\'))
m_logsDir.append("/");
}
+
m_logsTimestamp = "_" + GetTimestampStr();
+
/// Open specific log files
logfile = openLogFile("LogFile","LogTimestamp","w");
InitColors(sConfig.GetStringDefault("LogColors", ""));
+
m_gmlog_per_account = sConfig.GetBoolDefault("GmLogPerAccount",false);
if(!m_gmlog_per_account)
gmLogfile = openLogFile("GMLogFile","GmLogTimestamp","a");
@@ -110,32 +133,41 @@ void Log::Initialize()
if(!m_gmlog_filename_format.empty())
{
bool m_gmlog_timestamp = sConfig.GetBoolDefault("GmLogTimestamp",false);
+
size_t dot_pos = m_gmlog_filename_format.find_last_of(".");
if(dot_pos!=m_gmlog_filename_format.npos)
{
if(m_gmlog_timestamp)
m_gmlog_filename_format.insert(dot_pos,m_logsTimestamp);
+
m_gmlog_filename_format.insert(dot_pos,"_#%u");
}
else
{
m_gmlog_filename_format += "_#%u";
+
if(m_gmlog_timestamp)
m_gmlog_filename_format += m_logsTimestamp;
}
+
m_gmlog_filename_format = m_logsDir + m_gmlog_filename_format;
}
}
+
charLogfile = openLogFile("CharLogFile","CharLogTimestamp","a");
+
dberLogfile = openLogFile("DBErrorLogFile",NULL,"a");
raLogfile = openLogFile("RaLogFile",NULL,"a");
chatLogfile = openLogFile("ChatLogFile","ChatLogTimestamp","a");
arenaLogFile = openLogFile("ArenaLogFile",NULL,"a");
+
// Main log file settings
m_logLevel = sConfig.GetIntDefault("LogLevel", LOGL_NORMAL);
m_logFileLevel = sConfig.GetIntDefault("LogFileLevel", LOGL_NORMAL);
m_dbLogLevel = sConfig.GetIntDefault("DBLogLevel", LOGL_NORMAL);
+
m_logFilter = 0;
+
if(sConfig.GetBoolDefault("LogFilter_TransportMoves", true))
m_logFilter |= LOG_FILTER_TRANSPORT_MOVES;
if(sConfig.GetBoolDefault("LogFilter_CreatureMoves", true))
@@ -144,14 +176,18 @@ void Log::Initialize()
m_logFilter |= LOG_FILTER_VISIBILITY_CHANGES;
if(sConfig.GetBoolDefault("LogFilter_AchievementUpdates", true))
m_logFilter |= LOG_FILTER_ACHIEVEMENT_UPDATES;
+
// Char log settings
m_charLog_Dump = sConfig.GetBoolDefault("CharLogDump", false);
+
}
+
FILE* Log::openLogFile(char const* configFileName,char const* configTimeStampFlag, char const* mode)
{
std::string logfn=sConfig.GetStringDefault(configFileName, "");
if(logfn.empty())
return NULL;
+
if(configTimeStampFlag && sConfig.GetBoolDefault(configTimeStampFlag,false))
{
size_t dot_pos = logfn.find_last_of(".");
@@ -160,16 +196,20 @@ FILE* Log::openLogFile(char const* configFileName,char const* configTimeStampFla
else
logfn += m_logsTimestamp;
}
+
return fopen((m_logsDir+logfn).c_str(), mode);
}
+
FILE* Log::openGmlogPerAccount(uint32 account)
{
if(m_gmlog_filename_format.empty())
return NULL;
+
char namebuf[TRINITY_PATH_MAX];
snprintf(namebuf,TRINITY_PATH_MAX,m_gmlog_filename_format.c_str(),account);
return fopen(namebuf, "a");
}
+
void Log::outTimestamp(FILE* file)
{
time_t t = time(NULL);
@@ -182,6 +222,7 @@ void Log::outTimestamp(FILE* file)
// SS seconds (2 digits 00-59)
fprintf(file,"%-4d-%02d-%02d %02d:%02d:%02d ",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec);
}
+
void Log::InitColors(const std::string& str)
{
if(str.empty())
@@ -189,20 +230,28 @@ void Log::InitColors(const std::string& str)
m_colored = false;
return;
}
+
int color[4];
+
std::istringstream ss(str);
+
for(uint8 i = 0; i < LogLevels; ++i)
{
ss >> color[i];
+
if(!ss)
return;
+
if(color[i] < 0 || color[i] >= Colors)
return;
}
+
for(uint8 i = 0; i < LogLevels; ++i)
m_colors[i] = ColorTypes(color[i]);
+
m_colored = true;
}
+
void Log::SetColor(bool stdout_stream, ColorTypes color)
{
#if PLATFORM == PLATFORM_WINDOWS
@@ -230,6 +279,7 @@ void Log::SetColor(bool stdout_stream, ColorTypes color)
// WHITE_BOLD
FOREGROUND_RED | FOREGROUND_GREEN | FOREGROUND_BLUE | FOREGROUND_INTENSITY
};
+
HANDLE hConsole = GetStdHandle(stdout_stream ? STD_OUTPUT_HANDLE : STD_ERROR_HANDLE );
SetConsoleTextAttribute(hConsole, WinColorFG[color]);
#else
@@ -240,16 +290,19 @@ void Log::SetColor(bool stdout_stream, ColorTypes color)
TA_BLINK=5,
TA_REVERSE=7
};
+
enum ANSIFgTextAttr
{
FG_BLACK=30, FG_RED, FG_GREEN, FG_BROWN, FG_BLUE,
FG_MAGENTA, FG_CYAN, FG_WHITE, FG_YELLOW
};
+
enum ANSIBgTextAttr
{
BG_BLACK=40, BG_RED, BG_GREEN, BG_BROWN, BG_BLUE,
BG_MAGENTA, BG_CYAN, BG_WHITE
};
+
static uint8 UnixColorFG[Colors] =
{
FG_BLACK, // BLACK
@@ -268,9 +321,11 @@ void Log::SetColor(bool stdout_stream, ColorTypes color)
FG_CYAN, // LCYAN
FG_WHITE // LWHITE
};
+
fprintf((stdout_stream? stdout : stderr), "\x1b[%d%sm", UnixColorFG[color], (color >= YELLOW && color < Colors ? ";1" : ""));
#endif
}
+
void Log::ResetColor(bool stdout_stream)
{
#if PLATFORM == PLATFORM_WINDOWS
@@ -280,6 +335,7 @@ void Log::ResetColor(bool stdout_stream)
fprintf(( stdout_stream ? stdout : stderr ), "\x1b[0m");
#endif
}
+
std::string Log::GetTimestampStr()
{
time_t t = time(NULL);
@@ -294,27 +350,33 @@ std::string Log::GetTimestampStr()
snprintf(buf,20,"%04d-%02d-%02d_%02d-%02d-%02d",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec);
return std::string(buf);
}
+
void Log::outDB(LogTypes type, const char * str)
{
if (!str || type >= MAX_LOG_TYPES)
return;
+
std::string new_str(str);
if (new_str.empty())
return;
loginDatabase.escape_string(new_str);
+
loginDatabase.PExecute("INSERT INTO logs (time, realm, type, string) "
"VALUES (" UI64FMTD ", %u, %u, '%s');", uint64(time(0)), realm, type, new_str.c_str());
}
+
void Log::outString(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB)
{
// we don't want empty strings in the DB
std::string s(str);
if (s.empty() || s == " ")
return;
+
va_list ap2;
va_start(ap2, str);
char nnew_str[MAX_QUERY_LEN];
@@ -322,14 +384,19 @@ void Log::outString(const char * str, ...)
outDB(LOG_TYPE_STRING, nnew_str);
va_end(ap2);
}
+
if (m_colored)
SetColor(true,m_colors[LOGL_NORMAL]);
+
va_list ap;
+
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(true);
+
printf("\n");
if(logfile)
{
@@ -338,10 +405,12 @@ void Log::outString(const char * str, ...)
vfprintf(logfile, str, ap);
fprintf(logfile, "\n");
va_end(ap);
+
fflush(logfile);
}
fflush(stdout);
}
+
void Log::outString()
{
printf("\n");
@@ -353,10 +422,12 @@ void Log::outString()
}
fflush(stdout);
}
+
void Log::outCrash(const char * err, ...)
{
if (!err)
return;
+
if (m_enableLogDB)
{
va_list ap2;
@@ -366,31 +437,40 @@ void Log::outCrash(const char * err, ...)
outDB(LOG_TYPE_CRASH, nnew_str);
va_end(ap2);
}
+
if (m_colored)
SetColor(false,LRED);
+
va_list ap;
+
va_start(ap, err);
vutf8printf(stdout, err, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(false);
+
fprintf(stderr, "\n");
if (logfile)
{
outTimestamp(logfile);
fprintf(logfile, "CRASH ALERT: ");
+
va_start(ap, err);
vfprintf(logfile, err, ap);
va_end(ap);
+
fprintf(logfile, "\n");
fflush(logfile);
}
fflush(stderr);
}
+
void Log::outError(const char * err, ...)
{
if (!err)
return;
+
if (m_enableLogDB)
{
va_list ap2;
@@ -400,31 +480,40 @@ void Log::outError(const char * err, ...)
outDB(LOG_TYPE_ERROR, nnew_str);
va_end(ap2);
}
+
if (m_colored)
SetColor(false,LRED);
+
va_list ap;
+
va_start(ap, err);
vutf8printf(stderr, err, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(false);
+
fprintf( stderr, "\n");
if (logfile)
{
outTimestamp(logfile);
fprintf(logfile, "ERROR: ");
+
va_start(ap, err);
vfprintf(logfile, err, ap);
va_end(ap);
+
fprintf(logfile, "\n");
fflush(logfile);
}
fflush(stderr);
}
+
void Log::outArena(const char * str, ...)
{
if (!str)
return;
+
if (arenaLogFile)
{
va_list ap;
@@ -437,44 +526,57 @@ void Log::outArena(const char * str, ...)
}
fflush(stdout);
}
+
void Log::outErrorDb(const char * err, ...)
{
if (!err)
return;
+
if (m_colored)
SetColor(false,LRED);
+
va_list ap;
+
va_start(ap, err);
vutf8printf(stderr, err, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(false);
+
fprintf( stderr, "\n" );
+
if (logfile)
{
outTimestamp(logfile);
fprintf(logfile, "ERROR: " );
+
va_start(ap, err);
vfprintf(logfile, err, ap);
va_end(ap);
+
fprintf(logfile, "\n" );
fflush(logfile);
}
+
if (dberLogfile)
{
outTimestamp(dberLogfile);
va_start(ap, err);
vfprintf(dberLogfile, err, ap);
va_end(ap);
+
fprintf(dberLogfile, "\n" );
fflush(dberLogfile);
}
fflush(stderr);
}
+
void Log::outBasic(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB && m_dbLogLevel > LOGL_NORMAL)
{
va_list ap2;
@@ -484,17 +586,22 @@ void Log::outBasic(const char * str, ...)
outDB(LOG_TYPE_BASIC, nnew_str);
va_end(ap2);
}
+
if (m_logLevel > LOGL_NORMAL)
{
if (m_colored)
SetColor(true,m_colors[LOGL_BASIC]);
+
va_list ap;
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(true);
+
printf("\n");
+
if (logfile)
{
outTimestamp(logfile);
@@ -508,10 +615,12 @@ void Log::outBasic(const char * str, ...)
}
fflush(stdout);
}
+
void Log::outDetail(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB && m_dbLogLevel > LOGL_BASIC)
{
va_list ap2;
@@ -521,17 +630,22 @@ void Log::outDetail(const char * str, ...)
outDB(LOG_TYPE_DETAIL, nnew_str);
va_end(ap2);
}
+
if (m_logLevel > LOGL_BASIC)
{
if (m_colored)
SetColor(true,m_colors[LOGL_DETAIL]);
+
va_list ap;
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(true);
+
printf("\n");
+
if (logfile)
{
outTimestamp(logfile);
@@ -539,24 +653,30 @@ void Log::outDetail(const char * str, ...)
va_start(ap, str);
vfprintf(logfile, str, ap);
va_end(ap);
+
fprintf(logfile, "\n");
fflush(logfile);
}
}
+
fflush(stdout);
}
+
void Log::outDebugInLine(const char * str, ...)
{
if (!str)
return;
+
if (m_logLevel > LOGL_DETAIL)
{
va_list ap;
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
//if(m_colored)
// ResetColor(true);
+
if (logfile)
{
va_list ap;
@@ -566,10 +686,12 @@ void Log::outDebugInLine(const char * str, ...)
}
}
}
+
void Log::outDebug(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB && m_dbLogLevel > LOGL_DETAIL)
{
va_list ap2;
@@ -579,17 +701,22 @@ void Log::outDebug(const char * str, ...)
outDB(LOG_TYPE_DEBUG, nnew_str);
va_end(ap2);
}
+
if( m_logLevel > LOGL_DETAIL )
{
if (m_colored)
SetColor(true,m_colors[LOGL_DEBUG]);
+
va_list ap;
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
if(m_colored)
ResetColor(true);
+
printf( "\n" );
+
if (logfile)
{
outTimestamp(logfile);
@@ -597,20 +724,25 @@ void Log::outDebug(const char * str, ...)
va_start(ap, str);
vfprintf(logfile, str, ap);
va_end(ap);
+
fprintf(logfile, "\n" );
fflush(logfile);
}
}
fflush(stdout);
}
+
void Log::outStringInLine(const char * str, ...)
{
if (!str)
return;
+
va_list ap;
+
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
if (logfile)
{
va_start(ap, str);
@@ -618,10 +750,12 @@ void Log::outStringInLine(const char * str, ...)
va_end(ap);
}
}
+
void Log::outCommand(uint32 account, const char * str, ...)
{
if (!str)
return;
+
// TODO: support accountid
if (m_enableLogDB && m_dbGM)
{
@@ -632,17 +766,22 @@ void Log::outCommand(uint32 account, const char * str, ...)
outDB(LOG_TYPE_GM, nnew_str);
va_end(ap2);
}
+
if (m_logLevel > LOGL_NORMAL)
{
if (m_colored)
SetColor(true,m_colors[LOGL_BASIC]);
+
va_list ap;
va_start(ap, str);
vutf8printf(stdout, str, &ap);
va_end(ap);
+
if (m_colored)
ResetColor(true);
+
printf("\n");
+
if (logfile)
{
outTimestamp(logfile);
@@ -654,6 +793,7 @@ void Log::outCommand(uint32 account, const char * str, ...)
fflush(logfile);
}
}
+
if (m_gmlog_per_account)
{
if (FILE* per_file = openGmlogPerAccount (account))
@@ -677,12 +817,15 @@ void Log::outCommand(uint32 account, const char * str, ...)
va_end(ap);
fflush(gmLogfile);
}
+
fflush(stdout);
}
+
void Log::outChar(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB && m_dbChar)
{
va_list ap2;
@@ -692,6 +835,7 @@ void Log::outChar(const char * str, ...)
outDB(LOG_TYPE_CHAR, nnew_str);
va_end(ap2);
}
+
if (charLogfile)
{
outTimestamp(charLogfile);
@@ -703,6 +847,7 @@ void Log::outChar(const char * str, ...)
fflush(charLogfile);
}
}
+
void Log::outCharDump(const char * str, uint32 account_id, uint32 guid, const char * name)
{
if (charLogfile)
@@ -711,10 +856,12 @@ void Log::outCharDump(const char * str, uint32 account_id, uint32 guid, const ch
fflush(charLogfile);
}
}
+
void Log::outRemote(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB && m_dbRA)
{
va_list ap2;
@@ -724,6 +871,7 @@ void Log::outRemote(const char * str, ...)
outDB(LOG_TYPE_RA, nnew_str);
va_end(ap2);
}
+
if (raLogfile)
{
outTimestamp(raLogfile);
@@ -736,10 +884,12 @@ void Log::outRemote(const char * str, ...)
}
fflush(stdout);
}
+
void Log::outChat(const char * str, ...)
{
if (!str)
return;
+
if (m_enableLogDB && m_dbChat)
{
va_list ap2;
@@ -749,6 +899,7 @@ void Log::outChat(const char * str, ...)
outDB(LOG_TYPE_CHAT, nnew_str);
va_end(ap2);
}
+
if (chatLogfile)
{
outTimestamp(chatLogfile);
@@ -761,59 +912,74 @@ void Log::outChat(const char * str, ...)
}
fflush(stdout);
}
+
void outstring_log(const char * str, ...)
{
if (!str)
return;
+
char buf[256];
va_list ap;
va_start(ap, str);
vsnprintf(buf,256, str, ap);
va_end(ap);
+
Trinity::Singleton<Log>::Instance().outString(buf);
}
+
void detail_log(const char * str, ...)
{
if (!str)
return;
+
char buf[256];
va_list ap;
va_start(ap, str);
vsnprintf(buf,256, str, ap);
va_end(ap);
+
Trinity::Singleton<Log>::Instance().outDetail(buf);
}
+
void debug_log(const char * str, ...)
{
if (!str)
return;
+
char buf[256];
va_list ap;
va_start(ap, str);
vsnprintf(buf,256, str, ap);
va_end(ap);
+
Trinity::Singleton<Log>::Instance().outDebug(buf);
}
+
void error_log(const char * str, ...)
{
if (!str)
return;
+
char buf[256];
va_list ap;
va_start(ap, str);
vsnprintf(buf,256, str, ap);
va_end(ap);
+
Trinity::Singleton<Log>::Instance().outError(buf);
}
+
void error_db_log(const char * str, ...)
{
if (!str)
return;
+
char buf[256];
va_list ap;
va_start(ap, str);
vsnprintf(buf,256, str, ap);
va_end(ap);
+
Trinity::Singleton<Log>::Instance().outErrorDb(buf);
}
diff --git a/src/shared/Log.h b/src/shared/Log.h
index ff6f3ad39ee..283b8819a10 100644
--- a/src/shared/Log.h
+++ b/src/shared/Log.h
@@ -17,12 +17,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_LOG_H
#define TRINITYCORE_LOG_H
+
#include "Common.h"
#include "Policies/Singleton.h"
#include "Database/DatabaseEnv.h"
+
class Config;
+
enum LogFilters
{
LOG_FILTER_TRANSPORT_MOVES = 1,
@@ -30,6 +34,7 @@ enum LogFilters
LOG_FILTER_VISIBILITY_CHANGES = 4,
LOG_FILTER_ACHIEVEMENT_UPDATES = 8
};
+
enum LogTypes
{
LOG_TYPE_STRING = 0,
@@ -45,6 +50,7 @@ enum LogTypes
LOG_TYPE_CHAT = 10,
MAX_LOG_TYPES
};
+
enum LogLevel
{
LOGL_NORMAL = 0,
@@ -52,7 +58,9 @@ enum LogLevel
LOGL_DETAIL,
LOGL_DEBUG
};
+
const int LogLevels = int(LOGL_DEBUG)+1;
+
enum ColorTypes
{
BLACK,
@@ -71,17 +79,22 @@ enum ColorTypes
LCYAN,
WHITE
};
+
const int Colors = int(WHITE)+1;
+
class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_Thread_Mutex> >
{
friend class Trinity::OperatorNew<Log>;
Log();
~Log();
+
public:
void Initialize();
+
void InitColors(const std::string& init_str);
void SetColor(bool stdout_stream, ColorTypes color);
void ResetColor(bool stdout_stream);
+
void outDB( LogTypes type, const char * str );
void outString( const char * str, ... ) ATTR_PRINTF(2,3);
void outString( );
@@ -97,17 +110,21 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_
void outCommand( uint32 account, const char * str, ...) ATTR_PRINTF(3,4);
void outRemote( const char * str, ... ) ATTR_PRINTF(2,3);
void outChat( const char * str, ... ) ATTR_PRINTF(2,3);
- void outArena( const char * str, ... ) ATTR_PRINTF(2,3);
+ void outArena( const char * str, ... ) ATTR_PRINTF(2,3);
void outCharDump( const char * str, uint32 account_id, uint32 guid, const char * name );
+
static void outTimestamp(FILE* file);
static std::string GetTimestampStr();
+
void SetLogLevel(char * Level);
void SetLogFileLevel(char * Level);
void SetDBLogLevel(char * Level);
void SetRealmID(uint32 id) { realm = id; }
+
uint32 getLogFilter() const { return m_logFilter; }
bool IsOutDebug() const { return m_logLevel > 2 || (m_logFileLevel > 2 && logfile); }
bool IsOutCharDump() const { return m_charLog_Dump; }
+
bool GetLogDB() { return m_enableLogDB; }
bool GetLogDBLater() { return m_enableLogDBLater; }
void SetLogDB(bool enable) { m_enableLogDB = enable; }
@@ -115,6 +132,7 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_
private:
FILE* openLogFile(char const* configFileName,char const* configTimeStampFlag, char const* mode);
FILE* openGmlogPerAccount(uint32 account);
+
FILE* raLogfile;
FILE* logfile;
FILE* gmLogfile;
@@ -122,18 +140,23 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_
FILE* dberLogfile;
FILE* chatLogfile;
FILE* arenaLogFile;
+
// cache values for after initilization use (like gm log per account case)
std::string m_logsDir;
std::string m_logsTimestamp;
+
// gm log control
bool m_gmlog_per_account;
std::string m_gmlog_filename_format;
+
bool m_enableLogDBLater;
bool m_enableLogDB;
uint32 realm;
+
// log coloring
bool m_colored;
ColorTypes m_colors[4];
+
// log levels:
// 0 minimum/string, 1 basic/error, 2 detail, 3 full/debug
uint8 m_dbLogLevel;
@@ -146,12 +169,15 @@ class Log : public Trinity::Singleton<Log, Trinity::ClassLevelLockable<Log, ACE_
bool m_dbChat;
bool m_charLog_Dump;
};
+
#define sLog Trinity::Singleton<Log>::Instance()
+
#ifdef TRINITY_DEBUG
#define DEBUG_LOG Trinity::Singleton<Log>::Instance().outDebug
#else
#define DEBUG_LOG
#endif
+
// primary for script library
void TRINITY_DLL_SPEC outstring_log(const char * str, ...) ATTR_PRINTF(1,2);
void TRINITY_DLL_SPEC detail_log(const char * str, ...) ATTR_PRINTF(1,2);
diff --git a/src/shared/ProgressBar.cpp b/src/shared/ProgressBar.cpp
index 509e24b4a5b..2eba31c2c40 100644
--- a/src/shared/ProgressBar.cpp
+++ b/src/shared/ProgressBar.cpp
@@ -17,19 +17,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <stdio.h>
+
#include "ProgressBar.h"
+
char const* const barGoLink::empty = " ";
#ifdef _WIN32
char const* const barGoLink::full = "\x3D";
#else
char const* const barGoLink::full = "*";
#endif
+
barGoLink::~barGoLink()
{
printf( "\n" );
fflush(stdout);
}
+
barGoLink::barGoLink( int row_count )
{
rec_no = 0;
@@ -49,9 +54,11 @@ barGoLink::barGoLink( int row_count )
#endif
fflush(stdout);
}
+
void barGoLink::step( void )
{
int i, n;
+
if ( num_rec == 0 ) return;
++rec_no;
n = rec_no * indic_len / num_rec;
@@ -71,6 +78,7 @@ void barGoLink::step( void )
printf( "] %i%% \r[", (int)percent);
#endif
fflush(stdout);
+
rec_pos = n;
}
}
diff --git a/src/shared/ProgressBar.h b/src/shared/ProgressBar.h
index 29cca9b2a31..af7b5e03093 100644
--- a/src/shared/ProgressBar.h
+++ b/src/shared/ProgressBar.h
@@ -19,16 +19,21 @@
*/
#ifndef TRINITYCORE_PROGRESSBAR_H
#define TRINITYCORE_PROGRESSBAR_H
+
#include "Platform/Define.h"
+
class TRINITY_DLL_SPEC barGoLink
{
static char const * const empty;
static char const * const full;
+
int rec_no;
int rec_pos;
int num_rec;
int indic_len;
+
public:
+
void step( void );
barGoLink( int );
~barGoLink();
diff --git a/src/shared/ServiceWin32.cpp b/src/shared/ServiceWin32.cpp
index dc374a4ee41..5c68ba31954 100644
--- a/src/shared/ServiceWin32.cpp
+++ b/src/shared/ServiceWin32.cpp
@@ -17,12 +17,15 @@
* 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 "Common.h"
#include "Log.h"
#include <cstring>
#include <windows.h>
#include <winsvc.h>
+
#if !defined(WINADVAPI)
#if !defined(_ADVAPI32_)
#define WINADVAPI DECLSPEC_IMPORT
@@ -30,19 +33,26 @@
#define WINADVAPI
#endif
#endif
+
extern int main(int argc, char ** argv);
extern char serviceLongName[];
extern char serviceName[];
extern char serviceDescription[];
+
extern int m_ServiceStatus;
+
SERVICE_STATUS serviceStatus;
+
SERVICE_STATUS_HANDLE serviceStatusHandle = 0;
+
typedef WINADVAPI BOOL (WINAPI *CSD_T)(SC_HANDLE, DWORD, LPCVOID);
+
bool WinServiceInstall()
{
CSD_T ChangeService_Config2;
HMODULE advapi32;
SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
+
if (serviceControlManager)
{
char path[_MAX_PATH + 10];
@@ -73,6 +83,7 @@ bool WinServiceInstall()
CloseServiceHandle(serviceControlManager);
return false;
}
+
ChangeService_Config2 = (CSD_T) GetProcAddress(advapi32, "ChangeServiceConfig2A");
if (!ChangeService_Config2)
{
@@ -80,12 +91,14 @@ bool WinServiceInstall()
CloseServiceHandle(serviceControlManager);
return false;
}
+
SERVICE_DESCRIPTION sdBuf;
sdBuf.lpDescription = serviceDescription;
ChangeService_Config2(
service, // handle to service
SERVICE_CONFIG_DESCRIPTION, // change: description
&sdBuf); // new data
+
SC_ACTION _action[1];
_action[0].Type = SC_ACTION_RESTART;
_action[0].Delay = 10000;
@@ -98,16 +111,20 @@ bool WinServiceInstall()
service, // handle to service
SERVICE_CONFIG_FAILURE_ACTIONS, // information level
&sfa); // new data
+
CloseServiceHandle(service);
+
}
}
CloseServiceHandle(serviceControlManager);
}
return true;
}
+
bool WinServiceUninstall()
{
SC_HANDLE serviceControlManager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
+
if (serviceControlManager)
{
SC_HANDLE service = OpenService(serviceControlManager,
@@ -122,32 +139,39 @@ bool WinServiceUninstall()
}
CloseServiceHandle(service);
}
+
CloseServiceHandle(serviceControlManager);
}
return true;
}
+
void WINAPI ServiceControlHandler(DWORD controlCode)
{
switch (controlCode)
{
case SERVICE_CONTROL_INTERROGATE:
break;
+
case SERVICE_CONTROL_SHUTDOWN:
case SERVICE_CONTROL_STOP:
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
+
m_ServiceStatus = 0;
return;
+
case SERVICE_CONTROL_PAUSE:
m_ServiceStatus = 2;
serviceStatus.dwCurrentState = SERVICE_PAUSED;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
break;
+
case SERVICE_CONTROL_CONTINUE:
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
m_ServiceStatus = 1;
break;
+
default:
if ( controlCode >= 128 && controlCode <= 255 )
// user defined control code
@@ -156,8 +180,10 @@ void WINAPI ServiceControlHandler(DWORD controlCode)
// unrecognized control code
break;
}
+
SetServiceStatus(serviceStatusHandle, &serviceStatus);
}
+
void WINAPI ServiceMain(DWORD argc, char *argv[])
{
// initialise service status
@@ -168,42 +194,56 @@ void WINAPI ServiceMain(DWORD argc, char *argv[])
serviceStatus.dwServiceSpecificExitCode = NO_ERROR;
serviceStatus.dwCheckPoint = 0;
serviceStatus.dwWaitHint = 0;
+
serviceStatusHandle = RegisterServiceCtrlHandler(serviceName, ServiceControlHandler);
+
if ( serviceStatusHandle )
{
char path[_MAX_PATH + 1];
unsigned int i, last_slash = 0;
+
GetModuleFileName(0, path, sizeof(path)/sizeof(path[0]));
+
for (i = 0; i < std::strlen(path); i++)
{
if (path[i] == '\\') last_slash = i;
}
+
path[last_slash] = 0;
+
// service is starting
serviceStatus.dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
+
// do initialisation here
SetCurrentDirectory(path);
+
// running
serviceStatus.dwControlsAccepted |= (SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_RUNNING;
SetServiceStatus( serviceStatusHandle, &serviceStatus );
+
////////////////////////
// service main cycle //
////////////////////////
+
m_ServiceStatus = 1;
argc = 1;
main(argc , argv);
+
// service was stopped
serviceStatus.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
+
// do cleanup here
+
// service is now stopped
serviceStatus.dwControlsAccepted &= ~(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN);
serviceStatus.dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(serviceStatusHandle, &serviceStatus);
}
}
+
bool WinServiceRun()
{
SERVICE_TABLE_ENTRY serviceTable[] =
@@ -211,6 +251,7 @@ bool WinServiceRun()
{ serviceName, ServiceMain },
{ 0, 0 }
};
+
if (!StartServiceCtrlDispatcher(serviceTable))
{
sLog.outError("StartService Failed. Error [%u]", ::GetLastError());
diff --git a/src/shared/ServiceWin32.h b/src/shared/ServiceWin32.h
index a62526f4c70..18a52c396ea 100644
--- a/src/shared/ServiceWin32.h
+++ b/src/shared/ServiceWin32.h
@@ -17,12 +17,15 @@
* 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
#ifndef _WIN32_SERVICE_
#define _WIN32_SERVICE_
+
bool WinServiceInstall();
bool WinServiceUninstall();
bool WinServiceRun();
+
#endif // _WIN32_SERVICE_
#endif // WIN32
diff --git a/src/shared/SystemConfig.h b/src/shared/SystemConfig.h
index 0c13cd286ec..9d154b863d0 100644
--- a/src/shared/SystemConfig.h
+++ b/src/shared/SystemConfig.h
@@ -18,19 +18,25 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
// THIS FILE IS DEPRECATED
+
#ifndef TRINITY_SYSTEMCONFIG_H
#define TRINITY_SYSTEMCONFIG_H
+
#include "Platform/Define.h"
#include "revision.h"
+
#define _PACKAGENAME "TrinityCore2 "
#define _CODENAME "YUME"
+
#if TRINITY_ENDIAN == TRINITY_BIGENDIAN
# define _ENDIAN_STRING "big-endian"
#else
# define _ENDIAN_STRING "little-endian"
#endif
+
#if PLATFORM == PLATFORM_WINDOWS
# ifdef _WIN64
# define _FULLVERSION _PACKAGENAME "Rev: " _REVISION " Hash: " _HASH " (Win64," _ENDIAN_STRING ")"
@@ -40,6 +46,7 @@
#else
# define _FULLVERSION _PACKAGENAME "Rev: " _REVISION " Hash: " _HASH " (Unix," _ENDIAN_STRING ")"
#endif
+
#define DEFAULT_PLAYER_LIMIT 100
#define DEFAULT_WORLDSERVER_PORT 8085 //8129
#define DEFAULT_REALMSERVER_PORT 3724
diff --git a/src/shared/Threading.cpp b/src/shared/Threading.cpp
index e0c605ee9b4..90861b8f425 100644
--- a/src/shared/Threading.cpp
+++ b/src/shared/Threading.cpp
@@ -15,27 +15,36 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "Threading.h"
#include "Errors.h"
#include <ace/OS_NS_unistd.h>
#include <ace/Sched_Params.h>
#include <vector>
+
using namespace ACE_Based;
+
ThreadPriority::ThreadPriority()
{
for (int i = Idle; i < MAXPRIORITYNUM; ++i)
m_priority[i] = ACE_THR_PRI_OTHER_DEF;
+
m_priority[Idle] = ACE_Sched_Params::priority_min(ACE_SCHED_OTHER);
m_priority[Realtime] = ACE_Sched_Params::priority_max(ACE_SCHED_OTHER);
+
std::vector<int> _tmp;
+
ACE_Sched_Params::Policy _policy = ACE_SCHED_OTHER;
ACE_Sched_Priority_Iterator pr_iter(_policy);
+
while (pr_iter.more())
{
_tmp.push_back(pr_iter.priority());
pr_iter.next();
}
+
ASSERT (!_tmp.empty());
+
if(_tmp.size() >= MAXPRIORITYNUM)
{
const size_t max_pos = _tmp.size();
@@ -49,6 +58,7 @@ ThreadPriority::ThreadPriority()
break;
}
}
+
//since we have only 7(seven) values in enum Priority
//and 3 we know already (Idle, Normal, Realtime) so
//we need to split each list [Idle...Normal] and [Normal...Realtime]
@@ -57,103 +67,140 @@ ThreadPriority::ThreadPriority()
size_t _div = (norm_pos - min_pos) / _divider;
if(_div == 0)
_div = 1;
+
min_pos = (norm_pos - 1);
+
m_priority[Low] = _tmp[min_pos -= _div];
m_priority[Lowest] = _tmp[min_pos -= _div ];
+
_div = (max_pos - norm_pos) / _divider;
if(_div == 0)
_div = 1;
+
min_pos = norm_pos - 1;
+
m_priority[High] = _tmp[min_pos += _div];
m_priority[Highest] = _tmp[min_pos += _div];
}
}
+
int ThreadPriority::getPriority(Priority p) const
{
if(p < Idle)
p = Idle;
+
if(p > Realtime)
p = Realtime;
+
return m_priority[p];
}
+
#define THREADFLAG (THR_NEW_LWP | THR_SCHED_DEFAULT| THR_JOINABLE)
+
Thread::Thread() : m_task(0), m_iThreadId(0), m_hThreadHandle(0)
{
+
}
+
Thread::Thread(Runnable* instance) : m_task(instance), m_iThreadId(0), m_hThreadHandle(0)
{
// register reference to m_task to prevent it deeltion until destructor
if (m_task)
m_task->incReference();
+
bool _start = start();
ASSERT (_start);
}
+
Thread::~Thread()
{
//Wait();
+
// deleted runnable object (if no other references)
if (m_task)
m_task->decReference();
}
+
//initialize Thread's class static member
Thread::ThreadStorage Thread::m_ThreadStorage;
ThreadPriority Thread::m_TpEnum;
+
bool Thread::start()
{
if (m_task == 0 || m_iThreadId != 0)
return false;
+
bool res = (ACE_Thread::spawn(&Thread::ThreadTask, (void*)m_task, THREADFLAG, &m_iThreadId, &m_hThreadHandle) == 0);
+
if (res)
m_task->incReference();
+
return res;
}
+
bool Thread::wait()
{
if (!m_hThreadHandle || !m_task)
return false;
+
ACE_THR_FUNC_RETURN _value = ACE_THR_FUNC_RETURN(-1);
int _res = ACE_Thread::join(m_hThreadHandle, &_value);
+
m_iThreadId = 0;
m_hThreadHandle = 0;
+
return (_res == 0);
}
+
void Thread::destroy()
{
if (!m_iThreadId || !m_task)
return;
+
if (ACE_Thread::kill(m_iThreadId, -1) != 0)
return;
+
m_iThreadId = 0;
m_hThreadHandle = 0;
+
// reference set at ACE_Thread::spawn
m_task->decReference();
}
+
void Thread::suspend()
{
ACE_Thread::suspend(m_hThreadHandle);
}
+
void Thread::resume()
{
ACE_Thread::resume(m_hThreadHandle);
}
+
ACE_THR_FUNC_RETURN Thread::ThreadTask(void * param)
{
Runnable * _task = (Runnable*)param;
_task->run();
- // task execution complete, free referecne added at
+
+ // task execution complete, free referecne added at
_task->decReference();
+
return (ACE_THR_FUNC_RETURN)0;
}
+
ACE_thread_t Thread::currentId()
{
return ACE_Thread::self();
}
+
ACE_hthread_t Thread::currentHandle()
{
ACE_hthread_t _handle;
ACE_Thread::self(_handle);
+
return _handle;
}
+
Thread * Thread::current()
{
Thread * _thread = m_ThreadStorage.ts_object();
@@ -162,12 +209,15 @@ Thread * Thread::current()
_thread = new Thread();
_thread->m_iThreadId = Thread::currentId();
_thread->m_hThreadHandle = Thread::currentHandle();
+
Thread * _oldValue = m_ThreadStorage.ts_object(_thread);
if(_oldValue)
delete _oldValue;
}
+
return _thread;
}
+
void Thread::setPriority(Priority type)
{
int _priority = m_TpEnum.getPriority(type);
@@ -175,6 +225,7 @@ void Thread::setPriority(Priority type)
//remove this ASSERT in case you don't want to know is thread priority change was successful or not
ASSERT (_ok == 0);
}
+
void Thread::Sleep(unsigned long msecs)
{
ACE_OS::sleep(ACE_Time_Value(0, 1000 * msecs));
diff --git a/src/shared/Threading.h b/src/shared/Threading.h
index 36d35ff9f79..574b9641aac 100644
--- a/src/shared/Threading.h
+++ b/src/shared/Threading.h
@@ -15,19 +15,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef THREADING_H
#define THREADING_H
+
#include <ace/Thread.h>
#include <ace/TSS_T.h>
#include "ace/Atomic_Op.h"
#include <assert.h>
+
namespace ACE_Based
{
+
class Runnable
{
public:
virtual ~Runnable() {}
virtual void run() = 0;
+
void incReference() { ++m_refs; }
void decReference()
{
@@ -37,6 +42,7 @@ namespace ACE_Based
private:
ACE_Atomic_Op<ACE_Thread_Mutex, int> m_refs;
};
+
enum Priority
{
Idle,
@@ -47,43 +53,56 @@ namespace ACE_Based
Highest,
Realtime,
};
+
#define MAXPRIORITYNUM (Realtime + 1)
+
class ThreadPriority
{
public:
ThreadPriority();
int getPriority(Priority p) const;
+
private:
int m_priority[MAXPRIORITYNUM];
};
+
class Thread
{
public:
Thread();
explicit Thread(Runnable* instance);
~Thread();
+
bool start();
bool wait();
void destroy();
+
void suspend();
void resume();
+
void setPriority(Priority type);
+
static void Sleep(unsigned long msecs);
static ACE_thread_t currentId();
static ACE_hthread_t currentHandle();
static Thread * current();
+
private:
Thread(const Thread&);
Thread& operator=(const Thread&);
+
static ACE_THR_FUNC_RETURN ThreadTask(void * param);
+
ACE_thread_t m_iThreadId;
ACE_hthread_t m_hThreadHandle;
Runnable * m_task;
+
typedef ACE_TSS<Thread> ThreadStorage;
//global object - container for Thread class representation of every thread
static ThreadStorage m_ThreadStorage;
//use this object to determine current OS thread priority values mapped to enum Priority{}
static ThreadPriority m_TpEnum;
};
+
}
#endif
diff --git a/src/shared/Timer.h b/src/shared/Timer.h
index 03a379b2471..82f5be161d9 100644
--- a/src/shared/Timer.h
+++ b/src/shared/Timer.h
@@ -17,9 +17,12 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITY_TIMER_H
#define TRINITY_TIMER_H
+
#include "Platform/CompilerDefs.h"
+
#if PLATFORM == PLATFORM_WINDOWS
# include <ace/config-all.h>
# include <mmsystem.h>
@@ -31,6 +34,7 @@
# include <sys/time.h>
# include <sys/timeb.h>
#endif
+
#if PLATFORM == PLATFORM_WINDOWS
inline uint32 getMSTime() { return GetTickCount(); }
#else
@@ -42,6 +46,7 @@ inline uint32 getMSTime()
return (tv.tv_sec * 1000) + (tv.tv_usec / 1000);
}
#endif
+
inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
{
// getMSTime() have limited data range and this is case when it overflow in this tick
@@ -50,21 +55,26 @@ inline uint32 getMSTimeDiff(uint32 oldMSTime, uint32 newMSTime)
else
return newMSTime - oldMSTime;
}
+
class IntervalTimer
{
public:
IntervalTimer() : _interval(0), _current(0) {}
+
void Update(time_t diff) { _current += diff; if(_current<0) _current=0;}
bool Passed() { return _current >= _interval; }
void Reset() { if(_current >= _interval) _current -= _interval; }
+
void SetCurrent(time_t current) { _current = current; }
void SetInterval(time_t interval) { _interval = interval; }
time_t GetInterval() const { return _interval; }
time_t GetCurrent() const { return _current; }
+
private:
time_t _interval;
time_t _current;
};
+
struct TimeTracker
{
TimeTracker(time_t expiry) : i_expiryTime(expiry) {}
@@ -74,6 +84,7 @@ struct TimeTracker
time_t GetExpiry(void) const { return i_expiryTime; }
time_t i_expiryTime;
};
+
struct TimeTrackerSmall
{
TimeTrackerSmall(int32 expiry) : i_expiryTime(expiry) {}
@@ -83,5 +94,6 @@ struct TimeTrackerSmall
int32 GetExpiry(void) const { return i_expiryTime; }
int32 i_expiryTime;
};
+
#endif
diff --git a/src/shared/Util.cpp b/src/shared/Util.cpp
index 0f7386e4d33..ede7a27ea7b 100644
--- a/src/shared/Util.cpp
+++ b/src/shared/Util.cpp
@@ -17,14 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "Util.h"
+
#include "sockets/socket_include.h"
#include "utf8cpp/utf8.h"
#include "mersennetwister/MersenneTwister.h"
#include <ace/TSS_T.h>
+
typedef ACE_TSS<MTRand> MTRandTSS;
static MTRandTSS mtRand;
+
#ifdef MULTI_THREAD_MAP
+
int32 irand (int32 min, int32 max)
{
int32 result;
@@ -34,6 +39,7 @@ int32 irand (int32 min, int32 max)
}
return result;
}
+
uint32 urand (uint32 min, uint32 max)
{
uint32 result;
@@ -43,6 +49,7 @@ uint32 urand (uint32 min, uint32 max)
}
return result;
}
+
int32 rand32 ()
{
int32 result;
@@ -52,6 +59,7 @@ int32 rand32 ()
}
return result;
}
+
double rand_norm(void)
{
double result;
@@ -61,6 +69,7 @@ double rand_norm(void)
}
return result;
}
+
double rand_chance (void)
{
double result;
@@ -70,28 +79,36 @@ double rand_chance (void)
}
return result;
}
+
#else
+
int32 irand (int32 min, int32 max)
{
return int32 (mtRand->randInt (max - min)) + min;
}
+
uint32 urand (uint32 min, uint32 max)
{
return mtRand->randInt (max - min) + min;
}
+
int32 rand32 ()
{
return mtRand->randInt ();
}
+
double rand_norm(void)
{
return mtRand->randExc ();
}
+
double rand_chance (void)
{
return mtRand->randExc (100.0);
}
+
#endif
+
Tokens StrSplit(const std::string &src, const std::string &sep)
{
Tokens r;
@@ -111,10 +128,13 @@ Tokens StrSplit(const std::string &src, const std::string &sep)
if (s.length()) r.push_back(s);
return r;
}
+
void stripLineInvisibleChars(std::string &str)
{
static std::string invChars = " \t\7\n";
+
size_t wpos = 0;
+
bool space = false;
for(size_t pos = 0; pos < str.size(); ++pos)
{
@@ -135,17 +155,21 @@ void stripLineInvisibleChars(std::string &str)
space = false;
}
}
+
if(wpos < str.size())
str.erase(wpos,str.size());
if(str.find("|TInterface")!=std::string::npos)
str.clear();
+
}
+
std::string secsToTimeString(uint32 timeInSecs, bool shortText, bool hoursOnly)
{
uint32 secs = timeInSecs % MINUTE;
uint32 minutes = timeInSecs % HOUR / MINUTE;
uint32 hours = timeInSecs % DAY / HOUR;
uint32 days = timeInSecs / DAY;
+
std::ostringstream ss;
if(days)
ss << days << (shortText ? "d" : " Day(s) ");
@@ -158,13 +182,16 @@ std::string secsToTimeString(uint32 timeInSecs, bool shortText, bool hoursOnly)
if(secs || (!days && !hours && !minutes) )
ss << secs << (shortText ? "s" : " Second(s).");
}
+
return ss.str();
}
+
uint32 TimeStringToSecs(const std::string& timestring)
{
uint32 secs = 0;
uint32 buffer = 0;
uint32 multiplier = 0;
+
for(std::string::const_iterator itr = timestring.begin(); itr != timestring.end(); itr++ )
{
if(isdigit(*itr))
@@ -187,8 +214,10 @@ uint32 TimeStringToSecs(const std::string& timestring)
buffer=0;
}
}
+
return secs;
}
+
std::string TimeToTimestampStr(time_t t)
{
tm* aTm = localtime(&t);
@@ -202,30 +231,37 @@ std::string TimeToTimestampStr(time_t t)
snprintf(buf,20,"%04d-%02d-%02d_%02d-%02d-%02d",aTm->tm_year+1900,aTm->tm_mon+1,aTm->tm_mday,aTm->tm_hour,aTm->tm_min,aTm->tm_sec);
return std::string(buf);
}
+
/// Check if the string is a valid ip address representation
bool IsIPAddress(char const* ipaddress)
{
if(!ipaddress)
return false;
+
// Let the big boys do it.
// Drawback: all valid ip address formats are recognized e.g.: 12.23,121234,0xABCD)
return inet_addr(ipaddress) != INADDR_NONE;
}
+
/// create PID file
uint32 CreatePIDFile(const std::string& filename)
{
FILE * pid_file = fopen (filename.c_str(), "w" );
if (pid_file == NULL)
return 0;
+
#ifdef WIN32
DWORD pid = GetCurrentProcessId();
#else
pid_t pid = getpid();
#endif
+
fprintf(pid_file, "%d", pid );
fclose(pid_file);
+
return (uint32)pid;
}
+
size_t utf8length(std::string& utf8str)
{
try
@@ -238,6 +274,7 @@ size_t utf8length(std::string& utf8str)
return 0;
}
}
+
void utf8truncate(std::string& utf8str,size_t len)
{
try
@@ -245,6 +282,7 @@ void utf8truncate(std::string& utf8str,size_t len)
size_t wlen = utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size());
if(wlen <= len)
return;
+
std::wstring wstr;
wstr.resize(wlen);
utf8::utf8to16(utf8str.c_str(),utf8str.c_str()+utf8str.size(),&wstr[0]);
@@ -257,6 +295,7 @@ void utf8truncate(std::string& utf8str,size_t len)
utf8str = "";
}
}
+
bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize)
{
try
@@ -269,6 +308,7 @@ bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize)
wsize = 0;
return false;
}
+
wsize = len;
utf8::utf8to16(utf8str,utf8str+csize,wstr);
wstr[len] = L'\0';
@@ -280,14 +320,17 @@ bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize)
wsize = 0;
return false;
}
+
return true;
}
+
bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr)
{
try
{
size_t len = utf8::distance(utf8str.c_str(),utf8str.c_str()+utf8str.size());
wstr.resize(len);
+
utf8::utf8to16(utf8str.c_str(),utf8str.c_str()+utf8str.size(),&wstr[0]);
}
catch(std::exception)
@@ -295,14 +338,17 @@ bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr)
wstr = L"";
return false;
}
+
return true;
}
+
bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str)
{
try
{
std::string utf8str2;
utf8str2.resize(size*4); // allocate for most long case
+
char* oend = utf8::utf16to8(wstr,wstr+size,&utf8str2[0]);
utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail
utf8str = utf8str2;
@@ -312,14 +358,17 @@ bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str)
utf8str = "";
return false;
}
+
return true;
}
+
bool WStrToUtf8(std::wstring wstr, std::string& utf8str)
{
try
{
std::string utf8str2;
utf8str2.resize(wstr.size()*4); // allocate for most long case
+
char* oend = utf8::utf16to8(wstr.c_str(),wstr.c_str()+wstr.size(),&utf8str2[0]);
utf8str2.resize(oend-(&utf8str2[0])); // remove unused tail
utf8str = utf8str2;
@@ -329,15 +378,20 @@ bool WStrToUtf8(std::wstring wstr, std::string& utf8str)
utf8str = "";
return false;
}
+
return true;
}
+
typedef wchar_t const* const* wstrlist;
+
std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
{
// supported only Cyrillic cases
if(wname.size() < 1 || !isCyrillicCharacter(wname[0]) || declension > 5)
return wname;
+
// Important: end length must be <= MAX_INTERNAL_PLAYER_NAME-MAX_PLAYER_NAME (3 currently)
+
static wchar_t const a_End[] = { wchar_t(1), wchar_t(0x0430),wchar_t(0x0000)};
static wchar_t const o_End[] = { wchar_t(1), wchar_t(0x043E),wchar_t(0x0000)};
static wchar_t const ya_End[] = { wchar_t(1), wchar_t(0x044F),wchar_t(0x0000)};
@@ -354,6 +408,7 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
static wchar_t const ie_m_End[] = { wchar_t(2), wchar_t(0x0435),wchar_t(0x043C),wchar_t(0x0000)};
static wchar_t const soft_End[] = { wchar_t(1), wchar_t(0x044C),wchar_t(0x0000)};
static wchar_t const j_End[] = { wchar_t(1), wchar_t(0x0439),wchar_t(0x0000)};
+
static wchar_t const* const dropEnds[6][8] = {
{ &a_End[1], &o_End[1], &ya_End[1], &ie_End[1], &soft_End[1], &j_End[1], NULL, NULL },
{ &a_End[1], &ya_End[1], &yeru_End[1], &i_End[1], NULL, NULL, NULL, NULL },
@@ -362,34 +417,42 @@ std::wstring GetMainPartOfName(std::wstring wname, uint32 declension)
{ &oj_End[1], &io_j_End[1], &ie_j_End[1], &o_m_End[1], &io_m_End[1], &ie_m_End[1], &yu_End[1], NULL },
{ &ie_End[1], &i_End[1], NULL, NULL, NULL, NULL, NULL, NULL }
};
+
for(wchar_t const * const* itr = &dropEnds[declension][0]; *itr; ++itr)
{
size_t len = size_t((*itr)[-1]); // get length from string size field
+
if(wname.substr(wname.size()-len,len)==*itr)
return wname.substr(0,wname.size()-len);
}
+
return wname;
}
+
bool utf8ToConsole(const std::string& utf8str, std::string& conStr)
{
#if PLATFORM == PLATFORM_WINDOWS
std::wstring wstr;
if(!Utf8toWStr(utf8str,wstr))
return false;
+
conStr.resize(wstr.size());
CharToOemBuffW(&wstr[0],&conStr[0],wstr.size());
#else
// not implemented yet
conStr = utf8str;
#endif
+
return true;
}
+
bool consoleToUtf8(const std::string& conStr,std::string& utf8str)
{
#if PLATFORM == PLATFORM_WINDOWS
std::wstring wstr;
wstr.resize(conStr.size());
OemToCharBuffW(&conStr[0],&wstr[0],conStr.size());
+
return WStrToUtf8(wstr,utf8str);
#else
// not implemented yet
@@ -397,17 +460,23 @@ bool consoleToUtf8(const std::string& conStr,std::string& utf8str)
return true;
#endif
}
+
bool Utf8FitTo(const std::string& str, std::wstring search)
{
std::wstring temp;
+
if(!Utf8toWStr(str,temp))
return false;
+
// converting to lower case
wstrToLower( temp );
+
if(temp.find(search) == std::wstring::npos)
return false;
+
return true;
}
+
void utf8printf(FILE *out, const char *str, ...)
{
va_list ap;
@@ -415,20 +484,25 @@ void utf8printf(FILE *out, const char *str, ...)
vutf8printf(stdout, str, &ap);
va_end(ap);
}
+
void vutf8printf(FILE *out, const char *str, va_list* ap)
{
#if PLATFORM == PLATFORM_WINDOWS
char temp_buf[32*1024];
wchar_t wtemp_buf[32*1024];
+
size_t temp_len = vsnprintf(temp_buf, 32*1024, str, *ap);
+
size_t wtemp_len = 32*1024-1;
Utf8toWStr(temp_buf, temp_len, wtemp_buf, wtemp_len);
+
CharToOemBuffW(&wtemp_buf[0], &temp_buf[0], wtemp_len+1);
fprintf(out, temp_buf);
#else
vfprintf(out, str, *ap);
#endif
}
+
void hexEncodeByteArray(uint8* bytes, uint32 arrayLen, std::string& result)
{
std::ostringstream ss;
diff --git a/src/shared/Util.h b/src/shared/Util.h
index 7a0579e0a2f..04be6e93bed 100644
--- a/src/shared/Util.h
+++ b/src/shared/Util.h
@@ -17,47 +17,63 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _UTIL_H
#define _UTIL_H
+
#include "Common.h"
+
#include <string>
#include <vector>
+
typedef std::vector<std::string> Tokens;
+
Tokens StrSplit(const std::string &src, const std::string &sep);
+
void stripLineInvisibleChars(std::string &src);
+
std::string secsToTimeString(uint32 timeInSecs, bool shortText = false, bool hoursOnly = false);
uint32 TimeStringToSecs(const std::string& timestring);
std::string TimeToTimestampStr(time_t t);
+
inline uint32 secsToTimeBitFields(time_t secs)
{
tm* lt = localtime(&secs);
return (lt->tm_year - 100) << 24 | lt->tm_mon << 20 | (lt->tm_mday - 1) << 14 | lt->tm_wday << 11 | lt->tm_hour << 6 | lt->tm_min;
}
+
/* Return a random number in the range min..max; (max-min) must be smaller than 32768. */
TRINITY_DLL_SPEC int32 irand(int32 min, int32 max);
+
/* Return a random number in the range min..max (inclusive). For reliable results, the difference
* between max and min should be less than RAND32_MAX. */
TRINITY_DLL_SPEC uint32 urand(uint32 min, uint32 max);
+
/* Return a random number in the range 0 .. RAND32_MAX. */
TRINITY_DLL_SPEC int32 rand32();
+
/* Return a random double from 0.0 to 1.0 (exclusive). Floats support only 7 valid decimal digits.
* A double supports up to 15 valid decimal digits and is used internally (RAND32_MAX has 10 digits).
* With an FPU, there is usually no difference in performance between float and double. */
TRINITY_DLL_SPEC double rand_norm(void);
+
/* Return a random double from 0.0 to 99.9999999999999. Floats support only 7 valid decimal digits.
* A double supports up to 15 valid decimal digits and is used internaly (RAND32_MAX has 10 digits).
* With an FPU, there is usually no difference in performance between float and double. */
TRINITY_DLL_SPEC double rand_chance(void);
+
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_f(float chance)
{
return chance > rand_chance();
}
+
/* Return true if a random roll fits in the specified chance (range 0-100). */
inline bool roll_chance_i(int chance)
{
return chance > irand(0, 99);
}
+
inline void ApplyModUInt32Var(uint32& var, int32 val, bool apply)
{
int32 cur = var;
@@ -66,18 +82,21 @@ inline void ApplyModUInt32Var(uint32& var, int32 val, bool apply)
cur = 0;
var = cur;
}
+
inline void ApplyModFloatVar(float& var, float val, bool apply)
{
var += (apply ? val : -val);
if(var < 0)
var = 0;
}
+
inline void ApplyPercentModFloatVar(float& var, float val, bool apply)
{
if (val == -100.0f) // prevent set var to zero
val = -99.99f;
var *= (apply?(100.0f+val)/100.0f : 100.0f / (100.0f+val));
}
+
bool Utf8toWStr(const std::string& utf8str, std::wstring& wstr);
// in wsize==max size of buffer, out wsize==real string size
bool Utf8toWStr(char const* utf8str, size_t csize, wchar_t* wstr, size_t& wsize);
@@ -85,11 +104,14 @@ inline bool Utf8toWStr(const std::string& utf8str, wchar_t* wstr, size_t& wsize)
{
return Utf8toWStr(utf8str.c_str(), utf8str.size(), wstr, wsize);
}
+
bool WStrToUtf8(std::wstring wstr, std::string& utf8str);
// size==real string size
bool WStrToUtf8(wchar_t* wstr, size_t size, std::string& utf8str);
+
size_t utf8length(std::string& utf8str); // set string to "" if invalid utf8 sequence
void utf8truncate(std::string& utf8str,size_t len);
+
inline bool isBasicLatinCharacter(wchar_t wchar)
{
if(wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z
@@ -98,6 +120,7 @@ inline bool isBasicLatinCharacter(wchar_t wchar)
return true;
return false;
}
+
inline bool isExtendedLatinCharacter(wchar_t wchar)
{
if(isBasicLatinCharacter(wchar))
@@ -118,6 +141,7 @@ inline bool isExtendedLatinCharacter(wchar_t wchar)
return true;
return false;
}
+
inline bool isCyrillicCharacter(wchar_t wchar)
{
if(wchar >= 0x0410 && wchar <= 0x044F) // CYRILLIC CAPITAL LETTER A - CYRILLIC SMALL LETTER YA
@@ -126,6 +150,7 @@ inline bool isCyrillicCharacter(wchar_t wchar)
return true;
return false;
}
+
inline bool isEastAsianCharacter(wchar_t wchar)
{
if(wchar >= 0x1100 && wchar <= 0x11F9) // Hangul Jamo
@@ -146,18 +171,22 @@ inline bool isEastAsianCharacter(wchar_t wchar)
return true;
return false;
}
+
inline bool isNumeric(wchar_t wchar)
{
return (wchar >= L'0' && wchar <=L'9');
}
+
inline bool isNumeric(char c)
{
return (c >= '0' && c <='9');
}
+
inline bool isNumericOrSpace(wchar_t wchar)
{
return isNumeric(wchar) || wchar == L' ';
}
+
inline bool isBasicLatinString(std::wstring wstr, bool numericOrSpace)
{
for(size_t i = 0; i < wstr.size(); ++i)
@@ -165,6 +194,7 @@ inline bool isBasicLatinString(std::wstring wstr, bool numericOrSpace)
return false;
return true;
}
+
inline bool isExtendedLatinString(std::wstring wstr, bool numericOrSpace)
{
for(size_t i = 0; i < wstr.size(); ++i)
@@ -172,6 +202,7 @@ inline bool isExtendedLatinString(std::wstring wstr, bool numericOrSpace)
return false;
return true;
}
+
inline bool isCyrillicString(std::wstring wstr, bool numericOrSpace)
{
for(size_t i = 0; i < wstr.size(); ++i)
@@ -179,6 +210,7 @@ inline bool isCyrillicString(std::wstring wstr, bool numericOrSpace)
return false;
return true;
}
+
inline bool isEastAsianString(std::wstring wstr, bool numericOrSpace)
{
for(size_t i = 0; i < wstr.size(); ++i)
@@ -186,6 +218,7 @@ inline bool isEastAsianString(std::wstring wstr, bool numericOrSpace)
return false;
return true;
}
+
inline wchar_t wcharToUpper(wchar_t wchar)
{
if(wchar >= L'a' && wchar <= L'z') // LATIN SMALL LETTER A - LATIN SMALL LETTER Z
@@ -205,12 +238,15 @@ inline wchar_t wcharToUpper(wchar_t wchar)
return wchar_t(uint16(wchar)-0x0020);
if(wchar == 0x0451) // CYRILLIC SMALL LETTER IO
return wchar_t(0x0401);
+
return wchar;
}
+
inline wchar_t wcharToUpperOnlyLatin(wchar_t wchar)
{
return isBasicLatinCharacter(wchar) ? wcharToUpper(wchar) : wchar;
}
+
inline wchar_t wcharToLower(wchar_t wchar)
{
if(wchar >= L'A' && wchar <= L'Z') // LATIN CAPITAL LETTER A - LATIN CAPITAL LETTER Z
@@ -230,33 +266,43 @@ inline wchar_t wcharToLower(wchar_t wchar)
return wchar_t(0x0451);
if(wchar >= 0x0410 && wchar <= 0x042F) // CYRILLIC CAPITAL LETTER A - CYRILLIC CAPITAL LETTER YA
return wchar_t(uint16(wchar)+0x0020);
+
return wchar;
}
+
inline void wstrToUpper(std::wstring& str)
{
std::transform( str.begin(), str.end(), str.begin(), wcharToUpper );
}
+
inline void wstrToLower(std::wstring& str)
{
std::transform( str.begin(), str.end(), str.begin(), wcharToLower );
}
+
std::wstring GetMainPartOfName(std::wstring wname, uint32 declension);
+
bool utf8ToConsole(const std::string& utf8str, std::string& conStr);
bool consoleToUtf8(const std::string& conStr,std::string& utf8str);
bool Utf8FitTo(const std::string& str, std::wstring search);
void utf8printf(FILE *out, const char *str, ...);
void vutf8printf(FILE *out, const char *str, va_list* ap);
+
bool IsIPAddress(char const* ipaddress);
uint32 CreatePIDFile(const std::string& filename);
+
void hexEncodeByteArray(uint8* bytes, uint32 arrayLen, std::string& result);
#endif
+
//handler for operations on large flags
#ifndef _FLAG96
#define _FLAG96
+
#ifndef PAIR64_HIPART
#define PAIR64_HIPART(x) (uint32)((uint64(x) >> 32) & UI64LIT(0x00000000FFFFFFFF))
#define PAIR64_LOPART(x) (uint32)(uint64(x) & UI64LIT(0x00000000FFFFFFFF))
#endif
+
class flag96
{
private:
@@ -268,12 +314,14 @@ public:
part[1]=p2;
part[2]=p3;
}
+
flag96(uint64 p1, uint32 p2)
{
part[0]=PAIR64_LOPART(p1);
part[1]=PAIR64_HIPART(p1);
part[2]=p2;
}
+
inline bool IsEqual(uint32 p1=0, uint32 p2=0, uint32 p3=0) const
{
return (
@@ -281,6 +329,7 @@ public:
part[1]==p2 &&
part[2]==p3);
};
+
inline bool HasFlag(uint32 p1=0, uint32 p2=0, uint32 p3=0) const
{
return (
@@ -288,12 +337,14 @@ public:
part[1]&p2 ||
part[2]&p3);
};
+
inline void Set(uint32 p1=0, uint32 p2=0, uint32 p3=0)
{
part[0]=p1;
part[1]=p2;
part[2]=p3;
};
+
template<class type>
inline bool operator < (type & right)
{
@@ -306,6 +357,7 @@ public:
}
return 0;
};
+
template<class type>
inline bool operator < (type & right) const
{
@@ -318,6 +370,7 @@ public:
}
return 0;
};
+
template<class type>
inline bool operator != (type & right)
{
@@ -327,6 +380,7 @@ public:
return true;
return false;
}
+
template<class type>
inline bool operator != (type & right) const
{
@@ -336,6 +390,7 @@ public:
return true;
return false;
};
+
template<class type>
inline bool operator == (type & right)
{
@@ -345,6 +400,7 @@ public:
return false;
return true;
};
+
template<class type>
inline bool operator == (type & right) const
{
@@ -354,6 +410,7 @@ public:
return false;
return true;
};
+
template<class type>
inline void operator = (type & right)
{
@@ -361,6 +418,7 @@ public:
part[1]=right.part[1];
part[2]=right.part[2];
};
+
template<class type>
inline flag96 operator & (type & right)
{
@@ -375,11 +433,13 @@ public:
return
ret;
};
+
template<class type>
inline void operator &= (type & right)
{
*this=*this & right;
};
+
template<class type>
inline flag96 operator | (type & right)
{
@@ -387,6 +447,7 @@ public:
return
ret;
};
+
template<class type>
inline flag96 operator | (type & right) const
{
@@ -394,17 +455,20 @@ public:
return
ret;
};
+
template<class type>
inline void operator |= (type & right)
{
*this=*this | right;
};
+
inline void operator ~ ()
{
part[2]=~part[2];
part[1]=~part[1];
part[0]=~part[0];
};
+
template<class type>
inline flag96 operator ^ (type & right)
{
@@ -412,6 +476,7 @@ public:
return
ret;
};
+
template<class type>
inline flag96 operator ^ (type & right) const
{
@@ -419,11 +484,13 @@ public:
return
ret;
};
+
template<class type>
inline void operator ^= (type & right)
{
*this=*this^right;
};
+
inline operator bool() const
{
return(
@@ -431,6 +498,7 @@ public:
part[1] != 0 ||
part[2] != 0);
};
+
inline operator bool()
{
return(
@@ -438,6 +506,7 @@ public:
part[1] != 0 ||
part[2] != 0);
};
+
inline bool operator ! () const
{
return(
@@ -445,6 +514,7 @@ public:
part[1] == 0 &&
part[2] == 0);
};
+
inline bool operator ! ()
{
return(
@@ -452,10 +522,12 @@ public:
part[1] == 0 &&
part[2] == 0);
};
+
inline uint32 & operator[](uint8 el)
{
return (part[el]);
};
+
inline const uint32 & operator[](uint8 el) const
{
return (part[el]);
diff --git a/src/shared/WheatyExceptionReport.cpp b/src/shared/WheatyExceptionReport.cpp
index 9c537a20949..f2fd9b0f2e7 100644
--- a/src/shared/WheatyExceptionReport.cpp
+++ b/src/shared/WheatyExceptionReport.cpp
@@ -18,6 +18,7 @@
#include "revision.h"
#define CrashFolder _T("Crashes")
//#pragma comment(linker, "/defaultlib:dbghelp.lib")
+
inline LPTSTR ErrorMessage(DWORD dw)
{
LPVOID lpMsgBuf;
@@ -31,7 +32,9 @@ inline LPTSTR ErrorMessage(DWORD dw)
0, NULL);
return (LPTSTR)lpMsgBuf;
}
+
//============================== Global Variables =============================
+
//
// Declare the static variables of the WheatyExceptionReport class
//
@@ -39,15 +42,19 @@ TCHAR WheatyExceptionReport::m_szLogFileName[MAX_PATH];
LPTOP_LEVEL_EXCEPTION_FILTER WheatyExceptionReport::m_previousFilter;
HANDLE WheatyExceptionReport::m_hReportFile;
HANDLE WheatyExceptionReport::m_hProcess;
+
// Declare global instance of class
WheatyExceptionReport g_WheatyExceptionReport;
+
//============================== Class Methods =============================
+
WheatyExceptionReport::WheatyExceptionReport() // Constructor
{
// Install the unhandled exception filter function
m_previousFilter = SetUnhandledExceptionFilter(WheatyUnhandledExceptionFilter);
m_hProcess = GetCurrentProcess();
}
+
//============
// Destructor
//============
@@ -56,6 +63,7 @@ WheatyExceptionReport::~WheatyExceptionReport()
if (m_previousFilter)
SetUnhandledExceptionFilter(m_previousFilter);
}
+
//===========================================================
// Entry point where control comes on an unhandled exception
//===========================================================
@@ -69,6 +77,7 @@ PEXCEPTION_POINTERS pExceptionInfo)
return 0;
pos[0] = '\0';
++pos;
+
TCHAR crash_folder_path[MAX_PATH];
sprintf(crash_folder_path, "%s\\%s", module_folder_name, CrashFolder);
if (!CreateDirectory(crash_folder_path, NULL))
@@ -76,10 +85,12 @@ PEXCEPTION_POINTERS pExceptionInfo)
if (GetLastError() != ERROR_ALREADY_EXISTS)
return 0;
}
+
SYSTEMTIME systime;
GetLocalTime(&systime);
sprintf(m_szLogFileName, "%s\\%s_[%u-%u_%u-%u-%u].txt",
crash_folder_path, pos, systime.wDay, systime.wMonth, systime.wHour, systime.wMinute, systime.wSecond);
+
m_hReportFile = CreateFile(m_szLogFileName,
GENERIC_WRITE,
0,
@@ -87,22 +98,28 @@ PEXCEPTION_POINTERS pExceptionInfo)
OPEN_ALWAYS,
FILE_FLAG_WRITE_THROUGH,
0);
+
if (m_hReportFile)
{
SetFilePointer(m_hReportFile, 0, 0, FILE_END);
+
GenerateExceptionReport(pExceptionInfo);
+
CloseHandle(m_hReportFile);
m_hReportFile = 0;
}
+
if (m_previousFilter)
return m_previousFilter(pExceptionInfo);
else
return EXCEPTION_EXECUTE_HANDLER/*EXCEPTION_CONTINUE_SEARCH*/;
}
+
BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxcount)
{
if (!sProcessorName)
return FALSE;
+
HKEY hKey;
LONG lRet;
lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0"),
@@ -124,6 +141,7 @@ BOOL WheatyExceptionReport::_GetProcessorName(TCHAR* sProcessorName, DWORD maxco
_tcsncpy(sProcessorName, psz, maxcount);
return TRUE;
}
+
BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
{
// Try calling GetVersionEx using the OSVERSIONINFOEX structure.
@@ -155,6 +173,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
_tcsncat(szVersion, _T("Microsoft Windows 2000 "), cntMax);
if (osvi.dwMajorVersion <= 4)
_tcsncat(szVersion, _T("Microsoft Windows NT "), cntMax);
+
// Test for specific product on Windows NT 4.0 SP6 and later.
if (bOsVersionInfoEx)
{
@@ -247,6 +266,7 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
{
HKEY hKey;
LONG lRet;
+
// Test for SP6 versus SP6a.
lRet = ::RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Hotfix\\Q246009"), 0, KEY_QUERY_VALUE, &hKey);
if (lRet == ERROR_SUCCESS)
@@ -280,12 +300,15 @@ BOOL WheatyExceptionReport::_GetWindowsVersion(TCHAR* szVersion, DWORD cntMax)
_tcsncat(szVersion, wszTmp, cntMax);
break;
}
+
return TRUE;
}
+
void WheatyExceptionReport::PrintSystemInfo()
{
SYSTEM_INFO SystemInfo;
::GetSystemInfo(&SystemInfo);
+
MEMORYSTATUS MemoryStatus;
MemoryStatus.dwLength = sizeof (MEMORYSTATUS);
::GlobalMemoryStatus(&MemoryStatus);
@@ -297,24 +320,29 @@ void WheatyExceptionReport::PrintSystemInfo()
else
_tprintf(_T("*** Hardware ***\r\nProcessor: <unknown>\r\nNumber Of Processors: %d\r\nPhysical Memory: %d KB (Available: %d KB)\r\nCommit Charge Limit: %d KB\r\n"),
SystemInfo.dwNumberOfProcessors, MemoryStatus.dwTotalPhys/0x400, MemoryStatus.dwAvailPhys/0x400, MemoryStatus.dwTotalPageFile/0x400);
+
if (_GetWindowsVersion(sString, countof(sString)))
_tprintf(_T("\r\n*** Operation System ***\r\n%s\r\n"), sString);
else
_tprintf(_T("\r\n*** Operation System:\r\n<unknown>\r\n"));
}
+
//===========================================================================
void WheatyExceptionReport::printTracesForAllThreads()
{
HANDLE hThreadSnap = INVALID_HANDLE_VALUE;
THREADENTRY32 te32;
+
DWORD dwOwnerPID = GetCurrentProcessId();
m_hProcess = GetCurrentProcess();
// Take a snapshot of all running threads
hThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
if (hThreadSnap == INVALID_HANDLE_VALUE)
return;
+
// Fill in the size of the structure before using it.
te32.dwSize = sizeof(THREADENTRY32);
+
// Retrieve information about the first thread,
// and exit if unsuccessful
if (!Thread32First(hThreadSnap, &te32))
@@ -323,6 +351,7 @@ void WheatyExceptionReport::printTracesForAllThreads()
// snapshot object!
return;
}
+
// Now walk the thread list of the system,
// and display information about each thread
// associated with the specified process
@@ -340,9 +369,12 @@ void WheatyExceptionReport::printTracesForAllThreads()
CloseHandle(threadHandle);
}
} while(Thread32Next(hThreadSnap, &te32));
+
// Don't forget to clean up the snapshot object.
CloseHandle(hThreadSnap);
}
+
+
//===========================================================================
// Open the report file, and write the desired information to it. Called by
// WheatyUnhandledExceptionFilter
@@ -352,16 +384,19 @@ PEXCEPTION_POINTERS pExceptionInfo)
{
SYSTEMTIME systime;
GetLocalTime(&systime);
+
// Start out with a banner
_tprintf(_T("Revision: %s\r\n"), _FULLVERSION);
_tprintf(_T("Date %u:%u:%u. Time %u:%u \r\n"), systime.wDay, systime.wMonth, systime.wYear, systime.wHour, systime.wMinute);
PEXCEPTION_RECORD pExceptionRecord = pExceptionInfo->ExceptionRecord;
+
PrintSystemInfo();
// First print information about the type of fault
_tprintf(_T("\r\n//=====================================================\r\n"));
_tprintf(_T("Exception code: %08X %s\r\n"),
pExceptionRecord->ExceptionCode,
GetExceptionString(pExceptionRecord->ExceptionCode));
+
// Now print information about where the fault occured
TCHAR szFaultingModule[MAX_PATH];
DWORD section;
@@ -370,6 +405,7 @@ PEXCEPTION_POINTERS pExceptionInfo)
szFaultingModule,
sizeof(szFaultingModule),
section, offset);
+
#ifdef _M_IX86
_tprintf(_T("Fault address: %08X %02X:%08X %s\r\n"),
pExceptionRecord->ExceptionAddress,
@@ -380,13 +416,17 @@ PEXCEPTION_POINTERS pExceptionInfo)
pExceptionRecord->ExceptionAddress,
section, offset, szFaultingModule);
#endif
+
PCONTEXT pCtx = pExceptionInfo->ContextRecord;
+
// Show the registers
#ifdef _M_IX86 // X86 Only!
_tprintf(_T("\r\nRegisters:\r\n"));
+
_tprintf(_T("EAX:%08X\r\nEBX:%08X\r\nECX:%08X\r\nEDX:%08X\r\nESI:%08X\r\nEDI:%08X\r\n")
,pCtx->Eax, pCtx->Ebx, pCtx->Ecx, pCtx->Edx,
pCtx->Esi, pCtx->Edi);
+
_tprintf(_T("CS:EIP:%04X:%08X\r\n"), pCtx->SegCs, pCtx->Eip);
_tprintf(_T("SS:ESP:%04X:%08X EBP:%08X\r\n"),
pCtx->SegSs, pCtx->Esp, pCtx->Ebp);
@@ -394,6 +434,7 @@ PEXCEPTION_POINTERS pExceptionInfo)
pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
_tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags);
#endif
+
#ifdef _M_X64
_tprintf(_T("\r\nRegisters:\r\n"));
_tprintf(_T("RAX:%016I64X\r\nRBX:%016I64X\r\nRCX:%016I64X\r\nRDX:%016I64X\r\nRSI:%016I64X\r\nRDI:%016I64X\r\n")
@@ -407,30 +448,42 @@ PEXCEPTION_POINTERS pExceptionInfo)
pCtx->SegDs, pCtx->SegEs, pCtx->SegFs, pCtx->SegGs);
_tprintf(_T("Flags:%08X\r\n"), pCtx->EFlags);
#endif
+
SymSetOptions(SYMOPT_DEFERRED_LOADS);
+
// Initialize DbgHelp
if (!SymInitialize(GetCurrentProcess(), 0, TRUE))
{
_tprintf(_T("\n\rCRITICAL ERROR.\n\r Couldn't initialize the symbol handler for process.\n\rError [%s].\n\r\n\r"),
ErrorMessage(GetLastError()));
}
+
CONTEXT trashableContext = *pCtx;
+
WriteStackDetails(&trashableContext, false, NULL);
printTracesForAllThreads();
+
// #ifdef _M_IX86 // X86 Only!
+
_tprintf(_T("========================\r\n"));
_tprintf(_T("Local Variables And Parameters\r\n"));
+
trashableContext = *pCtx;
WriteStackDetails(&trashableContext, true, NULL);
+
_tprintf(_T("========================\r\n"));
_tprintf(_T("Global Variables\r\n"));
+
SymEnumSymbols(GetCurrentProcess(),
(DWORD64)GetModuleHandle(szFaultingModule),
0, EnumerateSymbolsCallback, 0);
// #endif // X86 Only!
+
SymCleanup(GetCurrentProcess());
+
_tprintf(_T("\r\n"));
}
+
//======================================================================
// Given an exception code, returns a pointer to a static string with a
// description of the exception
@@ -438,6 +491,7 @@ PEXCEPTION_POINTERS pExceptionInfo)
LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)
{
#define EXCEPTION(x) case EXCEPTION_##x: return _T(#x);
+
switch (dwCode)
{
EXCEPTION(ACCESS_VIOLATION)
@@ -463,14 +517,19 @@ LPTSTR WheatyExceptionReport::GetExceptionString(DWORD dwCode)
EXCEPTION(GUARD_PAGE)
EXCEPTION(INVALID_HANDLE)
}
+
// If not one of the "known" exceptions, try to get the string
// from NTDLL.DLL's message table.
+
static TCHAR szBuffer[512] = { 0 };
+
FormatMessage(FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_HMODULE,
GetModuleHandle(_T("NTDLL.DLL")),
dwCode, 0, szBuffer, sizeof(szBuffer), 0);
+
return szBuffer;
}
+
//=============================================================================
// Given a linear address, locates the module, section, and offset containing
// that address.
@@ -482,17 +541,25 @@ BOOL WheatyExceptionReport::GetLogicalAddress(
PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
{
MEMORY_BASIC_INFORMATION mbi;
+
if (!VirtualQuery(addr, &mbi, sizeof(mbi)))
return FALSE;
+
DWORD_PTR hMod = (DWORD_PTR)mbi.AllocationBase;
+
if (!GetModuleFileName((HMODULE)hMod, szModule, len))
return FALSE;
+
// Point to the DOS header in memory
PIMAGE_DOS_HEADER pDosHdr = (PIMAGE_DOS_HEADER)hMod;
+
// From the DOS header, find the NT (PE) header
PIMAGE_NT_HEADERS pNtHdr = (PIMAGE_NT_HEADERS)(hMod + DWORD_PTR(pDosHdr->e_lfanew));
+
PIMAGE_SECTION_HEADER pSection = IMAGE_FIRST_SECTION(pNtHdr);
+
DWORD_PTR rva = (DWORD_PTR)addr - hMod; // RVA is offset from module load address
+
// Iterate through the section table, looking for the one that encompasses
// the linear address.
for (unsigned i = 0;
@@ -502,6 +569,7 @@ PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
DWORD_PTR sectionStart = pSection->VirtualAddress;
DWORD_PTR sectionEnd = sectionStart
+ DWORD_PTR(max(pSection->SizeOfRawData, pSection->Misc.VirtualSize));
+
// Is the address in this section???
if ((rva >= sectionStart) && (rva <= sectionEnd))
{
@@ -513,8 +581,10 @@ PVOID addr, PTSTR szModule, DWORD len, DWORD& section, DWORD_PTR& offset)
return TRUE;
}
}
+
return FALSE; // Should never get here!
}
+
// It contains SYMBOL_INFO structure plus additional
// space for the name of the symbol
struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
@@ -525,6 +595,7 @@ struct CSymbolInfoPackage : public SYMBOL_INFO_PACKAGE
si.MaxNameLen = sizeof(name);
}
};
+
//============================================================
// Walks the stack, and writes the results to the report file
//============================================================
@@ -533,11 +604,15 @@ PCONTEXT pContext,
bool bWriteVariables, HANDLE pThreadHandle) // true if local/params should be output
{
_tprintf(_T("\r\nCall stack:\r\n"));
+
_tprintf(_T("Address Frame Function SourceFile\r\n"));
+
DWORD dwMachineType = 0;
// Could use SymSetOptions here to add the SYMOPT_DEFERRED_LOADS flag
+
STACKFRAME64 sf;
memset(&sf, 0, sizeof(sf));
+
#ifdef _M_IX86
// Initialize the STACKFRAME structure for the first call. This is only
// necessary for Intel CPUs, and isn't mentioned in the documentation.
@@ -547,8 +622,10 @@ bool bWriteVariables, HANDLE pThreadHandle)
sf.AddrStack.Mode = AddrModeFlat;
sf.AddrFrame.Offset = pContext->Ebp;
sf.AddrFrame.Mode = AddrModeFlat;
+
dwMachineType = IMAGE_FILE_MACHINE_I386;
#endif
+
#ifdef _M_X64
sf.AddrPC.Offset = pContext->Rip;
sf.AddrPC.Mode = AddrModeFlat;
@@ -558,6 +635,7 @@ bool bWriteVariables, HANDLE pThreadHandle)
sf.AddrFrame.Mode = AddrModeFlat;
dwMachineType = IMAGE_FILE_MACHINE_AMD64;
#endif
+
while (1)
{
// Get the next stack frame
@@ -579,8 +657,10 @@ bool bWriteVariables, HANDLE pThreadHandle)
#ifdef _M_X64
_tprintf(_T("%016I64X %016I64X "), sf.AddrPC.Offset, sf.AddrFrame.Offset);
#endif
+
DWORD64 symDisplacement = 0; // Displacement of the input address,
// relative to the start of the symbol
+
// Get the name of the function for this stack frame entry
CSymbolInfoPackage sip;
if (SymFromAddr(
@@ -590,12 +670,14 @@ bool bWriteVariables, HANDLE pThreadHandle)
&sip.si)) // Address of the SYMBOL_INFO structure (inside "sip" object)
{
_tprintf(_T("%hs+%I64X"), sip.si.Name, symDisplacement);
+
}
else // No symbol found. Print out the logical address instead.
{
TCHAR szModule[MAX_PATH] = _T("");
DWORD section = 0;
DWORD_PTR offset = 0;
+
GetLogicalAddress((PVOID)sf.AddrPC.Offset,
szModule, sizeof(szModule), section, offset);
#ifdef _M_IX86
@@ -605,6 +687,7 @@ bool bWriteVariables, HANDLE pThreadHandle)
_tprintf(_T("%04X:%016I64X %s"), section, offset, szModule);
#endif
}
+
// Get the source line for this stack frame entry
IMAGEHLP_LINE64 lineInfo = { sizeof(IMAGEHLP_LINE) };
DWORD dwLineDisplacement;
@@ -613,7 +696,9 @@ bool bWriteVariables, HANDLE pThreadHandle)
{
_tprintf(_T(" %s line %u"),lineInfo.FileName,lineInfo.LineNumber);
}
+
_tprintf(_T("\r\n"));
+
// Write out the variables, if desired
if (bWriteVariables)
{
@@ -621,22 +706,29 @@ bool bWriteVariables, HANDLE pThreadHandle)
IMAGEHLP_STACK_FRAME imagehlpStackFrame;
imagehlpStackFrame.InstructionOffset = sf.AddrPC.Offset;
SymSetContext(m_hProcess, &imagehlpStackFrame, 0);
+
// Enumerate the locals/parameters
SymEnumSymbols(m_hProcess, 0, 0, EnumerateSymbolsCallback, &sf);
+
_tprintf(_T("\r\n"));
}
}
+
}
+
//////////////////////////////////////////////////////////////////////////////
// The function invoked by SymEnumSymbols
//////////////////////////////////////////////////////////////////////////////
+
BOOL CALLBACK
WheatyExceptionReport::EnumerateSymbolsCallback(
PSYMBOL_INFO pSymInfo,
ULONG SymbolSize,
PVOID UserContext)
{
+
char szBuffer[2048];
+
__try
{
if (FormatSymbolValue(pSymInfo, (STACKFRAME*)UserContext,
@@ -647,8 +739,10 @@ PVOID UserContext)
{
_tprintf(_T("punting on symbol %s\r\n"), pSymInfo->Name);
}
+
return TRUE;
}
+
//////////////////////////////////////////////////////////////////////////////
// Given a SYMBOL_INFO representing a particular variable, displays its
// contents. If it's a user defined type, display the members and their
@@ -661,15 +755,19 @@ char * pszBuffer,
unsigned cbBuffer)
{
char * pszCurrBuffer = pszBuffer;
+
// Indicate if the variable is a local or parameter
if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_PARAMETER)
pszCurrBuffer += sprintf(pszCurrBuffer, "Parameter ");
else if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_LOCAL)
pszCurrBuffer += sprintf(pszCurrBuffer, "Local ");
+
// If it's a function, don't do anything.
if (pSym->Tag == 5) // SymTagFunction from CVCONST.H from the DIA SDK
return false;
+
DWORD_PTR pVariable = 0; // Will point to the variable's data in memory
+
if (pSym->Flags & IMAGEHLP_SYMBOL_INFO_REGRELATIVE)
{
// if (pSym->Register == 8) // EBP is the value 8 (in DBGHELP 5.1)
@@ -688,11 +786,13 @@ unsigned cbBuffer)
{
pVariable = (DWORD_PTR)pSym->Address; // It must be a global variable
}
+
// Determine if the variable is a user defined type (UDT). IF so, bHandled
// will return true.
bool bHandled;
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer,pSym->ModBase, pSym->TypeIndex,
0, pVariable, bHandled, pSym->Name);
+
if (!bHandled)
{
// The symbol wasn't a UDT, so do basic, stupid formatting of the
@@ -700,13 +800,17 @@ unsigned cbBuffer)
// DWORD.
BasicType basicType = GetBasicType(pSym->TypeIndex, pSym->ModBase);
pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
+
// Emit the variable name
pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", pSym->Name);
+
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType, pSym->Size,
(PVOID)pVariable);
}
+
return true;
}
+
//////////////////////////////////////////////////////////////////////////////
// If it's a user defined type (UDT), recurse through its members until we're
// at fundamental types. When he hit fundamental types, return
@@ -722,6 +826,7 @@ bool & bHandled,
char* Name)
{
bHandled = false;
+
// Get the name of the symbol. This will either be a Type name (if a UDT),
// or the structure member name.
WCHAR * pwszTypeName;
@@ -731,12 +836,15 @@ char* Name)
pszCurrBuffer += sprintf(pszCurrBuffer, " %ls", pwszTypeName);
LocalFree(pwszTypeName);
}
+
// Determine how many children this type has.
DWORD dwChildrenCount = 0;
SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_GET_CHILDRENCOUNT,
&dwChildrenCount);
+
if (!dwChildrenCount) // If no children, we're done
return pszCurrBuffer;
+
// Prepare to get an array of "TypeIds", representing each of the children.
// SymGetTypeInfo(TI_FINDCHILDREN) expects more memory than just a
// TI_FINDCHILDREN_PARAMS struct has. Use derivation to accomplish this.
@@ -745,29 +853,36 @@ char* Name)
ULONG MoreChildIds[1024];
FINDCHILDREN(){Count = sizeof(MoreChildIds) / sizeof(MoreChildIds[0]);}
} children;
+
children.Count = dwChildrenCount;
children.Start= 0;
+
// Get the array of TypeIds, one for each child type
if (!SymGetTypeInfo(m_hProcess, modBase, dwTypeIndex, TI_FINDCHILDREN,
&children))
{
return pszCurrBuffer;
}
+
// Append a line feed
pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
+
// Iterate through each of the children
for (unsigned i = 0; i < dwChildrenCount; i++)
{
// Add appropriate indentation level (since this routine is recursive)
for (unsigned j = 0; j <= nestingLevel+1; j++)
pszCurrBuffer += sprintf(pszCurrBuffer, "\t");
+
// Recurse for each of the child types
bool bHandled2;
BasicType basicType = GetBasicType(children.ChildId[i], modBase);
pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
+
pszCurrBuffer = DumpTypeIndex(pszCurrBuffer, modBase,
children.ChildId[i], nestingLevel+1,
offset, bHandled2, ""/*Name */);
+
// If the child wasn't a UDT, format it appropriately
if (!bHandled2)
{
@@ -775,30 +890,38 @@ char* Name)
DWORD dwMemberOffset;
SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
TI_GET_OFFSET, &dwMemberOffset);
+
// Get the real "TypeId" of the child. We need this for the
// SymGetTypeInfo(TI_GET_TYPEID) call below.
DWORD typeId;
SymGetTypeInfo(m_hProcess, modBase, children.ChildId[i],
TI_GET_TYPEID, &typeId);
+
// Get the size of the child member
ULONG64 length;
SymGetTypeInfo(m_hProcess, modBase, typeId, TI_GET_LENGTH,&length);
+
// Calculate the address of the member
DWORD_PTR dwFinalOffset = offset + dwMemberOffset;
+
// BasicType basicType = GetBasicType(children.ChildId[i], modBase);
//
// pszCurrBuffer += sprintf(pszCurrBuffer, rgBaseType[basicType]);
//
// Emit the variable name
// pszCurrBuffer += sprintf(pszCurrBuffer, "\'%s\'", Name);
+
pszCurrBuffer = FormatOutputValue(pszCurrBuffer, basicType,
length, (PVOID)dwFinalOffset);
+
pszCurrBuffer += sprintf(pszCurrBuffer, "\r\n");
}
}
+
bHandled = true;
return pszCurrBuffer;
}
+
char * WheatyExceptionReport::FormatOutputValue(char * pszCurrBuffer,
BasicType basicType,
DWORD64 length,
@@ -840,8 +963,10 @@ PVOID pAddress)
pszCurrBuffer += sprintf(pszCurrBuffer, " = %I64X",
*(DWORD64*)pAddress);
}
+
return pszCurrBuffer;
}
+
BasicType
WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
{
@@ -851,6 +976,7 @@ WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
{
return basicType;
}
+
// Get the real "TypeId" of the child. We need this for the
// SymGetTypeInfo(TI_GET_TYPEID) call below.
DWORD typeId;
@@ -862,8 +988,10 @@ WheatyExceptionReport::GetBasicType(DWORD typeIndex, DWORD64 modBase)
return basicType;
}
}
+
return btNoType;
}
+
//============================================================================
// Helper function that writes to the report file, and allows the user to use
// printf style formating
@@ -874,10 +1002,13 @@ int __cdecl WheatyExceptionReport::_tprintf(const TCHAR * format, ...)
int retValue;
DWORD cbWritten;
va_list argptr;
+
va_start(argptr, format);
retValue = vsprintf(szBuff, format, argptr);
va_end(argptr);
+
WriteFile(m_hReportFile, szBuff, retValue * sizeof(TCHAR), &cbWritten, 0);
+
return retValue;
}
diff --git a/src/shared/WheatyExceptionReport.h b/src/shared/WheatyExceptionReport.h
index a2ff31aefa8..33fb4bc5b0e 100644
--- a/src/shared/WheatyExceptionReport.h
+++ b/src/shared/WheatyExceptionReport.h
@@ -1,12 +1,15 @@
#ifndef _WHEATYEXCEPTIONREPORT_
#define _WHEATYEXCEPTIONREPORT_
+
#include <dbghelp.h>
+
#if _MSC_VER < 1400
# define countof(array) (sizeof(array) / sizeof(array[0]))
#else
# include <stdlib.h>
# define countof _countof
#endif // _MSC_VER < 1400
+
enum BasicType // Stolen from CVCONST.H in the DIA 2.0 SDK
{
btNoType = 0,
@@ -28,6 +31,7 @@ enum BasicType // Stolen from CVCON
btBSTR = 30,
btHresult = 31
};
+
const char* const rgBaseType[] =
{
" <user defined> ", // btNoType = 0,
@@ -63,14 +67,18 @@ const char* const rgBaseType[] =
" BSTR ", // btBSTR = 30,
" HRESULT " // btHresult = 31
};
+
class WheatyExceptionReport
{
public:
+
WheatyExceptionReport();
~WheatyExceptionReport();
+
// entry point where control comes on an unhandled exception
static LONG WINAPI WheatyUnhandledExceptionFilter(
PEXCEPTION_POINTERS pExceptionInfo);
+
static void printTracesForAllThreads();
private:
// where report info is extracted and generated
@@ -78,23 +86,33 @@ class WheatyExceptionReport
static void PrintSystemInfo();
static BOOL _GetWindowsVersion(TCHAR* szVersion, DWORD cntMax);
static BOOL _GetProcessorName(TCHAR* sProcessorName, DWORD maxcount);
+
// Helper functions
static LPTSTR GetExceptionString(DWORD dwCode);
static BOOL GetLogicalAddress(PVOID addr, PTSTR szModule, DWORD len,
DWORD& section, DWORD_PTR& offset);
+
static void WriteStackDetails(PCONTEXT pContext, bool bWriteVariables, HANDLE pThreadHandle);
+
static BOOL CALLBACK EnumerateSymbolsCallback(PSYMBOL_INFO,ULONG, PVOID);
+
static bool FormatSymbolValue(PSYMBOL_INFO, STACKFRAME *, char * pszBuffer, unsigned cbBuffer);
+
static char * DumpTypeIndex(char *, DWORD64, DWORD, unsigned, DWORD_PTR, bool & , char*);
+
static char * FormatOutputValue(char * pszCurrBuffer, BasicType basicType, DWORD64 length, PVOID pAddress);
+
static BasicType GetBasicType(DWORD typeIndex, DWORD64 modBase);
+
static int __cdecl _tprintf(const TCHAR * format, ...);
+
// Variables used by the class
static TCHAR m_szLogFileName[MAX_PATH];
static LPTOP_LEVEL_EXCEPTION_FILTER m_previousFilter;
static HANDLE m_hReportFile;
static HANDLE m_hProcess;
};
+
extern WheatyExceptionReport g_WheatyExceptionReport; // global instance of class
#endif //WheatyExceptionReport
diff --git a/src/shared/WorldPacket.h b/src/shared/WorldPacket.h
index 055cc0f6088..1eb3f12dd86 100644
--- a/src/shared/WorldPacket.h
+++ b/src/shared/WorldPacket.h
@@ -17,10 +17,13 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef TRINITYCORE_WORLDPACKET_H
#define TRINITYCORE_WORLDPACKET_H
+
#include "Common.h"
#include "ByteBuffer.h"
+
class WorldPacket : public ByteBuffer
{
public:
@@ -33,14 +36,17 @@ class WorldPacket : public ByteBuffer
WorldPacket(const WorldPacket &packet) : ByteBuffer(packet), m_opcode(packet.m_opcode)
{
}
+
void Initialize(uint16 opcode, size_t newres=200)
{
clear();
_storage.reserve(newres);
m_opcode = opcode;
}
+
uint16 GetOpcode() const { return m_opcode; }
void SetOpcode(uint16 opcode) { m_opcode = opcode; }
+
protected:
uint16 m_opcode;
};
diff --git a/src/shared/vmap/AABSPTree.h b/src/shared/vmap/AABSPTree.h
index 5fe9c59cf2b..ff4335b6774 100644
--- a/src/shared/vmap/AABSPTree.h
+++ b/src/shared/vmap/AABSPTree.h
@@ -1,14 +1,21 @@
/**
@file AABSPTree.h
+
@maintainer Morgan McGuire, matrix@graphics3d.com
+
@created 2004-01-11
@edited 2007-02-16
+
Copyright 2000-2007, Morgan McGuire.
All rights reserved.
+
*/
+
#ifndef G3D_AABSPTREE_H
#define G3D_AABSPTREE_H
+
#include "VMapTools.h"
+
#include "G3D/platform.h"
#include "G3D/Array.h"
#include "G3D/Table.h"
@@ -26,47 +33,60 @@
#include "G3D/CollisionDetection.h"
#include "G3D/GCamera.h"
#include <algorithm>
+
// If defined, in debug mode the tree is checked for consistency
// as a way of detecting corruption due to implementation bugs
// #define VERIFY_TREE
+
inline void getBounds(const G3D::Vector3& v, G3D::AABox& out) {
out = G3D::AABox(v);
}
+
inline void getBounds(const G3D::AABox& a, G3D::AABox& out) {
out = a;
}
+
inline void getBounds(const G3D::Sphere& s, G3D::AABox& out) {
s.getBounds(out);
}
+
inline void getBounds(const G3D::Box& b, G3D::AABox& out) {
b.getBounds(out);
}
+
inline void getBounds(const G3D::Triangle& t, G3D::AABox& out) {
t.getBounds(out);
}
+
+
inline void getBounds(const G3D::Vector3* v, G3D::AABox& out) {
out = G3D::AABox(*v);
}
+
inline void getBounds(const G3D::AABox* a, G3D::AABox& out) {
getBounds(*a, out);
}
+
inline void getBounds(const G3D::Sphere* s, G3D::AABox& out) {
s->getBounds(out);
}
+
inline void getBounds(const G3D::Box* b, G3D::AABox& out) {
b->getBounds(out);
}
+
inline void getBounds(const G3D::Triangle* t, G3D::AABox& out) {
t->getBounds(out);
}
namespace G3D {
namespace _internal {
+
/**
Wraps a pointer value so that it can be treated as the instance itself;
convenient for inserting pointers into a Table but using the
@@ -76,50 +96,65 @@ namespace G3D {
class Indirector {
public:
Type* handle;
+
inline Indirector(Type* h) : handle(h) {}
+
inline Indirector() : handle(NULL) {}
+
/** Returns true iff the values referenced by the handles are equivalent. */
inline bool operator==(const Indirector& m) {
return *handle == *(m.handle);
}
+
inline bool operator==(const Type& m) {
return *handle == m;
}
+
inline size_t hashCode() const {
return handle->hashCode();
}
};
} // namespace internal
} // namespace G3D
+
template <class Handle>
struct GHashCode< G3D::_internal::Indirector<Handle> >
{
size_t operator()(const G3D::_internal::Indirector<Handle>& key) const { return key.hashCode(); }
};
+
namespace G3D {
+
/**
A set that supports spatial queries using an axis-aligned
BSP tree for speed.
+
AABSPTree allows you to quickly find objects in 3D that lie within
a box or along a ray. For large sets of objects it is much faster
than testing each object for a collision.
+
AABSPTree is as powerful as but more general than a Quad Tree, Oct
Tree, or KD Tree, but less general than an unconstrained BSP tree
(which is much slower to create).
+
Internally, objects
are arranged into an axis-aligned BSP-tree according to their
axis-aligned bounds. This increases the cost of insertion to
O(log n) but allows fast overlap queries.
+
<B>Template Parameters</B>
<DT>The template parameter <I>T</I> must be one for which
the following functions are all overloaded:
+
<P><CODE>void ::getBounds(const T&, G3D::AABox&);</CODE>
<DT><CODE>bool ::operator==(const T&, const T&);</CODE>
<DT><CODE>unsigned int ::hashCode(const T&);</CODE>
<DT><CODE>T::T();</CODE> <I>(public constructor of no arguments)</I>
+
G3D provides these for common classes like G3D::Vector3 and G3D::Sphere.
If you use a custom class, or a pointer to a custom class, you will need
to define those functions.
+
<B>Moving %Set Members</B>
<DT>It is important that objects do not move without updating the
AABSPTree. If the axis-aligned bounds of an object are about
@@ -130,21 +165,26 @@ namespace G3D {
you can use the AABSPTree::update method as a shortcut to
insert/remove an object in one step after it has moved.
+
Note: Do not mutate any value once it has been inserted into AABSPTree. Values
are copied interally. All AABSPTree iterators convert to pointers to constant
values to reinforce this.
+
If you want to mutate the objects you intend to store in a AABSPTree
simply insert <I>pointers</I> to your objects instead of the objects
themselves, and ensure that the above operations are defined. (And
actually, because values are copied, if your values are large you may
want to insert pointers anyway, to save space and make the balance
operation faster.)
+
<B>Dimensions</B>
Although designed as a 3D-data structure, you can use the AABSPTree
for data distributed along 2 or 1 axes by simply returning bounds
that are always zero along one or more dimensions.
+
*/
namespace _AABSPTree {
+
/** Wrapper for a value that includes a cache of its bounds.
Except for the test value used in a set-query operation, there
is only ever one instance of the handle associated with any
@@ -156,75 +196,97 @@ namespace _AABSPTree {
public:
/** The bounds of each object are constrained to AABox::maxFinite */
AABox bounds;
+
/** Center of bounds. We cache this value to avoid recomputing it
during the median sort, and because MSVC 6 std::sort goes into
an infinite loop if we compute the midpoint on the fly (possibly
a floating point roundoff issue, where B<A and A<B both are true).*/
Vector3 center;
+
TValue value;
+
Handle<TValue>() {}
+
inline Handle<TValue>(const TValue& v) : value(v) {
getBounds(v, bounds);
bounds = bounds.intersect(AABox::maxFinite());
center = bounds.center();
}
+
inline bool operator==(const Handle<TValue>& other) const {
return (*value).operator==(*other.value);
}
+
inline size_t hashCode() const {
return value->hashCode();
}
};
+
template<>
class Handle<Triangle> {
public:
/** The bounds of each object are constrained to AABox::maxFinite */
AABox bounds;
+
/** Center of bounds. We cache this value to avoid recomputing it
during the median sort, and because MSVC 6 std::sort goes into
an infinite loop if we compute the midpoint on the fly (possibly
a floating point roundoff issue, where B<A and A<B both are true).*/
Vector3 center;
+
Triangle value;
+
Handle<Triangle>() {}
+
inline Handle<Triangle>(const Triangle& v) : value(v) {
getBounds(v, bounds);
bounds = bounds.intersect(AABox::maxFinite());
center = bounds.center();
}
+
inline bool operator==(const Handle<Triangle>& other) const {
return value.operator==(other.value);
}
+
inline size_t hashCode() const {
return value.hashCode();
}
};
}
+
template<class T> class AABSPTree {
protected:
public:
+
/** Returns the bounds of the sub array. Used by makeNode. */
static AABox computeBounds(
const Array<_AABSPTree::Handle<T>*>& point,
int beginIndex,
int endIndex) {
+
Vector3 lo = Vector3::inf();
Vector3 hi = -lo;
+
for (int p = beginIndex; p <= endIndex; ++p) {
lo = lo.min(point[p]->bounds.low());
hi = hi.max(point[p]->bounds.high());
}
+
return AABox(lo, hi);
}
+
/** Compares centers */
class CenterComparator {
public:
Vector3::Axis sortAxis;
+
CenterComparator(Vector3::Axis a) : sortAxis(a) {}
+
inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const {
float a = A->center[sortAxis];
float b = B->center[sortAxis];
+
if (a < b) {
return 1;
} else if (a > b) {
@@ -235,14 +297,18 @@ public:
}
};
+
/** Compares bounds for strict >, <, or overlap*/
class BoundsComparator {
public:
Vector3::Axis sortAxis;
+
BoundsComparator(Vector3::Axis a) : sortAxis(a) {}
+
inline int operator()(_AABSPTree::Handle<T>* A, const _AABSPTree::Handle<T>* B) const {
const AABox& a = A->bounds;
const AABox& b = B->bounds;
+
if (a.high()[sortAxis] < b.low()[sortAxis]) {
return 1;
} else if (a.low()[sortAxis] > b.high()[sortAxis]) {
@@ -253,15 +319,19 @@ public:
}
};
+
/** Compares bounds to the sort location */
class Comparator {
public:
Vector3::Axis sortAxis;
float sortLocation;
+
Comparator(Vector3::Axis a, float l) : sortAxis(a), sortLocation(l) {}
+
inline int operator()(_AABSPTree::Handle<T>* /*ignore*/, const _AABSPTree::Handle<T>* handle) const {
const AABox& box = handle->bounds;
debugAssert(ignore == NULL);
+
if (box.high()[sortAxis] < sortLocation) {
// Box is strictly below the sort location
return -1;
@@ -274,31 +344,41 @@ public:
}
}
};
+
// Using System::malloc with this class provided no speed improvement.
class Node {
public:
+
/** Spatial bounds on all values at this node and its children, based purely on
the parent's splitting planes. May be infinite. */
AABox splitBounds;
+
Vector3::Axis splitAxis;
+
/** Location along the specified axis */
float splitLocation;
+
/** child[0] contains all values strictly
smaller than splitLocation along splitAxis.
+
child[1] contains all values strictly
larger.
+
Both may be NULL if there are not enough
values to bother recursing.
*/
Node* child[2];
+
/** Array of values at this node (i.e., values
straddling the split plane + all values if
this is a leaf node).
+
This is an array of pointers because that minimizes
data movement during tree building, which accounts
for about 15% of the time cost of tree building.
*/
Array<_AABSPTree::Handle<T> * > valueArray;
+
/** For each object in the value array, a copy of its bounds.
Packing these into an array at the node level
instead putting them in the valueArray improves
@@ -306,6 +386,7 @@ public:
increase when performing intersection computations.
*/
Array<AABox> boundsArray;
+
/** Creates node with NULL children */
Node() {
splitAxis = Vector3::X_AXIS;
@@ -315,6 +396,7 @@ public:
child[i] = NULL;
}
}
+
/**
Doesn't clone children.
*/
@@ -326,6 +408,7 @@ public:
child[i] = NULL;
}
}
+
/** Copies the specified subarray of pt into point, NULLs the children.
Assumes a second pass will set splitBounds. */
Node(const Array<_AABSPTree::Handle<T> * >& pt) : valueArray(pt) {
@@ -334,22 +417,26 @@ public:
for (int i = 0; i < 2; ++i) {
child[i] = NULL;
}
+
boundsArray.resize(valueArray.size());
for (int i = 0; i < valueArray.size(); ++i) {
boundsArray[i] = valueArray[i]->bounds;
}
}
+
/** Deletes the children (but not the values) */
~Node() {
for (int i = 0; i < 2; ++i) {
delete child[i];
}
}
+
/** Returns true if this node is a leaf (no children) */
inline bool isLeaf() const {
return (child[0] == NULL) && (child[1] == NULL);
}
+
/**
Recursively appends all handles and children's handles
to the array.
@@ -362,35 +449,44 @@ public:
}
}
}
+
void verifyNode(const Vector3& lo, const Vector3& hi) {
// debugPrintf("Verifying: split %d @ %f [%f, %f, %f], [%f, %f, %f]\n",
// splitAxis, splitLocation, lo.x, lo.y, lo.z, hi.x, hi.y, hi.z);
+
debugAssert(lo == splitBounds.low());
debugAssert(hi == splitBounds.high());
+
for (int i = 0; i < valueArray.length(); ++i) {
const AABox& b = valueArray[i]->bounds;
debugAssert(b == boundsArray[i]);
+
for(int axis = 0; axis < 3; ++axis) {
debugAssert(b.low()[axis] <= b.high()[axis]);
debugAssert(b.low()[axis] >= lo[axis]);
debugAssert(b.high()[axis] <= hi[axis]);
}
}
+
if (child[0] || child[1]) {
debugAssert(lo[splitAxis] < splitLocation);
debugAssert(hi[splitAxis] > splitLocation);
}
+
Vector3 newLo = lo;
newLo[splitAxis] = splitLocation;
Vector3 newHi = hi;
newHi[splitAxis] = splitLocation;
+
if (child[0] != NULL) {
child[0]->verifyNode(lo, newHi);
}
+
if (child[1] != NULL) {
child[1]->verifyNode(newLo, hi);
}
}
+
#if 0
/**
Stores the locations of the splitting planes (the structure but not the content)
@@ -410,6 +506,7 @@ public:
}
}
}
+
/** Clears the member table */
static Node* deserializeStructure(BinaryInput& bi) {
if (bi.readUInt8() == 0) {
@@ -427,6 +524,7 @@ public:
#endif
/** Returns the deepest node that completely contains bounds. */
Node* findDeepestContainingNode(const AABox& bounds) {
+
// See which side of the splitting plane the bounds are on
if (bounds.high()[splitAxis] < splitLocation) {
// Bounds are on the low side. Recurse into the child
@@ -441,11 +539,13 @@ public:
return child[1]->findDeepestContainingNode(bounds);
}
}
+
// There was no containing child, so this node is the
// deepest containing node.
return this;
}
+
/** Appends all members that intersect the box.
If useSphere is true, members that pass the box test
face a second test against the sphere. */
@@ -454,6 +554,7 @@ public:
const Sphere& sphere,
Array<T>& members,
bool useSphere) const {
+
// Test all values at this node
for (int v = 0; v < boundsArray.size(); ++v) {
const AABox& bounds = boundsArray[v];
@@ -462,22 +563,27 @@ public:
members.append(valueArray[v]->value);
}
}
+
// If the left child overlaps the box, recurse into it
if ((child[0] != NULL) && (box.low()[splitAxis] < splitLocation)) {
child[0]->getIntersectingMembers(box, sphere, members, useSphere);
}
+
// If the right child overlaps the box, recurse into it
if ((child[1] != NULL) && (box.high()[splitAxis] > splitLocation)) {
child[1]->getIntersectingMembers(box, sphere, members, useSphere);
}
}
+
/**
Recurse through the tree, assigning splitBounds fields.
*/
void assignSplitBounds(const AABox& myBounds) {
splitBounds = myBounds;
+
AABox childBounds[2];
myBounds.split(splitAxis, splitLocation, childBounds[0], childBounds[1]);
+
# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
// Verify the split
for (int v = 0; v < boundsArray.size(); ++v) {
@@ -485,12 +591,14 @@ public:
debugAssert(myBounds.contains(bounds));
}
# endif
+
for (int c = 0; c < 2; ++c) {
if (child[c]) {
child[c]->assignSplitBounds(childBounds[c]);
}
}
}
+
/** Returns true if the ray intersects this node */
bool intersects(const Ray& ray, float distance) const {
// See if the ray will ever hit this node or its children
@@ -499,10 +607,13 @@ public:
bool rayWillHitBounds =
VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
ray.origin, ray.direction, splitBounds, location, alreadyInsideBounds);
+
bool canHitThisNode = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
+
return canHitThisNode;
}
+
template<typename RayCallback>
void intersectRay(
const Ray& ray,
@@ -511,13 +622,16 @@ public:
bool pStopAtFirstHit,
bool intersectCallbackIsFast) const {
float enterDistance = distance;
+
if (! intersects(ray, distance)) {
// The ray doesn't hit this node, so it can't hit the children of the node.
return;
}
+
// Test for intersection against every object at this node.
for (int v = 0; v < valueArray.size(); ++v) {
bool canHitThisObject = true;
+
if (! intersectCallbackIsFast) {
// See if
Vector3 location;
@@ -526,9 +640,11 @@ public:
bool rayWillHitBounds =
VMAP::MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
ray.origin, ray.direction, bounds, location, alreadyInsideBounds);
+
canHitThisObject = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - ray.origin).squaredLength() < square(distance))));
}
+
if (canHitThisObject) {
// It is possible that this ray hits this object. Look for the intersection using the
// callback.
@@ -538,24 +654,32 @@ public:
if(pStopAtFirstHit && distance < enterDistance)
return;
}
+
// There are three cases to consider next:
//
// 1. the ray can start on one side of the splitting plane and never enter the other,
// 2. the ray can start on one side and enter the other, and
// 3. the ray can travel exactly down the splitting plane
+
enum {NONE = -1};
int firstChild = NONE;
int secondChild = NONE;
+
if (ray.origin[splitAxis] < splitLocation) {
+
// The ray starts on the small side
firstChild = 0;
+
if (ray.direction[splitAxis] > 0) {
// The ray will eventually reach the other side
secondChild = 1;
}
+
} else if (ray.origin[splitAxis] > splitLocation) {
+
// The ray starts on the large side
firstChild = 1;
+
if (ray.direction[splitAxis] < 0) {
secondChild = 0;
}
@@ -569,12 +693,14 @@ public:
firstChild = 1;
}
}
+
// Test on the side closer to the ray origin.
if ((firstChild != NONE) && child[firstChild]) {
child[firstChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast);
if(pStopAtFirstHit && distance < enterDistance)
return;
}
+
if (ray.direction[splitAxis] != 0) {
// See if there was an intersection before hitting the splitting plane.
// If so, there is no need to look on the far side and recursion terminates.
@@ -586,16 +712,21 @@ public:
return;
}
}
+
// Test on the side farther from the ray origin.
if ((secondChild != NONE) && child[secondChild]) {
child[secondChild]->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast);
}
+
}
};
+
/**
Recursively subdivides the subarray.
+
Clears the source array as soon as it is no longer needed.
+
Call assignSplitBounds() on the root node after making a tree.
*/
Node* makeNode(
@@ -603,28 +734,40 @@ public:
int valuesPerNode,
int numMeanSplits,
Array<_AABSPTree::Handle<T> * >& temp) {
+
Node* node = NULL;
+
if (source.size() <= valuesPerNode) {
// Make a new leaf node
node = new Node(source);
+
// Set the pointers in the memberTable
for (int i = 0; i < source.size(); ++i) {
memberTable.set(Member(source[i]), node);
}
source.clear();
+
} else {
// Make a new internal node
node = new Node();
+
const AABox bounds = computeBounds(source, 0, source.size() - 1);
const Vector3 extent = bounds.high() - bounds.low();
+
Vector3::Axis splitAxis = extent.primaryAxis();
+
float splitLocation;
+
// Arrays for holding the children
Array<_AABSPTree::Handle<T> * > lt, gt;
+
if (numMeanSplits <= 0) {
+
source.medianPartition(lt, node->valueArray, gt, temp, CenterComparator(splitAxis));
+
// Choose the split location to be the center of whatever fell in the center
splitLocation = node->valueArray[0]->center[splitAxis];
+
// Some of the elements in the lt or gt array might really overlap the split location.
// Move them as needed.
for (int i = 0; i < lt.size(); ++i) {
@@ -636,6 +779,7 @@ public:
lt.fastRemove(i); --i;
}
}
+
for (int i = 0; i < gt.size(); ++i) {
const AABox& bounds = gt[i]->bounds;
if ((bounds.low()[splitAxis] <= splitLocation) && (bounds.high()[splitAxis] >= splitLocation)) {
@@ -645,6 +789,7 @@ public:
gt.fastRemove(i); --i;
}
}
+
if ((node->valueArray.size() > (source.size() / 2)) &&
(source.size() > 6)) {
// This was a bad partition; we ended up putting the splitting plane right in the middle of most of the
@@ -654,16 +799,21 @@ public:
numMeanSplits = 1;
}
}
+
// Note: numMeanSplits may have been increased by the code in the previous case above in order to
// force a re-partition.
+
if (numMeanSplits > 0) {
// Split along the mean
splitLocation = (bounds.high()[splitAxis] +
bounds.low()[splitAxis]) / 2.0;
+
source.partition(NULL, lt, node->valueArray, gt, Comparator(splitAxis, splitLocation));
+
// The Comparator ensures that elements are strictly on the correct side of the split
}
+
# if defined(G3D_DEBUG) && defined(VERIFY_TREE)
debugAssert(lt.size() + node->valueArray.size() + gt.size() == source.size());
// Verify that all objects ended up on the correct side of the split.
@@ -672,20 +822,25 @@ public:
const AABox& bounds = lt[i]->bounds;
debugAssert(bounds.high()[splitAxis] < splitLocation);
}
+
for (int i = 0; i < gt.size(); ++i) {
const AABox& bounds = gt[i]->bounds;
debugAssert(bounds.low()[splitAxis] > splitLocation);
}
+
for (int i = 0; i < node->valueArray.size(); ++i) {
const AABox& bounds = node->valueArray[i]->bounds;
debugAssert(bounds.high()[splitAxis] >= splitLocation);
debugAssert(bounds.low()[splitAxis] <= splitLocation);
}
# endif
+
// The source array is no longer needed
source.clear();
+
node->splitAxis = splitAxis;
node->splitLocation = splitLocation;
+
// Update the bounds array and member table
node->boundsArray.resize(node->valueArray.size());
for (int i = 0; i < node->valueArray.size(); ++i) {
@@ -693,15 +848,20 @@ public:
node->boundsArray[i] = v->bounds;
memberTable.set(Member(v), node);
}
+
if (lt.size() > 0) {
node->child[0] = makeNode(lt, valuesPerNode, numMeanSplits - 1, temp);
}
+
if (gt.size() > 0) {
node->child[1] = makeNode(gt, valuesPerNode, numMeanSplits - 1, temp);
}
+
}
+
return node;
}
+
/**
Recursively clone the passed in node tree, setting
pointers for members in the memberTable as appropriate.
@@ -709,36 +869,47 @@ public:
*/
Node* cloneTree(Node* src) {
Node* dst = new Node(*src);
+
// Make back pointers
for (int i = 0; i < dst->valueArray.size(); ++i) {
memberTable.set(Member(dst->valueArray[i]), dst);
}
+
// Clone children
for (int i = 0; i < 2; ++i) {
if (src->child[i] != NULL) {
dst->child[i] = cloneTree(src->child[i]);
}
}
+
return dst;
}
+
/**
Wrapper for a Handle; used to create a memberTable that acts like Table<Handle, Node*> but
stores only Handle* internally to avoid memory copies.
*/
typedef _internal::Indirector<_AABSPTree::Handle<T> > Member;
+
typedef Table<Member, Node*> MemberTable;
+
/** Maps members to the node containing them */
MemberTable memberTable;
+
Node* root;
+
public:
+
/** To construct a balanced tree, insert the elements and then call
AABSPTree::balance(). */
AABSPTree() : root(NULL) {}
+
AABSPTree(const AABSPTree& src) : root(NULL) {
*this = src;
}
+
AABSPTree& operator=(const AABSPTree& src) {
delete root;
// Clone tree takes care of filling out the memberTable.
@@ -746,14 +917,17 @@ public:
return *this;
}
+
~AABSPTree() {
clear();
}
+
/**
Throws out all elements of the set.
*/
void clear() {
typedef typename Table<_internal::Indirector<_AABSPTree::Handle<T> >, Node* >::Iterator It;
+
// Delete all handles stored in the member table
It cur = memberTable.begin();
It end = memberTable.end();
@@ -763,13 +937,16 @@ public:
++cur;
}
memberTable.clear();
+
// Delete the tree structure itself
delete root;
root = NULL;
}
+
size_t size() const {
return memberTable.size();
}
+
/**
Inserts an object into the set if it is not
already present. O(log n) time. Does not
@@ -780,19 +957,25 @@ public:
// Already in the set
return;
}
+
_AABSPTree::Handle<T>* h = new _AABSPTree::Handle<T>(value);
+
if (root == NULL) {
// This is the first node; create a root node
root = new Node();
}
+
Node* node = root->findDeepestContainingNode(h->bounds);
+
// Insert into the node
node->valueArray.append(h);
node->boundsArray.append(h->bounds);
+
// Insert into the node table
Member m(h);
memberTable.set(m, node);
}
+
/** Inserts each elements in the array in turn. If the tree
begins empty (no structure and no elements), this is faster
than inserting each element in turn. You still need to balance
@@ -815,6 +998,7 @@ public:
root->boundsArray[j] = h->bounds;
memberTable.set(Member(h), root);
}
+
} else {
// Insert at appropriate tree depth.
for (int i = 0; i < valueArray.size(); ++i) {
@@ -823,6 +1007,7 @@ public:
}
}
+
/**
Returns true if this object is in the set, otherwise
returns false. O(1) time.
@@ -833,10 +1018,12 @@ public:
return memberTable.containsKey(Member(&h));
}
+
/**
Removes an object from the set in O(1) time.
It is an error to remove members that are not already
present. May unbalance the tree.
+
Removing an element never causes a node (split plane) to be removed...
nodes are only changed when the tree is rebalanced. This behavior
is desirable because it allows the split planes to be serialized,
@@ -846,34 +1033,44 @@ public:
debugAssertM(contains(value),
"Tried to remove an element from a "
"AABSPTree that was not present");
+
// Get the list of elements at the node
_AABSPTree::Handle<T> h(value);
Member m(&h);
+
Array<_AABSPTree::Handle<T> * >& list = memberTable[m]->valueArray;
+
_AABSPTree::Handle<T>* ptr = NULL;
+
// Find the element and remove it
for (int i = list.length() - 1; i >= 0; --i) {
if (list[i]->value == value) {
// This was the element. Grab the pointer so that
// we can delete it below
ptr = list[i];
+
// Remove the handle from the node
list.fastRemove(i);
+
// Remove the corresponding bounds
memberTable[m]->boundsArray.fastRemove(i);
break;
}
}
+
// Remove the member
memberTable.remove(m);
+
// Delete the handle data structure
delete ptr;
ptr = NULL;
}
+
/**
If the element is in the set, it is removed.
The element is then inserted.
+
This is useful when the == and hashCode methods
on <I>T</I> are independent of the bounds. In
that case, you may call update(v) to insert an
@@ -888,18 +1085,22 @@ public:
insert(value);
}
+
/**
Rebalances the tree (slow). Call when objects
have moved substantially from their original positions
(which unbalances the tree and causes the spatial
queries to be slow).
+
@param valuesPerNode Maximum number of elements to put at
a node.
+
@param numMeanSplits numMeanSplits = 0 gives a
fully axis aligned BSP-tree, where the balance operation attempts to balance
the tree so that every splitting plane has an equal number of left
and right children (i.e. it is a <B>median</B> split along that axis).
This tends to maximize average performance.
+
You can override this behavior by
setting a number of <B>mean</B> (average) splits. numMeanSplits = MAX_INT
creates a full oct-tree, which tends to optimize peak performance at the expense of
@@ -911,33 +1112,41 @@ public:
// Tree is empty
return;
}
+
// Get all handles and delete the old tree structure
Node* oldRoot = root;
for (int c = 0; c < 2; ++c) {
if (root->child[c] != NULL) {
root->child[c]->getHandles(root->valueArray);
+
// Delete the child; this will delete all structure below it
delete root->child[c];
root->child[c] = NULL;
}
}
+
Array<_AABSPTree::Handle<T> * > temp;
// Make a new root. Work with a copy of the value array because
// makeNode clears the source array as it progresses
Array<_AABSPTree::Handle<T> * > copy(oldRoot->valueArray);
root = makeNode(copy, valuesPerNode, numMeanSplits, temp);
+
// Throw away the old root node
delete oldRoot;
oldRoot = NULL;
+
// Walk the tree, assigning splitBounds. We start with unbounded
// space. This will override the current member table.
root->assignSplitBounds(AABox::maxFinite());
+
# ifdef _DEBUG
// Ensure that the balanced tree is till correct
root->verifyNode(Vector3::minFinite(), Vector3::maxFinite());
# endif
}
+
protected:
+
/**
@param parentMask The mask that this node returned from culledBy.
*/
@@ -946,12 +1155,15 @@ protected:
Array<T>& members,
Node* node,
uint32 parentMask) {
+
int dummy;
+
if (parentMask == 0) {
// None of these planes can cull anything
for (int v = node->valueArray.size() - 1; v >= 0; --v) {
members.append(node->valueArray[v]->value);
}
+
// Iterate through child nodes
for (int c = 0; c < 2; ++c) {
if (node->child[c]) {
@@ -959,13 +1171,16 @@ protected:
}
}
} else {
+
// Test values at this node against remaining planes
for (int v = node->boundsArray.size() - 1; v >= 0; --v) {
if (! node->boundsArray[v].culledBy(plane, dummy, parentMask)) {
members.append(node->valueArray[v]->value);
}
}
+
uint32 childMask = 0xFFFFFF;
+
// Iterate through child nodes
for (int c = 0; c < 2; ++c) {
if (node->child[c] &&
@@ -976,7 +1191,9 @@ protected:
}
}
}
+
public:
+
/**
Returns all members inside the set of planes.
@param members The results are appended to this array.
@@ -985,12 +1202,15 @@ public:
if (root == NULL) {
return;
}
+
getIntersectingMembers(plane, members, root, 0xFFFFFF);
}
+
/**
Typically used to find all visible
objects inside the view frustum (see also GCamera::getClipPlanes)... i.e. all objects
<B>not<B> culled by frustum.
+
Example:
<PRE>
Array<Object*> visible;
@@ -1001,11 +1221,14 @@ public:
*/
void getIntersectingMembers(const GCamera::Frustum& frustum, Array<T>& members) const {
Array<Plane> plane;
+
for (int i = 0; i < frustum.faceArray.size(); ++i) {
plane.append(frustum.faceArray[i].plane);
}
+
getIntersectingMembers(plane, members);
}
+
/**
C++ STL style iterator variable. See beginBoxIntersection().
The iterator overloads the -> (dereference) operator, so this
@@ -1019,26 +1242,34 @@ public:
class BoxIntersectionIterator {
private:
friend class AABSPTree<T>;
+
/** True if this is the "end" iterator instance */
bool isEnd;
+
/** The box that we're testing against. */
AABox box;
+
/** Node that we're currently looking at. Undefined if isEnd
is true. */
Node* node;
+
/** Nodes waiting to be processed */
// We could use backpointers within the tree and careful
// state management to avoid ever storing the stack-- but
// it is much easier this way and only inefficient if the
// caller uses post increment (which they shouldn't!).
Array<Node*> stack;
+
/** The next index of current->valueArray to return.
Undefined when isEnd is true.*/
int nextValueArrayIndex;
+
BoxIntersectionIterator() : isEnd(true) {}
+
BoxIntersectionIterator(const AABox& b, const Node* root) :
isEnd(root == NULL), box(b),
node(const_cast<Node*>(root)), nextValueArrayIndex(-1) {
+
// We intentionally start at the "-1" index of the current
// node so we can use the preincrement operator to move
// ourselves to element 0 instead of repeating all of the
@@ -1046,10 +1277,13 @@ public:
// cause us to become the "end" instance.
++(*this);
}
+
public:
+
inline bool operator!=(const BoxIntersectionIterator& other) const {
return ! (*this == other);
}
+
bool operator==(const BoxIntersectionIterator& other) const {
if (isEnd) {
return other.isEnd;
@@ -1064,41 +1298,49 @@ public:
(stack.length() != other.stack.length())) {
return false;
}
+
// See if the stacks are the same
for (int i = 0; i < stack.length(); ++i) {
if (stack[i] != other.stack[i]) {
return false;
}
}
+
// We failed to find a difference; they must be the same
return true;
}
}
+
/**
Pre increment.
*/
BoxIntersectionIterator& operator++() {
++nextValueArrayIndex;
+
bool foundIntersection = false;
while (! isEnd && ! foundIntersection) {
+
// Search for the next node if we've exhausted this one
while ((! isEnd) && (nextValueArrayIndex >= node->valueArray.length())) {
// If we entered this loop, then the iterator has exhausted the elements at
// node (possibly because it just switched to a child node with no members).
// This loop continues until it finds a node with members or reaches
// the end of the whole intersection search.
+
// If the right child overlaps the box, push it onto the stack for
// processing.
if ((node->child[1] != NULL) &&
(box.high()[node->splitAxis] > node->splitLocation)) {
stack.push(node->child[1]);
}
+
// If the left child overlaps the box, push it onto the stack for
// processing.
if ((node->child[0] != NULL) &&
(box.low()[node->splitAxis] < node->splitLocation)) {
stack.push(node->child[0]);
}
+
if (stack.length() > 0) {
// Go on to the next node (which may be either one of the ones we
// just pushed, or one from farther back the tree).
@@ -1109,6 +1351,7 @@ public:
isEnd = true;
}
}
+
// Search for the next intersection at this node until we run out of children
while (! isEnd && ! foundIntersection && (nextValueArrayIndex < node->valueArray.length())) {
if (box.intersects(node->boundsArray[nextValueArrayIndex])) {
@@ -1120,8 +1363,10 @@ public:
}
}
}
+
return *this;
}
+
private:
/**
Post increment (much slower than preincrement!). Intentionally overloaded to preclude accidentally slow code.
@@ -1132,19 +1377,23 @@ public:
++this;
return old;
}*/
+
public:
+
/** Overloaded dereference operator so the iterator can masquerade as a pointer
to a member */
const T& operator*() const {
alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
return node->valueArray[nextValueArrayIndex]->value;
}
+
/** Overloaded dereference operator so the iterator can masquerade as a pointer
to a member */
T const * operator->() const {
alwaysAssertM(! isEnd, "Can't dereference the end element of an iterator");
return &(stack.last()->valueArray[nextValueArrayIndex]->value);
}
+
/** Overloaded cast operator so the iterator can masquerade as a pointer
to a member */
operator T*() const {
@@ -1153,16 +1402,19 @@ public:
}
};
+
/**
Iterates through the members that intersect the box
*/
BoxIntersectionIterator beginBoxIntersection(const AABox& box) const {
return BoxIntersectionIterator(box, root);
}
+
BoxIntersectionIterator endBoxIntersection() const {
// The "end" iterator instance
return BoxIntersectionIterator();
}
+
/**
Appends all members whose bounds intersect the box.
See also AABSPTree::beginBoxIntersection.
@@ -1174,18 +1426,25 @@ public:
root->getIntersectingMembers(box, Sphere(Vector3::zero(), 0), members, false);
}
+
/**
Invoke a callback for every member along a ray until the closest intersection is found.
+
@param callback either a function or an instance of a class with an overloaded operator() of the form:
+
<code>void callback(const Ray& ray, const T& object, float& distance)</code>. If the ray hits the object
before travelling distance <code>distance</code>, updates <code>distance</code> with the new distance to
the intersection, otherwise leaves it unmodified. A common example is:
+
<pre>
class Entity {
public:
+
void intersect(const Ray& ray, float& maxDist, Vector3& outLocation, Vector3& outNormal) {
float d = maxDist;
+
// ... search for intersection distance d
+
if ((d > 0) && (d < maxDist)) {
// Intersection occured
maxDist = d;
@@ -1194,24 +1453,30 @@ public:
}
}
};
+
// Finds the surface normal and location of the first intersection with the scene
class Intersection {
public:
Entity* closestEntity;
Vector3 hitLocation;
Vector3 hitNormal;
+
void operator()(const Ray& ray, const Entity* entity, float& distance) {
entity->intersect(ray, distance, hitLocation, hitNormal);
}
};
+
AABSPTree<Entity*> scene;
+
Intersection intersection;
float distance = inf();
scene.intersectRay(camera.worldRay(x, y), intersection, distance);
</pre>
+
@param distance When the method is invoked, this is the maximum distance that the tree should search for an intersection.
On return, this is set to the distance to the first intersection encountered.
+
@param intersectCallbackIsFast If false, each object's bounds are tested before the intersectCallback is invoked.
If the intersect callback runs at the same speed or faster than AABox-ray intersection, set this to true.
*/
@@ -1222,9 +1487,12 @@ public:
float& distance,
bool pStopAtFirstHit,
bool intersectCallbackIsFast = false) const {
+
root->intersectRay(ray, intersectCallback, distance, pStopAtFirstHit, intersectCallbackIsFast);
+
}
+
/**
@param members The results are appended to this array.
*/
@@ -1232,9 +1500,11 @@ public:
if (root == NULL) {
return;
}
+
AABox box;
sphere.getBounds(box);
root->getIntersectingMembers(box, sphere, members, true);
+
}
#if 0
/**
@@ -1245,6 +1515,7 @@ public:
void serializeStructure(BinaryOutput& bo) const {
Node::serializeStructure(root, bo);
}
+
/** Clears the member table */
void deserializeStructure(BinaryInput& bi) {
clear();
@@ -1262,6 +1533,7 @@ public:
}
}
+
/**
C++ STL style iterator variable. See begin().
Overloads the -> (dereference) operator, so this acts like a pointer
@@ -1270,17 +1542,23 @@ public:
class Iterator {
private:
friend class AABSPTree<T>;
+
// Note: this is a Table iterator, we are currently defining
// Set iterator
typename Table<Member, Node*>::Iterator it;
+
Iterator(const typename Table<Member, Node*>::Iterator& it) : it(it) {}
+
public:
+
inline bool operator!=(const Iterator& other) const {
return !(*this == other);
}
+
bool operator==(const Iterator& other) const {
return it == other.it;
}
+
/**
Pre increment.
*/
@@ -1288,6 +1566,7 @@ public:
++it;
return *this;
}
+
private:
/**
Post increment (slower than preincrement). Intentionally unimplemented to prevent slow code.
@@ -1298,17 +1577,21 @@ public:
return old;
}*/
public:
+
const T& operator*() const {
return it->key.handle->value;
}
+
T* operator->() const {
return &(it->key.handle->value);
}
+
operator T*() const {
return &(it->key.handle->value);
}
};
+
/**
C++ STL style iterator method. Returns the first member.
Use preincrement (++entry) to get to the next element (iteration
@@ -1319,6 +1602,7 @@ public:
return Iterator(memberTable.begin());
}
+
/**
C++ STL style iterator method. Returns one after the last iterator
element.
@@ -1327,7 +1611,11 @@ public:
return Iterator(memberTable.end());
}
};
+
}
+
#endif
+
+
diff --git a/src/shared/vmap/BaseModel.cpp b/src/shared/vmap/BaseModel.cpp
index 335a9a128e0..2ffd5672218 100644
--- a/src/shared/vmap/BaseModel.cpp
+++ b/src/shared/vmap/BaseModel.cpp
@@ -17,12 +17,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "BaseModel.h"
#include "VMapTools.h"
+
using namespace G3D;
+
namespace VMAP
{
//==========================================================
+
void BaseModel::getMember(Array<TriangleBox>& pMembers)
{
for(unsigned int i=0; i<iNTriangles; i++)
@@ -30,12 +34,15 @@ namespace VMAP
pMembers.append(iTriangles[i]);
}
}
+
//==========================================================
BaseModel::BaseModel(unsigned int pNNodes, unsigned int pNTriangles)
{
init(pNNodes, pNTriangles);
};
+
//==========================================================
+
void BaseModel::init(unsigned int pNNodes, unsigned int pNTriangles)
{
iNNodes = pNNodes;
@@ -45,16 +52,21 @@ namespace VMAP
if(iNNodes >0) iTreeNodes = new TreeNode[iNNodes];
if(iNTriangles >0) iTriangles = new TriangleBox[iNTriangles];
}
+
//==========================================================
+
void BaseModel::free()
{
if(getTriangles() != 0) delete [] getTriangles(); setNTriangles(0);
if(getTreeNodes() != 0) delete [] getTreeNodes(); setNNodes(0);
}
+
//==========================================================
+
void BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& /*pOutNormal*/) const
{
bool isInside = false;
+
float d = MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
pRay.origin, pRay.direction,
pBox,
@@ -64,7 +76,9 @@ namespace VMAP
pMaxDist = d;
}
}
+
//==========================================================
+
bool BaseModel::intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const
{
// See if the ray will ever hit this node or its children
@@ -73,9 +87,12 @@ namespace VMAP
bool rayWillHitBounds =
MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
pRay.origin, pRay.direction, pBox, location, alreadyInsideBounds);
+
bool canHitThisNode = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - pRay.origin).squaredLength() < (pMaxDist * pMaxDist))));
+
return canHitThisNode;
}
+
} // VMAP
diff --git a/src/shared/vmap/BaseModel.h b/src/shared/vmap/BaseModel.h
index 1e896bb62ec..098e1d9381b 100644
--- a/src/shared/vmap/BaseModel.h
+++ b/src/shared/vmap/BaseModel.h
@@ -17,22 +17,29 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _BASEMODEL_H_
#define _BASEMODEL_H_
+
#include <G3D/AABox.h>
#include <G3D/Vector3.h>
+
#include "ShortVector.h"
#include "ShortBox.h"
#include "TreeNode.h"
+
/**
A model is based on triangles. To be able to check intersection we need a BSP-Tree.
This Class holds the array of triangles as well as the management information for the BSP-Tree.
Both are stored in static array and index information is used instead of pointers.
Therefore we can load the whole object as a binary block.
+
The vectors are relative to a base position.
*/
+
namespace VMAP
{
+
class BaseModel
{
protected:
@@ -48,31 +55,48 @@ namespace VMAP
iNNodes = pNNodes; iNTriangles = pNTriangles; iTriangles = pTriangleBox; iTreeNodes = pTreeNode;
};
BaseModel(unsigned int pNNodes, unsigned int pNTriangles);
+
// destructor does nothing ! The subclass controles the array memory and knows when to free it
~BaseModel() {}
+
void free();
void init(unsigned int pNNodes, unsigned int pNTriangles);
+
void getMember(G3D::Array<TriangleBox>& pMembers);
+
inline const TriangleBox& getTriangle(int pPos) const { return(iTriangles[pPos]); }
inline TriangleBox& getTriangle(int pPos) { return(iTriangles[pPos]); }
+
inline void setTriangle(const TriangleBox& pTriangleBox, int pPos) { iTriangles[pPos] = pTriangleBox; }
+
inline const TreeNode& getTreeNode(int pPos) const { return(getTreeNodes()[pPos]); }
inline TreeNode& getTreeNode(int pPos) { return(getTreeNodes()[pPos]); }
+
inline void setTreeNode(const TreeNode& pTreeNode, int pPos) { getTreeNodes()[pPos] = pTreeNode; }
+
inline void setBasePosition(const G3D::Vector3& pBasePosition) { iBasePosition = pBasePosition; }
+
inline const G3D::Vector3& getBasePosition() const { return(iBasePosition); }
+
inline unsigned int getNNodes() const { return(iNNodes); }
inline unsigned int getNTriangles() const { return(iNTriangles); }
+
inline void setNNodes(unsigned int pNNodes) { iNNodes = pNNodes; }
inline void setNTriangles(unsigned int pNTriangles) { iNTriangles = pNTriangles; }
+
inline void setTriangleArray(TriangleBox *pGlobalTriangleArray ) { iTriangles = pGlobalTriangleArray ; }
inline void setTreeNodeArray(TreeNode *pGlobalTreeNodeArray ) { iTreeNodes = pGlobalTreeNodeArray ; }
+
inline TriangleBox* getTriangles() const { return(iTriangles); }
+
inline TreeNode* getTreeNodes() const{ return(iTreeNodes); }
+
inline size_t getMemUsage() { return(iNTriangles * sizeof(TriangleBox) + iNNodes * sizeof(TreeNode) + sizeof(BaseModel)); }
+
void intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const;
bool intersect(const G3D::AABox& pBox, const G3D::Ray& pRay, float& pMaxDist) const;
};
+
}
#endif /*BASEMODEL_H_*/
diff --git a/src/shared/vmap/CoordModelMapping.cpp b/src/shared/vmap/CoordModelMapping.cpp
index e4aa5e08893..39d1165f115 100644
--- a/src/shared/vmap/CoordModelMapping.cpp
+++ b/src/shared/vmap/CoordModelMapping.cpp
@@ -17,34 +17,45 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "CoordModelMapping.h"
+
#include <string.h>
#include <stdio.h>
+
using namespace G3D;
+
namespace VMAP
{
+
//============================================================
//============================================================
+
void CMappingEntry::addFilename(char *pName)
{
std::string name = std::string(pName);
if(!iFilenames.contains(name))
iFilenames.append(std::string(pName));
}
+
//============================================================
+
const std::string CMappingEntry::getKeyString() const
{
return(CMappingEntry::getKeyString(iMapId,xPos, yPos));
}
+
const std::string CMappingEntry::getKeyString( unsigned int pMapId, int pXPos, int pYPos )
{
char b[100];
sprintf(b,"%03u_%d_%d", pMapId, pXPos, pYPos);
return(std::string(b));
}
+
//============================================================
//============================================================
//============================================================
+
CoordModelMapping::~CoordModelMapping()
{
Array<std::string> keys = iMapObjectFiles.getKeys();
@@ -58,7 +69,9 @@ namespace VMAP
}
}
}
+
//============================================================
+
int findPosChar(const char *namebuffer, char pSearch, int pCount)
{
int result = -1;
@@ -87,7 +100,9 @@ namespace VMAP
printf("ERROR: Can't open file: %s\n",pDirectoryFileName.c_str());
return false;
}
+
char buffer[500+1];
+
CMappingEntry* cMappingEntry;
while(fgets(buffer, 500, f))
{
@@ -96,7 +111,9 @@ namespace VMAP
int xpos, ypos, noVec;
float scale;
xpos = ypos = noVec = 0;
+
//sscanf(buffer, "%d %d %s %s %f %d", &xpos, &ypos, namebuffer,positionbuffer, &scale, &noVec);
+
// this is ugly, but the format has no read delimiter and a space could be in the first part of the name
int nameStart = findPosChar(buffer, ' ', 2);// find the 2. space
if(nameStart > -1 && (iFilterMethod == NULL || (*iFilterMethod)(buffer)))
@@ -107,6 +124,7 @@ namespace VMAP
// find the 1. space (after the name)
nameEnd += findPosChar(&buffer[nameEnd], ' ', 1);
buffer[nameEnd] = 0; // terminate the name
+
sscanf(buffer, "%d %d", &xpos, &ypos);
sscanf(&buffer[nameEnd+1], "%s %f %d", positionbuffer, &scale, &noVec);
unsigned int mapId = getMapIdFromFilename(std::string(&buffer[nameStart]));
@@ -120,6 +138,7 @@ namespace VMAP
xpos = 0; // store all files under the groupKey
ypos = 0;
}
+
std::string key = CMappingEntry::getKeyString(mapId, xpos, ypos);
cMappingEntry = getCMappingEntry(key);
if(cMappingEntry == 0)
@@ -136,15 +155,19 @@ namespace VMAP
fclose(f);
return true;
}
+
//============================================================
+
const NameCollection CoordModelMapping::getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos)
{
NameCollection result;
Array<std::string> rawNames;
+
CMappingEntry *entry = getCMappingEntry(CMappingEntry::getKeyString(pMapId, xPos, yPos));
if(entry != 0)
{
rawNames = entry->getFilenames();
+
int pos = 0;
while(pos < rawNames.size())
{
@@ -170,6 +193,8 @@ namespace VMAP
}
return result;
}
+
//=================================================================
+
}
diff --git a/src/shared/vmap/CoordModelMapping.h b/src/shared/vmap/CoordModelMapping.h
index 8600620cc99..7684bf1b373 100644
--- a/src/shared/vmap/CoordModelMapping.h
+++ b/src/shared/vmap/CoordModelMapping.h
@@ -17,32 +17,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _COORDMODELMAPPING_H_
#define _COORDMODELMAPPING_H_
+
#include <cstdio>
#include <G3D/Table.h>
#include <G3D/Array.h>
+
/**
This Class is a helper Class to convert the raw vector data into BSP-Trees.
We read the directory file of the raw data output and build logical groups.
Models with a lot of vectors are not merged into a resulting model, but separated into an additional file.
*/
+
namespace VMAP
{
+
#define MIN_VERTICES_FOR_OWN_CONTAINER_FILE 65000
+
// if we are in an instance
#define MIN_INST_VERTICES_FOR_OWN_CONTAINER_FILE 40000
+
//=====================================================
class NameCollection
{
public:
G3D::Array<std::string> iMainFiles;
G3D::Array<std::string> iSingeFiles;
+
void appendToMain(const std::string& pStr) { iMainFiles.append(pStr); }
void appendToSingle(const std::string& pStr) { iSingeFiles.append(pStr); }
+
size_t size() { return (iMainFiles.size() + iSingeFiles.size()); }
};
+
//=====================================================
+
class CMappingEntry
{
private:
@@ -50,6 +61,7 @@ namespace VMAP
int yPos;
unsigned int iMapId;
G3D::Array<std::string> iFilenames;
+
public:
CMappingEntry() { };
CMappingEntry(unsigned int pMapId, const int pXPos, const int pYPos)
@@ -58,12 +70,17 @@ namespace VMAP
xPos = pXPos; yPos = pYPos;
};
~CMappingEntry() {};
+
void addFilename(char *pName);
const std::string getKeyString() const;
inline const G3D::Array<std::string>& getFilenames() const { return(iFilenames); }
+
static const std::string getKeyString(unsigned int pMapId, int pXPos, int pYPos);
+
};
+
//=====================================================
+
class CoordModelMapping
{
private:
@@ -72,10 +89,12 @@ namespace VMAP
G3D::Array<unsigned int> iMapIds;
G3D::Array<unsigned int> iWorldAreaGroups;
bool (*iFilterMethod)(char *pName);
+
inline void addCMappingEntry(CMappingEntry* pCMappingEntry)
{
iMapObjectFiles.set(pCMappingEntry->getKeyString(), pCMappingEntry);
}
+
inline CMappingEntry* getCMappingEntry(const std::string& pKey)
{
if(iMapObjectFiles.containsKey(pKey))
@@ -83,14 +102,19 @@ namespace VMAP
else
return 0;
}
+
public:
CoordModelMapping() { iFilterMethod = NULL; }
virtual ~CoordModelMapping();
+
bool readCoordinateMapping(const std::string& pDirectoryFileName);
+
const NameCollection getFilenamesForCoordinate(unsigned int pMapId, int xPos, int yPos);
+
static unsigned int getMapIdFromFilename(const std::string& pName)
{
size_t spos;
+
spos = pName.find_last_of('/');
std::string basename = pName.substr(0, spos);
spos = basename.find_last_of('/');
@@ -98,9 +122,11 @@ namespace VMAP
unsigned int mapId = atoi(groupname.c_str());
return(mapId);
}
+
const G3D::Array<unsigned int>& getMaps() const { return iMapIds; }
bool isAlreadyProcessedSingleFile(const std::string& pName) const { return iProcesseSingleFiles.containsKey(pName); }
void addAlreadyProcessedSingleFile(const std::string& pName) { iProcesseSingleFiles.set(pName,pName); }
+
inline void addWorldAreaMap(unsigned int pMapId)
{
if(!iWorldAreaGroups.contains(pMapId))
@@ -110,6 +136,7 @@ namespace VMAP
}
inline bool isWorldAreaMap(unsigned int pMapId) { return(iWorldAreaGroups.contains(pMapId)); }
void setModelNameFilterMethod(bool (*pFilterMethod)(char *pName)) { iFilterMethod = pFilterMethod; }
+
};
}
#endif /*_COORDMODELMAPPING_H_*/
diff --git a/src/shared/vmap/DebugCmdLogger.cpp b/src/shared/vmap/DebugCmdLogger.cpp
index 5b5fbebdd59..c899606045b 100644
--- a/src/shared/vmap/DebugCmdLogger.cpp
+++ b/src/shared/vmap/DebugCmdLogger.cpp
@@ -17,12 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <cstdio>
+
#include "DebugCmdLogger.h"
#include <stdio.h>
+
using namespace G3D;
+
namespace VMAP
{
+
bool CommandFileRW::appendCmd(const Command&
#ifdef _DEBUG
pCommand
@@ -50,7 +55,9 @@ namespace VMAP
return true;
#endif
}
+
//=========================================================
+
bool CommandFileRW::appendCmds(const Array<Command>&
#ifdef _DEBUG
pCmdArray
@@ -67,6 +74,7 @@ namespace VMAP
else
f = fopen(iFileName.c_str(), "ab");
resetfile = false;
+
if(f)
{
result = true;
@@ -86,7 +94,9 @@ namespace VMAP
return true;
#endif
}
+
//=========================================================
+
bool CommandFileRW::getNewCommands(Array<Command>& pCmdArray)
{
bool result = false;
diff --git a/src/shared/vmap/DebugCmdLogger.h b/src/shared/vmap/DebugCmdLogger.h
index f67687343c1..5493ab6f332 100644
--- a/src/shared/vmap/DebugCmdLogger.h
+++ b/src/shared/vmap/DebugCmdLogger.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _DEBUGCMDLOGGER_H
#define _DEBUGCMDLOGGER_H
+
#include <G3D/Vector3.h>
#include <G3D/Array.h>
+
/**
Class is used for debugging. We log activities into a file.
With an external Class we read that log and display the activity in a graphical view.
*/
+
namespace VMAP
{
+
//==========================================
enum C_TYPES
{
@@ -41,6 +46,7 @@ namespace VMAP
TEST_HEIGHT,
TEST_OBJECT_HIT,
};
+
class Command
{
int iType;
@@ -48,11 +54,14 @@ namespace VMAP
int ints[4];
char buffer[100];
public:
+
Command() { iType = STOP; }
+
inline int getType() { return iType; }
inline G3D::Vector3 getVector(int pos) { return(G3D::Vector3(floats[pos*3+0], floats[pos*3+1], floats[pos*3+2])); }
inline int getInt(int pos) { return(ints[pos]); }
inline char* getBuffer() { return(buffer); }
+
void fillStopCmd() { iType = STOP; }
void fillStartCmd() { iType = START; }
void fillLoadTileCmd(int x, int y, G3D::uint32 pMapId) { iType = LOAD_TILE; ints[0] = x; ints[1] = y; ints[2] = pMapId; }
@@ -78,9 +87,12 @@ namespace VMAP
floats[6] = pResultPos.x; floats[7]=pResultPos.y; floats[8]=pResultPos.z;
ints[0] = result; ints[1] = pMapId;
}
+
bool isCoreCmd() const { return(iType != TEST_VIS); }
};
+
//==========================================
+
class CommandFileRW
{
private:
@@ -97,9 +109,11 @@ namespace VMAP
void setFileName(const std::string& pName) { iFileName = pName; }
bool getNewCommands(G3D::Array<Command>& commandArray);
const G3D::Array<G3D::Array<Command> >& getFullCommandArray() { return iCommandArray; }
+
bool appendCmd(const Command& pCommand);
bool appendCmds(const G3D::Array<Command>& pCmdArray);
};
+
}
#endif
diff --git a/src/shared/vmap/IVMapManager.h b/src/shared/vmap/IVMapManager.h
index 40bb3164c0e..243a15aef73 100644
--- a/src/shared/vmap/IVMapManager.h
+++ b/src/shared/vmap/IVMapManager.h
@@ -17,36 +17,50 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _IVMAPMANAGER_H
#define _IVMAPMANAGER_H
+
#include<string>
+
//===========================================================
+
/**
This is the minimum interface to the VMapMamager.
*/
+
namespace VMAP
{
+
enum VMAP_LOAD_RESULT
{
VMAP_LOAD_RESULT_ERROR,
VMAP_LOAD_RESULT_OK,
VMAP_LOAD_RESULT_IGNORED,
};
+
#define VMAP_INVALID_HEIGHT -100000.0f // for check
#define VMAP_INVALID_HEIGHT_VALUE -200000.0f // real assigned value in unknown height case
+
//===========================================================
class IVMapManager
{
private:
bool iEnableLineOfSightCalc;
bool iEnableHeightCalc;
+
public:
IVMapManager() : iEnableLineOfSightCalc(true), iEnableHeightCalc(true) {}
+
virtual ~IVMapManager(void) {}
+
virtual int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
+
virtual bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y) = 0;
+
virtual void unloadMap(unsigned int pMapId, int x, int y) = 0;
virtual void unloadMap(unsigned int pMapId) = 0;
+
virtual bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) = 0;
virtual float getHeight(unsigned int pMapId, float x, float y, float z) = 0;
/**
@@ -58,6 +72,7 @@ namespace VMAP
send debug commands
*/
virtual bool processCommand(char *pCommand)= 0;
+
/**
Enable/disable LOS calculation
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
@@ -68,9 +83,11 @@ namespace VMAP
It is enabled by default. If it is enabled in mid game the maps have to loaded manualy
*/
void setEnableHeightCalc(bool pVal) { iEnableHeightCalc = pVal; }
+
bool isLineOfSightCalcEnabled() const { return(iEnableLineOfSightCalc); }
bool isHeightCalcEnabled() const { return(iEnableHeightCalc); }
bool isMapLoadingEnabled() const { return(iEnableLineOfSightCalc || iEnableHeightCalc ); }
+
virtual std::string getDirFileName(unsigned int pMapId, int x, int y) const =0;
/**
Block maps from being used.
@@ -79,6 +96,7 @@ namespace VMAP
*/
virtual void preventMapsFromBeingUsed(const char* pMapIdString) =0;
};
+
}
#endif
diff --git a/src/shared/vmap/ManagedModelContainer.cpp b/src/shared/vmap/ManagedModelContainer.cpp
index a5258d86660..c7bfefe7179 100644
--- a/src/shared/vmap/ManagedModelContainer.cpp
+++ b/src/shared/vmap/ManagedModelContainer.cpp
@@ -17,16 +17,22 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "ManagedModelContainer.h"
+
using namespace G3D;
+
namespace VMAP
{
+
ManagedModelContainer::ManagedModelContainer(void) : ModelContainer()
{
refCount = 0;
}
+
ManagedModelContainer::~ManagedModelContainer(void)
{
}
+
}
diff --git a/src/shared/vmap/ManagedModelContainer.h b/src/shared/vmap/ManagedModelContainer.h
index c909aa878cf..e6862f915c7 100644
--- a/src/shared/vmap/ManagedModelContainer.h
+++ b/src/shared/vmap/ManagedModelContainer.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _MANAGEDMODELCONTAINER_H
#define _MANAGEDMODELCONTAINER_H
+
#include "ModelContainer.h"
+
//=======================================================
/**
This is a ModelContainer with reference count information.
*/
+
namespace VMAP
{
//=======================================================
+
class ManagedModelContainer :
public ModelContainer
{
@@ -35,10 +40,12 @@ namespace VMAP
public:
ManagedModelContainer(void) ;
~ManagedModelContainer(void);
+
void incRefCount() { ++refCount; }
void decRefCount() { --refCount; if(refCount < 0) refCount = 0; }
int getRefCount() { return refCount; }
};
+
//=======================================================
}
#endif
diff --git a/src/shared/vmap/ModelContainer.cpp b/src/shared/vmap/ModelContainer.cpp
index 94d453079d2..4a722d2585b 100644
--- a/src/shared/vmap/ModelContainer.cpp
+++ b/src/shared/vmap/ModelContainer.cpp
@@ -17,12 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <iostream>
#include <fstream>
+
#include <string.h>
+
#include "ModelContainer.h"
#include "VMapDefinitions.h"
+
using namespace G3D;
+
namespace VMAP
{
//==========================================================
@@ -34,25 +39,32 @@ namespace VMAP
return (pMc.getBasePosition() * pMc.getNTriangles()).hashCode();
}
//==========================================================
+
ModelContainer::ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel) :
BaseModel(pNNodes, pNTriangles)
{
+
iNSubModel = pNSubModel;
iSubModel = 0;
if(pNSubModel > 0) iSubModel = new SubModel[iNSubModel];
}
+
//==========================================================
+
bool ModelContainer::operator==(const ModelContainer& pMc2) const
{
if (this->iNSubModel == 0 && pMc2.iNSubModel == 0 && this->iSubModel == 0 && pMc2.iSubModel == 0)
return true;
return this == &pMc2;
}
+
//==========================================================
+
void ModelContainer::countSubModelsAndNodesAndTriangles(AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles)
{
// For this node we will need a TreeNode as well as for the internal nodes
++nNodes;
+
nSubModels += pNode.valueArray.size();
for(int i=0;i<pNode.valueArray.size(); i++)
{
@@ -62,6 +74,7 @@ namespace VMAP
nNodes += m->getNNodes();
nTriangles += m->getNTriangles();
}
+
if(pNode.child[0] != 0)
{
countSubModelsAndNodesAndTriangles(*pNode.child[0], nSubModels, nNodes, nTriangles);
@@ -72,6 +85,7 @@ namespace VMAP
}
}
//==========================================================
+
void ModelContainer::fillContainer(const AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi, Vector3& pFinalLo, Vector3& pFinalHi)
{
// TreeNode for the SubModel
@@ -79,20 +93,26 @@ namespace VMAP
treeNode.setSplitAxis(pNode.splitAxis);
treeNode.setSplitLocation(pNode.splitLocation);
int currentTreeNodePos = pTreeNodePos++;
+
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
+
for(int i=0;i<pNode.valueArray.size(); i++)
{
G3D::_AABSPTree::Handle<SubModel*>* h= pNode.valueArray[i];
SubModel *m = h->value;
+
memcpy(&getTreeNodes()[pTreeNodePos], &m->getTreeNode(0), sizeof(TreeNode) * m->getNNodes());
memcpy(&getTriangles()[pTrianglePos], &m->getTriangle(0), sizeof(TriangleBox) * m->getNTriangles());
+
SubModel newModel = SubModel(m->getNTriangles(), getTriangles(), pTrianglePos, m->getNNodes(), getTreeNodes(), pTreeNodePos);
newModel.setReletiveBounds(m->getReletiveBounds().getLo(), m->getReletiveBounds().getHi());
newModel.setBasePosition(m->getBasePosition());
iSubModel[pSubModelPos++] = newModel;
+
pTreeNodePos += m->getNNodes();
pTrianglePos += m->getNTriangles();
+
AABox box = m->getAABoxBounds();
lo = lo.min(box.low());
hi = hi.max(box.high());
@@ -105,6 +125,7 @@ namespace VMAP
}
*/
// get absolute bounds
+
if(pNode.child[0] != 0)
{
treeNode.setChildPos(0, pTreeNodePos);
@@ -115,45 +136,62 @@ namespace VMAP
treeNode.setChildPos(1, pTreeNodePos);
fillContainer(*pNode.child[1], pSubModelPos, pTreeNodePos, pTrianglePos, lo, hi,pFinalLo,pFinalHi);
}
+
pLo = pLo.min(lo);
pHi = pHi.max(hi);
+
treeNode.setBounds(lo,hi);
+
setTreeNode(treeNode, currentTreeNodePos);
+
}
+
//==========================================================
/**
Create the structure out of a AABSPTree
*/
+
ModelContainer::ModelContainer(AABSPTree<SubModel *> *pTree)
{
+
int nSubModels, nNodes, nTriangles;
nSubModels = nNodes = nTriangles = 0;
countSubModelsAndNodesAndTriangles(*pTree->root, nSubModels, nNodes, nTriangles);
+
init(nNodes, nTriangles);
+
iNSubModel = nSubModels;
+
iSubModel = new SubModel[iNSubModel];
+
int subModelPos,treeNodePos, trianglePos;
subModelPos = treeNodePos = trianglePos = 0;
+
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
Vector3 finalLo, finalHi;
finalLo = lo;
finalHi = hi;
+
fillContainer(*pTree->root, subModelPos, treeNodePos, trianglePos, lo, hi, finalLo, finalHi);
setBounds(finalLo, finalHi);
}
+
//==========================================================
+
ModelContainer::~ModelContainer(void)
{
free();
if(iSubModel != 0) delete [] iSubModel;
}
//==========================================================
+
bool ModelContainer::writeFile(const char *filename)
{
bool result = false;
unsigned int flags=0;
unsigned int size;
+
FILE *wf =fopen(filename,"wb");
if(wf)
{
@@ -161,11 +199,13 @@ namespace VMAP
result = true;
if(result && fwrite("CTREE01",8,1,wf) != 1) result = false;
if(result && fwrite(&flags,sizeof(unsigned int),1,wf) != 1) result = false;
+
if(result && fwrite("POS ",4,1,wf) != 1) result = false;
size = sizeof(float)*3;
if(result && fwrite(&size,4,1,wf) != 1) result = false;
Vector3 basePos = getBasePosition();
if(result && fwrite(&basePos,sizeof(float),3,wf) != 3) result = false;
+
if(result && fwrite("BOX ",4,1,wf) != 1) result = false;
size = sizeof(float)*6;
if(result && fwrite(&size,4,1,wf) != 1) result = false;
@@ -173,28 +213,35 @@ namespace VMAP
if(result && fwrite(&low,sizeof(float),3,wf) != 3) result = false;
Vector3 high = iBox.high();
if(result && fwrite(&high,sizeof(float),3,wf) != 3) result = false;
+
if(result && fwrite("NODE",4,1,wf) != 1) result = false;
size = sizeof(unsigned int)+ sizeof(TreeNode)*getNNodes();
if(result && fwrite(&size,4,1,wf) != 1) result = false;
unsigned int val = getNNodes();
if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite(getTreeNodes(),sizeof(TreeNode),getNNodes(),wf) != getNNodes()) result = false;
+
if(result && fwrite("TRIB",4,1,wf) != 1) result = false;
size = sizeof(unsigned int)+ sizeof(TriangleBox)*getNTriangles();
if(result && fwrite(&size,4,1,wf) != 1) result = false;
val = getNTriangles();
if(result && fwrite(&val,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite(getTriangles(),sizeof(TriangleBox),getNTriangles(),wf) != getNTriangles()) result = false;
+
if(result && fwrite("SUBM",4,1,wf) != 1) result = false;
size = sizeof(unsigned int)+ sizeof(SubModel)*iNSubModel;
if(result && fwrite(&size,4,1,wf) != 1) result = false;
if(result && fwrite(&iNSubModel,sizeof(unsigned int),1,wf) != 1) result = false;
if(result && fwrite(iSubModel,sizeof(SubModel),iNSubModel,wf) != iNSubModel) result = false;
+
fclose(wf);
}
+
return(result);
}
+
//===============================================================
+
bool ModelContainer::readFile(const char *filename)
{
bool result = false;
@@ -207,6 +254,7 @@ namespace VMAP
if(rf)
{
free();
+
result = true;
char magic[8]; // Ignore the added magic header
fread(magic,1,8,rf);
@@ -219,6 +267,7 @@ namespace VMAP
Vector3 basePos;
if(result && fread(&basePos,sizeof(float),3,rf) != 3) result = false;
setBasePosition(basePos);
+
//---- Box
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
@@ -226,25 +275,32 @@ namespace VMAP
if(result && fread(&low,sizeof(float),3,rf) != 3) result = false;
if(result && fread(&high,sizeof(float),3,rf) != 3) result = false;
setBounds(low, high);
+
//---- TreeNodes
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
+
if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false;
if(result) setNNodes(ival);
if(result) setTreeNodeArray(new TreeNode[getNNodes()]);
if(result && fread(getTreeNodes(),sizeof(TreeNode),getNNodes(),rf) != getNNodes()) result = false;
+
//---- TriangleBoxes
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
+
if(result && fread(&ival,sizeof(unsigned int),1,rf) != 1) result = false;
setNTriangles(ival);
if(result) setTriangleArray(new TriangleBox[getNTriangles()]);
if(result && fread(getTriangles(),sizeof(TriangleBox),getNTriangles(),rf) != getNTriangles()) result = false;
+
//---- SubModel
if(result && fread(chunk,4,1,rf) != 1) result = false;
if(result && fread(&size,4,1,rf) != 1) result = false;
+
if(result && fread(&iNSubModel,sizeof(unsigned int),1,rf) != 1) result = false;
if(result) iSubModel = new SubModel[iNSubModel];
+
if(result)
{
for(unsigned int i=0;i<iNSubModel && result; ++i)
@@ -260,12 +316,15 @@ namespace VMAP
}
return result;
}
+
//=================================================================
+
size_t ModelContainer::getMemUsage()
{
// BaseModel is included in ModelContainer
return(iNSubModel * sizeof(SubModel) + BaseModel::getMemUsage() + sizeof(ModelContainer) - sizeof(BaseModel));
}
+
//=================================================================
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
@@ -275,6 +334,7 @@ namespace VMAP
extern bool myfound;
#endif
#endif
+
void ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const
{
IntersectionCallBack<SubModel> intersectCallback;
@@ -282,12 +342,16 @@ namespace VMAP
Ray relativeRay = Ray::fromOriginAndDirection(pRay.origin - getBasePosition(), pRay.direction);
iTreeNodes[0].intersectRay(pRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false);
}
+
//==========================================================
+
bool ModelContainer::intersect(const G3D::Ray& pRay, float& pMaxDist) const
{
return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist);
}
+
//=================================================================
+
template<typename RayCallback>
void ModelContainer::intersectRay(const G3D::Ray& pRay, RayCallback& intersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast)
{
@@ -302,7 +366,9 @@ namespace VMAP
{
pAABox = pMc.getAABoxBounds();
}
+
//=================================================================
+
void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox)
{
pAABox = pMc->getAABoxBounds();
diff --git a/src/shared/vmap/ModelContainer.h b/src/shared/vmap/ModelContainer.h
index 6a595bec39f..abc96261050 100644
--- a/src/shared/vmap/ModelContainer.h
+++ b/src/shared/vmap/ModelContainer.h
@@ -17,18 +17,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _MODELCONTAINER_H
#define _MODELCONTAINER_H
+
// load our modified version first !!
#include "AABSPTree.h"
+
#include <G3D/AABox.h>
#include <G3D/Vector3.h>
#include <G3D/Ray.h>
+
#include "ShortBox.h"
#include "TreeNode.h"
#include "VMapTools.h"
#include "SubModel.h"
#include "BaseModel.h"
+
namespace VMAP
{
/**
@@ -39,41 +44,65 @@ namespace VMAP
The references are done by indexes within these static arrays.
Therefore we are able to just load a binary block and do not need to mess around with memory allocation and pointers.
*/
+
//=====================================================
+
class ModelContainer : public BaseModel
{
private:
unsigned int iNSubModel;
SubModel *iSubModel;
G3D::AABox iBox;
+
ModelContainer (const ModelContainer& c): BaseModel(c) {}
ModelContainer& operator=(const ModelContainer& ) {}
+
public:
ModelContainer() : BaseModel() { iNSubModel =0; iSubModel = 0; };
+
// for the mainnode
ModelContainer(unsigned int pNTriangles, unsigned int pNNodes, unsigned int pNSubModel);
+
ModelContainer(G3D::AABSPTree<SubModel *> *pTree);
+
~ModelContainer(void);
+
inline const void setSubModel(const SubModel& pSubModel, int pPos) { iSubModel[pPos] = pSubModel; }
+
inline const SubModel& getSubModel(int pPos) const { return iSubModel[pPos]; }
+
inline unsigned int getNSubModel() const { return(iNSubModel); }
+
void countSubModelsAndNodesAndTriangles(G3D::AABSPTree<SubModel *>::Node& pNode, int& nSubModels, int& nNodes, int& nTriangles);
+
void fillContainer(const G3D::AABSPTree<SubModel *>::Node& pNode, int &pSubModelPos, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi, G3D::Vector3& pFinalLo, G3D::Vector3& pFinalHi);
+
bool readRawFile(const char *name);
+
inline const G3D::AABox& getAABoxBounds() const { return(iBox); }
+
inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBox.set(lo,hi); }
+
bool writeFile(const char *filename);
+
bool readFile(const char *filename);
+
size_t getMemUsage();
size_t hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); }
+
void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const;
bool intersect(const G3D::Ray& pRay, float& pMaxDist) const;
+
template<typename RayCallback>
void intersectRay(const G3D::Ray& ray, RayCallback& intersectCallback, float& distance, bool pStopAtFirstHit, bool intersectCallbackIsFast = false);
+
bool operator==(const ModelContainer& pMc2) const;
};
+
//=====================================================
+
//=====================================================
+
size_t hashCode(const ModelContainer& pMc);
void getBounds(const ModelContainer& pMc, G3D::AABox& pAABox);
void getBounds(const ModelContainer* pMc, G3D::AABox& pAABox);
diff --git a/src/shared/vmap/NodeValueAccess.h b/src/shared/vmap/NodeValueAccess.h
index 01c085be834..54fc5ee99b6 100644
--- a/src/shared/vmap/NodeValueAccess.h
+++ b/src/shared/vmap/NodeValueAccess.h
@@ -17,25 +17,32 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _NODEVALUEACCESS_H
#define _NODEVALUEACCESS_H
+
namespace VMAP
{
/**
This is a helper Class to get access to SubModels or triangles when analyzing the BSP-Tree.
*/
+
template<class TNode, class TValue> class NodeValueAccess
{
private:
TNode const* iNodeArray;
TValue const* iValueArray;
+
public:
inline NodeValueAccess() : iNodeArray(NULL), iValueArray(NULL) {}
+
inline NodeValueAccess(TNode const* pNodeArray, TValue const* pValueArray) : iNodeArray(pNodeArray), iValueArray(pValueArray) {}
inline TNode const* getNodePtr() const { return(iNodeArray); }
inline TValue const* getValuePtr() const { return(iValueArray); }
+
inline TNode const& getNode(unsigned int pPos) const { return(iNodeArray[pPos]); }
inline void setNode(const TNode& pNode, unsigned int pPos) { iNodeArray[pPos] = pNode; }
+
inline TValue const& getValue(unsigned int pPos) const { return(iValueArray[pPos]); }
inline void setValue(const TValue& pValue, unsigned int pPos) { iValueArray[pPos] = pValue; }
};
diff --git a/src/shared/vmap/ShortBox.h b/src/shared/vmap/ShortBox.h
index f2d87bc6abe..0e98677aa9e 100644
--- a/src/shared/vmap/ShortBox.h
+++ b/src/shared/vmap/ShortBox.h
@@ -17,18 +17,24 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _SHORTBOX_H
#define _SHORTBOX_H
+
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
#include <G3D/Triangle.h>
#include <G3D/Ray.h>
+
#include "ShortVector.h"
+
/**
This is a box and a triangle Class using ShortVectors. Each vector has 16 bit an a fixed point 12.4 representation.
*/
+
namespace VMAP
{
+
class ShortBox
{
private:
@@ -42,15 +48,18 @@ namespace VMAP
inline void setHi(const ShortVector& pV){ iV2 = pV; }
inline void setLo(const G3D::Vector3& pV){ iV1 = ShortVector(pV); }
inline void setHi(const G3D::Vector3& pV){ iV2 = ShortVector(pV); }
+
inline bool operator==(const ShortBox& b) const
{
return ((iV1 == b.iV1) && (iV2 == b.iV2));
}
+
inline bool operator!=(const ShortBox& b) const
{
return !((iV1 == b.iV1) && (iV2 == b.iV2));
}
};
+
//=====================================================================
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
@@ -61,7 +70,9 @@ namespace VMAP
extern bool myfound;
#endif
#endif
+
static const G3D::Vector3 dummyZeroPosition = G3D::Vector3(0,0,0);
+
class TriangleBox
{
private:
@@ -74,16 +85,20 @@ namespace VMAP
_vertex[0] = pV1;
_vertex[1] = pV2;
_vertex[2] = pV3;
+
}
inline const ShortVector& vertex (int n) const
{
return(_vertex[n]);
}
+
inline const ShortBox getBounds()const
{
ShortBox box;
+
ShortVector lo = _vertex[0];
ShortVector hi = lo;
+
for (int i = 1; i < 3; ++i)
{
lo = lo.min(_vertex[i]);
@@ -94,15 +109,19 @@ namespace VMAP
return(box);
}
inline const G3D::Vector3& getBasePosition() { return(dummyZeroPosition); }
+
inline const G3D::AABox getAABoxBounds() const { ShortBox box = getBounds(); return(G3D::AABox(box.getLo().getVector3(), box.getHi().getVector3())); }
+
inline bool operator==(const TriangleBox& t) const
{
return ((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2]));
}
+
inline bool operator!=(const TriangleBox& t) const
{
return !((_vertex[0] == t._vertex[0]) && (_vertex[1] == t._vertex[1]) &&(_vertex[2] == t._vertex[2]));
}
+
inline void intersect(const G3D::Ray& pRay, float& pMaxDist, bool /*pStopAtFirstHitDummy*/, G3D::Vector3& /*pOutLocationDummy*/, G3D::Vector3& /*pOutNormalDummy*/) const
{
static const double epsilon = 0.00001;
@@ -113,6 +132,7 @@ namespace VMAP
else
{
testT = G3D::Triangle(vertex(2).getVector3(),vertex(1).getVector3(),vertex(0).getVector3());
+
#ifdef _DEBUG_VMAPS
{
G3D::Triangle myt(testT.vertex(0)+p6, testT.vertex(1)+p6,testT.vertex(2)+p6);
@@ -125,6 +145,7 @@ namespace VMAP
}
}
};
+
}
#endif
diff --git a/src/shared/vmap/ShortVector.h b/src/shared/vmap/ShortVector.h
index 4de8cfde73d..5f0fb7d9fd8 100644
--- a/src/shared/vmap/ShortVector.h
+++ b/src/shared/vmap/ShortVector.h
@@ -17,25 +17,31 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _SHORTVECTOR_H
#define _SHORTVECTOR_H
+
#include <G3D/Vector3.h>
+
namespace VMAP
{
/**
Vector with 16 bit fix point values 12.4 bit.
*/
+
class ShortVector
{
private:
short iX;
short iY;
short iZ;
+
const static short maxvalue = 0x7fff;
const static short minvalue = -0x7fff;
const static int fixpointdiv = 16;
const static short fixpoint_maxvalue = maxvalue / fixpointdiv;
const static short fixpoint_minvalue = minvalue / fixpointdiv;
+
inline short float2Short(float fv) const
{
short sv;
@@ -59,6 +65,7 @@ namespace VMAP
fv = ((float)sv) / fixpointdiv;
return fv;
}
+
inline float getFX() const { return(short2Float(iX)); }
inline float getFY() const { return(short2Float(iY)); }
inline float getFZ() const { return(short2Float(iZ)); }
@@ -70,6 +77,7 @@ namespace VMAP
iY = float2Short(pVector.y);
iZ = float2Short(pVector.z);
}
+
inline ShortVector(float pX, float pY, float pZ)
{
iX = float2Short(pX);
@@ -88,10 +96,13 @@ namespace VMAP
iY = pShortVector.iY;
iZ = pShortVector.iZ;
}
+
inline float getX() const { return(iX); }
inline float getY() const { return(iY); }
inline float getZ() const { return(iZ); }
+
inline G3D::Vector3 getVector3() const { return(G3D::Vector3(getFX(), getFY(), getFZ())); }
+
inline ShortVector min(const ShortVector pShortVector)
{
ShortVector result = pShortVector;
@@ -100,6 +111,7 @@ namespace VMAP
if(pShortVector.iZ > iZ) { result.iZ = iZ; }
return(result);
}
+
inline ShortVector max(const ShortVector pShortVector)
{
ShortVector result = pShortVector;
@@ -108,14 +120,17 @@ namespace VMAP
if(pShortVector.iZ < iZ) { result.iZ = iZ; }
return(result);
}
+
inline bool operator==(const ShortVector& v) const
{
return (iX == v.iX && iY == v.iY && iZ == v.iZ);
}
+
inline bool operator!=(const ShortVector& v) const
{
return !(iX == v.iX && iY == v.iY && iZ == v.iZ);
}
+
};
}
#endif
diff --git a/src/shared/vmap/SubModel.cpp b/src/shared/vmap/SubModel.cpp
index f04090c6269..370c80062d6 100644
--- a/src/shared/vmap/SubModel.cpp
+++ b/src/shared/vmap/SubModel.cpp
@@ -17,13 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "SubModel.h"
+
#ifdef _ASSEMBLER_DEBUG
extern FILE *::g_df;
#endif
+
using namespace G3D;
+
namespace VMAP
{
+
//==========================================================
/**
Functions to use ModelContainer with a AABSPTree
@@ -32,16 +37,19 @@ namespace VMAP
{
return pSm.getNTriangles();
}
+
void getBounds(const SubModel& pSm, G3D::AABox& pAABox)
{
ShortBox box = pSm.getReletiveBounds();
pAABox.set(box.getLo().getVector3()+pSm.getBasePosition(), box.getHi().getVector3()+pSm.getBasePosition());
}
+
void getBounds(const SubModel* pSm, G3D::AABox& pAABox)
{
ShortBox box = pSm->getReletiveBounds();
pAABox.set(box.getLo().getVector3()+pSm->getBasePosition(), box.getHi().getVector3()+pSm->getBasePosition());
}
+
//==========================================================
//==========================================================
//==========================================================
@@ -53,7 +61,9 @@ namespace VMAP
iNodesPos = pNodesPos;
iHasInternalMemAlloc = false;
}
+
//==========================================================
+
SubModel::~SubModel(void)
{
if(iHasInternalMemAlloc)
@@ -61,10 +71,13 @@ namespace VMAP
free();
}
}
+
//==========================================================
+
bool SubModel::operator==(const SubModel& pSm2) const
{
bool result = false;
+
if(getNNodes() == pSm2.getNNodes() &&
getNTriangles() == pSm2.getNTriangles() &&
getBasePosition() == pSm2.getBasePosition() &&
@@ -76,6 +89,7 @@ namespace VMAP
return result;
}
//==========================================================
+
enum BIN_POSITIONS
{
BP_iNTriangles=8,
@@ -99,14 +113,18 @@ namespace VMAP
iHasInternalMemAlloc = *((bool *) (((char *) pBinBlock) + BP_iHasInternalMemAlloc));
iBox = *((ShortBox *) (((char *) pBinBlock) + BP_iBox));
}
+
//==========================================================
+
void SubModel::countNodesAndTriangles(AABSPTree<Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles)
{
++pNNodes;
pNTriabgles += pNode.valueArray.size();
+
#ifdef _ASSEMBLER_DEBUG
fprintf(::g_df, "Nodes: %d, Tris: %d\n",pNNodes, pNTriabgles);
#endif
+
if(pNode.child[0] != 0)
{
countNodesAndTriangles(*pNode.child[0], pNNodes, pNTriabgles);
@@ -116,15 +134,20 @@ namespace VMAP
countNodesAndTriangles(*pNode.child[1], pNNodes, pNTriabgles);
}
}
+
//==========================================================
+
void SubModel::fillContainer(const AABSPTree<Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, Vector3& pLo, Vector3& pHi)
{
TreeNode treeNode = TreeNode(pNode.valueArray.size(), pTrianglePos);
treeNode.setSplitAxis(pNode.splitAxis);
treeNode.setSplitLocation(pNode.splitLocation);
+
int currentTreeNodePos = pTreeNodePos++;
+
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
+
for(int i=0;i<pNode.valueArray.size(); i++)
{
G3D::_AABSPTree::Handle<Triangle>* h= pNode.valueArray[i];
@@ -132,8 +155,10 @@ namespace VMAP
TriangleBox triangleBox = TriangleBox(t.vertex(0),t.vertex(1), t.vertex(2));
lo = lo.min(triangleBox.getBounds().getLo().getVector3());
hi = hi.max(triangleBox.getBounds().getHi().getVector3());
+
getTriangles()[pTrianglePos++] = triangleBox;
}
+
if(pNode.child[0] != 0)
{
treeNode.setChildPos(0, pTreeNodePos);
@@ -144,29 +169,39 @@ namespace VMAP
treeNode.setChildPos(1, pTreeNodePos);
fillContainer(*pNode.child[1], pTreeNodePos, pTrianglePos, lo, hi);
}
+
treeNode.setBounds(lo,hi);
+
// get absolute bounds
pLo = pLo.min(lo);
pHi = pHi.max(hi);
+
getTreeNodes()[currentTreeNodePos] = treeNode;
}
+
//==========================================================
+
SubModel::SubModel(AABSPTree<Triangle> *pTree)
{
int nNodes, nTriangles;
nNodes = nTriangles = 0;
countNodesAndTriangles(*pTree->root, nNodes, nTriangles);
+
init(nNodes, nTriangles);
+
iTrianglesPos = 0; // this is the global array
iNodesPos = 0; // this is the global array
iHasInternalMemAlloc = true;
int treeNodePos, trianglePos;
treeNodePos = trianglePos = 0;
+
Vector3 lo = Vector3(inf(),inf(),inf());
Vector3 hi = Vector3(-inf(),-inf(),-inf());
+
fillContainer(*pTree->root, treeNodePos, trianglePos, lo, hi);
setReletiveBounds(lo, hi);
}
+
//==========================================================
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
@@ -177,6 +212,7 @@ namespace VMAP
extern bool myfound;
#endif
#endif
+
//==========================================================
void SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& /*pOutLocation*/, G3D::Vector3& /*pOutNormal*/) const
{
@@ -189,12 +225,16 @@ namespace VMAP
#endif
getTreeNode(0).intersectRay(relativeRay, intersectCallback, pMaxDist, vna, pStopAtFirstHit, false);
}
+
//==========================================================
+
bool SubModel::intersect(const G3D::Ray& pRay, float& pMaxDist) const
{
return BaseModel::intersect(getAABoxBounds(), pRay, pMaxDist);
}
+
//==========================================================
+
template<typename RayCallback>
void SubModel::intersectRay(const Ray& pRay, RayCallback& pIntersectCallback, float& pMaxDist, bool pStopAtFirstHit, bool intersectCallbackIsFast)
{
@@ -206,5 +246,6 @@ namespace VMAP
}
}
//==========================================================
+
}
diff --git a/src/shared/vmap/SubModel.h b/src/shared/vmap/SubModel.h
index d562f36d94e..89ea9b91d7b 100644
--- a/src/shared/vmap/SubModel.h
+++ b/src/shared/vmap/SubModel.h
@@ -17,15 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _SUBMODEL_H
#define _SUBMODEL_H
+
// load our modified version first !!
#include "AABSPTree.h"
+
#include "ShortVector.h"
#include "ShortBox.h"
#include "TreeNode.h"
#include "VMapTools.h"
#include "BaseModel.h"
+
namespace VMAP
{
/**
@@ -46,30 +50,44 @@ namespace VMAP
#endif
public:
SubModel() : BaseModel(){ };
+
SubModel(unsigned int pNTriangles, TriangleBox *pTriangles, unsigned int pTrianglesPos, unsigned int pNNodes, TreeNode *pTreeNodes, unsigned int pNodesPos);
SubModel(G3D::AABSPTree<G3D::Triangle> *pTree);
~SubModel(void);
//Gets a 50 byte binary block
void initFromBinBlock(void *pBinBlock);
+
void fillRenderArray(G3D::Array<TriangleBox> &pArray, const TreeNode* pTreeNode);
+
void countNodesAndTriangles(G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pNNodes, int &pNTriabgles);
+
void fillContainer(const G3D::AABSPTree<G3D::Triangle>::Node& pNode, int &pTreeNodePos, int &pTrianglePos, G3D::Vector3& pLo, G3D::Vector3& pHi);
+
inline const ShortBox& getReletiveBounds() const { return(iBox); }
+
inline void setReletiveBounds(const ShortVector& lo, const ShortVector& hi) { iBox.setLo(lo); iBox.setHi(hi); }
+
inline const G3D::AABox getAABoxBounds() const { return(G3D::AABox(iBox.getLo().getVector3() + getBasePosition(), iBox.getHi().getVector3()+ getBasePosition())); }
+
// get start pos bases on the global array
inline TriangleBox const* getTriangles() const { return &BaseModel::getTriangle(iTrianglesPos); }
inline TriangleBox * getTriangles() { return &BaseModel::getTriangle(iTrianglesPos); }
+
// get start pos bases on the global array
inline TreeNode const* getTreeNodes() const { return &BaseModel::getTreeNode(iNodesPos); }
inline TreeNode * getTreeNodes() { return &BaseModel::getTreeNode(iNodesPos); }
+
// internal method usign internal offset
inline const TreeNode& getTreeNode(int pPos) const { return(SubModel::getTreeNodes()[pPos]); }
+
// internal method usign internal offset
inline const TriangleBox& getTriangle(int pPos) const { return(SubModel::getTriangles()[pPos]); }
+
inline unsigned int getNodesPos() const { return(iNodesPos); }
inline unsigned int getTrianglesPos() const { return(iTrianglesPos); }
+
//unsigned int hashCode() { return (getBasePosition() * getNTriangles()).hashCode(); }
+
void intersect(const G3D::Ray& pRay, float& pMaxDist, bool pStopAtFirstHit, G3D::Vector3& pOutLocation, G3D::Vector3& pOutNormal) const;
bool intersect(const G3D::Ray& pRay, float& pMaxDist) const;
template<typename RayCallback>
@@ -77,6 +95,7 @@ namespace VMAP
bool operator==(const SubModel& pSm2) const;
unsigned int hashCode() const { return BaseModel::getNTriangles(); }
};
+
unsigned int hashCode(const SubModel& pSm);
void getBounds(const SubModel& pSm, G3D::AABox& pAABox);
void getBounds(const SubModel* pSm, G3D::AABox& pAABox);
diff --git a/src/shared/vmap/TileAssembler.cpp b/src/shared/vmap/TileAssembler.cpp
index 509696f39a2..75997a847a2 100644
--- a/src/shared/vmap/TileAssembler.cpp
+++ b/src/shared/vmap/TileAssembler.cpp
@@ -17,20 +17,27 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <G3D/Vector3.h>
#include <G3D/Triangle.h>
+
#include "TileAssembler.h"
#include "CoordModelMapping.h"
#include "ModelContainer.h"
+
#include <limits.h>
#include <string.h>
+
#ifdef _ASSEMBLER_DEBUG
FILE *g_df = NULL;
#endif
+
using namespace G3D;
+
namespace VMAP
{
//=================================================================
+
Vector3 ModelPosition::transform(const Vector3& pIn) const
{
//return(pIn);
@@ -39,8 +46,10 @@ namespace VMAP
out = ixMatrix * out;
out = iyMatrix * out;
return(out);
+
}
//=================================================================
+
TileAssembler::TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName)
{
iCurrentUniqueNameId = 0;
@@ -50,12 +59,16 @@ namespace VMAP
//mkdir(iDestDir);
init();
}
+
//=================================================================
+
TileAssembler::~TileAssembler()
{
delete iCoordModelMapping;
}
+
//=================================================================
+
void TileAssembler::init()
{
iCoordModelMapping = new CoordModelMapping();
@@ -65,16 +78,20 @@ namespace VMAP
addWorldAreaMapId(571); //Expansion02
}
//=================================================================
+
std::string getModNameFromModPosName(const std::string& pModPosName)
{
size_t spos = pModPosName.find_first_of('#');
std::string modelFileName = pModPosName.substr(0,spos);
return(modelFileName);
}
+
//=================================================================
+
unsigned int TileAssembler::getUniqueNameId(const std::string pName)
{
unsigned int result;
+
if(!iUniqueNameIds.containsKey(pName))
{
++iCurrentUniqueNameId;
@@ -83,11 +100,14 @@ namespace VMAP
result = iUniqueNameIds.get(pName);
return result;
}
+
//=================================================================
+
std::string TileAssembler::getDirEntryNameFromModName(unsigned int pMapId, const std::string& pModPosName)
{
size_t spos;
char buffer[20];
+
std::string modelFileName = getModNameFromModPosName(pModPosName);
//std::string fext = pModPosName.substr(modelFileName.length(),pModPosName.length());
unsigned int fextId = getUniqueNameId(pModPosName);
@@ -105,7 +125,9 @@ namespace VMAP
dirEntry.append(".vmap");
return(dirEntry);
}
+
//=================================================================
+
void emptyArray(Array<ModelContainer*>& mc)
{
int no=mc.size();
@@ -116,6 +138,7 @@ namespace VMAP
mc.remove(no);
}
}
+
//=================================================================
bool TileAssembler::convertWorld()
{
@@ -126,22 +149,27 @@ namespace VMAP
::g_df = fopen("../TileAssembler_release.txt", "wb");
# endif
#endif
+
std::string fname = iSrcDir;
fname.append("/");
fname.append("dir");
iCoordModelMapping->setModelNameFilterMethod(iFilterMethod);
+
printf("Read coordinate mapping...\n");
if(!iCoordModelMapping->readCoordinateMapping(fname))
return false;
+
Array<unsigned int> mapIds = iCoordModelMapping->getMaps();
if(mapIds.size() == 0)
{
printf("Fatal error: empty map list!\n");
return false;
}
+
for(int i=0; i<mapIds.size(); ++i)
{
unsigned int mapId = mapIds[i];
+
#ifdef _ASSEMBLER_DEBUG
if(mapId == 0) // "Azeroth" just for debug
{
@@ -172,12 +200,15 @@ namespace VMAP
{
sprintf(buffer, "%03u",mapId);
dirname = std::string(buffer);
+
// prevent spam for small maps
if(x==0 && y==0)
printf("%s...\n",dirname.c_str());
}
+
bool result = fillModelContainerArray(dirname, mapId, x, y, mc);
emptyArray(mc);
+
if(!result)
return false;
}
@@ -187,15 +218,20 @@ namespace VMAP
#ifdef _ASSEMBLER_DEBUG
if(::g_df) fclose(::g_df);
#endif
+
return true;
}
+
//=================================================================
+
bool TileAssembler::fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, Array<ModelContainer*>& pMC)
{
ModelContainer* modelContainer;
+
NameCollection nameCollection = iCoordModelMapping->getFilenamesForCoordinate(pMapId, pXPos, pYPos);
if(nameCollection.size() == 0)
return true; // no data...
+
char dirfilename[500];
sprintf(dirfilename,"%s/%s.vmdir",iDestDir.c_str(),pDirFileName.c_str());
FILE *dirfile = fopen(dirfilename, "ab");
@@ -204,8 +240,10 @@ namespace VMAP
printf("ERROR: Can't create file %s",dirfilename);
return false;
}
+
char destnamebuffer[500];
char fullnamedestnamebuffer[500];
+
if(nameCollection.iMainFiles.size() >0)
{
sprintf(destnamebuffer,"%03u_%i_%i.vmap",pMapId, pYPos, pXPos); // flip it here too
@@ -241,8 +279,10 @@ namespace VMAP
iCoordModelMapping->addAlreadyProcessedSingleFile(checkDoubleStr);
fprintf(dirfile, "%s\n",dirEntryName.c_str());
destFileName.append(dirEntryName);
+
Array<std::string> positionarray;
positionarray.append(nameCollection.iSingeFiles[pos]);
+
if(!iCoordModelMapping->isAlreadyProcessedSingleFile(nameCollection.iSingeFiles[pos]))
{
modelContainer = processNames(positionarray, destFileName.c_str());
@@ -255,10 +295,13 @@ namespace VMAP
}
++pos;
}
+
fclose(dirfile);
return true;
}
+
//=================================================================
+
void removeEntriesFromTree(AABSPTree<SubModel *>* pTree)
{
Array<SubModel *> submodelArray;
@@ -270,18 +313,24 @@ namespace VMAP
delete submodelArray[no];
}
}
+
//=================================================================
+
ModelContainer* TileAssembler::processNames(const Array<std::string>& pPositions, const char* pDestFileName)
{
ModelContainer *modelContainer = 0;
+
Vector3 basepos = Vector3(0,0,0);
AABSPTree<SubModel *>* mainTree = new AABSPTree<SubModel *>();
+
int pos = 0;
+
bool result = true;
while(result && (pos < pPositions.size()))
{
std::string modelPosString = pPositions[pos];
std::string modelFileName = getModNameFromModPosName(modelPosString);
+
if(!fillModelIntoTree(mainTree, basepos, modelPosString, modelFileName))
{
result = false;
@@ -296,9 +345,12 @@ namespace VMAP
modelContainer->writeFile(pDestFileName);
}
removeEntriesFromTree(mainTree);
+
delete mainTree;
+
return(modelContainer);
}
+
//=================================================================
bool TileAssembler::readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, AABSPTree<SubModel *> *pMainTree)
{
@@ -317,14 +369,18 @@ namespace VMAP
filename.append(baseModelFilename);
rf = fopen(filename.c_str(), "rb");
}
+
if(!rf)
{
printf("ERROR: Can't open model file in form: %s",pModelFilename.c_str());
printf("... or form: %s",filename.c_str() );
return false;
}
+
char ident[8];
+
int trianglecount =0;
+
#ifdef _ASSEMBLER_DEBUG
int startgroup = 0; //2;
int endgroup = INT_MAX; //2;
@@ -335,11 +391,13 @@ namespace VMAP
int startgroup = 0;
int endgroup = INT_MAX;
#endif
+
// temporary use defines to simplify read/check code (close file and return at fail)
#define READ_OR_RETURN(V,S) if(fread((V), (S), 1, rf) != 1) { \
fclose(rf); return(false); }
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); return(false); }
+
READ_OR_RETURN(&ident, 8);
if(strcmp(ident, "VMAP001") == 0)
{
@@ -350,6 +408,7 @@ namespace VMAP
// we have to read one int. This is needed during the export and we have to skip it here
int tempNVectors;
READ_OR_RETURN(&tempNVectors, sizeof(int));
+
}
else
{
@@ -361,13 +420,17 @@ namespace VMAP
char blockId[5];
blockId[4] = 0;
int blocksize;
+
READ_OR_RETURN(&groups, sizeof(G3D::uint32));
+
for(int g=0;g<(int)groups;g++)
{
// group MUST NOT have more then 65536 indexes !! Array will have a problem with that !! (strange ...)
Array<int> tempIndexArray;
Array<Vector3> tempVertexArray;
+
AABSPTree<Triangle> *gtree = new AABSPTree<Triangle>();
+
// add free gtree at fail
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
@@ -375,8 +438,10 @@ namespace VMAP
fclose(rf); delete gtree; return(false); }
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); delete gtree; return(false); }
+
G3D::uint32 flags;
READ_OR_RETURN(&flags, sizeof(G3D::uint32));
+
G3D::uint32 branches;
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "GRP ");
@@ -388,6 +453,7 @@ namespace VMAP
// indexes for each branch (not used jet)
READ_OR_RETURN(&indexes, sizeof(G3D::uint32));
}
+
// ---- indexes
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "INDX");
@@ -405,13 +471,16 @@ namespace VMAP
}
delete[] indexarray;
}
+
// ---- vectors
READ_OR_RETURN(&blockId, 4);
CMP_OR_RETURN(blockId, "VERT");
READ_OR_RETURN(&blocksize, sizeof(int));
unsigned int nvectors;
READ_OR_RETURN(&nvectors, sizeof(int));
+
float *vectorarray = 0;
+
// add vectorarray free
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
@@ -419,6 +488,7 @@ namespace VMAP
fclose(rf); delete gtree; delete[] vectorarray; return(false); }
#define CMP_OR_RETURN(V,S) if(strcmp((V),(S)) != 0) { \
fclose(rf); delete gtree; delete[] vectorarray; return(false); }
+
if(nvectors >0)
{
vectorarray = new float[nvectors*sizeof(float)*3];
@@ -434,22 +504,27 @@ namespace VMAP
fseek(rf, blocksize, SEEK_CUR);
}
+
for(unsigned int i=0, indexNo=0; indexNo<nvectors; indexNo++)
{
Vector3 v = Vector3(vectorarray[i+2], vectorarray[i+1], vectorarray[i+0]);
i+=3;
v = pModelPosition.transform(v);
+
float swapy = v.y;
v.y = v.x;
v.x = swapy;
+
tempVertexArray.append(v);
}
+
// ---- calculate triangles
int rest = nindexes%3;
if(rest != 0)
{
nindexes -= rest;
}
+
for(unsigned int i=0;i<(nindexes);)
{
Triangle t = Triangle(tempVertexArray[tempIndexArray[i+2]], tempVertexArray[tempIndexArray[i+1]], tempVertexArray[tempIndexArray[i+0]] );
@@ -460,13 +535,16 @@ namespace VMAP
gtree->insert(t);
}
}
+
// drop of temporary use defines
#undef READ_OR_RETURN
#undef CMP_OR_RETURN
+
if(vectorarray != 0)
{
delete vectorarray;
}
+
if(gtree->size() >0)
{
gtree->balance();
@@ -486,31 +564,40 @@ namespace VMAP
fclose(rf);
return true;
}
+
//=================================================================
+
bool TileAssembler::fillModelIntoTree(AABSPTree<SubModel *> *pMainTree, const Vector3& pBasePos, std::string& pPos, std::string& pModelFilename)
{
ModelPosition modelPosition;
getModelPosition(pPos, modelPosition);
// all should be relative to object base position
modelPosition.moveToBasePos(pBasePos);
+
modelPosition.init();
+
return readRawFile(pModelFilename, modelPosition, pMainTree);
}
+
//=================================================================
void TileAssembler::getModelPosition(std::string& pPosString, ModelPosition& pModelPosition)
{
float vposarray[3];
float vdirarray[3];
float scale;
+
size_t spos = pPosString.find_first_of('#');
std::string stripedPosString = pPosString.substr(spos+1,pPosString.length());
+
sscanf(stripedPosString.c_str(), "%f,%f,%f_%f,%f,%f_%f",
&vposarray[0],&vposarray[1],&vposarray[2],
&vdirarray[0],&vdirarray[1],&vdirarray[2],
&scale);
+
pModelPosition.iPos = Vector3(vposarray[0], vposarray[1], vposarray[2]);
pModelPosition.iDir = Vector3(vdirarray[0], vdirarray[1], vdirarray[2]);
pModelPosition.iScale = scale;
+
}
//==========================================
} // VMAP
diff --git a/src/shared/vmap/TileAssembler.h b/src/shared/vmap/TileAssembler.h
index 27cddbc2c1c..d2183794a9e 100644
--- a/src/shared/vmap/TileAssembler.h
+++ b/src/shared/vmap/TileAssembler.h
@@ -17,14 +17,19 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _TILEASSEMBLER_H_
#define _TILEASSEMBLER_H_
+
// load our modified version first !!
#include "AABSPTree.h"
+
#include <G3D/Vector3.h>
+
#include "CoordModelMapping.h"
#include "SubModel.h"
#include "ModelContainer.h"
+
namespace VMAP
{
/**
@@ -32,6 +37,7 @@ namespace VMAP
To start the conversion call convertWorld().
*/
//===============================================
+
class ModelPosition
{
private:
@@ -44,15 +50,18 @@ namespace VMAP
float iScale;
void init()
{
+
// Swap x and y the raw data uses the axis differently
ixMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitY(),-(G3D::pi()*iDir.x/180.0));
iyMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitX(),-(G3D::pi()*iDir.y/180.0));
izMatrix = G3D::Matrix3::fromAxisAngle(G3D::Vector3::unitZ(),-(G3D::pi()*iDir.z/180.0));
+
}
G3D::Vector3 transform(const G3D::Vector3& pIn) const;
void moveToBasePos(const G3D::Vector3& pBasePos) { iPos -= pBasePos; }
};
//===============================================
+
class TileAssembler
{
private:
@@ -62,13 +71,17 @@ namespace VMAP
bool (*iFilterMethod)(char *pName);
G3D::Table<std::string, unsigned int > iUniqueNameIds;
unsigned int iCurrentUniqueNameId;
+
public:
TileAssembler(const std::string& pSrcDirName, const std::string& pDestDirName);
virtual ~TileAssembler();
+
bool fillModelContainerArray(const std::string& pDirFileName, unsigned int pMapId, int pXPos, int pYPos, G3D::Array<ModelContainer*>& pMC);
ModelContainer* processNames(const G3D::Array<std::string>& pPosFileNames, const char* pDestFileName);
+
void init();
bool convertWorld();
+
bool fillModelIntoTree(G3D::AABSPTree<SubModel *> *pMainTree, const G3D::Vector3& pBasePos, std::string& pPosFilename, std::string& pModelFilename);
void getModelPosition(std::string& pPosString, ModelPosition& pModelPosition);
bool readRawFile(std::string& pModelFilename, ModelPosition& pModelPosition, G3D::AABSPTree<SubModel *> *pMainTree);
diff --git a/src/shared/vmap/TreeNode.cpp b/src/shared/vmap/TreeNode.cpp
index d40f2acde25..c884f9b3b1d 100644
--- a/src/shared/vmap/TreeNode.cpp
+++ b/src/shared/vmap/TreeNode.cpp
@@ -17,10 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "TreeNode.h"
+
using namespace G3D;
+
namespace VMAP
{
+
TreeNode const* TreeNode::getChild(TreeNode const* pValueArray,int pNo) const
{
if(iChilds[pNo] != -1)
@@ -28,6 +32,7 @@ namespace VMAP
else
return(NULL);
}
+
//=================================================================
//=================================================================
//=================================================================
diff --git a/src/shared/vmap/TreeNode.h b/src/shared/vmap/TreeNode.h
index 2b14de9ac33..1b9532001e5 100644
--- a/src/shared/vmap/TreeNode.h
+++ b/src/shared/vmap/TreeNode.h
@@ -17,14 +17,18 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _TREENODE_H
#define _TREENODE_H
+
#include "ShortVector.h"
#include "ShortBox.h"
#include "NodeValueAccess.h"
#include "VMapTools.h"
+
#include <G3D/Vector3.h>
#include <G3D/AABox.h>
+
namespace VMAP
{
/**
@@ -32,7 +36,9 @@ namespace VMAP
It is the node within our static BSP-Trees.
It does not use pointers but indexes to access the values and other nodes.
*/
+
//=====================================================
+
class TreeNode
{
private:
@@ -54,19 +60,30 @@ namespace VMAP
iStartPosition = pStartPosition;
iNumberOfValues = pNValues;
}
+
bool hasChilds() const { return(iChilds[0] >= 0 || iChilds[1] >= 0); }
+
TreeNode const* getChild(TreeNode const* pValueArray, int pNo) const;
// pChildNo = 0 or 1
inline void setChildPos(int pChildNo, int pChildPosInTreeNodeArray) { iChilds[pChildNo] = pChildPosInTreeNodeArray; }
+
inline G3D::Vector3::Axis getSplitAxis() const { return(iSplitAxis); }
+
inline void setSplitAxis(G3D::Vector3::Axis a) { iSplitAxis = a; }
inline void setSplitLocation(float l) { iSplitLocation = l; }
+
inline void setBounds(const G3D::AABox& pBox) { iBounds = pBox; }
+
inline void setBounds(const G3D::Vector3& lo, const G3D::Vector3& hi) { iBounds.set(lo,hi); }
+
inline void getBounds(G3D::AABox& pBox) const { pBox.set(iBounds.low(),iBounds.high()); }
+
inline float getSplitLocation() const { return(iSplitLocation); }
+
inline unsigned short getNValues() const { return (iNumberOfValues); }
+
inline unsigned int getStartPosition() const { return(iStartPosition); }
+
inline bool operator==(const TreeNode& n) const
{
return ((iSplitLocation == n.iSplitLocation) &&
@@ -76,6 +93,7 @@ namespace VMAP
(iBounds == n.iBounds) &&
(iNumberOfValues == n.iNumberOfValues));
}
+
inline bool operator!=(const TreeNode& n) const
{
return !((iSplitLocation == n.iSplitLocation) &&
@@ -85,6 +103,7 @@ namespace VMAP
(iBounds == n.iBounds) &&
(iNumberOfValues == n.iNumberOfValues));
}
+
/** Returns true if the ray intersects this node */
bool intersects(const G3D::Ray& ray, float distance) const {
// See if the ray will ever hit this node or its children
@@ -93,10 +112,13 @@ namespace VMAP
bool rayWillHitBounds =
MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
ray.origin, ray.direction, iBounds, location, alreadyInsideBounds);
+
bool canHitThisNode = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - ray.origin).squaredLength() < (distance*distance))));
+
return canHitThisNode;
}
+
template<typename RayCallback, typename TNode, typename TValue>
void intersectRay(
const G3D::Ray& ray,
@@ -110,6 +132,7 @@ namespace VMAP
// The ray doesn't hit this node, so it can't hit the children of the node.
return;
}
+
// Test for intersection against every object at this node.
for (unsigned int v = iStartPosition; v < (iNumberOfValues+iStartPosition); ++v) {
const TValue& nodeValue = pNodeValueAccess.getValue(v);
@@ -122,9 +145,11 @@ namespace VMAP
bool rayWillHitBounds =
MyCollisionDetection::collisionLocationForMovingPointFixedAABox(
ray.origin, ray.direction, bounds, location, alreadyInsideBounds);
+
canHitThisObject = (alreadyInsideBounds ||
(rayWillHitBounds && ((location - ray.origin).squaredLength() < (distance*distance))));
}
+
if (canHitThisObject) {
// It is possible that this ray hits this object. Look for the intersection using the
// callback.
@@ -133,24 +158,32 @@ namespace VMAP
if(pStopAtFirstHit && distance < enterDistance)
return;
}
+
// There are three cases to consider next:
//
// 1. the ray can start on one side of the splitting plane and never enter the other,
// 2. the ray can start on one side and enter the other, and
// 3. the ray can travel exactly down the splitting plane
+
enum {NONE = -1};
int firstChild = NONE;
int secondChild = NONE;
+
if (ray.origin[iSplitAxis] < iSplitLocation) {
+
// The ray starts on the small side
firstChild = 0;
+
if (ray.direction[iSplitAxis] > 0) {
// The ray will eventually reach the other side
secondChild = 1;
}
+
} else if (ray.origin[iSplitAxis] > iSplitLocation) {
+
// The ray starts on the large side
firstChild = 1;
+
if (ray.direction[iSplitAxis] < 0) {
secondChild = 0;
}
@@ -164,6 +197,7 @@ namespace VMAP
firstChild = 1;
}
}
+
// Test on the side closer to the ray origin.
if ((firstChild != NONE) && iChilds[firstChild]>0) {
getChild(pNodeValueAccess.getNodePtr() , firstChild)->intersectRay(ray, intersectCallback, distance, pNodeValueAccess, pStopAtFirstHit,intersectCallbackIsFast);
diff --git a/src/shared/vmap/VMapDefinitions.h b/src/shared/vmap/VMapDefinitions.h
index 811546d44de..fd28a91d515 100644
--- a/src/shared/vmap/VMapDefinitions.h
+++ b/src/shared/vmap/VMapDefinitions.h
@@ -17,19 +17,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _VMAPDEFINITIONS_H
#define _VMAPDEFINITIONS_H
#include <cstring>
+
namespace VMAP
{
//=====================================
#define MAX_CAN_FALL_DISTANCE 10.0
const char VMAP_MAGIC[] = "VMAP_2.0";
+
class VMapDefinitions
{
public:
static const double getMaxCanFallDistance() { return(MAX_CAN_FALL_DISTANCE); }
};
+
//======================================
}
#endif
diff --git a/src/shared/vmap/VMapFactory.cpp b/src/shared/vmap/VMapFactory.cpp
index c979a33ff82..5189f79daba 100644
--- a/src/shared/vmap/VMapFactory.cpp
+++ b/src/shared/vmap/VMapFactory.cpp
@@ -17,17 +17,23 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <sys/types.h>
#include "VMapFactory.h"
#include "VMapManager.h"
+
using namespace G3D;
+
namespace VMAP
{
extern void chompAndTrim(std::string& str);
+
VMapManager *gVMapManager = 0;
Table<unsigned int , bool>* iIgnoreSpellIds=0;
+
//===============================================
// result false, if no more id are found
+
bool getNextId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId)
{
bool result = false;
@@ -49,10 +55,12 @@ namespace VMAP
}
return(result);
}
+
//===============================================
/**
parameter: String of map ids. Delimiter = ","
*/
+
void VMapFactory::preventSpellsFromBeingTestedForLoS(const char* pSpellIdString)
{
if(!iIgnoreSpellIds)
@@ -69,11 +77,14 @@ namespace VMAP
}
}
}
+
//===============================================
+
bool VMapFactory::checkSpellForLoS(unsigned int pSpellId)
{
return(!iIgnoreSpellIds->containsKey(pSpellId));
}
+
//===============================================
// just return the instance
IVMapManager* VMapFactory::createOrGetVMapManager()
@@ -82,6 +93,7 @@ namespace VMAP
gVMapManager= new VMapManager(); // should be taken from config ... Please change if you like :-)
return gVMapManager;
}
+
//===============================================
// delete all internal data structures
void VMapFactory::clear()
diff --git a/src/shared/vmap/VMapFactory.h b/src/shared/vmap/VMapFactory.h
index ca529bd2715..f3375d723c1 100644
--- a/src/shared/vmap/VMapFactory.h
+++ b/src/shared/vmap/VMapFactory.h
@@ -17,23 +17,30 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _VMAPFACTORY_H
#define _VMAPFACTORY_H
+
#include "IVMapManager.h"
+
/**
This is the access point to the VMapManager.
*/
+
namespace VMAP
{
//===========================================================
+
class VMapFactory
{
public:
static IVMapManager* createOrGetVMapManager();
static void clear();
+
static void preventSpellsFromBeingTestedForLoS(const char* pSpellIdString);
static bool checkSpellForLoS(unsigned int pSpellId);
};
+
}
#endif
diff --git a/src/shared/vmap/VMapManager.cpp b/src/shared/vmap/VMapManager.cpp
index 782c9d39c8a..342da0eb9e2 100644
--- a/src/shared/vmap/VMapManager.cpp
+++ b/src/shared/vmap/VMapManager.cpp
@@ -17,12 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "VMapManager.h"
#include "VMapDefinitions.h"
+
using namespace G3D;
+
namespace VMAP
{
+
//=========================================================
+
VMapManager::VMapManager()
{
#ifdef _VMAP_LOG_DEBUG
@@ -30,7 +35,9 @@ namespace VMAP
iCommandLogger.setResetFile();
#endif
}
+
//=========================================================
+
VMapManager::~VMapManager(void)
{
Array<unsigned int > keyArray = iInstanceMapTrees.getKeys();
@@ -40,7 +47,9 @@ namespace VMAP
iInstanceMapTrees.remove(keyArray[i]);
}
}
+
//=========================================================
+
Vector3 VMapManager::convertPositionToInternalRep(float x, float y, float z) const
{
float pos[3];
@@ -51,9 +60,12 @@ namespace VMAP
double mid = full/2.0;
pos[0] = full- (pos[0] + mid);
pos[2] = full- (pos[2] + mid);
+
return(Vector3(pos));
}
+
//=========================================================
+
Vector3 VMapManager::convertPositionToTrinityRep(float x, float y, float z) const
{
float pos[3];
@@ -64,19 +76,24 @@ namespace VMAP
double mid = full/2.0;
pos[0] = -((mid+pos[0])-full);
pos[1] = -((mid+pos[1])-full);
+
return(Vector3(pos));
}
//=========================================================
+
std::string VMapManager::getDirFileName(unsigned int pMapId, int x, int y) const
{
char name[FILENAMEBUFFER_SIZE];
+
sprintf(name, "%03u_%d_%d%s",pMapId, x, y, DIR_FILENAME_EXTENSION);
return(std::string(name));
}
+
//=========================================================
std::string VMapManager::getDirFileName(unsigned int pMapId) const
{
char name[FILENAMEBUFFER_SIZE];
+
sprintf(name, "%03d%s",pMapId, DIR_FILENAME_EXTENSION);
return(std::string(name));
}
@@ -98,6 +115,7 @@ namespace VMAP
}
}
//=========================================================
+
void chompAndTrim(std::string& str)
{
while(str.length() >0)
@@ -125,8 +143,10 @@ namespace VMAP
}
}
}
+
//=========================================================
// result false, if no more id are found
+
bool getNextMapId(const std::string& pString, unsigned int& pStartPos, unsigned int& pId)
{
bool result = false;
@@ -148,12 +168,14 @@ namespace VMAP
}
return(result);
}
+
//=========================================================
/**
Block maps from being used.
parameter: String of map ids. Delimiter = ","
e.g.: "0,1,590"
*/
+
void VMapManager::preventMapsFromBeingUsed(const char* pMapIdString)
{
if(pMapIdString != NULL)
@@ -168,7 +190,9 @@ namespace VMAP
}
}
}
+
//=========================================================
+
int VMapManager::loadMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
int result = VMAP_LOAD_RESULT_IGNORED;
@@ -201,8 +225,10 @@ namespace VMAP
}
return result;
}
+
//=========================================================
// load one tile (internal use only)
+
bool VMapManager::_loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad)
{
bool result = false;
@@ -223,6 +249,7 @@ namespace VMAP
}
else
instanceTree = iInstanceMapTrees.get(pMapId);
+
unsigned int mapTileIdent = MAP_TILE_IDENT(x,y);
result = instanceTree->loadMap(dirFileName, mapTileIdent);
if(!result) // remove on fail
@@ -235,7 +262,9 @@ namespace VMAP
}
return(result);
}
+
//=========================================================
+
bool VMapManager::_existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad)
{
bool result = false;
@@ -275,7 +304,9 @@ namespace VMAP
}
return result;
}
+
//=========================================================
+
bool VMapManager::existsMap(const char* pBasePath, unsigned int pMapId, int x, int y)
{
std::string basePath = std::string(pBasePath);
@@ -295,17 +326,22 @@ namespace VMAP
}
return found;
}
+
//=========================================================
+
void VMapManager::unloadMap(unsigned int pMapId, int x, int y)
{
_unloadMap(pMapId, x, y);
+
#ifdef _VMAP_LOG_DEBUG
Command c = Command();
c.fillUnloadTileCmd(pMapId, x,y);
iCommandLogger.appendCmd(c);
#endif
}
+
//=========================================================
+
void VMapManager::_unloadMap(unsigned int pMapId, int x, int y)
{
if(iInstanceMapTrees.containsKey(pMapId))
@@ -329,7 +365,9 @@ namespace VMAP
}
}
}
+
//=========================================================
+
void VMapManager::unloadMap(unsigned int pMapId)
{
if(iInstanceMapTrees.containsKey(pMapId))
@@ -350,6 +388,7 @@ namespace VMAP
}
}
//==========================================================
+
bool VMapManager::isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2)
{
bool result = true;
@@ -404,10 +443,12 @@ namespace VMAP
}
return result;
}
+
//=========================================================
/**
get height or INVALID_HEIGHT if to height was calculated
*/
+
//int gGetHeightCounter = 0;
float VMapManager::getHeight(unsigned int pMapId, float x, float y, float z)
{
@@ -429,6 +470,7 @@ namespace VMAP
}
return(height);
}
+
//=========================================================
/**
used for debugging
@@ -440,6 +482,7 @@ namespace VMAP
if(cmd == "startlog")
{
#ifdef _VMAP_LOG_DEBUG
+
iCommandLogger.enableWriting(true);
#endif
result = true;
@@ -466,9 +509,11 @@ namespace VMAP
}
return result;
}
+
//=========================================================
//=========================================================
//=========================================================
+
MapTree::MapTree(const char* pBaseDir)
{
iBasePath = std::string(pBaseDir);
@@ -478,6 +523,7 @@ namespace VMAP
}
iTree = new AABSPTree<ModelContainer *>();
}
+
//=========================================================
MapTree::~MapTree()
{
@@ -492,6 +538,7 @@ namespace VMAP
delete iTree;
}
//=========================================================
+
// just for visual debugging with an external debug class
#ifdef _DEBUG_VMAPS
#ifndef gBoxArray
@@ -501,10 +548,12 @@ namespace VMAP
extern bool myfound;
#endif
#endif
+
//=========================================================
/**
return dist to hit or inf() if no hit
*/
+
float MapTree::getIntersectionTime(const Ray& pRay, float pMaxDist, bool pStopAtFirstHit)
{
float firstDistance = inf();
@@ -527,6 +576,7 @@ namespace VMAP
return firstDistance;
}
//=========================================================
+
bool MapTree::isInLineOfSight(const Vector3& pos1, const Vector3& pos2)
{
bool result = true;
@@ -545,6 +595,7 @@ namespace VMAP
When moving from pos1 to pos2 check if we hit an object. Return true and the position if we hit one
Return the hit pos or the original dest pos
*/
+
bool MapTree::getObjectHitPos(const Vector3& pPos1, const Vector3& pPos2, Vector3& pResultHitPos, float pModifyDist)
{
bool result;
@@ -579,7 +630,9 @@ namespace VMAP
}
return result;
}
+
//=========================================================
+
float MapTree::getHeight(const Vector3& pPos)
{
float height = inf();
@@ -593,12 +646,15 @@ namespace VMAP
}
return(height);
}
+
//=========================================================
+
bool MapTree::PrepareTree()
{
iTree->balance();
return true;
}
+
bool MapTree::loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent)
{
bool result = true;
@@ -669,7 +725,9 @@ namespace VMAP
}
return (result);
}
+
//=========================================================
+
void MapTree::unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce)
{
if(hasDirFile(dirFileName) && (pForce || containsLoadedMapTile(pMapTileIdent)))
@@ -703,8 +761,10 @@ namespace VMAP
}
}
}
+
//=========================================================
//=========================================================
+
void MapTree::addModelContainer(const std::string& pName, ManagedModelContainer *pMc)
{
iLoadedModelContainer.set(pName, pMc);
diff --git a/src/shared/vmap/VMapManager.h b/src/shared/vmap/VMapManager.h
index 4e0e6bdfb18..bfeba3cfe67 100644
--- a/src/shared/vmap/VMapManager.h
+++ b/src/shared/vmap/VMapManager.h
@@ -17,8 +17,10 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _VMAPMANAGER_H
#define _VMAPMANAGER_H
+
// load our modified version first !!
#include "AABSPTree.h"
#include "ManagedModelContainer.h"
@@ -27,9 +29,13 @@
#include "DebugCmdLogger.h"
#endif
#include <G3D/Table.h>
+
//===========================================================
+
#define DIR_FILENAME_EXTENSION ".vmdir"
+
#define FILENAMEBUFFER_SIZE 500
+
/**
This is the main Class to manage loading and unloading of maps, line of sight, height calculation and so on.
For each map or map tile to load it reads a directory file that contains the ModelContainer files used by this map or map tile.
@@ -37,18 +43,22 @@ Each global map or instance has its own dynamic BSP-Tree.
The loaded ModelContainers are included in one of these BSP-Trees.
Additionally a table to match map ids and map names is used.
*/
+
// Create a value describing the map tile
#define MAP_TILE_IDENT(x,y) ((x<<8) + y)
//===========================================================
+
namespace VMAP
{
//===========================================================
+
class FilesInDir
{
private:
int iRefCount;
G3D::Array<std::string> iFiles;
public:
+
FilesInDir() { iRefCount = 0; }
void append(const std::string& pName) { iFiles.append(pName); }
void incRefCount() { ++iRefCount; }
@@ -56,22 +66,28 @@ namespace VMAP
int getRefCount() { return iRefCount; }
const G3D::Array<std::string>& getFiles() const { return iFiles; }
};
+
//===========================================================
//===========================================================
//===========================================================
//===========================================================
+
class MapTree
{
private:
G3D::AABSPTree<ModelContainer *> *iTree;
+
// Key: filename, value ModelContainer
G3D::Table<std::string, ManagedModelContainer *> iLoadedModelContainer;
+
// Key: dir file name, value FilesInDir
G3D::Table<std::string, FilesInDir> iLoadedDirFiles;
+
// Store all the map tile idents that are loaded for that map
// some maps are not splitted into tiles and we have to make sure, not removing the map before all tiles are removed
G3D::Table<unsigned int, bool> iLoadedMapTiles;
std::string iBasePath;
+
private:
float getIntersectionTime(const G3D::Ray& pRay, float pMaxDist, bool pStopAtFirstHit);
bool isAlreadyLoaded(const std::string& pName) { return(iLoadedModelContainer.containsKey(pName)); }
@@ -86,17 +102,21 @@ namespace VMAP
public:
MapTree(const char *pBasePath);
~MapTree();
+
bool isInLineOfSight(const G3D::Vector3& pos1, const G3D::Vector3& pos2);
bool getObjectHitPos(const G3D::Vector3& pos1, const G3D::Vector3& pos2, G3D::Vector3& pResultHitPos, float pModifyDist);
float getHeight(const G3D::Vector3& pPos);
+
bool PrepareTree();
bool loadMap(const std::string& pDirFileName, unsigned int pMapTileIdent);
void addModelContainer(const std::string& pName, ManagedModelContainer *pMc);
void unloadMap(const std::string& dirFileName, unsigned int pMapTileIdent, bool pForce=false);
+
void getModelContainer(G3D::Array<ModelContainer *>& pArray ) { iTree->getMembers(pArray); }
const void addDirFile(const std::string& pDirName, const FilesInDir& pFilesInDir) { iLoadedDirFiles.set(pDirName, pFilesInDir); }
size_t size() { return(iTree->size()); }
};
+
//===========================================================
class MapIdNames
{
@@ -104,6 +124,7 @@ namespace VMAP
std::string iDirName;
std::string iMapGroupName;
};
+
//===========================================================
class VMapManager : public IVMapManager
{
@@ -112,6 +133,7 @@ namespace VMAP
G3D::Table<unsigned int , MapTree *> iInstanceMapTrees;
G3D::Table<unsigned int , bool> iMapsSplitIntoTiles;
G3D::Table<unsigned int , bool> iIgnoreMapIds;
+
#ifdef _VMAP_LOG_DEBUG
CommandFileRW iCommandLogger;
#endif
@@ -119,6 +141,7 @@ namespace VMAP
bool _loadMap(const char* pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad=false);
void _unloadMap(unsigned int pMapId, int x, int y);
bool _existsMap(const std::string& pBasePath, unsigned int pMapId, int x, int y, bool pForceTileLoad);
+
public:
// public for debug
G3D::Vector3 convertPositionToInternalRep(float x, float y, float z) const;
@@ -129,17 +152,23 @@ namespace VMAP
public:
VMapManager();
~VMapManager(void);
+
int loadMap(const char* pBasePath, unsigned int pMapId, int x, int y);
+
bool existsMap(const char* pBasePath, unsigned int pMapId, int x, int y);
+
void unloadMap(unsigned int pMapId, int x, int y);
void unloadMap(unsigned int pMapId);
+
bool isInLineOfSight(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2) ;
/**
fill the hit pos and return true, if an object was hit
*/
bool getObjectHitPos(unsigned int pMapId, float x1, float y1, float z1, float x2, float y2, float z2, float& rx, float &ry, float& rz, float pModifyDist);
float getHeight(unsigned int pMapId, float x, float y, float z);
+
bool processCommand(char *pCommand); // for debug and extensions
+
void preventMapsFromBeingUsed(const char* pMapIdString);
};
}
diff --git a/src/shared/vmap/VMapTools.h b/src/shared/vmap/VMapTools.h
index 2460bf9dcc4..3af3a29310d 100644
--- a/src/shared/vmap/VMapTools.h
+++ b/src/shared/vmap/VMapTools.h
@@ -17,16 +17,21 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#ifndef _VMAPTOOLS_H
#define _VMAPTOOLS_H
+
#include <G3D/CollisionDetection.h>
#include <G3D/AABox.h>
+
#include "NodeValueAccess.h"
+
/**
The Class is mainly taken from G3D/AABSPTree.h but modified to be able to use our internal data structure.
This is an iterator that helps us analysing the BSP-Trees.
The collision detection is modified to return true, if we are inside an object.
*/
+
namespace VMAP
{
template<class TValue>
@@ -35,17 +40,21 @@ namespace VMAP
TValue* closestEntity;
G3D::Vector3 hitLocation;
G3D::Vector3 hitNormal;
+
void operator()(const G3D::Ray& ray, const TValue* entity, bool pStopAtFirstHit, float& distance) {
entity->intersect(ray, distance, pStopAtFirstHit, hitLocation, hitNormal);
}
};
+
//==============================================================
//==============================================================
//==============================================================
+
class MyCollisionDetection
{
private:
public:
+
static bool collisionLocationForMovingPointFixedAABox(
const G3D::Vector3& origin,
const G3D::Vector3& dir,
@@ -53,12 +62,15 @@ namespace VMAP
G3D::Vector3& location,
bool& Inside)
{
+
// Integer representation of a floating-point value.
#define IR(x) ((G3D::uint32&)x)
+
Inside = true;
const G3D::Vector3& MinB = box.low();
const G3D::Vector3& MaxB = box.high();
G3D::Vector3 MaxT(-1.0f, -1.0f, -1.0f);
+
// Find candidate planes.
for (int i = 0; i < 3; ++i)
{
@@ -66,6 +78,7 @@ namespace VMAP
{
location[i] = MinB[i];
Inside = false;
+
// Calculate T distances to candidate planes
if (IR(dir[i]))
{
@@ -76,6 +89,7 @@ namespace VMAP
{
location[i] = MaxB[i];
Inside = false;
+
// Calculate T distances to candidate planes
if (IR(dir[i]))
{
@@ -83,28 +97,33 @@ namespace VMAP
}
}
}
+
if (Inside)
{
// definite hit
location = origin;
return true;
}
+
// Get largest of the maxT's for final choice of intersection
int WhichPlane = 0;
if (MaxT[1] > MaxT[WhichPlane])
{
WhichPlane = 1;
}
+
if (MaxT[2] > MaxT[WhichPlane])
{
WhichPlane = 2;
}
+
// Check final candidate actually inside box
if (IR(MaxT[WhichPlane]) & 0x80000000)
{
// Miss the box
return false;
}
+
for (int i = 0; i < 3; ++i)
{
if (i != WhichPlane)
@@ -125,6 +144,7 @@ namespace VMAP
normal[WhichPlane] = (dir[WhichPlane] > 0) ? -1.0 : 1.0;
*/
return true;
+
#undef IR
}
};
diff --git a/src/tools/genrevision/genrevision.cpp b/src/tools/genrevision/genrevision.cpp
index de9f771a067..85b11a6461c 100644
--- a/src/tools/genrevision/genrevision.cpp
+++ b/src/tools/genrevision/genrevision.cpp
@@ -15,12 +15,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include <fstream>
#include <sstream>
#include <time.h>
#include <stdio.h>
#include <string.h>
+
#pragma warning(disable:4996)
+
struct RawData
{
char hash_str[200];
@@ -28,11 +31,14 @@ struct RawData
char date_str[200];
char time_str[200];
};
+
void extractDataFromSvn(FILE* EntriesFile, bool url, RawData& data)
{
char buf[200];
+
char repo_str[200];
char num_str[200];
+
fgets(buf,200,EntriesFile);
fgets(buf,200,EntriesFile);
fgets(buf,200,EntriesFile);
@@ -43,16 +49,20 @@ void extractDataFromSvn(FILE* EntriesFile, bool url, RawData& data)
fgets(buf,200,EntriesFile);
fgets(buf,200,EntriesFile);
fgets(buf,200,EntriesFile); sscanf(buf,"%10sT%8s",data.date_str,data.time_str);
+
if(url)
sprintf(data.rev_str,"%s at %s",num_str,repo_str);
else
strcpy(data.rev_str,num_str);
}
+
void extractDataFromHG(FILE* EntriesFile, std::string path, bool url, RawData& data)
{
char buf[200];
+
char hash_str[200];
char revision_str[200];
+
bool found = false;
while(fgets(buf,200,EntriesFile))
{
@@ -62,6 +72,7 @@ void extractDataFromHG(FILE* EntriesFile, std::string path, bool url, RawData& d
break;
}
}
+
if(!found)
{
strcpy(data.hash_str,"*");
@@ -70,22 +81,28 @@ void extractDataFromHG(FILE* EntriesFile, std::string path, bool url, RawData& d
strcpy(data.time_str,"*");
return;
}
+
char thash_str[200];
for(int i = 11;i >= 0; --i)
{
thash_str[i] = hash_str[i];
}
thash_str[12] = '\0';
+
strcpy(data.hash_str,thash_str);
strcpy(data.rev_str,revision_str);
+
strcpy(data.date_str,"*");
strcpy(data.time_str,"*");
}
+
void extractDataFromArchive(FILE* EntriesFile, std::string path, bool url, RawData& data)
{
char buf[200];
+
char hash_str[200];
char revision_str[200];
+
bool found = false;
fgets(buf,200,EntriesFile);
while(fgets(buf,200,EntriesFile))
@@ -96,6 +113,7 @@ void extractDataFromArchive(FILE* EntriesFile, std::string path, bool url, RawDa
break;
}
}
+
if(!found)
{
strcpy(data.hash_str,"*");
@@ -104,23 +122,29 @@ void extractDataFromArchive(FILE* EntriesFile, std::string path, bool url, RawDa
strcpy(data.time_str,"*");
return;
}
+
char thash_str[200];
for(int i = 11;i >= 0; --i)
{
thash_str[i] = hash_str[i];
}
thash_str[12] = '\0';
+
strcpy(data.hash_str,thash_str);
strcpy(data.rev_str,"Archive");
+
strcpy(data.date_str,"*");
strcpy(data.time_str,"*");
}
+
void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData& data)
{
char buf[200];
+
char hash_str[200];
char branch_str[200];
char url_str[200];
+
bool found = false;
while(fgets(buf,200,EntriesFile))
{
@@ -130,6 +154,7 @@ void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData&
break;
}
}
+
if(!found)
{
strcpy(data.hash_str,"*");
@@ -138,11 +163,13 @@ void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData&
strcpy(data.time_str,"*");
return;
}
+
if(url)
{
char* host_str = NULL;
char* acc_str = NULL;
char* repo_str = NULL;
+
// parse URL like git@github.com:mangos/mangos
char url_buf[200];
int res = sscanf(url_str,"git@%s",url_buf);
@@ -162,6 +189,7 @@ void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData&
repo_str = strtok(NULL,".");
}
}
+
// can generate nice link
if(res)
sprintf(data.rev_str,"http://%s/%s/%s/commit/%s",host_str,acc_str,repo_str,hash_str);
@@ -172,6 +200,7 @@ void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData&
else
strcpy(data.rev_str,hash_str);
strcpy(data.hash_str,"*");
+
time_t rev_time = 0;
// extracting date/time
FILE* LogFile = fopen((path+".git/logs/HEAD").c_str(), "r");
@@ -185,12 +214,16 @@ void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData&
int res2 = sscanf(buf,"%s %s %s %s %i",buf2,new_hash,buf2,buf2,&unix_time);
if(res2!=5)
continue;
+
if(strcmp(hash_str,new_hash))
continue;
+
rev_time = unix_time;
break;
}
+
fclose(LogFile);
+
if(rev_time)
{
tm* aTm = localtime(&rev_time);
@@ -215,42 +248,51 @@ void extractDataFromGit(FILE* EntriesFile, std::string path, bool url, RawData&
strcpy(data.time_str,"*");
}
}
+
bool extractDataFromSvn(std::string filename, bool url, RawData& data)
{
FILE* EntriesFile = fopen(filename.c_str(), "r");
if(!EntriesFile)
return false;
+
extractDataFromSvn(EntriesFile,url,data);
fclose(EntriesFile);
return true;
}
+
bool extractDataFromGit(std::string filename, std::string path, bool url, RawData& data)
{
FILE* EntriesFile = fopen(filename.c_str(), "r");
if(!EntriesFile)
return false;
+
extractDataFromGit(EntriesFile,path,url,data);
fclose(EntriesFile);
return true;
}
+
bool extractDataFromHG(std::string filename, std::string path, bool url, RawData& data)
{
FILE* EntriesFile = fopen(filename.c_str(), "r");
if(!EntriesFile)
return false;
+
extractDataFromHG(EntriesFile,path,url,data);
fclose(EntriesFile);
return true;
}
+
bool extractDataFromArchive(std::string filename, std::string path, bool url, RawData& data)
{
FILE* EntriesFile = fopen(filename.c_str(), "r");
if(!EntriesFile)
return false;
+
extractDataFromArchive(EntriesFile,path,url,data);
fclose(EntriesFile);
return true;
}
+
std::string generateHeader(char const* rev_str, char const* date_str, char const* time_str, char const* hash_str)
{
std::ostringstream newData;
@@ -273,8 +315,10 @@ std::string generateHeader(char const* rev_str, char const* date_str, char const
newData << " #define STRFILEVER \"0, 0, " << rev_str << ", " << hash_str << "\""<< std::endl;
newData << " #define STRPRODUCTVER \"0, 0, " << rev_str << ", " << hash_str << "\""<< std::endl;
newData << "#endif // __REVISION_H__" << std::endl;
+
return newData.str();
}
+
int main(int argc, char **argv)
{
bool use_url = false;
@@ -282,6 +326,7 @@ int main(int argc, char **argv)
bool git_prefered = false;
bool svn_prefered = false;
std::string path;
+
// Call: tool {options} [path]
// -h use hg prefered (default)
// -g use git prefered
@@ -292,6 +337,7 @@ int main(int argc, char **argv)
{
if(!argv[k] || !*argv[k])
break;
+
if(argv[k][0]!='-')
{
path = argv[k];
@@ -299,6 +345,7 @@ int main(int argc, char **argv)
path += '/';
break;
}
+
switch(argv[k][1])
{
case 'h':
@@ -328,11 +375,15 @@ int main(int argc, char **argv)
}
}
+
/// new data extraction
std::string newData;
+
{
RawData data;
+
bool res = false;
+
if(svn_prefered)
{
/// SVN data
@@ -385,6 +436,7 @@ int main(int argc, char **argv)
if (!res)
res = extractDataFromArchive(path+"_hg_archival.txt",path,use_url,data);
}
+
else if(hg_prefered)
{
// HG data
@@ -411,13 +463,16 @@ int main(int argc, char **argv)
if (!res)
res = extractDataFromArchive(path+"_hg_archival.txt",path,use_url,data);
}
+
if(res)
newData = generateHeader(data.rev_str,data.date_str,data.time_str,data.hash_str);
else
newData = generateHeader("*", "*", "*", "*");
}
+
/// get existed header data for compare
std::string oldData;
+
if(FILE* HeaderFile = fopen("revision.h","rb"))
{
while(!feof(HeaderFile))
@@ -427,8 +482,10 @@ int main(int argc, char **argv)
break;
oldData += (char)c;
}
+
fclose(HeaderFile);
}
+
/// update header only if different data
if(newData != oldData)
{
@@ -438,6 +495,7 @@ int main(int argc, char **argv)
fclose(OutputFile);
}
}
+
return 0;
}
diff --git a/src/trinitycore/CliRunnable.cpp b/src/trinitycore/CliRunnable.cpp
index 42d465037d8..966b0b4dabb 100644
--- a/src/trinitycore/CliRunnable.cpp
+++ b/src/trinitycore/CliRunnable.cpp
@@ -17,14 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup Trinityd
/// @{
/// \file
+
#include "Common.h"
#include "ObjectMgr.h"
#include "World.h"
#include "WorldSession.h"
#include "Config/ConfigEnv.h"
+
#include "AccountMgr.h"
#include "Chat.h"
#include "CliRunnable.h"
@@ -34,19 +37,23 @@
#include "Player.h"
#include "ScriptCalls.h"
#include "Util.h"
+
#if PLATFORM != WINDOWS
#include <readline/readline.h>
#include <readline/history.h>
+
char * command_finder(const char* text, int state)
{
static int idx,len;
const char* ret;
ChatCommand *cmd = ChatHandler::getCommandTable();
+
if(!state)
{
idx = 0;
len = strlen(text);
}
+
while(ret = cmd[idx].Name)
{
if(!cmd[idx].AllowConsole)
@@ -54,6 +61,7 @@ char * command_finder(const char* text, int state)
idx++;
continue;
}
+
idx++;
//printf("Checking %s \n", cmd[idx].Name);
if (strncmp(ret, text, len) == 0)
@@ -61,12 +69,15 @@ char * command_finder(const char* text, int state)
if(cmd[idx].Name == NULL)
break;
}
+
return ((char*)NULL);
}
+
char ** cli_completion(const char * text, int start, int end)
{
char ** matches;
matches = (char**)NULL;
+
if(start == 0)
matches = rl_completion_matches((char*)text,&command_finder);
else
@@ -74,6 +85,7 @@ char ** cli_completion(const char * text, int start, int end)
return (matches);
}
#endif
+
void utf8print(const char* str)
{
#if PLATFORM == PLATFORM_WINDOWS
@@ -81,6 +93,7 @@ void utf8print(const char* str)
size_t wtemp_len = 6000-1;
if(!Utf8toWStr(str,strlen(str),wtemp_buf,wtemp_len))
return;
+
char temp_buf[6000];
CharToOemBuffW(&wtemp_buf[0],&temp_buf[0],wtemp_len+1);
printf(temp_buf);
@@ -88,16 +101,19 @@ void utf8print(const char* str)
printf(str);
#endif
}
+
/// Delete a user account and all associated characters in this realm
/// \todo This function has to be enhanced to respect the login/realm split (delete char, delete account chars in realm, delete account chars in realm then delete account
bool ChatHandler::HandleAccountDeleteCommand(const char* args)
{
if(!*args)
return false;
+
///- Get the account name from the command line
char *account_name_str=strtok ((char*)args," ");
if (!account_name_str)
return false;
+
std::string account_name = account_name_str;
if(!AccountMgr::normalizeString(account_name))
{
@@ -105,6 +121,7 @@ bool ChatHandler::HandleAccountDeleteCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
uint32 account_id = accmgr.GetId(account_name);
if(!account_id)
{
@@ -112,11 +129,13 @@ bool ChatHandler::HandleAccountDeleteCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
/// Commands not recommended call from chat, but support anyway
/// can delete only for account with less security
/// This is also reject self apply in fact
if(HasLowerSecurityAccount (NULL,account_id,true))
return false;
+
AccountOpResult result = accmgr.DeleteAccount(account_id);
switch(result)
{
@@ -136,20 +155,26 @@ bool ChatHandler::HandleAccountDeleteCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
bool ChatHandler::HandleCharacterDeleteCommand(const char* args)
{
if(!*args)
return false;
+
char *character_name_str = strtok((char*)args," ");
if(!character_name_str)
return false;
+
std::string character_name = character_name_str;
if(!normalizePlayerName(character_name))
return false;
+
uint64 character_guid;
uint32 account_id;
+
Player *player = objmgr.GetPlayer(character_name.c_str());
if(player)
{
@@ -166,14 +191,18 @@ bool ChatHandler::HandleCharacterDeleteCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
account_id = objmgr.GetPlayerAccountIdByGUID(character_guid);
}
+
std::string account_name;
accmgr.GetName (account_id,account_name);
+
Player::DeleteFromDB(character_guid, account_id, true);
PSendSysMessage(LANG_CHARACTER_DELETED,character_name.c_str(),GUID_LOPART(character_guid),account_name.c_str(), account_id);
return true;
}
+
/// Exit the realm
bool ChatHandler::HandleServerExitCommand(const char* /*args*/)
{
@@ -181,6 +210,7 @@ bool ChatHandler::HandleServerExitCommand(const char* /*args*/)
World::StopNow(SHUTDOWN_EXIT_CODE);
return true;
}
+
/// Display info on users currently in the realm
bool ChatHandler::HandleAccountOnlineListCommand(const char* /*args*/)
{
@@ -191,47 +221,59 @@ bool ChatHandler::HandleAccountOnlineListCommand(const char* /*args*/)
SendSysMessage(LANG_ACCOUNT_LIST_EMPTY);
return true;
}
+
///- Display the list of account/characters online
SendSysMessage(LANG_ACCOUNT_LIST_BAR);
SendSysMessage(LANG_ACCOUNT_LIST_HEADER);
SendSysMessage(LANG_ACCOUNT_LIST_BAR);
+
///- Circle through accounts
do
{
Field *fieldsDB = resultDB->Fetch();
std::string name = fieldsDB[0].GetCppString();
uint32 account = fieldsDB[1].GetUInt32();
+
///- Get the username, last IP and GM level of each account
// No SQL injection. account is uint32.
// 0 1 2 3
QueryResult *resultLogin = loginDatabase.PQuery("SELECT username, last_ip, gmlevel, expansion FROM account WHERE id = '%u'",account);
+
if(resultLogin)
{
Field *fieldsLogin = resultLogin->Fetch();
PSendSysMessage(LANG_ACCOUNT_LIST_LINE,
fieldsLogin[0].GetString(),name.c_str(),fieldsLogin[1].GetString(),fieldsLogin[2].GetUInt32(),fieldsLogin[3].GetUInt32());
+
delete resultLogin;
}
else
PSendSysMessage(LANG_ACCOUNT_LIST_ERROR,name.c_str());
+
}while(resultDB->NextRow());
+
delete resultDB;
+
SendSysMessage(LANG_ACCOUNT_LIST_BAR);
return true;
}
+
/// Create an account
bool ChatHandler::HandleAccountCreateCommand(const char* args)
{
if(!*args)
return false;
+
///- %Parse the command line arguments
char *szAcc = strtok((char*)args, " ");
char *szPassword = strtok(NULL, " ");
if(!szAcc || !szPassword)
return false;
+
// normalized in accmgr.CreateAccount
std::string account_name = szAcc;
std::string password = szPassword;
+
AccountOpResult result = accmgr.CreateAccount(account_name, password);
switch(result)
{
@@ -255,47 +297,60 @@ bool ChatHandler::HandleAccountCreateCommand(const char* args)
SetSentErrorMessage(true);
return false;
}
+
return true;
}
+
/// Set the level of logging
bool ChatHandler::HandleServerSetLogFileLevelCommand(const char *args)
{
if(!*args)
return false;
+
char *NewLevel = strtok((char*)args, " ");
if (!NewLevel)
return false;
+
sLog.SetLogFileLevel(NewLevel);
return true;
}
+
/// Set the level of logging
bool ChatHandler::HandleServerSetLogLevelCommand(const char *args)
{
if(!*args)
return false;
+
char *NewLevel = strtok((char*)args, " ");
if (!NewLevel)
return false;
+
sLog.SetLogLevel(NewLevel);
return true;
}
+
/// set diff time record interval
bool ChatHandler::HandleServerSetDiffTimeCommand(const char *args)
{
if(!*args)
return false;
+
char *NewTimeStr = strtok((char*)args, " ");
if(!NewTimeStr)
return false;
+
int32 NewTime =atoi(NewTimeStr);
if(NewTime < 0)
return false;
+
sWorld.SetRecordDiffInterval(NewTime);
printf( "Record diff every %u ms\n", NewTime);
return true;
}
+
/// @}
+
#ifdef linux
// Non-blocking keypress detector, when return pressed, return 1, else always return 0
int kb_hit_return()
@@ -310,11 +365,13 @@ int kb_hit_return()
return FD_ISSET(STDIN_FILENO, &fds);
}
#endif
+
/// %Thread start
void CliRunnable::run()
{
///- Init new SQL thread for the world database (one connection call enough)
WorldDatabase.ThreadStart(); // let thread do safe mySQL requests
+
char commandbuf[256];
bool canflush = true;
///- Display the list of available CLI functions then beep
@@ -324,14 +381,18 @@ void CliRunnable::run()
#endif
if(sConfig.GetBoolDefault("BeepAtStart", true))
printf("\a"); // \a = Alert
+
// print this here the first time
// later it will be printed after command queue updates
printf("TC>");
+
///- As long as the World is running (no World::m_stopEvent), get the command line and handle it
while (!World::IsStopped())
{
fflush(stdout);
+
char *command_str ; // = fgets(commandbuf,sizeof(commandbuf),stdin);
+
#if PLATFORM == WINDOWS
command_str = fgets(commandbuf,sizeof(commandbuf),stdin);
#else
@@ -347,6 +408,7 @@ void CliRunnable::run()
break;
}
+
if(!*command_str)
{
#if PLATFORM == WINDOWS
@@ -354,6 +416,7 @@ void CliRunnable::run()
#endif
continue;
}
+
std::string command;
if(!consoleToUtf8(command_str,command)) // convert from console encoding to utf8
{
@@ -367,12 +430,15 @@ void CliRunnable::run()
#if PLATFORM != WINDOWS
add_history(command.c_str());
#endif
+
}
else if (feof(stdin))
{
World::StopNow(SHUTDOWN_EXIT_CODE);
}
+
}
+
///- End the database thread
WorldDatabase.ThreadEnd(); // free mySQL thread resources
}
diff --git a/src/trinitycore/CliRunnable.h b/src/trinitycore/CliRunnable.h
index 34c677d69d2..cf745bbb195 100644
--- a/src/trinitycore/CliRunnable.h
+++ b/src/trinitycore/CliRunnable.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup Trinityd
/// @{
/// \file
+
#ifndef __CLIRUNNABLE_H
#define __CLIRUNNABLE_H
+
/// Command Line Interface handling thread
class CliRunnable : public ACE_Based::Runnable
{
diff --git a/src/trinitycore/Main.cpp b/src/trinitycore/Main.cpp
index b33b05372a5..342252741fb 100644
--- a/src/trinitycore/Main.cpp
+++ b/src/trinitycore/Main.cpp
@@ -17,25 +17,32 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup Trinityd Trinity Daemon
/// @{
/// \file
+
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
+
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "Config/ConfigEnv.h"
+
#include "Log.h"
#include "Master.h"
+
#ifndef _TRINITY_CORE_CONFIG
# define _TRINITY_CORE_CONFIG "TrinityCore.conf"
#endif //_TRINITY_CORE_CONFIG
+
// Format is YYYYMMDDRR where RR is the change in the conf file
// for that day.
#ifndef _TRINITY_CORE_CONFVER
# define _TRINITY_CORE_CONFVER 2009101701
#endif //_TRINITY_CORE_CONFVER
+
#ifdef WIN32
#include "ServiceWin32.h"
char serviceName[] = "TrinityCore";
@@ -49,10 +56,13 @@ char serviceDescription[] = "Massive Network Game Object Server";
*/
int m_ServiceStatus = -1;
#endif
+
DatabaseType WorldDatabase; ///< Accessor to the world database
DatabaseType CharacterDatabase; ///< Accessor to the character database
DatabaseType loginDatabase; ///< Accessor to the realm/login database
+
uint32 realmID; ///< Id of the realm
+
/// Print out the usage string for this program on the console.
void usage(const char *prog)
{
@@ -66,6 +76,7 @@ void usage(const char *prog)
#endif
,prog);
}
+
/// Launch the Trinity server
extern int main(int argc, char **argv)
{
@@ -85,6 +96,7 @@ extern int main(int argc, char **argv)
else
cfg_file = argv[c];
}
+
#ifdef WIN32
////////////
//Services//
@@ -124,12 +136,14 @@ extern int main(int argc, char **argv)
#endif
++c;
}
+
if (!sConfig.SetSource(cfg_file))
{
sLog.outError("Could not find configuration file %s.", cfg_file);
return 1;
}
sLog.outString("Using configuration file %s.", cfg_file);
+
uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0);
if (confVersion < _TRINITY_CORE_CONFVER)
{
@@ -141,6 +155,7 @@ extern int main(int argc, char **argv)
clock_t pause = 3000 + clock();
while (pause > clock()) {}
}
+
sLog.outDetail("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
if (SSLeay() < 0x009080bfL )
{
@@ -150,12 +165,15 @@ extern int main(int argc, char **argv)
while (pause > clock()) {}
return 1;
}
+
///- and run the 'Master'
/// \todo Why do we need this 'Master'? Can't all of this be in the Main as for Realmd?
return sMaster.Run();
+
// at sMaster return function exist with codes
// 0 - normal shutdown
// 1 - shutdown at error
// 2 - restart command used, this code can be used by restarter for restart Trinityd
}
+
/// @}
diff --git a/src/trinitycore/Master.cpp b/src/trinitycore/Master.cpp
index 6c334733ee0..7e56cb7a636 100644
--- a/src/trinitycore/Master.cpp
+++ b/src/trinitycore/Master.cpp
@@ -17,10 +17,13 @@
* 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 Trinityd
*/
+
#include <ace/OS_NS_signal.h>
+
#include "Common.h"
#include "SystemConfig.h"
#include "World.h"
@@ -30,6 +33,7 @@
#include "Config/ConfigEnv.h"
#include "Database/DatabaseEnv.h"
#include "Policies/SingletonImp.h"
+
#include "CliRunnable.h"
#include "Log.h"
#include "Master.h"
@@ -37,20 +41,26 @@
#include "ScriptCalls.h"
#include "Timer.h"
#include "Util.h"
+
#include "sockets/TcpSocket.h"
#include "sockets/Utility.h"
#include "sockets/Parse.h"
#include "sockets/Socket.h"
#include "sockets/SocketHandler.h"
#include "sockets/ListenSocket.h"
+
#ifdef WIN32
#include "ServiceWin32.h"
extern int m_ServiceStatus;
#endif
+
/// \todo Warning disabling not useful under VC++2005. Can somebody say on which compiler it is useful?
#pragma warning(disable:4305)
+
INSTANTIATE_SINGLETON_1( Master );
+
volatile uint32 Master::m_masterLoopCounter = 0;
+
class FreezeDetectorRunnable : public ACE_Based::Runnable
{
public:
@@ -73,6 +83,7 @@ public:
ACE_Based::Thread::Sleep(1000);
uint32 curtime = getMSTime();
//DEBUG_LOG("anti-freeze: time=%u, counters=[%u; %u]",curtime,Master::m_masterLoopCounter,World::m_worldLoopCounter);
+
// There is no Master anymore
// TODO: clear the rest of the code
// // normal work
@@ -87,6 +98,7 @@ public:
// sLog.outError("Main/Sockets Thread hangs, kicking out server!");
// *((uint32 volatile*)NULL) = 0; // bang crash
// }
+
// normal work
if(w_loops != World::m_worldLoopCounter)
{
@@ -103,16 +115,19 @@ public:
sLog.outString("Anti-freeze thread exiting without problems.");
}
};
+
class RARunnable : public ACE_Based::Runnable
{
public:
uint32 numLoops, loopCounter;
+
RARunnable ()
{
uint32 socketSelecttime = sWorld.getConfig (CONFIG_SOCKET_SELECTTIME);
numLoops = (sConfig.GetIntDefault ("MaxPingTime", 30) * (MINUTE * 1000000 / socketSelecttime));
loopCounter = 0;
}
+
void checkping ()
{
// ping if need
@@ -125,12 +140,15 @@ public:
delete CharacterDatabase.Query ("SELECT 1 FROM bugreport LIMIT 1");
}
}
+
void run ()
{
SocketHandler h;
+
// Launch the RA listener socket
ListenSocket<RASocket> RAListenSocket (h);
bool usera = sConfig.GetBoolDefault ("Ra.Enable", false);
+
if (usera)
{
port_t raport = sConfig.GetIntDefault ("Ra.Port", 3443);
@@ -143,11 +161,14 @@ public:
else
{
h.Add (&RAListenSocket);
+
sLog.outString ("Starting Remote access listner on port %d on %s", raport, stringip.c_str ());
}
}
+
// Socket Selet time is in microseconds , not miliseconds!!
uint32 socketSelecttime = sWorld.getConfig (CONFIG_SOCKET_SELECTTIME);
+
// if use ra spend time waiting for io, if not use ra ,just sleep
if (usera)
{
@@ -167,17 +188,21 @@ public:
}
}
};
+
Master::Master()
{
}
+
Master::~Master()
{
}
+
/// Main function
int Master::Run()
{
sLog.outString( "%s (core-daemon)", _FULLVERSION );
sLog.outString( "<Ctrl-C> to stop.\n" );
+
sLog.outString( " ______ __");
sLog.outString( "/\\__ _\\ __ __/\\ \\__");
sLog.outString( "\\/_/\\ \\/ _ __ /\\_\\ ___ /\\_\\ \\ ,_\\ __ __");
@@ -187,6 +212,7 @@ int Master::Run()
sLog.outString( " \\/_/\\/_/ \\/_/\\/_/\\/_/\\/_/\\/__/ `/___/> \\");
sLog.outString( " C O R E /\\___/");
sLog.outString( "http://TrinityCore.org \\/__/\n");
+
/// worldd PID file creation
std::string pidfile = sConfig.GetStringDefault("PidFile", "");
if(!pidfile.empty())
@@ -197,21 +223,29 @@ int Master::Run()
sLog.outError( "Cannot create PID file %s.\n", pidfile.c_str() );
return 1;
}
+
sLog.outString( "Daemon PID: %u\n", pid );
}
+
///- Start the databases
if (!_StartDB())
return 1;
+
///- Initialize the World
sWorld.SetInitialWorldSettings();
+
///- Catch termination signals
_HookSignals();
+
///- Launch WorldRunnable thread
ACE_Based::Thread world_thread(new WorldRunnable);
world_thread.setPriority(ACE_Based::Highest);
+
// set server online
loginDatabase.PExecute("UPDATE realmlist SET color = 0, population = 0 WHERE id = '%d'",realmID);
+
ACE_Based::Thread* cliThread = NULL;
+
#ifdef WIN32
if (sConfig.GetBoolDefault("Console.Enable", true) && (m_ServiceStatus == -1)/* need disable console in service mode*/)
#else
@@ -221,19 +255,24 @@ int Master::Run()
///- Launch CliRunnable thread
cliThread = new ACE_Based::Thread(new CliRunnable);
}
+
ACE_Based::Thread rar_thread(new RARunnable);
+
///- Handle affinity for multiple processors and process priority on Windows
#ifdef WIN32
{
HANDLE hProcess = GetCurrentProcess();
+
uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
if(Aff > 0)
{
ULONG_PTR appAff;
ULONG_PTR sysAff;
+
if(GetProcessAffinityMask(hProcess,&appAff,&sysAff))
{
ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
+
if(!curAff )
{
sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for Trinityd. Accessible processors bitmask (hex): %x",Aff,appAff);
@@ -248,7 +287,9 @@ int Master::Run()
}
sLog.outString("");
}
+
bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
+
// if(Prio && (m_ServiceStatus == -1)/* need set to default process priority class in service mode*/)
if(Prio)
{
@@ -260,9 +301,12 @@ int Master::Run()
}
}
#endif
+
uint32 realCurrTime, realPrevTime;
realCurrTime = realPrevTime = getMSTime();
+
uint32 socketSelecttime = sWorld.getConfig(CONFIG_SOCKET_SELECTTIME);
+
///- Start up freeze catcher thread
if(uint32 freeze_delay = sConfig.GetIntDefault("MaxCoreStuckTime", 0))
{
@@ -271,34 +315,45 @@ int Master::Run()
ACE_Based::Thread freeze_thread(fdr);
freeze_thread.setPriority(ACE_Based::Highest);
}
+
///- Launch the world listener socket
port_t wsport = sWorld.getConfig (CONFIG_PORT_WORLD);
std::string bind_ip = sConfig.GetStringDefault ("BindIP", "0.0.0.0");
+
if (sWorldSocketMgr->StartNetwork (wsport, bind_ip.c_str ()) == -1)
{
sLog.outError ("Failed to start network");
World::StopNow(ERROR_EXIT_CODE);
// go down and shutdown the server
}
+
sWorldSocketMgr->Wait ();
+
// set server offline
loginDatabase.PExecute("UPDATE realmlist SET color = 2 WHERE id = '%d'",realmID);
+
///- Remove signal handling before leaving
_UnhookSignals();
+
// when the main thread closes the singletons get unloaded
// since worldrunnable uses them, it will crash if unloaded after master
world_thread.wait();
rar_thread.wait ();
+
///- Clean database before leaving
clearOnlineAccounts();
+
///- Wait for delay threads to end
CharacterDatabase.HaltDelayThread();
WorldDatabase.HaltDelayThread();
loginDatabase.HaltDelayThread();
+
sLog.outString( "Halting process..." );
+
if (cliThread)
{
#ifdef WIN32
+
// this only way to terminate CLI thread exist at Win32 (alt. way exist only in Windows Vista API)
//_exit(1);
// send keyboard input to safely unblock the CLI thread
@@ -309,11 +364,13 @@ int Master::Run()
b[0].Event.KeyEvent.uChar.AsciiChar = 'X';
b[0].Event.KeyEvent.wVirtualKeyCode = 'X';
b[0].Event.KeyEvent.wRepeatCount = 1;
+
b[1].EventType = KEY_EVENT;
b[1].Event.KeyEvent.bKeyDown = FALSE;
b[1].Event.KeyEvent.uChar.AsciiChar = 'X';
b[1].Event.KeyEvent.wVirtualKeyCode = 'X';
b[1].Event.KeyEvent.wRepeatCount = 1;
+
b[2].EventType = KEY_EVENT;
b[2].Event.KeyEvent.bKeyDown = TRUE;
b[2].Event.KeyEvent.dwControlKeyState = 0;
@@ -321,6 +378,7 @@ int Master::Run()
b[2].Event.KeyEvent.wVirtualKeyCode = VK_RETURN;
b[2].Event.KeyEvent.wRepeatCount = 1;
b[2].Event.KeyEvent.wVirtualScanCode = 0x1c;
+
b[3].EventType = KEY_EVENT;
b[3].Event.KeyEvent.bKeyDown = FALSE;
b[3].Event.KeyEvent.dwControlKeyState = 0;
@@ -330,23 +388,32 @@ int Master::Run()
b[3].Event.KeyEvent.wRepeatCount = 1;
DWORD numb;
BOOL ret = WriteConsoleInput(hStdIn, b, 4, &numb);
+
cliThread->wait();
- #else
+
+ #else
+
cliThread->destroy();
+
#endif
+
delete cliThread;
}
+
// for some unknown reason, unloading scripts here and not in worldrunnable
// fixes a memory leak related to detaching threads from the module
UnloadScriptingModule();
+
// Exit the process with specified return value
return World::GetExitCode();
}
+
/// Initialize connection to the databases
bool Master::_StartDB()
{
sLog.SetLogDB(false);
std::string dbstring;
+
///- Get world database info from configuration file
dbstring = sConfig.GetStringDefault("WorldDatabaseInfo", "");
if(dbstring.empty())
@@ -354,12 +421,14 @@ bool Master::_StartDB()
sLog.outError("Database not specified in configuration file");
return false;
}
+
///- Initialise the world database
if(!WorldDatabase.Initialize(dbstring.c_str()))
{
sLog.outError("Cannot connect to world database %s",dbstring.c_str());
return false;
}
+
///- Get character database info from configuration file
dbstring = sConfig.GetStringDefault("CharacterDatabaseInfo", "");
if(dbstring.empty())
@@ -367,12 +436,14 @@ bool Master::_StartDB()
sLog.outError("Character Database not specified in configuration file");
return false;
}
+
///- Initialise the Character database
if(!CharacterDatabase.Initialize(dbstring.c_str()))
{
sLog.outError("Cannot connect to Character database %s",dbstring.c_str());
return false;
}
+
///- Get login database info from configuration file
dbstring = sConfig.GetStringDefault("loginDatabaseInfo", "");
if(dbstring.empty())
@@ -380,12 +451,14 @@ bool Master::_StartDB()
sLog.outError("Login database not specified in configuration file");
return false;
}
+
///- Initialise the login database
if(!loginDatabase.Initialize(dbstring.c_str()))
{
sLog.outError("Cannot connect to login database %s",dbstring.c_str());
return false;
}
+
///- Get the realm Id from the configuration file
realmID = sConfig.GetIntDefault("RealmID", 0);
if(!realmID)
@@ -394,6 +467,7 @@ bool Master::_StartDB()
return false;
}
sLog.outString("Realm running as realm ID %d", realmID);
+
///- Initialize the DB logging system
if(sConfig.GetBoolDefault("EnableLogDB", false))
{
@@ -408,15 +482,20 @@ bool Master::_StartDB()
sLog.SetLogDB(false);
sLog.SetRealmID(realmID);
}
+
///- Clean the database before starting
clearOnlineAccounts();
+
///- Insert version info into DB
WorldDatabase.PExecute("UPDATE version SET core_version = '%s', core_revision = '%s'", _FULLVERSION, _REVISION);
+
sWorld.LoadDBVersion();
+
sLog.outString("Using World DB: %s", sWorld.GetDBVersion());
sLog.outString("Using creature EventAI: %s", sWorld.GetCreatureEventAIVersion());
return true;
}
+
/// Clear 'online' status for all accounts with characters in this realm
void Master::clearOnlineAccounts()
{
@@ -425,10 +504,13 @@ void Master::clearOnlineAccounts()
loginDatabase.PExecute(
"UPDATE account SET online = 0 WHERE online > 0 "
"AND id IN (SELECT acctid FROM realmcharacters WHERE realmid = '%d')",realmID);
+
CharacterDatabase.Execute("UPDATE characters SET online = 0 WHERE online<>0");
+
// Battleground instance ids reset at server restart
CharacterDatabase.Execute("UPDATE character_battleground_data SET instance_id = 0");
}
+
/// Handle termination signals
void Master::_OnSignal(int s)
{
@@ -445,8 +527,10 @@ void Master::_OnSignal(int s)
World::StopNow(SHUTDOWN_EXIT_CODE);
break;
}
+
signal(s, _OnSignal);
}
+
/// Define hook '_OnSignal' for all termination signals
void Master::_HookSignals()
{
@@ -457,6 +541,7 @@ void Master::_HookSignals()
signal(SIGBREAK, _OnSignal);
#endif
}
+
/// Unhook the signals before leaving
void Master::_UnhookSignals()
{
diff --git a/src/trinitycore/Master.h b/src/trinitycore/Master.h
index 49b29bd8639..967f497051a 100644
--- a/src/trinitycore/Master.h
+++ b/src/trinitycore/Master.h
@@ -17,13 +17,17 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup Trinityd
/// @{
/// \file
+
#ifndef _MASTER_H
#define _MASTER_H
+
#include "Common.h"
#include "Policies/Singleton.h"
+
/// Start the server
class Master
{
@@ -32,13 +36,17 @@ class Master
~Master();
int Run();
static volatile uint32 m_masterLoopCounter;
+
private:
bool _StartDB();
+
void _HookSignals();
void _UnhookSignals();
static void _OnSignal(int s);
+
void clearOnlineAccounts();
};
+
#define sMaster Trinity::Singleton<Master>::Instance()
#endif
/// @}
diff --git a/src/trinitycore/RASocket.cpp b/src/trinitycore/RASocket.cpp
index a86acfb6078..d0ee5cc8053 100644
--- a/src/trinitycore/RASocket.cpp
+++ b/src/trinitycore/RASocket.cpp
@@ -17,9 +17,11 @@
* 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 Trinityd
*/
+
#include "Common.h"
#include "Config/ConfigEnv.h"
#include "Database/DatabaseEnv.h"
@@ -28,38 +30,51 @@
#include "RASocket.h"
#include "Util.h"
#include "World.h"
+
/// \todo Make this thread safe if in the future 2 admins should be able to log at the same time.
SOCKET r;
+
#define dropclient {Sendf("I'm busy right now, come back later."); \
SetCloseAndDelete(); \
return; \
}
+
uint32 iSession=0; ///< Session number (incremented each time a new connection is made)
unsigned int iUsers=0; ///< Number of active administrators
+
typedef int(* pPrintf)(const char*,...);
+
void ParseCommand(CliCommandHolder::Print*, char*command);
+
/// RASocket constructor
RASocket::RASocket(ISocketHandler &h): TcpSocket(h)
{
+
///- Increment the session number
iSess =iSession++ ;
+
///- Get the config parameters
bSecure = sConfig.GetBoolDefault( "RA.Secure", true );
iMinLevel = sConfig.GetIntDefault( "RA.MinLevel", 3 );
+
///- Initialize buffer and data
iInputLength=0;
buff=new char[RA_BUFF_SIZE];
stage=NONE;
}
+
/// RASocket destructor
RASocket::~RASocket()
{
///- Delete buffer and decrease active admins count
delete [] buff;
+
sLog.outRemote("Connection was closed.\n");
+
if(stage==OK)
iUsers--;
}
+
/// Accept an incoming connection
void RASocket::OnAccept()
{
@@ -68,14 +83,17 @@ void RASocket::OnAccept()
///- If there is already an active admin, drop the connection
if(iUsers)
dropclient
+
///- Else print Motd
Sendf("%s\r\n",sWorld.GetMotd());
}
+
/// Read data from the network
void RASocket::OnRead()
{
///- Read data and check input length
TcpSocket::OnRead();
+
unsigned int sz=ibuf.GetLength();
if(iInputLength+sz>=RA_BUFF_SIZE)
{
@@ -83,11 +101,14 @@ void RASocket::OnRead()
SetCloseAndDelete();
return;
}
+
///- If there is already an active admin (other than you), drop the connection
if(stage!=OK && iUsers)
dropclient
+
char *inp = new char [sz+1];
ibuf.Read(inp,sz);
+
/// \todo Can somebody explain this 'Linux bugfix'?
if(stage==NONE)
if(sz>4) //linux remote telnet
@@ -96,6 +117,7 @@ void RASocket::OnRead()
delete [] inp;return;
printf("lin bugfix");
} //linux bugfix
+
///- Discard data after line break or line feed
bool gotenter=false;
unsigned int y=0;
@@ -105,12 +127,14 @@ void RASocket::OnRead()
gotenter=true;
break;
}
+
//No buffer overflow (checked above)
memcpy(&buff[iInputLength],inp,y);
iInputLength+=y;
delete [] inp;
if(gotenter)
{
+
buff[iInputLength]=0;
iInputLength=0;
switch(stage)
@@ -120,13 +144,18 @@ void RASocket::OnRead()
if(!memcmp(buff,"USER ",5)) //got "USER" cmd
{
szLogin=&buff[5];
+
///- Get the gmlevel and password from the account table
std::string login = szLogin;
+
///- Convert Account name to Upper Format
AccountMgr::normalizeString(login);
+
///- Escape the Login to allow quotes in names
loginDatabase.escape_string(login);
+
QueryResult* result = loginDatabase.PQuery("SELECT gmlevel FROM account WHERE username = '%s'",login.c_str());
+
///- If the user is not found, deny access
if(!result)
{
@@ -137,7 +166,9 @@ void RASocket::OnRead()
else
{
Field *fields = result->Fetch();
+
//szPass=fields[0].GetString();
+
///- if gmlevel is too low, deny access
if(fields[0].GetUInt32()<iMinLevel)
{
@@ -159,19 +190,23 @@ void RASocket::OnRead()
///- If password is correct, increment the number of active administrators
std::string login = szLogin;
std::string pw = &buff[5];
+
AccountMgr::normalizeString(login);
AccountMgr::normalizeString(pw);
loginDatabase.escape_string(login);
loginDatabase.escape_string(pw);
+
QueryResult *check = loginDatabase.PQuery(
"SELECT 1 FROM account WHERE username = '%s' AND sha_pass_hash=SHA1(CONCAT('%s',':','%s'))",
login.c_str(), login.c_str(), pw.c_str());
+
if(check)
{
delete check;
r=GetSocket();
stage=OK;
++iUsers;
+
Sendf("+Logged in.\r\n");
sLog.outRemote("User %s has logged in.\n",szLogin.c_str());
Sendf("TC>");
@@ -197,21 +232,28 @@ void RASocket::OnRead()
break;
///</ul>
};
+
}
}
+
/// Output function
void RASocket::zprint( const char * szText )
{
if( !szText )
return;
+
#ifdef RA_CRYPT
+
char *megabuffer=strdup(szText);
unsigned int sz=strlen(megabuffer);
Encrypt(megabuffer,sz);
send(r,megabuffer,sz,0);
delete [] megabuffer;
+
#else
+
unsigned int sz=strlen(szText);
send(r,szText,sz,0);
+
#endif
}
diff --git a/src/trinitycore/RASocket.h b/src/trinitycore/RASocket.h
index 813627766a5..330c85defa4 100644
--- a/src/trinitycore/RASocket.h
+++ b/src/trinitycore/RASocket.h
@@ -17,24 +17,35 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup Trinityd
/// @{
/// \file
+
#ifndef _RASOCKET_H
#define _RASOCKET_H
+
#include "sockets/TcpSocket.h"
+
#include "Common.h"
+
#define RA_BUFF_SIZE 1024
+
class ISocketHandler;
+
/// Remote Administration socket
class RASocket: public TcpSocket
{
public:
+
RASocket(ISocketHandler& h);
~RASocket();
+
void OnAccept();
void OnRead();
+
private:
+
char * buff;
std::string szLogin;
uint32 iSess;
@@ -50,6 +61,7 @@ class RASocket: public TcpSocket
LG, //only login was entered
OK, //both login and pass were given, and they are correct and user have enough priv.
}stage;
+
static void zprint( const char * szText );
};
#endif
diff --git a/src/trinitycore/TrinityCore.rc b/src/trinitycore/TrinityCore.rc
index 156f1ac21f5..151185f3cec 100644
--- a/src/trinitycore/TrinityCore.rc
+++ b/src/trinitycore/TrinityCore.rc
@@ -15,34 +15,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "resource.h"
#include "../shared/revision.h"
+
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h"
+
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
+
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
+
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APPICON ICON "TrinityCore.ico"
+
/////////////////////////////////////////////////////////////////////////////
// Neutre (Par défaut système) resources
+
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
#pragma code_page(1252)
#endif //_WIN32
+
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
+
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILEVER
PRODUCTVERSION PRODUCTVER
diff --git a/src/trinitycore/WorldRunnable.cpp b/src/trinitycore/WorldRunnable.cpp
index 8847f57cee3..06c0e69fa16 100644
--- a/src/trinitycore/WorldRunnable.cpp
+++ b/src/trinitycore/WorldRunnable.cpp
@@ -17,27 +17,33 @@
* 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 Trinityd
*/
+
#include "Common.h"
#include "ObjectAccessor.h"
#include "World.h"
#include "WorldSocketMgr.h"
#include "Database/DatabaseEnv.h"
+
#include "BattleGroundMgr.h"
#include "MapManager.h"
#include "Timer.h"
#include "WorldRunnable.h"
+
#if (defined(WIN32) || defined(SHORT_SLEEP))
#define WORLD_SLEEP_CONST 50
#else
#define WORLD_SLEEP_CONST 100 //Is this still needed?? [On linux some time ago not working 50ms]
#endif
+
#ifdef WIN32
#include "ServiceWin32.h"
extern int m_ServiceStatus;
#endif
+
/// Heartbeat for the World
void WorldRunnable::run()
{
@@ -46,17 +52,23 @@ void WorldRunnable::run()
CharacterDatabase.ThreadStart();
loginDatabase.ThreadStart();
sWorld.InitResultQueue();
+
uint32 realCurrTime = 0;
uint32 realPrevTime = getMSTime();
+
uint32 prevSleepTime = 0; // used for balanced full tick time length near WORLD_SLEEP_CONST
+
///- While we have not World::m_stopEvent, update the world
while (!World::IsStopped())
{
++World::m_worldLoopCounter;
realCurrTime = getMSTime();
+
uint32 diff = getMSTimeDiff(realPrevTime,realCurrTime);
+
sWorld.Update( diff );
realPrevTime = realCurrTime;
+
// diff (D0) include time of previous sleep (d0) + tick time (t0)
// we want that next d1 + t1 == WORLD_SLEEP_CONST
// we can't know next t1 and then can use (t0 + d1) == WORLD_SLEEP_CONST requirement
@@ -68,17 +80,23 @@ void WorldRunnable::run()
}
else
prevSleepTime = 0;
+
#ifdef WIN32
if (m_ServiceStatus == 0) World::StopNow(SHUTDOWN_EXIT_CODE);
while (m_ServiceStatus == 2) Sleep(1000);
#endif
}
+
sWorld.KickAll(); // save and kick all players
sWorld.UpdateSessions( 1 ); // real players unload required UpdateSessions call
+
// unload battleground templates before different singletons destroyed
sBattleGroundMgr.DeleteAllBattleGrounds();
+
sWorldSocketMgr->StopNetwork();
+
MapManager::Instance().UnloadAll(); // unload all grids (including locked in memory)
+
///- End the database thread
WorldDatabase.ThreadEnd(); // free mySQL thread resources
CharacterDatabase.ThreadStart();
diff --git a/src/trinitycore/WorldRunnable.h b/src/trinitycore/WorldRunnable.h
index 036b8711a39..07ce246b04f 100644
--- a/src/trinitycore/WorldRunnable.h
+++ b/src/trinitycore/WorldRunnable.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup Trinityd
/// @{
/// \file
+
#ifndef __WORLDRUNNABLE_H
#define __WORLDRUNNABLE_H
+
/// Heartbeat thread for the World
class WorldRunnable : public ACE_Based::Runnable
{
diff --git a/src/trinitycore/resource.h b/src/trinitycore/resource.h
index 33985dd3ede..7dc5cb9ef7b 100644
--- a/src/trinitycore/resource.h
+++ b/src/trinitycore/resource.h
@@ -2,6 +2,7 @@
// Microsoft Visual C++ generated include file.
// Used by TrinityCore.rc
//
+
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
diff --git a/src/trinityrealm/AuthCodes.h b/src/trinityrealm/AuthCodes.h
index ce9c8f3a537..73728898503 100644
--- a/src/trinityrealm/AuthCodes.h
+++ b/src/trinityrealm/AuthCodes.h
@@ -17,11 +17,14 @@
* 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 realmd
*/
+
#ifndef _AUTHCODES_H
#define _AUTHCODES_H
+
enum eAuthResults
{
REALM_AUTH_SUCCESS = 0x00,
@@ -41,6 +44,7 @@ enum eAuthResults
REALM_AUTH_UNKNOWN5 = 0x0e, ///< Connected.
REALM_AUTH_PARENTAL_CONTROL = 0x0f ///< Access to this account has been blocked by parental controls. Your settings may be changed in your account preferences at <site>
};
+
enum LoginResult
{
LOGIN_OK = 0x00,
@@ -61,11 +65,14 @@ enum LoginResult
LOGIN_PARENTALCONTROL = 0x0F,
LOGIN_LOCKED_ENFORCED = 0x10,
};
+
//multirealm supported versions:
//1.12.1 build 5875
//1.12.2 build 6005
//2.4.3 build 8606
//3.1.3 build 9947
//3.1.3 build 10146 Chinese build
+
#define EXPECTED_TRINITY_CLIENT_BUILD {10146, 9947, 8606, 5875, 6005, 0}
+
#endif
diff --git a/src/trinityrealm/AuthSocket.cpp b/src/trinityrealm/AuthSocket.cpp
index 4430be86d25..90a2a27d5b9 100644
--- a/src/trinityrealm/AuthSocket.cpp
+++ b/src/trinityrealm/AuthSocket.cpp
@@ -17,9 +17,11 @@
* 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 realmd
*/
+
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "ByteBuffer.h"
@@ -31,9 +33,13 @@
#include <openssl/md5.h>
#include "Auth/Sha1.h"
//#include "Util.h" -- for commented utf8ToUpperOnlyLatin
+
extern RealmList m_realmList;
+
extern DatabaseType loginDatabase;
+
#define ChunkSize 2048
+
enum eAuthCmd
{
//AUTH_NO_CMD = 0xFF,
@@ -49,17 +55,20 @@ enum eAuthCmd
XFER_RESUME = 0x33,
XFER_CANCEL = 0x34
};
+
enum eStatus
{
STATUS_CONNECTED = 0,
STATUS_AUTHED
};
+
// 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 paltform
#if defined(__GNUC__)
#pragma pack(1)
#else
#pragma pack(push,1)
#endif
+
typedef struct AUTH_LOGON_CHALLENGE_C
{
uint8 cmd;
@@ -78,6 +87,7 @@ typedef struct AUTH_LOGON_CHALLENGE_C
uint8 I_len;
uint8 I[1];
} sAuthLogonChallenge_C;
+
//typedef sAuthLogonChallenge_C sAuthReconnectChallenge_C;
/*
typedef struct
@@ -94,6 +104,7 @@ typedef struct
uint8 unk3[16];
} sAuthLogonChallenge_S;
*/
+
typedef struct AUTH_LOGON_PROOF_C
{
uint8 cmd;
@@ -121,6 +132,7 @@ typedef struct AUTH_LOGON_PROOF_S
uint32 unk2;
uint16 unk3;
} sAuthLogonProof_S;
+
typedef struct AUTH_LOGON_PROOF_S_OLD
{
uint8 cmd;
@@ -130,6 +142,7 @@ typedef struct AUTH_LOGON_PROOF_S_OLD
uint32 unk2;
//uint16 unk3;
} sAuthLogonProof_S_Old;
+
typedef struct AUTH_RECONNECT_PROOF_C
{
uint8 cmd;
@@ -138,6 +151,7 @@ typedef struct AUTH_RECONNECT_PROOF_C
uint8 R3[20];
uint8 number_of_keys;
} sAuthReconnectProof_C;
+
typedef struct XFER_INIT
{
uint8 cmd; // XFER_INITIATE
@@ -146,37 +160,44 @@ typedef struct XFER_INIT
uint64 file_size; // file size (bytes)
uint8 md5[MD5_DIGEST_LENGTH]; // MD5
}XFER_INIT;
+
typedef struct XFER_DATA
{
uint8 opcode;
uint16 data_size;
uint8 data[ChunkSize];
}XFER_DATA_STRUCT;
+
typedef struct AuthHandler
{
eAuthCmd cmd;
uint32 status;
bool (AuthSocket::*handler)(void);
}AuthHandler;
+
// GCC have alternative #pragma pack() syntax and old gcc version not support pack(pop), also any gcc version not support it at some paltform
#if defined(__GNUC__)
#pragma pack()
#else
#pragma pack(pop)
#endif
+
/// Launch a thread to transfer a patch to the client
class PatcherRunnable: public ACE_Based::Runnable
{
public:
PatcherRunnable(class AuthSocket *);
void run();
+
private:
AuthSocket * mySocket;
};
+
typedef struct PATCH_INFO
{
uint8 md5[MD5_DIGEST_LENGTH];
}PATCH_INFO;
+
/// Caches MD5 hash of client patches present on the server
class Patcher
{
@@ -188,10 +209,12 @@ class Patcher
Patches::const_iterator end() const { return _patches.end(); }
void LoadPatchMD5(char*);
bool GetHash(char * pat,uint8 mymd5[16]);
+
private:
void LoadPatchesInfo();
Patches _patches;
};
+
const AuthHandler table[] =
{
{ AUTH_LOGON_CHALLENGE, STATUS_CONNECTED, &AuthSocket::_HandleLogonChallenge },
@@ -203,9 +226,12 @@ const AuthHandler table[] =
{ XFER_RESUME, STATUS_CONNECTED, &AuthSocket::_HandleXferResume },
{ XFER_CANCEL, STATUS_CONNECTED, &AuthSocket::_HandleXferCancel }
};
+
#define AUTH_TOTAL_COMMANDS sizeof(table)/sizeof(AuthHandler)
+
///Holds the MD5 hash of client patches present on the server
Patcher PatchesCache;
+
/// Constructor - set the N and g values for SRP6
AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h)
{
@@ -213,21 +239,27 @@ AuthSocket::AuthSocket(ISocketHandler &h) : TcpSocket(h)
g.SetDword(7);
_authed = false;
pPatch = NULL;
+
_accountSecurityLevel = SEC_PLAYER;
}
+
/// Close patch file descriptor before leaving
AuthSocket::~AuthSocket()
{
ACE_Guard<ACE_Thread_Mutex> g(patcherLock);
+
if (pPatch)
fclose(pPatch);
}
+
/// Accept the connection and set the s random value for SRP6
void AuthSocket::OnAccept()
{
sLog.outBasic("Accepting connection from '%s:%d'",
GetRemoteAddress().c_str(), GetRemotePort());
+
}
+
/// Read the packet from the client
void AuthSocket::OnRead()
{
@@ -238,9 +270,12 @@ void AuthSocket::OnRead()
{
if (!ibuf.GetLength())
return;
+
///- Get the command out of it
ibuf.SoftRead((char *)&_cmd, 1); // UQ1: No longer exists in new net code ???
+
size_t i;
+
///- Circle through known commands and call the correct command handler
for (i = 0; i < AUTH_TOTAL_COMMANDS; ++i)
{
@@ -249,6 +284,7 @@ void AuthSocket::OnRead()
(_authed && table[i].status == STATUS_AUTHED)))
{
DEBUG_LOG("[Auth] got data for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength());
+
if (!(*this.*table[i].handler)())
{
DEBUG_LOG("Command handler failed for cmd %u ibuf length %u", (uint32)_cmd, ibuf.GetLength());
@@ -257,6 +293,7 @@ void AuthSocket::OnRead()
break;
}
}
+
///- Report unknown commands in the debug log
if (i == AUTH_TOTAL_COMMANDS)
{
@@ -265,18 +302,23 @@ void AuthSocket::OnRead()
}
}
}
+
/// Make the SRP6 calculation from hash in dB
void AuthSocket::_SetVSFields(const std::string& rI)
{
s.SetRand(s_BYTE_SIZE * 8);
+
BigNumber I;
I.SetHexStr(rI.c_str());
+
// In case of leading zeros in the rI 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);
+
Sha1Hash sha;
sha.UpdateData(s.AsByteArray(), s.GetNumBytes());
sha.UpdateData(mDigest, SHA_DIGEST_LENGTH);
@@ -292,25 +334,32 @@ void AuthSocket::_SetVSFields(const std::string& rI)
OPENSSL_free((void*)v_hex);
OPENSSL_free((void*)s_hex);
}
+
/// Logon Challenge command handler
bool AuthSocket::_HandleLogonChallenge()
{
DEBUG_LOG("Entering _HandleLogonChallenge");
if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C))
return false;
+
///- Read the first 4 bytes (header) to get the length of the remaining of the packet
std::vector<uint8> buf;
buf.resize(4);
+
ibuf.Read((char *)&buf[0], 4);
+
EndianConvert(*((uint16*)(buf[0])));
uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size;
DEBUG_LOG("[AuthChallenge] got header, body is %#04x bytes", remaining);
+
if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining))
return false;
+
//No big fear of memory outage (size is int16, i.e. < 65536)
buf.resize(remaining + buf.size() + 1);
buf[buf.size() - 1] = 0;
sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0];
+
// BigEndian code, nop in little endian case
// size already converted
EndianConvert(*((uint32*)(&ch->gamename[0])));
@@ -320,24 +369,32 @@ bool AuthSocket::_HandleLogonChallenge()
EndianConvert(*((uint32*)(&ch->country[0])));
EndianConvert(ch->timezone_bias);
EndianConvert(ch->ip);
+
///- Read the remaining of the packet
ibuf.Read((char *)&buf[4], remaining);
DEBUG_LOG("[AuthChallenge] got full packet, %#04x bytes", ch->size);
DEBUG_LOG("[AuthChallenge] name(%d): '%s'", ch->I_len, ch->I);
+
ByteBuffer pkt;
+
_login = (const char*)ch->I;
_build = ch->build;
+
///- Normalize account name
//utf8ToUpperOnlyLatin(_login); -- client already send account in expected form
+
//Escape the user login to avoid further SQL injection
//Memory will be freed on AuthSocket object destruction
_safelogin = _login;
loginDatabase.escape_string(_safelogin);
+
pkt << (uint8) AUTH_LOGON_CHALLENGE;
pkt << (uint8) 0x00;
+
///- Verify that this IP is not in the ip_banned table
// No SQL injection possible (paste the IP address as passed by the socket)
loginDatabase.Execute("DELETE FROM ip_banned WHERE unbandate<=UNIX_TIMESTAMP() AND unbandate<>bandate");
+
std::string address = GetRemoteAddress();
loginDatabase.escape_string(address);
QueryResult *result = loginDatabase.PQuery("SELECT * FROM ip_banned WHERE ip = '%s'",address.c_str());
@@ -351,6 +408,7 @@ bool AuthSocket::_HandleLogonChallenge()
{
///- Get the account details from the account table
// No SQL injection (escaped user name)
+
result = loginDatabase.PQuery("SELECT sha_pass_hash,id,locked,last_ip,gmlevel,v,s FROM account WHERE username = '%s'",_safelogin.c_str ());
if (result)
{
@@ -375,6 +433,7 @@ bool AuthSocket::_HandleLogonChallenge()
{
DEBUG_LOG("[AuthChallenge] Account '%s' is not locked to ip", _login.c_str());
}
+
if (!locked)
{
//set expired bans to inactive
@@ -393,16 +452,20 @@ bool AuthSocket::_HandleLogonChallenge()
pkt << (uint8) REALM_AUTH_ACCOUNT_FREEZED;
sLog.outBasic("[AuthChallenge] Temporarily banned account %s tries to login!",_login.c_str ());
}
+
delete banresult;
}
else
{
///- Get the password from the account table, upper it, and make the SRP6 calculation
std::string rI = (*result)[0].GetCppString();
+
///- Don't calculate (v, s) if there are already some in the database
std::string databaseV = (*result)[5].GetCppString();
std::string databaseS = (*result)[6].GetCppString();
+
sLog.outDebug("database authentication values: v='%s' s='%s'", databaseV.c_str(), databaseS.c_str());
+
// multiply with 2, bytes are stored as hexstring
if (databaseV.size() != s_BYTE_SIZE*2 || databaseS.size() != s_BYTE_SIZE*2)
_SetVSFields(rI);
@@ -411,14 +474,19 @@ bool AuthSocket::_HandleLogonChallenge()
s.SetHexStr(databaseS.c_str());
v.SetHexStr(databaseV.c_str());
}
+
b.SetRand(19 * 8);
BigNumber gmod = g.ModExp(b, N);
B = ((v * 3) + gmod) % N;
+
ASSERT(gmod.GetNumBytes() <= 32);
+
BigNumber unk3;
unk3.SetRand(16 * 8);
+
///- Fill the response packet with the result
pkt << uint8(REALM_AUTH_SUCCESS);
+
// B may be calculated < 32B so we force minimal length to 32B
pkt.append(B.AsByteArray(32), 32); // 32 bytes
pkt << uint8(1);
@@ -429,11 +497,13 @@ bool AuthSocket::_HandleLogonChallenge()
pkt.append(unk3.AsByteArray(16), 16);
uint8 securityFlags = 0;
pkt << uint8(securityFlags); // security flags (0x0...0x04)
+
if (securityFlags & REALM_AUTH_FAILURE) // PIN input
{
pkt << uint32(0);
pkt << uint64(0) << uint64(0); // 16 bytes hash?
}
+
if (securityFlags & REALM_AUTH_UNKNOWN1) // Matrix input
{
pkt << uint8(0);
@@ -442,15 +512,19 @@ bool AuthSocket::_HandleLogonChallenge()
pkt << uint8(0);
pkt << uint64(0);
}
+
if (securityFlags & REALM_AUTH_NO_MATCH) // Security token input
{
pkt << uint8(1);
}
+
uint8 secLevel = (*result)[4].GetUInt8();
_accountSecurityLevel = secLevel <= SEC_ADMINISTRATOR ? AccountTypes(secLevel) : SEC_ADMINISTRATOR;
+
_localizationName.resize(4);
for (int i = 0; i < 4; ++i)
_localizationName[i] = ch->country[4-i-1];
+
sLog.outBasic("[AuthChallenge] account %s is using '%c%c%c%c' locale (%u)", _login.c_str (), ch->country[3], ch->country[2], ch->country[1], ch->country[0], GetLocaleByName(_localizationName));
}
}
@@ -464,6 +538,7 @@ bool AuthSocket::_HandleLogonChallenge()
SendBuf((char const*)pkt.contents(), pkt.size());
return true;
}
+
/// Logon Proof command handler
bool AuthSocket::_HandleLogonProof()
{
@@ -473,6 +548,7 @@ bool AuthSocket::_HandleLogonProof()
return false;
sAuthLogonProof_C lp;
ibuf.Read((char *)&lp, sizeof(sAuthLogonProof_C));
+
///- Check if the client has one of the expected version numbers
bool valid_version = false;
int accepted_versions[] = EXPECTED_TRINITY_CLIENT_BUILD;
@@ -484,16 +560,19 @@ bool AuthSocket::_HandleLogonProof()
break;
}
}
+
/// <ul><li> If the client has no valid version
if (!valid_version)
{
///- Check if we have the apropriate patch on the disk
+
// 24 = len("./patches/65535enGB.mpq")+1
char tmp[24];
// No buffer overflow (fixed length of arguments)
sprintf(tmp, "./patches/%d%s.mpq", _build, _localizationName.c_str());
// This will be closed at the destruction of the AuthSocket (client disconnection)
FILE *pFile = fopen(tmp, "rb");
+
if (!pFile)
{
ByteBuffer pkt;
@@ -509,6 +588,7 @@ bool AuthSocket::_HandleLogonProof()
{
pPatch = pFile;
XFER_INIT xferh;
+
///- Get the MD5 hash of the patch file (get it from preloaded Patcher cache or calculate it)
if (PatchesCache.GetHash(tmp, (uint8*)&xferh.md5))
{
@@ -520,30 +600,38 @@ bool AuthSocket::_HandleLogonProof()
PatchesCache.LoadPatchMD5(tmp);
PatchesCache.GetHash(tmp, (uint8*)&xferh.md5);
}
+
///- Send a packet to the client with the file length and MD5 hash
uint8 data[2] = { AUTH_LOGON_PROOF, REALM_AUTH_UPDATE_CLIENT };
SendBuf((const char*)data, sizeof(data));
+
memcpy(&xferh, "0\x05Patch", 7);
xferh.cmd = XFER_INITIATE;
fseek(pPatch, 0, SEEK_END);
xferh.file_size = ftell(pPatch);
+
SendBuf((const char*)&xferh, sizeof(xferh));
return true;
}
}
/// </ul>
+
///- Continue the SRP6 calculation based on data received from the client
BigNumber A;
+
A.SetBinary(lp.A, 32);
+
// SRP safeguard: abort if A==0
if (A.isZero())
return false;
+
Sha1Hash sha;
sha.UpdateBigNumbers(&A, &B, NULL);
sha.Finalize();
BigNumber u;
u.SetBinary(sha.GetDigest(), 20);
BigNumber S = (A * (v.ModExp(u, N))).ModExp(b, N);
+
uint8 t[32];
uint8 t1[16];
uint8 vK[40];
@@ -571,7 +659,9 @@ bool AuthSocket::_HandleLogonProof()
vK[i * 2 + 1] = sha.GetDigest()[i];
}
K.SetBinary(vK, 40);
+
uint8 hash[20];
+
sha.Initialize();
sha.UpdateBigNumbers(&N, NULL);
sha.Finalize();
@@ -585,11 +675,13 @@ bool AuthSocket::_HandleLogonProof()
}
BigNumber t3;
t3.SetBinary(hash, 20);
+
sha.Initialize();
sha.UpdateData(_login);
sha.Finalize();
uint8 t4[SHA_DIGEST_LENGTH];
memcpy(t4, sha.GetDigest(), SHA_DIGEST_LENGTH);
+
sha.Initialize();
sha.UpdateBigNumbers(&t3, NULL);
sha.UpdateData(t4, SHA_DIGEST_LENGTH);
@@ -597,19 +689,23 @@ bool AuthSocket::_HandleLogonProof()
sha.Finalize();
BigNumber M;
M.SetBinary(sha.GetDigest(), 20);
+
///- Check if SRP6 results match (password is correct), else send an error
if (!memcmp(M.AsByteArray(), lp.M1, 20))
{
sLog.outBasic("User '%s' successfully authenticated", _login.c_str());
+
///- Update the sessionkey, last_ip, last login time and reset number of failed logins in the account table for this account
// No SQL injection (escaped user name) and IP address as received by socket
const char* K_hex = K.AsHexStr();
loginDatabase.PExecute("UPDATE account SET sessionkey = '%s', last_ip = '%s', last_login = NOW(), locale = '%u', failed_logins = 0 WHERE username = '%s'", K_hex, GetRemoteAddress().c_str(), GetLocaleByName(_localizationName), _safelogin.c_str());
OPENSSL_free((void*)K_hex);
+
///- Finish SRP6 and send the final result to the client
sha.Initialize();
sha.UpdateBigNumbers(&A, &M, &K, NULL);
sha.Finalize();
+
if (_build == 8606 || _build == 9947 || _build == 10146)//2.4.3 and 3.1.3 clients (10146 is Chinese build for 3.1.3)
{
sAuthLogonProof_S proof;
@@ -630,6 +726,7 @@ bool AuthSocket::_HandleLogonProof()
//proof.unk3 = 0x00;
SendBuf((char *)&proof, sizeof(proof));
}
+
///- Set _authed to true!
_authed = true;
}
@@ -638,19 +735,23 @@ bool AuthSocket::_HandleLogonProof()
char data[4]= { AUTH_LOGON_PROOF, REALM_AUTH_NO_MATCH, 3, 0};
SendBuf(data, sizeof(data));
sLog.outBasic("[AuthChallenge] account %s tried to login with wrong password!",_login.c_str ());
+
uint32 MaxWrongPassCount = sConfig.GetIntDefault("WrongPass.MaxCount", 0);
if (MaxWrongPassCount > 0)
{
//Increment number of failed logins by one and if it reaches the limit temporarily ban that account or IP
loginDatabase.PExecute("UPDATE account SET failed_logins = failed_logins + 1 WHERE username = '%s'",_safelogin.c_str());
+
if (QueryResult *loginfail = loginDatabase.PQuery("SELECT id, failed_logins FROM account WHERE username = '%s'", _safelogin.c_str()))
{
Field* fields = loginfail->Fetch();
uint32 failed_logins = fields[1].GetUInt32();
+
if (failed_logins >= MaxWrongPassCount)
{
uint32 WrongPassBanTime = sConfig.GetIntDefault("WrongPass.BanTime", 600);
bool WrongPassBanType = sConfig.GetBoolDefault("WrongPass.BanType", false);
+
if (WrongPassBanType)
{
uint32 acc_id = fields[0].GetUInt32();
@@ -675,32 +776,42 @@ bool AuthSocket::_HandleLogonProof()
}
return true;
}
+
/// Reconnect Challenge command handler
bool AuthSocket::_HandleReconnectChallenge()
{
DEBUG_LOG("Entering _HandleReconnectChallenge");
if (ibuf.GetLength() < sizeof(sAuthLogonChallenge_C))
return false;
+
///- Read the first 4 bytes (header) to get the length of the remaining of the packet
std::vector<uint8> buf;
buf.resize(4);
+
ibuf.Read((char *)&buf[0], 4);
+
EndianConvert(*((uint16*)(buf[0])));
uint16 remaining = ((sAuthLogonChallenge_C *)&buf[0])->size;
DEBUG_LOG("[ReconnectChallenge] got header, body is %#04x bytes", remaining);
+
if ((remaining < sizeof(sAuthLogonChallenge_C) - buf.size()) || (ibuf.GetLength() < remaining))
return false;
+
//No big fear of memory outage (size is int16, i.e. < 65536)
buf.resize(remaining + buf.size() + 1);
buf[buf.size() - 1] = 0;
sAuthLogonChallenge_C *ch = (sAuthLogonChallenge_C*)&buf[0];
+
///- Read the remaining of the packet
ibuf.Read((char *)&buf[4], remaining);
DEBUG_LOG("[ReconnectChallenge] got full packet, %#04x bytes", ch->size);
DEBUG_LOG("[ReconnectChallenge] name(%d): '%s'", ch->I_len, ch->I);
+
_login = (const char*)ch->I;
_safelogin = _login;
+
QueryResult *result = loginDatabase.PQuery ("SELECT sessionkey FROM account WHERE username = '%s'", _safelogin.c_str ());
+
// Stop if the account is not found
if (!result)
{
@@ -708,9 +819,11 @@ bool AuthSocket::_HandleReconnectChallenge()
SetCloseAndDelete();
return false;
}
+
Field* fields = result->Fetch ();
K.SetHexStr (fields[0].GetString ());
delete result;
+
///- Sending response
ByteBuffer pkt;
pkt << (uint8) AUTH_RECONNECT_CHALLENGE;
@@ -721,6 +834,7 @@ bool AuthSocket::_HandleReconnectChallenge()
SendBuf((char const*)pkt.contents(), pkt.size());
return true;
}
+
/// Reconnect Proof command handler
bool AuthSocket::_HandleReconnectProof()
{
@@ -732,13 +846,16 @@ bool AuthSocket::_HandleReconnectProof()
return false;
sAuthReconnectProof_C lp;
ibuf.Read((char *)&lp, sizeof(sAuthReconnectProof_C));
+
BigNumber t1;
t1.SetBinary(lp.R1, 16);
+
Sha1Hash sha;
sha.Initialize();
sha.UpdateData(_login);
sha.UpdateBigNumbers(&t1, &_reconnectProof, &K, NULL);
sha.Finalize();
+
if (!memcmp(sha.GetDigest(), lp.R2, SHA_DIGEST_LENGTH))
{
///- Sending response
@@ -747,8 +864,10 @@ bool AuthSocket::_HandleReconnectProof()
pkt << (uint8) 0x00;
pkt << (uint16) 0x00; // 2 bytes zeros
SendBuf((char const*)pkt.contents(), pkt.size());
+
///- Set _authed to true!
_authed = true;
+
return true;
}
else
@@ -758,15 +877,19 @@ bool AuthSocket::_HandleReconnectProof()
return false;
}
}
+
/// %Realm List command handler
bool AuthSocket::_HandleRealmList()
{
DEBUG_LOG("Entering _HandleRealmList");
if (ibuf.GetLength() < 5)
return false;
+
ibuf.Remove(5);
+
///- Get the user id (else close the connection)
// No SQL injection (escaped user name)
+
QueryResult *result = loginDatabase.PQuery("SELECT id,sha_pass_hash FROM account WHERE username = '%s'",_safelogin.c_str());
if (!result)
{
@@ -774,11 +897,14 @@ bool AuthSocket::_HandleRealmList()
SetCloseAndDelete();
return false;
}
+
uint32 id = (*result)[0].GetUInt32();
std::string rI = (*result)[1].GetCppString();
delete result;
+
///- Update realm list if need
m_realmList.UpdateIfNeed();
+
RealmList::RealmMap::const_iterator rlm;
RealmList built_realmList;
for (rlm = m_realmList.begin(); rlm != m_realmList.end(); ++rlm)
@@ -793,7 +919,9 @@ bool AuthSocket::_HandleRealmList()
if (rlm->second.gamebuild == 5875 || rlm->second.gamebuild == 6005)
built_realmList.AddRealm(rlm->second);
}
+
}
+
///- Circle through realms in the RealmList and construct the return packet (including # of user characters in each realm)
ByteBuffer pkt;
pkt << (uint32) 0;
@@ -805,6 +933,7 @@ bool AuthSocket::_HandleRealmList()
for (i = built_realmList.begin(); i != built_realmList.end(); ++i)
{
uint8 AmountOfCharacters;
+
// No SQL injection. id of realm is controlled by the database.
result = loginDatabase.PQuery("SELECT numchars FROM realmcharacters WHERE realmid = '%d' AND acctid='%u'",i->second.m_ID,id);
if (result)
@@ -815,7 +944,9 @@ bool AuthSocket::_HandleRealmList()
}
else
AmountOfCharacters = 0;
+
uint8 lock = (i->second.allowedSecurityLevel > _accountSecurityLevel) ? 1 : 0;
+
pkt << i->second.icon; // realm type
if (i->second.gamebuild == 9947 || i->second.gamebuild == 10146 || i->second.gamebuild == 8606)//only 2.4.3 and 3.1.3 cliens
pkt << lock; // if 1, then realm locked
@@ -830,6 +961,7 @@ bool AuthSocket::_HandleRealmList()
else
pkt << (uint8) 0x0; //1.12.1 and 1.12.2 clients
}
+
if (_build == 9947 || _build == 10146 || _build == 8606)//2.4.3 and 3.1.3 cliens
{
pkt << (uint8) 0x10;
@@ -838,13 +970,17 @@ bool AuthSocket::_HandleRealmList()
pkt << (uint8) 0x00;
pkt << (uint8) 0x02;
}
+
ByteBuffer hdr;
hdr << (uint8) REALM_LIST;
hdr << (uint16)pkt.size();
hdr.append(pkt);
+
SendBuf((char const*)hdr.contents(), hdr.size());
+
return true;
}
+
/// Resume patch transfer
bool AuthSocket::_HandleXferResume()
{
@@ -855,54 +991,69 @@ bool AuthSocket::_HandleXferResume()
sLog.outError("Error while resuming patch transfer (wrong packet)");
return false;
}
+
///- Launch a PatcherRunnable thread starting at given patch file offset
uint64 start;
ibuf.Remove(1);
ibuf.Read((char*)&start,sizeof(start));
fseek(pPatch, start, 0);
+
ACE_Based::Thread u(new PatcherRunnable(this));
return true;
}
+
/// Cancel patch transfer
bool AuthSocket::_HandleXferCancel()
{
DEBUG_LOG("Entering _HandleXferCancel");
+
///- Close and delete the socket
ibuf.Remove(1); //clear input buffer
+
SetCloseAndDelete();
+
return true;
}
+
/// Accept patch transfer
bool AuthSocket::_HandleXferAccept()
{
DEBUG_LOG("Entering _HandleXferAccept");
+
///- Check packet length and patch existence
if (!pPatch)
{
sLog.outError("Error while accepting patch transfer (wrong packet)");
return false;
}
+
///- Launch a PatcherRunnable thread, starting at the beginning of the patch file
ibuf.Remove(1); // clear input buffer
fseek(pPatch, 0, 0);
+
ACE_Based::Thread u(new PatcherRunnable(this));
return true;
}
+
/// Check if there is lag on the connection to the client
bool AuthSocket::IsLag()
{
return (TCP_BUFSIZE_READ-GetOutputLength() < 2 * ChunkSize);
}
+
PatcherRunnable::PatcherRunnable(class AuthSocket * as)
{
mySocket = as;
}
+
/// Send content of patch file to the client
void PatcherRunnable::run()
{
ACE_Guard<ACE_Thread_Mutex> g(mySocket->patcherLock);
+
XFER_DATA_STRUCT xfdata;
xfdata.opcode = XFER_DATA;
+
while(!feof(mySocket->pPatch) && mySocket->Ready())
{
///- Wait until output buffer is reasonably empty
@@ -915,6 +1066,7 @@ void PatcherRunnable::run()
mySocket->SendBuf((const char*)&xfdata, xfdata.data_size + (sizeof(XFER_DATA_STRUCT) - ChunkSize));
}
}
+
/// Preload MD5 hashes of existing patch files on server
#ifndef _WIN32
#include <dirent.h>
@@ -948,9 +1100,11 @@ void Patcher::LoadPatchesInfo()
break;
}
}
+
if (dirp)
closedir(dirp);
}
+
#else
void Patcher::LoadPatchesInfo()
{
@@ -958,6 +1112,7 @@ void Patcher::LoadPatchesInfo()
HANDLE hFil=FindFirstFile("./patches/*.mpq", &fil);
if (hFil == INVALID_HANDLE_VALUE)
return; // no patches were found
+
do
{
LoadPatchMD5(fil.cFileName);
@@ -965,6 +1120,7 @@ void Patcher::LoadPatchesInfo()
while(FindNextFile(hFil, &fil));
}
#endif
+
/// Calculate and store MD5 hash for a given patch file
void Patcher::LoadPatchMD5(char * szFileName)
{
@@ -978,10 +1134,12 @@ void Patcher::LoadPatchMD5(char * szFileName)
sLog.outError("Error loading patch %s\n", path.c_str());
return;
}
+
///- Calculate the MD5 hash
MD5_CTX ctx;
MD5_Init(&ctx);
uint8* buf = new uint8[512*1024];
+
while (!feof(pPatch))
{
size_t read = fread(buf, 1, 512*1024, pPatch);
@@ -989,10 +1147,12 @@ void Patcher::LoadPatchMD5(char * szFileName)
}
delete [] buf;
fclose(pPatch);
+
///- Store the result in the internal patch hash map
_patches[path] = new PATCH_INFO;
MD5_Final((uint8 *)&_patches[path]->md5, &ctx);
}
+
/// Get cached MD5 hash for a given patch file
bool Patcher::GetHash(char * pat, uint8 mymd5[16])
{
@@ -1002,13 +1162,16 @@ bool Patcher::GetHash(char * pat, uint8 mymd5[16])
memcpy(mymd5, i->second->md5, 16);
return true;
}
+
return false;
}
+
/// Launch the patch hashing mechanism on object creation
Patcher::Patcher()
{
LoadPatchesInfo();
}
+
/// Empty and delete the patch map on termination
Patcher::~Patcher()
{
diff --git a/src/trinityrealm/AuthSocket.h b/src/trinityrealm/AuthSocket.h
index 7142c32dd5e..ecfdd159000 100644
--- a/src/trinityrealm/AuthSocket.h
+++ b/src/trinityrealm/AuthSocket.h
@@ -17,11 +17,14 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup realmd
/// @{
/// \file
+
#ifndef _AUTHSOCKET_H
#define _AUTHSOCKET_H
+
#include "Common.h"
#include "Auth/BigNumber.h"
#include "sockets/TcpSocket.h"
@@ -30,36 +33,48 @@
#include "sockets/Utility.h"
#include "sockets/Parse.h"
#include "sockets/Socket.h"
+
/// Handle login commands
class AuthSocket: public TcpSocket
{
public:
const static int s_BYTE_SIZE = 32;
+
AuthSocket(ISocketHandler& h);
~AuthSocket();
+
void OnAccept();
void OnRead();
+
bool _HandleLogonChallenge();
bool _HandleLogonProof();
bool _HandleReconnectChallenge();
bool _HandleReconnectProof();
bool _HandleRealmList();
//data transfer handle for patch
+
bool _HandleXferResume();
bool _HandleXferCancel();
bool _HandleXferAccept();
+
void _SetVSFields(const std::string& rI);
+
FILE *pPatch;
ACE_Thread_Mutex patcherLock;
bool IsLag();
+
private:
+
BigNumber N, s, g, v;
BigNumber b, B;
BigNumber K;
BigNumber _reconnectProof;
+
bool _authed;
+
std::string _login;
std::string _safelogin;
+
// Since GetLocaleByName() is _NOT_ bijective, we have to store the locale as a string. Otherwise we can't differ
// between enUS and enGB, which is important for the patch system
std::string _localizationName;
diff --git a/src/trinityrealm/Main.cpp b/src/trinityrealm/Main.cpp
index 061d8a9e26f..7e53ad15094 100644
--- a/src/trinityrealm/Main.cpp
+++ b/src/trinityrealm/Main.cpp
@@ -17,12 +17,15 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup realmd Realm Daemon
/// @{
/// \file
+
#include "Common.h"
#include "Database/DatabaseEnv.h"
#include "RealmList.h"
+
#include "Config/ConfigEnv.h"
#include "Log.h"
#include "sockets/ListenSocket.h"
@@ -31,14 +34,17 @@
#include "Util.h"
#include <openssl/opensslv.h>
#include <openssl/crypto.h>
+
// Format is YYYYMMDDRR where RR is the change in the conf file
// for that day.
#ifndef _REALMDCONFVERSION
# define _REALMDCONFVERSION 2009081701
#endif
+
#ifndef _TRINITY_REALM_CONFIG
# define _TRINITY_REALM_CONFIG "TrinityRealm.conf"
#endif //_TRINITY_REALM_CONFIG
+
#ifdef WIN32
#include "ServiceWin32.h"
char serviceName[] = "TrinityRealm";
@@ -52,12 +58,16 @@ char serviceDescription[] = "Massive Network Game Object Server";
*/
int m_ServiceStatus = -1;
#endif
+
bool StartDB();
void UnhookSignals();
void HookSignals();
+
bool stopEvent = false; ///< Setting it to true stops the server
RealmList m_realmList; ///< Holds the list of realms for this server
+
DatabaseType loginDatabase; ///< Accessor to the realm server database
+
/// Print out the usage string for this program on the console.
void usage(const char *prog)
{
@@ -71,6 +81,7 @@ void usage(const char *prog)
#endif
,prog);
}
+
/// Launch the realm server
extern int main(int argc, char **argv)
{
@@ -91,6 +102,7 @@ extern int main(int argc, char **argv)
else
cfg_file = argv[c];
}
+
#ifdef WIN32
////////////
//Services//
@@ -130,15 +142,18 @@ extern int main(int argc, char **argv)
#endif
++c;
}
+
if (!sConfig.SetSource(cfg_file))
{
sLog.outError("Could not find configuration file %s.", cfg_file);
return 1;
}
sLog.Initialize();
+
sLog.outString("%s (realm-daemon)", _FULLVERSION);
sLog.outString("<Ctrl-C> to stop.\n");
sLog.outString("Using configuration file %s.", cfg_file);
+
///- Check the version of the configuration file
uint32 confVersion = sConfig.GetIntDefault("ConfVersion", 0);
if (confVersion < _REALMDCONFVERSION)
@@ -149,8 +164,10 @@ extern int main(int argc, char **argv)
sLog.outError(" strange behavior.");
sLog.outError("*****************************************************************************");
clock_t pause = 3000 + clock();
+
while (pause > clock()) {}
}
+
sLog.outDetail("%s (Library: %s)", OPENSSL_VERSION_TEXT, SSLeay_version(SSLEAY_VERSION));
if (SSLeay() < 0x009080bfL)
{
@@ -160,6 +177,7 @@ extern int main(int argc, char **argv)
while (pause > clock()) {}
return 1;
}
+
/// realmd PID file creation
std::string pidfile = sConfig.GetStringDefault("PidFile", "");
if (!pidfile.empty())
@@ -170,11 +188,14 @@ extern int main(int argc, char **argv)
sLog.outError("Cannot create PID file %s.\n", pidfile.c_str());
return 1;
}
+
sLog.outString("Daemon PID: %u\n", pid);
}
+
///- Initialize the database connection
if (!StartDB())
return 1;
+
///- Initialize the log database
if (sConfig.GetBoolDefault("EnableLogDB", false))
{
@@ -190,6 +211,7 @@ extern int main(int argc, char **argv)
sLog.SetLogDB(false);
sLog.SetRealmID(0);
}
+
///- Get the list of realms for the server
m_realmList.Initialize(sConfig.GetIntDefault("RealmsStateUpdateDelay", 20));
if (m_realmList.size() == 0)
@@ -197,9 +219,11 @@ extern int main(int argc, char **argv)
sLog.outError("No valid realms specified.");
return 1;
}
+
///- Launch the listening network socket
port_t rmport = sConfig.GetIntDefault("RealmServerPort", DEFAULT_REALMSERVER_PORT);
std::string bind_ip = sConfig.GetStringDefault("BindIP", "0.0.0.0");
+
SocketHandler h;
ListenSocket<AuthSocket> authListenSocket(h);
if (authListenSocket.Bind(bind_ip.c_str(),rmport))
@@ -207,21 +231,27 @@ extern int main(int argc, char **argv)
sLog.outError("Trinity realm can not bind to %s:%d",bind_ip.c_str(), rmport);
return 1;
}
+
h.Add(&authListenSocket);
+
///- Catch termination signals
HookSignals();
+
///- Handle affinity for multiple processors and process priority on Windows
#ifdef WIN32
{
HANDLE hProcess = GetCurrentProcess();
+
uint32 Aff = sConfig.GetIntDefault("UseProcessors", 0);
if (Aff > 0)
{
ULONG_PTR appAff;
ULONG_PTR sysAff;
+
if (GetProcessAffinityMask(hProcess,&appAff,&sysAff))
{
ULONG_PTR curAff = Aff & appAff; // remove non accessible processors
+
if (!curAff)
{
sLog.outError("Processors marked in UseProcessors bitmask (hex) %x not accessible for realmd. Accessible processors bitmask (hex): %x",Aff,appAff);
@@ -236,7 +266,9 @@ extern int main(int argc, char **argv)
}
sLog.outString();
}
+
bool Prio = sConfig.GetBoolDefault("ProcessPriority", false);
+
if (Prio)
{
if (SetPriorityClass(hProcess,HIGH_PRIORITY_CLASS))
@@ -247,9 +279,11 @@ extern int main(int argc, char **argv)
}
}
#endif
+
// maximum counter for next ping
uint32 numLoops = (sConfig.GetIntDefault("MaxPingTime", 30) * (MINUTE * 1000000 / 100000));
uint32 loopCounter = 0;
+
// possibly enable db logging; avoid massive startup spam by doing it here.
if (sLog.GetLogDBLater())
{
@@ -263,10 +297,13 @@ extern int main(int argc, char **argv)
sLog.SetLogDB(false);
sLog.SetLogDBLater(false);
}
+
///- Wait for termination signal
while (!stopEvent)
{
+
h.Select(0, 100000);
+
if ((++loopCounter) == numLoops)
{
loopCounter = 0;
@@ -278,14 +315,18 @@ extern int main(int argc, char **argv)
while (m_ServiceStatus == 2) Sleep(1000);
#endif
}
+
///- Wait for the delay thread to exit
loginDatabase.ThreadEnd();
loginDatabase.HaltDelayThread();
+
///- Remove signal handling before leaving
UnhookSignals();
+
sLog.outString("Halting process...");
return 0;
}
+
/// Handle termination signals
/** Put the global variable stopEvent to 'true' if a termination signal is caught **/
void OnSignal(int s)
@@ -303,8 +344,10 @@ void OnSignal(int s)
break;
#endif
}
+
signal(s, OnSignal);
}
+
/// Initialize connection to the database
bool StartDB()
{
@@ -314,14 +357,17 @@ bool StartDB()
sLog.outError("Database not specified");
return false;
}
+
if (!loginDatabase.Initialize(dbstring.c_str()))
{
sLog.outError("Cannot connect to database");
return false;
}
loginDatabase.ThreadStart();
+
return true;
}
+
/// Define hook 'OnSignal' for all termination signals
void HookSignals()
{
@@ -332,6 +378,7 @@ void HookSignals()
signal(SIGBREAK, OnSignal);
#endif
}
+
/// Unhook the signals before leaving
void UnhookSignals()
{
@@ -342,4 +389,5 @@ void UnhookSignals()
signal(SIGBREAK, 0);
#endif
}
+
/// @}
diff --git a/src/trinityrealm/RealmList.cpp b/src/trinityrealm/RealmList.cpp
index c4b78bfe482..be8a7738ef7 100644
--- a/src/trinityrealm/RealmList.cpp
+++ b/src/trinityrealm/RealmList.cpp
@@ -17,29 +17,38 @@
* 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 realmd
*/
+
#include "Common.h"
#include "RealmList.h"
#include "Policies/SingletonImp.h"
#include "Database/DatabaseEnv.h"
+
INSTANTIATE_SINGLETON_1(RealmList);
+
extern DatabaseType loginDatabase;
+
RealmList::RealmList() : m_UpdateInterval(0), m_NextUpdateTime(time(NULL))
{
}
+
/// Load the realm list from the database
void RealmList::Initialize(uint32 updateInterval)
{
m_UpdateInterval = updateInterval;
+
///- Get the content of the realmlist table in the database
UpdateRealms(true);
}
+
void RealmList::UpdateRealm(uint32 ID, const std::string& name, const std::string& address, uint32 port, uint8 icon, uint8 color, uint8 timezone, AccountTypes allowedSecurityLevel, float popu, uint32 build)
{
///- Create new if not exist or update existed
Realm& realm = m_realms[name];
+
realm.m_ID = ID;
realm.name = name;
realm.icon = icon;
@@ -47,34 +56,44 @@ void RealmList::UpdateRealm(uint32 ID, const std::string& name, const std::strin
realm.timezone = timezone;
realm.allowedSecurityLevel = allowedSecurityLevel;
realm.populationLevel = popu;
+
///- Append port to IP address.
std::ostringstream ss;
ss << address << ":" << port;
realm.address = ss.str();
realm.gamebuild = build;
}
+
void RealmList::UpdateIfNeed()
{
// maybe disabled or updated recently
if (!m_UpdateInterval || m_NextUpdateTime > time(NULL))
return;
+
m_NextUpdateTime = time(NULL) + m_UpdateInterval;
+
// Clears Realm list
m_realms.clear();
+
// Get the content of the realmlist table in the database
UpdateRealms(false);
}
+
void RealmList::UpdateRealms(bool init)
{
sLog.outDetail("Updating Realm List...");
+
QueryResult *result = loginDatabase.Query("SELECT id, name, address, port, icon, color, timezone, allowedSecurityLevel, population, gamebuild FROM realmlist WHERE color <> 3 ORDER BY name");
+
///- Circle through results and add them to the realm map
if (result)
{
do
{
Field *fields = result->Fetch();
+
uint8 allowedSecurityLevel = fields[7].GetUInt8();
+
UpdateRealm(fields[0].GetUInt32(), fields[1].GetCppString(),fields[2].GetCppString(),fields[3].GetUInt32(),fields[4].GetUInt8(), fields[5].GetUInt8(), fields[6].GetUInt8(), (allowedSecurityLevel <= SEC_ADMINISTRATOR ? AccountTypes(allowedSecurityLevel) : SEC_ADMINISTRATOR), fields[8].GetFloat(), fields[9].GetUInt32());
if (init)
sLog.outString("Added realm \"%s\".", fields[1].GetString());
diff --git a/src/trinityrealm/RealmList.h b/src/trinityrealm/RealmList.h
index 9939bb39921..25f0c01da14 100644
--- a/src/trinityrealm/RealmList.h
+++ b/src/trinityrealm/RealmList.h
@@ -17,12 +17,16 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
/// \addtogroup realmd
/// @{
/// \file
+
#ifndef _REALMLIST_H
#define _REALMLIST_H
+
#include "Common.h"
+
/// Storage object for a realm
struct Realm
{
@@ -36,16 +40,22 @@ struct Realm
float populationLevel;
uint32 gamebuild;
};
+
/// Storage object for the list of realms on the server
class RealmList
{
public:
typedef std::map<std::string, Realm> RealmMap;
+
RealmList();
~RealmList() {}
+
void Initialize(uint32 updateInterval);
+
void UpdateIfNeed();
+
void AddRealm(Realm NewRealm) {m_realms[NewRealm.name] = NewRealm;}
+
RealmMap::const_iterator begin() const { return m_realms.begin(); }
RealmMap::const_iterator end() const { return m_realms.end(); }
uint32 size() const { return m_realms.size(); }
diff --git a/src/trinityrealm/TrinityRealm.rc b/src/trinityrealm/TrinityRealm.rc
index badcc45ebe5..dfc548305fd 100644
--- a/src/trinityrealm/TrinityRealm.rc
+++ b/src/trinityrealm/TrinityRealm.rc
@@ -15,34 +15,43 @@
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
+
#include "resource.h"
#include "../shared/revision.h"
+
#define APSTUDIO_READONLY_SYMBOLS
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 2 resource.
//
#include "windows.h" //"afxres.h"
+
/////////////////////////////////////////////////////////////////////////////
#undef APSTUDIO_READONLY_SYMBOLS
+
/////////////////////////////////////////////////////////////////////////////
//
// Icon
//
+
// Icon with lowest ID value placed first to ensure application icon
// remains consistent on all systems.
IDI_APPICON ICON "TrinityRealm.ico"
+
/////////////////////////////////////////////////////////////////////////////
// Neutre (Par défaut système) resources
+
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_NEUSD)
#ifdef _WIN32
LANGUAGE LANG_NEUTRAL, SUBLANG_SYS_DEFAULT
#pragma code_page(1252)
#endif //_WIN32
+
/////////////////////////////////////////////////////////////////////////////
//
// Version
//
+
VS_VERSION_INFO VERSIONINFO
FILEVERSION FILEVER
PRODUCTVERSION PRODUCTVER